Listing

AskTom Home
ViewForPrinting
Home
Recent Archives Advanced Search FAQ by Category Most Popular Hot Articles Your Questions [ Listing ]
Subject | Article | Last Updated (East coast time)
Return Multiple Records in a Stored Procedure | article only | Jan 01, 2002 11:31 am
Catching a SQL statment. | article only | Jan 02, 2002 07:21 am
Influence of Order by in the Ora Error "ORA-1555" | article only | Jan 02, 2002 07:23 am
Difficult to remember all the tables | article only | Jan 02, 2002 12:41 pm
PL/SQL function faster than correlated subquery? | article only | Jan 02, 2002 02:57 pm
how to determine how long a snapshot takes to refresh | article only | Jan 02, 2002 04:52 pm
Updates not happening | article only | Jan 02, 2002 08:09 pm
Security Related | article only | Jan 02, 2002 08:10 pm
Number of characters in SID | article only | Jan 02, 2002 08:12 pm
Tunning | article only | Jan 02, 2002 08:13 pm
sql loader error | article only | Jan 03, 2002 09:01 am
variable in FROM clause inside pl/sql | article only | Jan 03, 2002 10:01 am
SNP processes busy | article only | Jan 03, 2002 03:40 pm
Bfiles | article only | Jan 04, 2002 08:11 am
Data Dicitionary | article only | Jan 04, 2002 09:34 am
Is there any way to have a common tnsnames.ora file for multiple oracle home | article only | Jan 04, 2002 08:15 pm
Outer Join w/Subquery "Table" | article only | Jan 05, 2002 11:18 am
list of data types with which we can store data in the database | article only | Jan 05, 2002 08:01 pm
How to pass a CLOB as an IN OUT parameter to external routine | article only | Jan 06, 2002 01:27 pm
SQLJ - retreiving rowid- | article only | Jan 07, 2002 09:33 am
How to show a cursor is not closed? | article only | Jan 07, 2002 12:30 pm
row(s) 1 - 21 of 21
January 01, 2002 Kathy -- Thanks for the question regarding "Return Multiple Records in a Stored Procedure", version Oracle 8i
You Asked
I need to return the results of a query (multiple records) using a stored
procedure.  I know I can do it using a cursor and walking through each record
one by one.  It seems like there would be an easier way to return a "record set"
using a stored procedure.  I've tried "Select into" statement can only return
one record.
Create Procedure GetChildren
(
      lngParentID IN tblTree.ParentID%type,
      p_ParentID  OUT tblTree.ID%type,
      p_ID  OUT tblTree.ID%type,
      p_Description  OUT  tblTree.Description%type,
      p_NodeType  OUT  tblTree.NodeType%type
)
IS
BEGIN
 SELECT ID, ParentID, Description, NodeType
 INTO p_ParentID, p_ID, p_Description, p_NodeType
 FROM tblTree
 WHERE ParentID=lngParentID;
 IF SQL%NOTFOUND THEN
  RAISE_APPLICATION_ERROR(-20001, 'No records found');
 END IF;
END;
and we said...
see
http://osi.oracle.com/~tkyte/ResultSets/index.html
for the complete solution with examples in many languages.
Review & Followup
Rating: 5
Result Set December 31, 2001
Reviewer: Kathy Overcash from Waldorf, Maryland USA
Thanks a million.  That's exactly what I needed.

Rating: 3
Parag January 01, 2002
Reviewer: Parag from India
Glad to read ur Ans. Is it possible to return Multiple Cursor's from Stored
Procedure/Function.

Ex u have shonw here is returning One Ref-Curosr ( Which can hold Multiple Rows
& Columns ) , Instead of returning One Ref-Cursors , We wanted to return
Multiple Ref-Cursers.

Secondly , How to Eexcute the same at PL-SQL Level. Beca u have shown same at
SQL PLUS Level.

Regards

Followup:
You can return as many as you like, you can use IN OUT parameters of a ref
cursor type and return as many (or as few) as you like in a single call.

I didn't show a PLSQL example cause I thought that was sort of self evident?
Its a cursor, after you open it, just fetch from it if you want to process it in
plsql.  Its not any different from any other cursor at that point.

January 02, 2002 Jacques -- Thanks for the question regarding "Catching a SQL statment.", version 8.1.7
You Asked
Hi Tom

I know I'm supposed to use Oracle auditing from this, but how would you go by
creating an on update trigger that will catch the statement that was used to
update the table?

Thanks,

Jacques
and we said...
Well, its not 100% possible until 9i.  In 9i, its somewhat trivial:

ops$tkyte@ORA9I.WORLD> create table t ( x int );

Table created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> create or replace trigger t_trigger
  2  before update on t
  3  declare
  4          l_sql_text ora_name_list_t;
  5          l_n        number;
  6  begin
  7          l_n := ora_sql_txt(l_sql_text) ;
  8          for i in 1 .. l_n
  9          loop
 10             dbms_output.put_line( 'trigger output: ' || l_sql_text(i) );
 11          end loop;
 12  end;
 13  /

Trigger created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> insert into t select rownum from all_users;

66 rows created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> update t
  2     set x = -x
  3   where x > 0;
trigger output: update t
   set x = -x
 where x > 0

66 rows updated.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> update t
  2     set x = -x
  3   where x = -4;
trigger output: update t
   set x = -x
 where x = -4

1 row updated.

ops$tkyte@ORA9I.WORLD>

In 8i, you would have to query v$open_cursor or some related view and grab the
sql_text, the only problem is, there could be hundreds of UPDATES in there for
your session and finding THE update that is causing the trigger to fire this
time is problematic.
Review & Followup
Rating: 2
? December 21, 2001
Reviewer: A reader
ora_name_list_t;

is this a user written functions or out of box oracle function.

TQ

Followup:
Its a built in type with 8i -- its a datatype, not a function.  The function is
ora_sql_txt

Rating: 3
how about on 7.x January 02, 2002
Reviewer: Mohammed Ahmed
Is it possible on versions below 8?

Followup:
This paragraph:

In 8i, you would have to query v$open_cursor or some related view and grab the
sql_text, the only problem is, there could be hundreds of UPDATES in there for
your session and finding THE update that is causing the trigger to fire this
time is problematic.

applies to 8i and below.  I doubt you can accurately get the firing sql
statement however.

Rating: 5
what is the difference January 10, 2002
Reviewer: Kulguru
Tom

thanks for allowing us to download the 9i presentation.

Further,

What is the difference between the sql_text we get from
v$open_cursor and V$sql_text, v$sqlarea, v$sql .

When we can grap the sqltext from v$sqltext why are you recommending that we
grab it from v$open_cursor.

-----
Further, what is the difference between V$sql_text, v$sqlarea, v$sql .

is v$sql_text , summarized data from v$sqlarea.

is

January 02, 2002 Dinesh -- Thanks for the question regarding "Influence of Order by in the Ora Error "ORA-1555"", version 7.3
You Asked
Dear Tom,
     In reference to the solution in the URL:
http://asktom.oracle.com/pls/ask/f?p=4950:8:348993::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:416790780998,%7Border%7D%20and%20%7Bby%7D
(1) Taking the scenario of the query ( from the above URL link )

ops$tkyte@ORA8I.WORLD> select *
  2  from ( select ename, count(*) from emp group by ename )
  3  where rownum < 500;

  Does the "group by" ( or for that matter the "order by/group by" on any query
) on this query have any influence on "Snapshot too old" error during the time
the query is under processing and an update is issued against the table?

Thanks,
dinesh ...
and we said...
Snapshot too old is related to the duration of the query -- not the complexity
per say.  It is a function of how long it takes to execute and how fast your
rollback is reused.  If you reuse your rollback faster then you execute your
queries -- you run the chance of ora-1555.
Review & Followup
Rating: 5
Thanks ... December 21, 2001
Reviewer: Dinesh Sivasankaran from St. Petersburg, FL USA
Thanks very much for the prompt and clear response for my question. I really
appreciate it.

dinesh ...

Rating: 5
why would "select ......" will use rollback? January 02, 2002
Reviewer: Mohammed Ahmed
The sampled query uses "select ....".
I wonder why there is a question of rollback.

Followup:
It is a fundemental concept in Oracle -- something that makes us very different
from other RDBMS's.  It is what gives us non-blocking reads and consistent (eg:
CORRECT) answers to queries.  It is perhaps one of the most important concepts
to understand with regards to Oracle.  See
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c23cnsis.htm#2570
for a discussion of this topic.  Also, I go into this topic heavily in my book
as well.

January 02, 2002 Rajesh -- Thanks for the question regarding "Difficult to remember all the tables", version ORacle 8
You Asked
Hi Tom!
For tunning and administration of Oracle databases, one should
remember all the dba tables and all sys tables. Its very
difficult to remember all those. Is there any way  to remember
those table?
and we said...
Time, experience, the server reference manual.

The Server reference manual has a list of all of the data
dictionary objects, all of the ALL_*, USER_*, DBA_*, and V$
views are documented and each column and its meaning is listed.

See
http://technet.oracle.com/doc/server.815/a67790/toc.htm
for the entire listing.

Also, a tool such as Oracle Enterprise Manager (OEM) removes
much of the need for you to become intimate with the data
dictionary as it writes all of the queries for you.
Review & Followup
Rating: 5
tunning SQL January 02, 2002
Reviewer: walter from Brazil -S?o Paulo
I would like know how to make tunning the SQL?
Which are the criteria to tune a SQL query?
For example which table I should place first the lager or the smaller. how I
identify which index in which table to use, and so on.

Followup:
You should use the cost based optimizer, have the tables analyzed.

Then the order of tables and other such stuff is quite simply not relevant.

January 02, 2002 J -- Thanks for the question regarding "PL/SQL function faster than correlated subquery?", version 8.1.7
You Asked
Tom,
  I searched for "correlated function" and found the document
http://asktom.oracle.com/pls/ask/f?p=4950:8:366073::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:913197255649,%7Bcorrelated%7D%20and%20%7Bfunction%7D
in which you
say that generally native SQL will be faster than code.

  I agree with your statement, though I found a different recommendation in 8i
Data Warehousing Guide, page 18-49 (Tuning Parallel Execution --> Miscellaneous
Tuning Tips --> Rewriting SQL Statements).  It says, "Use a PL/SQL function in
the WHERE clause of the main query instead of a correlated subquery."

  In general what are the potential benefits (if any) in following the document
recommendation?  We are running 8.1.7 on Solaris for OLTP with heavy reporting.

Thanks and Happy New Year!
and we said...
The entire quote is:

<quote>
By making the following changes you can increase the optimizer's ability to
generate parallel plans:

    * Convert subqueries, especially correlated subqueries, into joins. Oracle
can parallelize joins more efficiently than subqueries. This also applies to
updates.

    * Use a PL/SQL function in the WHERE clause of the main query instead of a
correlated subquery.

    * Rewrite queries with distinct aggregates as nested queries. For example,
rewrite.......

</quote>

here is is saying "a correlated subquery might prevent us from developing a
parallel plan, if you use a plsql function, we can do it in parallel".

You would have to be having a query that is not executing in parallel due to a
correlated subquery -- that you NEED to have execute in parallel and the speed
up you get by going parallel offsets the decrease you would get via using a
correlated subquery.

Parallel Query and OLTP systems do not mix at all (parallel query is very "non
scalable", it is designed to consume an entire machine, OLTP is all about being
"fair" and getting as much done by as many users as fast as possible).  Hence, I
do not believe this is of interest to you.

I personally would thing to convert to a join before converting a correlated
subquery to a PLSQL function.
January 02, 2002 Babu -- Thanks for the question regarding "how to determine how long a snapshot takes to refresh", version 8172
You Asked
is there any way to tell the time taken by a snapshot to refresh.

one way is to manually do a dbms_mview.refresh and monitor it... I am looking
for a more automated solution... can i retreive that info from some data
dictionary view.

I have posted this question in metalink before and got the answer back as "no"

thanks a lot

Babu
and we said...
There is a job created for each snapshot.

In the user_jobs table, there is a column TOTAL_TIME.  This is the total
cumulative execution time of the snapshot.

If you look at this column and "write it down", then come back after the
snapshot refreshed and subtract -- you'll have what you want.

Alternatively, you can create the snapshot as an on demand refresh only, create
a job that measures the elapsed time and schedule that as the refresh, something
like:

create procedure my_refresh
as
   l_start number default dbms_utility.get_time;
begin
   dbms_mview.refresh( 'MY_SNAPSHOT' );
   l_start := dbms_utility.get_time - l_start;
   insert into log values ( 'MY_SNAPSHOT', sysdate, l_start );
   commit;
end;
/

then schedule that using dbms_job (which is what the snapshot does under the
covers) and you'll have a log of every refresh, when it finished and how long in
hsecs it took.
Review & Followup
Rating: 4
very good January 02, 2002
Reviewer: Babu Nagarajan from Columbus, IN USA
the answer was exactly what i was looking for. the best part was it was almost
immediate... thanks tom.

January 02, 2002 Krishna -- Thanks for the question regarding "Updates not happening", version 8.1.5
You Asked
Tom,
In one of the ASP Apps updates are not happening and this is not consistent. So
I can not blame the developer. All she is doing is
Opening the connection, defining a qerystr which has the Update
ststement and executing the querystr. This looks like this
querystr = "Update t1 "
querystr = querystr & " set a= '" & var1 & "',"
querystr = querystr & " and b='" & var2 & "'"
querystr = querystr & " Where c='" & var3 & "' and "
querystr = querystr & " d='" & var4 & "'"
set rs = dbconn.execute(querystr)
response.redirec("confirm.asp")
var3 and var4 are from prev page's dropdown which are pulled from the database.
After updating she is redirecting to a confirmation page.
In some cases this above update is not happening. It is not erroring out either.
End users are getting the confirmation page but the data is not getting updated
in the Database. I selected sql_text from sql_area for one of such cases and
executed it at the sqlprompt which ran ok. If the sql_text is in v$sql_area ,
that means it is not a connection problem , it did not error out - that means
the sql was  ok. Then where is the problem. I am lost.
Please help
Thanks
Krishna Roy
and we said...
Arg -- teach this developer about the existence of BIND VARIABLES.  I hate code
like this, makes me sooooo mad.  She is killing your performance and limiting
your scalability by doing the update this way.

I sure don't see a "commit" anywhere.

This developer will have to add some debug code to their routine.  They will
have to use whatever ODBC calls to verify that a row was updated.  They should
be committing.  THEY MUST USE bind variables.

I personally would use a stored procedure for ALL database access.  Think about
how simple your life would be if this code just called a procedure -- now you
could update that procedure to have code like:

 begin
    update ....;

    -- added for debugging purposes!!
    if ( sql%rowcount = 0 )
    then
         rollback;
         insert into log_table ( some information );
         commit;
         raise_application_error( -20001, 'Update failed ' || <relevant info );
    end if;
  end;

or whatever you needed.  That is what I would recommend (and force them to use
bind variables.  The fact you found this statement in the shared pool should
have been a HUGE red flag to you or the DBA -- a HUGE red flag)
Review & Followup
Rating: 4
How January 02, 2002
Reviewer: A reader
Thanks for your valuable response.

But could you show me an example how to use bind
variables in this specific case(ASP execute update
stmt above)?

Thanks

Followup:
I'm not a VB programmer at all but here is a small example I dug up showing the
gist of what you want to do.  If you read about the database API available and
become familar with all of the functions you have -- it should be pretty
straight forward:

Private Sub Command1_Click()
On Error Resume Next

Dim goOraSession As Object
Dim goOraDatabase As Object

Set goOraSession = CreateObject("OracleInProcServer.XOraSession")
Set goOraDatabase = goOraSession.DbOpenDatabase("T:ORLNT-5:ORCL", "scott/tiger",
0&)

'Parameter Types
Const ORAPARM_INPUT = 1
Const ORAPARM_OUTPUT = 2
Const ORAPARM_BOTH = 3

Const ORATYPE_VARCHAR2 = 1
Const ORATYPE_NUMBER = 2

Dim sParmName As String
Dim iDataType As Integer
Dim iDataSize As Integer
Dim sSQL As String
Dim iRC As Integer

sParmName = "Parm1"
iDataType = ORATYPE_VARCHAR2
iDataSize = 100

' The use of the string initial value here is crucial
' if we are binding to a varchar
goOraDatabase.Parameters.Add sParmName, "Some Value", ORAPARM_OUTPUT

' Set the parameter properties
goOraDatabase.Parameters(sParmName).ServerType = iDataType
goOraDatabase.Parameters(sParmName).MinimumSize = iDataSize

' Define the select statement
sSQL = "BEGIN select loc into :" & sParmName & " from dept where deptno = 50;
End;"

iRC = goOraDatabase.ExecuteSQL(sSQL)

If IsNull(goOraDatabase.Parameters(sParmName).Value) Then
  MsgBox "NULL VALUE"
Else
  MsgBox goOraDatabase.Parameters(sParmName).Value
End If

goOraDatabase.Parameters.Remove sParmName

End Sub

January 02, 2002 d -- Thanks for the question regarding "Security Related", version 7.3& 8i
You Asked
Hi Tom,

I am a regular reader of this site.

I have 4 to 5 users in one instance. I would like to give previledge to execute
the following statment to all users.

alter tablespace tablespace_name coalesce

How can I acheive this. If I give alter tablespace privs to all users, then any
one can add datafile and change the tablespace parameters. But I would like to
give only coalesce option, I don't want to give any other previledges. Please
help me.

Regards
Shan
and we said...
This is what stored procedures are GREAT for.  They run with the privs of the
definer, so you can do things like this.

Create a procedure:

create or replace procedure coalesce_tablespace( p_tablespace in varchar2 )
is
    cursor_name     pls_integer default dbms_sql.open_cursor;
    ignore          pls_integer;
BEGIN
    dbms_sql.parse(cursor_name,
                   'alter tablespace ' || p_tablespace || ' coalesce',
                    dbms_sql.native);
    ignore := dbms_sql.execute(cursor_name);
    dbms_sql.close_cursor(cursor_name);
END;
/

grant EXECUTE on that to whomever.  (note, in 8i use locally managed tablespaces
and the need to coalesce for anything "disappears").

See
http://osi.oracle.com/~tkyte/Misc/RolesAndProcedures.html
if you get an "insufficient priv" when you run this.
Review & Followup
Rating: 4
why use pls_integer January 02, 2002
Reviewer: Kulguru
why not number, in the above case

Is there any specific reason(performance related) reason behind it.

Followup:
no reason, just copied it from somewhere.  could be a number.

Rating: 4
January 03, 2002
Reviewer: shan from Singapore
Thank you very much. This is what my requirement. Once again Thanks.

January 02, 2002 Rob -- Thanks for the question regarding "Number of characters in SID", version 8.1.7
You Asked
Tom:

Is there any reason to limit the Oracle SID to 8 (or some number)
of characters. Eight characters seems to be some kind of convention.

Rob
and we said...
its really 4 characters as the convention (ORCL for example).

The reason is in support of 8.3 filenames.  ALRTORCL.LOG, INITORCL.ORA, etc.  We
use 4, you use 4 and we can get 8 character filenames.

See your OS guide for the max's on your OS.
Review & Followup
Rating: 4
January 02, 2002
Reviewer: A reader

Rating: 4
Any functional reason January 02, 2002
Reviewer: Benny Ching from Irvine
I read in the Solaris guide they recommended 4 characters for the Oracle SID.
Besides the 8.3 format, is there any functional reason for the 4 character
recommendation on the Oracle SID?

Followup:
we use it and the oracle_home as a key for the SGA on Unix.  We only use part of
it in the key -- so, longer sids could result in the same key being generated
for two sids under the same oracle home.  So, using just the first four and the
fact that a sid must be unique, avoids any issues with regards to the key
generation.

January 02, 2002 Ak -- Thanks for the question regarding "Tunning", version 8.1.5
You Asked
Hi tom,
    Thanks for your answer(export & import regarding AQ).

1)I have a database server with ONE CPU running.
2)Same type of config. i have in various site.ALL the sites are
working perfactly ok,except ONE.
3)The One which is having probs is having more number of workstation
connected to the server,meaning from many machine at the same time
they start application.And i have noticed server touches..cpu 100% many
times(almost often).

4)I was wondering how this can happen ..when same type of code is
running in all other site...and same type of config we have for every site.

and i  ran your function for finding the statement which is
COMMON in shared pool..here is the info..

SQL> select sql_text_wo_constants, count(*)
  2    from t1
  3   group by sql_text_wo_constants
  4  having count(*) > 100
  5   order by 2
  6  /

no rows selected

SQL>  select sql_text_wo_constants, count(*)
  2     from t1
  3    group by sql_text_wo_constants
  4  having count(*) > 50
  5  order by 2
  6  /

SQL_TEXT_WO_CONSTANTS
---------------------------------------------------------------------------------
-------------------
 COUNT(*)
---------
SELECT A.IDTYPE || '#' || B.DOCTYPEDESC ,A.IDNO, A.ROWID,
B.ROWID FROM APPLICATION A, JPN_DOCTYPE B
WHERE A.IDTYPE = B.DOCTYPECODE AND A.APPLID = '#'
       61

INSERT INTO COLL_STMT_TXNS(COLLSTMTNO, PTJCODE, JANMREVNCODE,
AGENCYCODEPCS, APPLID, TXNSERNO, FEEAM
T) VALUES('#',@,@,@,'#',@,@)
       63

SELECT NVL(PHOTOCAPTURE,'#'), APPLICATION.ROWID FROM
APPLICATION WHERE UPPER(LTRIM(RTRIM(APPLID))) =
 '#'
       74

DELETE FROM GMPC_JPN_FILE@ WHERE KPTNO = '#'
       75

DELETE FROM GMPC_JPJ_FILE@ WHERE KPTNO = '#'
       87

INSERT INTO PROGRAM_ACCESS_LOG (APPLID,
ACCESSSTARTDATETIME, ACCESSENDDATETIME, PROGRAMID, REMARKS,

SQL_TEXT_WO_CONSTANTS
---------------------------------------------------------------------------------
-------------------
 COUNT(*)
---------
BRANCHCODE, USERID, WSID) VALUES
( '#', TO_DATE('#','#'), TO_DATE('#','#'), SUBSTR('#',@,@), SUBSTR(
'#', @, @), '#', '#', '#')
       88

6 rows selected.

Can you help me to find out why this server is slow,always cpu consump
tion is 100%.

Thanks,
  Regards
     Ak
and we said...
well -- it seems obvious to me.  You clearly state:

<quote>
The One which is having probs is having more number of workstation
connected to the server,meaning from many machine at the same time
they start application.
</quote>

The REAL problem -- and you seem to be a classic case of it -- is your lack of
use of bind variables.  Look at this example:

INSERT INTO PROGRAM_ACCESS_LOG (APPLID,
ACCESSSTARTDATETIME, ACCESSENDDATETIME, PROGRAMID, REMARKS,
BRANCHCODE, USERID, WSID) VALUES
( '#', TO_DATE('#','#'), TO_DATE('#','#'), SUBSTR('#',@,@),
  SUBSTR('#', @, @), '#', '#', '#')

You have a simple insert statement that SHOULD look like:

INSERT INTO PROGRAM_ACCESS_LOG (APPLID,
ACCESSSTARTDATETIME, ACCESSENDDATETIME, PROGRAMID, REMARKS,
BRANCHCODE, USERID, WSID) VALUES
( :b1, TO_DATE(:b2,'#'), TO_DATE(:b3,'#'), SUBSTR(:b4,:b5,:b6),
  SUBSTR(:b7, :b8, :b9), :b10, :b11, :b12)

it should be PARSED and OPTIMIZED once and only once.  Instead, since you choose
not to use bind variables, it is parsed and fully optimized each and every time
you use it.

This is like compiling your subroutines/methods everytime before you call them
-- instead of compiling them once and calling them over and over.  You would
never consider doing that (but people seem to have no problems doing it with
their SQL)....

This lack of use of bind variables

a) sucks the CPU dry.  You are spending 90% of your CPU time parsing queries I
bet.

b) causes massive contention on a busy system as it makes people wait in line
for the shared pool.  These people use latches to access this shared data
structure.  Latches "spin", using yet more CPU.  The more people you have
contending, the worse it gets.

c) causes the shared pool to be wasted, you are not caching and reusing query
plans.

You MUST fix this, I'll go out on a limb here and say "this is the cause of your
problems".  It is caused by having slightly more users.  You have severely
limited your performance and scalability by not using bind variables --
severely.
Review & Followup
Rating: 4
January 02, 2002
Reviewer: mike from atlanta
What is your function the reader used?

"
and i  ran your function for finding the statement which is
    COMMON in shared pool..here is the info..

    SQL> select sql_text_wo_constants, count(*)
      2    from t1
      3   group by sql_text_wo_constants
      4  having count(*) > 100
      5   order by 2
      6  /
"

Followup:
see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1163635055580
...

Rating: 4
January 03, 2002
Reviewer: A reader

January 03, 2002 Prasanna -- Thanks for the question regarding "sql loader error", version 8.1.6
You Asked
Hi Tom,
I have a dbo file (dbase 4) which has to loaded into a table using sqlloader. My
table structure is as follows,
 ID                                                  VARCHAR2(7)
 NAME                                                VARCHAR2(75)
 DATE1                                              DATE
 DATE2                                                     DATE
 DATE3                                              DATE
 START1                                               NUMBER
 START2                                               NUMBER
 START3                                               NUMBER
 START4                                               NUMBER
 START5                                               NUMBER

Whenver i try loading this file, i get the following error,
"SQL*Loader-510: Physical record in data file (C:\PSR\project\PPP1.dbo) is
longer than the maximum(1048576)."
What is read is this error is generated when trying to use SQL*Loader to load
data that is fixed length, but has no delimiter or physical end-of-record
marker.
I have couple of questions here
1) Do we have to specify the positional value in the control file or this can be
used with comma or tab delimited files also?
2)Others mention that we should use the FIX option in the control file which
will actually split the records into x number of characters specified in the FIX
option. How do we calculate that in a given dbo file, a single record =x
characters.

Can you also please provide me with the control file also.

reg
yerra
and we said...
sqlldr cannot load dbase files, that functionality was dropped many years ago.

You can use a CSV file or tab delimited.  Look at $ORACLE_HOME/rdbms/demo/u*.ctl
- there are many examples there.

If you have a raw DBASE dbf file, you might be interested in:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:711825134415
and if you have developer 2k which gives SQLPlus the ability to use odbc, you
might want to look at:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1139834557335
...
Review & Followup
Rating: 3
March 30, 2001
Reviewer: A reader

Rating: 4
Not able to open above links. January 03, 2002
Reviewer: DV
Dear Tom,
This is exactly what I am looking for, but unable to open the above links
specified.

Please help.
Thanks and regards,
DV

Followup:
They both:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:711825134415
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1139834557335
worked fine for me.

January 03, 2002 ls -- Thanks for the question regarding "variable
      in FROM clause inside pl/sql", version 8.1.7.2

      You Asked
Hi Tom

We have an anonymous pl/sql block which looks like follows but using dbms_sql
(the following doesnt work)

declare
  vRows number;
begin
  for i in (select * from user_tables)
  loop
    select count(*)
    into vRows
    from i.table_name;
    dbms_output.put_line(vRows);
  end loop;
end;
/

I also tried to use excute immediate to use bind variables

declare
  vRows number;
  vStmt varchar2(128);
begin
  for i in (select * from user_tables)
  loop
    vStmt := 'select count(*)
              from :tabname';
    execute immediate 'vStmt' into vRows using i.table_name;
    dbms_output.put_line(vRows);
  end loop;
end;
/

declare
  vRows number;
  vTab varchar2(36);
  vStmt varchar2(128);
begin
  select table_name
  into vTab
  from user_tables
  where table_name='EMP';
  vStmt := 'select count(*)
            from :vTab';
  execute immediate 'vStmt' into vRows using vTab;
  dbms_output.put_line(vTab);
end;
/

none of this works, the only way is to use dbms_sql?

      and we said...
You cannot bind an identifier.  You can only BIND where a character string
constant can go.  For example the following is legitimate:

select * from emp where ename = :x;

since you can code:

select * from emp where ename = 'X';

but the following it NOT legal:

select * from :x;

since you cannot:

select * from 'X';

To do what you want, you simply code:

  execute immediate 'select count(*) from ' || vTab into vRows;

Review &
      Followup

      Rating: 5
variable in
      FROM clause inside pl/sql  December 24, 2001

Reviewer:  Kathy Overcash  from Waldorf MD

I'm converting my SQL Server databases to Oracle.  I just looked at one of the
stored procedures that I have to convert.  It contains a sql string that is
executed using Exec.  Your example is exactly what I need to get the job done.
I bought your book and look forward to reading it.  It just arrived in the mail
today.
I love your web-site.

      Rating: 5
why concatenate
      would work?  January 03, 2002
Reviewer:  ls

hi tom

have been on vacations so couldnt see this answer until today!

I wonder why

execute immediate 'select count(*) from ' || vTab into vRows

works?. I thought we would get an error since there is no table name inside the
statement we are executing, how did || do this magic :-?

          Followup:

you are building a string

'select count(*) from ' || vTab

say vtab := 'DUAL'

you built the string 'select count(*) from dual'

that works.  you cannot bind an identifier as you tried, you can only use binds
where a character string constant would work.  For example, given:

select * from emp where ename = :x

I know that is OK since: "select * from emp where ename = 'X'" is ok.  however:

select * from :x

I know that is NOT ok since "select * from 'X'" is not valid.

January 03, 2002 Naveed -- Thanks for the question regarding "SNP
      processes busy", version 8.1.6

      You Asked
Using the dbms_job package, I have scheduled 6 jobs to run every morning at 4:00
am. Every once in a while they run late. For example they ran at 7:27 this
morning, but all of the jobs ran within seconds of each other.

I think the cause is that SNP processes ( 10 for our database ) that are busy
with other one time scheduled jobs when this happens.

How can I verify what is happening? Where can I look for logs? if any.

Thanks

      and we said...
we don't keep a history of jobs, you would have to do that yourself, by having
the jobs code something like:

declare
    l_rowid rowid;
begin
   insert into log (msg, starting )
   values ( 'job xxxx ', sysdate ) returning rowid into l_rowid;
   commit;
   the_job_you_currently_schedule;
   update log set ending = sysdate where rowid = l_rowid;
   commit;
exception
   when others
   then
      rollback;
      update log set failed_at = sysdate where rowid = l_rowid;
      commit;
      RAISE;
end;

so, you would schedule THAT instead of just

   the_job_you_currently_schedule;

Review &
      Followup

      Rating:
      5
Excellent  January 03, 2002
Reviewer:
      Naveed  from West Des Moines, Iowa
That is what I thought, and I appreciate your help.

Someone just said that the database was down last night due to a backup running
awry. Where do I look to find out what happened? How do I distinguish unix
problems if any from database?

          Followup:

You would look in your alert log.  It would show you when it was up/down and how
it was brought down and so on.

January 04, 2002 Sudip -- Thanks for the question regarding
      "Bfiles", version 8.1.6

      You Asked
Hi Thomas,
I have an oracle database setup on a solaris machine. My App server is Weblogic
it is situated on a separate Win NT machine. Now I have a Excel File situated on
the Win NT machine.
The question is can I store the reference to that excel file residing on the Win
NT server using Bfiles? If Yes how?

Thanks in Advance
Sudip

      and we said...
Only if you NFS mount or use Samba to SMB "mount" the Win NT disk onto the
server.

BFILES are a server based thing, the file must be physically accessible by the
SERVER itself.  If the database cannot see it -- cannot open it -- it cannot be
a bfile.
Review
      & Followup

      Rating: 4
Related
      Question  January 04, 2002
Reviewer:  Sudip
      Sarkar  from Calcutta, India
Thanks Thomas for the Quick clarification. I have one related question,
What is the added benefit of using Bfile, I could have kept a VARCHAR2 file
storing the path name of the specific file

          Followup:

how would you read that binary file from oracle?

how would you use that with dbms_lob.loadfromfile to get the os file into the
database?
bfiles let you treat an OS file as if it were a read only blob.  thats the added
benefit.

      Rating:
      5
Bfiles  January 04, 2002
Reviewer:
      Sudip Sarkar  from Calcutta, India
That was a very useful information and I my fundamentals about Bfiles are now
clear.
Thanks a lot Tom,

January 04, 2002 Anand -- Thanks for the question regarding "Data
      Dicitionary", version 7.3.4

      You Asked
Hi tom,

I have 3 questions

1. Why in the data dictionary details are maintained for dropped objects. For
example if have say 100 tables and in future if they are dropped still details
are stored in the data dictionary with status as invalid( as this looks like
some kind of forceful auditing). Can we not save this space, because all future
queries to other tables inolve some addditional overhead while object validation
stage,similarly Why should there are entries in the index tablespace for data
deleted (in the form of deleted leaf rows) forcing index rebuild if it exceeds a
certain why cant it be handled automatically by oracle.

2. Do we have renaming schema/user in oracle 9i?.

3. Do we have any automatic handling of high watermark issue in oracle 9i? .

Thanks for your time,

Regards,
Anand.G

      and we said...
1) data dictionary information is not maintained for dropped objects for ever.
SMON is responsible for cleaning this up in the background over time.

All future queries are no affected by these deleted objects as we store the data
in a cluster, clustered by object id.  Hence these blocks are not even
considered in the future.  They quite simply impose no overhead to you.

As for the index entries, this is a much longer discussion (if you have my book
-- it is in there).  Think of it this way.  You have an index with entries

A, B, B, C, D, E, E, F, F, F, G, H, I, J, K

in it.  You delete one of the rows so now its:
A, B, B, C, D, E, x, F, F, F, G, H, I, J, K
                 ^^^^

you have this deleted entry there.  Remembering that an INDEX is a complex data
structure (a huge b*tree) and that splitting/merging blocks is extremely
EXPENSIVE, we would like to not split or merge blocks unless we need to. Well,
we cannot avoid SPLITTING blocks (we have to accomidate future inserts into the
structure after all) but we can avoid merging blocks.  Hence, we'll leave that
"x" on there in the hopes that a value between 'E' and 'F' will be inserted at
some point.  So, if you issue "update t set column = 'F' where column = 'K'",
we'll end up with:

A, B, B, C, D, E, F, F, F, F, G, H, I, J, x
                                         ^^^^

and if that 'x' is all alone on a block, or we do a delete like:

delete from t where column betweeen 'G' and 'J'

and that cleans out a block -- that block will be removed from the index
structure, placed back on the freelist and reused.

It is EXCESSIVELY, EXTREMELY, very rare to ever have to rebuild an index. It
happens only in certain degenerative (rare) cases.  I cannot recall ever having
to rebuild an index personally.

2) no. (and in a production system, I've never seen an even marginally
compelling business case for doing so).

3) and exactly what is the percieved issue?  If the percieved issue is "we like
to spend time compacting space just to watch it grow again" (which is what
happens 99.99% of the time, we compact space and watch it expand out to the same
space, compact it, watch it grow -- when we could just watch it grow once and
then leave it alone) then we added "alter table T move tablespace FOO storage
..." in 8i (fixes "tables"), alter index T_idx rebuild <online> in 7.x and in 9i
we have online table redefinitions so you can reorg a table while people are
modifying it.

most of the time, this is quite simply an exercise in futility as the table will
quite simply grow to its steady state again.  You have gained no additional
space.

Review &
      Followup

      Rating: 3
What about a
      Sequentially increamenting key Index  January 04, 2002

Reviewer:  A reader  from India
I agree with the opinion that for deleted keys we do not need to rebuild the
index. We may actually end up degarding the performance as Oracle may have to
split blocks after rebuild.

But what about a B Tree index on a Sequentially increamenting key. Would not
such an index become one side heavy over a period of time. And rebuilding the
index will improve performance of an Index Scan

          Followup:

Not really -- you should pick of Jonathan Lewis's book, practical Oracle8i.  He
has a really good discussion on this.

The trees for the most part balance out just fine.

January 04, 2002 Neeraj -- Thanks for the question regarding "Is
      there any way to have a common tnsnames.ora file for multiple oracle
      home", version 8i

      You Asked
Tom

1)  I created two oracle home on my machine, so I have to TNSNANES.ORA files.Is
there any way to have a common tnsnames.ora file for all oracle home.

2)  How many oracle home can I create on a single machine. and how many database
can be created in single home.

3) can you please suggest me benefits to have multiple oracle home on single
machine.

      and we said...
1) set up a TNS_ADMIN environment variable that points to the directory where
this file is.

2) as many as you want

3) the ability to have more then one version of the software.  If you have only
one oracle home -- you have only one product.  If you want more then one
product, you'll have more then one oracle home.
Review &
      Followup

      Rating: 4
Common
      tnsnames  January 03, 2002
Reviewer:  jan van
      mourik  from Houston, TX
We use the 'ifile=' parameter in all the tnsnames.ora files to point to a common
tnsnames.ora file on an NT shared drive.
Pretty handy, as long as the shared drive doesn't go down :-)

jan

      Rating:
      5
TNS_ADMIN  January 03, 2002
Reviewer:
      Sofie Ye  from Canada

      Rating: 4
TNS_ADMIN
      environment variable   January 04, 2002
Reviewer:
      Neeraj  from New Delhi,India
Tom, how I can set up a TNS_ADMIN environment variable .

          Followup:

depends on your OS, on win/nt/2000, you use control panel->system->"advanced"
(why setting an environment variable on windows is an "advanced" operation has
always puzzled me -- seems to be a very straightforward, common thing but anyway
- I digress)

On win/95/blah blah blah -- no idea, never used them.

On unix, modify your .profile, .cshrc, .kshrc, .whatever_your_shell_demands and
add the appropriate SET or SETENV command.

      Rating: 5
TNS_ADMIN on
      Windows  January 04, 2002
Reviewer:  Mark A.
      Williams  from Indianapolis, IN USA
The best place to put the tns_admin variable on Windows is in the registry under
each oracle home.  For example:

HKLM\software\oracle\home0\tns_admin

and set the value to the location of your tnsnames.ora file.

Of course, this is documented in the Administrators Guide...

      Rating: 5
TNSNAMES.ora
      with ifile  January 04, 2002
Reviewer:
      kumar  from San Diego
What could be the exact syntax of the tnsnames.ora file looks with this
parameter added as 'ifile= '.

I want the example of tnsnames.ora file with ifile entry.

          Followup:

It is just:

ifile=c:\temp\tnsnames.ora

undocumented, unsupported (hence, un-mentioned by me)

      Rating: 5
Just what I
      needed  February 14, 2002
Reviewer:  Tony
      Davies  from England
I think the above title says it all really.  No more problems looking at one
database with one product and another database with a different one.

      Rating: 4
Oracle homes,
      TNS Names  February 18, 2002
Reviewer:  Mike
      Rainville  from Montreal, QC Canada
I have used two homes with different
TNS Names files in two different directories
and NO TNS_ADMIN registry entry.
The directories were NETWORK\ADMIN and NET80\ADMIN
set up by Oracles installer. If all the entries have
unique names, they can all be combined and you could put a copy in each
directory.

It's possible with different MAJOR releases,
and we wanted to use 4.5 and 6.0 on the same client
PCs. Oracle switches as necessary, depending on the application (form in our
case).

January 05, 2002 C. David -- Thanks for the question regarding
      "Outer Join w/Subquery "Table"", version 7.3.4

      You Asked
Hi Tom, can this be done?

SELECT
  <whatever...>
FROM
  table_A LEFT JOIN
  (SELECT rec_id,code FROM table_B WHERE select_col = 123)
table_subq
  ON table_A.rec_id = table_subq.rec_id

I wanted to use a subquery in the select block, but apparently I
need some version of 8 to do that...

I'm kind of stuck.  Any suggestions, in case the above is
impossible?  I need to do an outer join between the tables
because I know that most records in A won't have a record in B.
However, the records in B are typed, and I'm only concerned with
one type, so I want to filter out the others, but while avoiding
filtering the records in A without a match.

Thanks much!
David

      and we said...
pretend emp is TABLE_A and dept is TABLE_B and select_col is
deptno in the following (and 10 is your 123).  Doesn't this do
it for you??

scott@TKYTE816> select a.ename, b.deptno
  2  from emp a, dept b
  3  where a.deptno = b.deptno(+)
  4  and b.deptno(+) = 10;

ENAME          DEPTNO
---------- ----------
SMITH
ALLEN
WARD
JONES
MARTIN
BLAKE
CLARK              10
SCOTT
KING               10
TURNER
ADAMS
JAMES
FORD
MILLER             10

14 rows selected.

Review &
      Followup

      Rating: 4
How to convert
      SQL92  December 05, 2001
Reviewer:  Sivababu
       from INDIA
Hello TOM,

How to convert this query into SQL92 standard like supporting ORACLE 9i,
SQLSERVER and SYBASE etc.

select a.ename, b.deptno
    from emp a, dept b
    where a.deptno = b.deptno(+)
    and b.deptno(+) = 50
 It will return the rows eventhoug the deptno 50 doesn't have the record.

Please reply me.

regards,
sivababu

          Followup:

That query makes no sense to me -- why would you want it.  All is does is:

  bring back every row in EMP and if the deptno is 50, give me the DEPT info as
well

I understand the outer join from EMP to DEPT but what would the "b.deptno(+) =
50" do beside blank out the dept????

Ok, you can achive this via:

scott@ORA9I.WORLD> select a.ename, b.deptno
  2      from emp a, dept b
  3      where a.deptno = b.deptno(+)
  4      and b.deptno(+) = 50
  5  /

ENAME          DEPTNO
---------- ----------
SMITH
ALLEN
WARD
JONES
MARTIN
BLAKE
CLARK
SCOTT
KING
TURNER
ADAMS
JAMES
FORD
MILLER

14 rows selected.

scott@ORA9I.WORLD>
scott@ORA9I.WORLD> select a.ename, b.deptno
  2    from emp a LEFT OUTER JOIN ( select * from dept where deptno = 50 ) b
  3    on ( a.deptno = b .deptno )
  4  /

ENAME          DEPTNO
---------- ----------
SMITH
ALLEN
WARD
JONES
MARTIN
BLAKE
CLARK
SCOTT
KING
TURNER
ADAMS
JAMES
FORD
MILLER

14 rows selected.

Thats ANSI and does the same thing.

      Rating: 4
ANSI Standard -
      Oracle 9.0.1  January 05, 2002
Reviewer:
      sivababu  from INDIA
Hello TOM,
 I have the doubt about working the above query. Can i use both queries or will
it effect the performance any one of this query?. But i'm getting same result
and same COST. Which one i can use.

 select e.ename, d.deptno, d.dname from emp e
 left outer join ( select * from dept  where LOC = 'NY') d
 on (d.deptno = e.deptno)
 where  e.ename = 'KING'

result :

ENAME          DEPTNO DNAME
---------- ---------- --------------
KING

 select e.ename, d.deptno, d.dname from emp e
 left outer join  dept d
 on (d.deptno = e.deptno and LOC = 'NY')
 where  e.ename = 'KING'
 /

ENAME          DEPTNO DNAME
---------- ---------- --------------
KING

thanks and Expecting your reply.

with regards,
sivababu

          Followup:

Not only are the costs the same (not that you can really compare the costs of
two DIFFERENT queries, you can only really compare the costs of the different
plans generated for a SINGLE query -- the costs are compared within the set of
plans generated for a single query by the CBO to determine which one is best.  A
query with a cost of 1,000 might outrun a query with a cost of 2 -- cost is not
necessarily an indicator of performance!) but the PLANS are identical.

The queries are semantically identical, they result in the same plans, they are
in fact the same for all intents and purposes.

January 05, 2002 Sam -- Thanks for the question regarding "list of
      data types with which we can store data in the database ", version 8.1.7

      You Asked
1.Can you give me an example of chek option for a view , and how it is useful.

2.Can you give a list of data types with which we can store dta in an oracle
table.

3.What is the difference between Define and Accept  and & commands in
sqlplus,here are the test tables:

and lastly

4.In sqlplus when a user disconnects from a session , will the uncommitted data
is committed or rolled back.

Finally tom, in the ocp syllabus, I dont see any questions ( in sql and pl/sql
exam) dealing with the new object oriented stuff like nested tables, varrays,
analytical functions , why is this so.

Thank you

      and we said...
1) the check option simply ensures that the data you insert into a view would be
SELECTED by that view.  Its usefullness is in the eye of the beholder.  If you
FIND this to be a useful attribute, use it (you can use it to enforce some
constraints for example).  It is useful in that sort of situation -- when you
want to make sure that data you insert INTO the view, will in fact be SELECTED
by that view.  Its an option.

2) see the server concepts manual.  It lists the Oracle datatypes (heck, there
is an entire chapter titled "built-in datatypes")

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c10datyp.htm#629
3) define defines a variable.  You, the programmer, are SETTING its value.

Accept lets the end user enter a value

(one wonders, did you read the docs?)
accept:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a82950/ch8.htm#1011740
define:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a82950/ch8.htm#1013158
4) did you try it out to see???

sqlplus commits by default on exit.  It is very easy to try it and see...

If you have doubts, read

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a82950/ch8.htm#1013621
which shows you can:

exit commit;

exit rollback;

exit;

with commit being the default....

for the finally.... I don't have anything to do with OCP.  I have no idea.

January 06, 2002 Mayank -- Thanks for the question regarding "How
      to pass a CLOB as an IN OUT parameter to external routine", version 8.0.5

      You Asked
Hi Tom,
When I try to pass a CLOB as an IN OUT parameter to the exeternal procedure,
then it is always passed as NULL. My Oracle version is 8.0.5.

  The following is the PL/SQL function registering the external C procedure:

  CREATE OR REPLACE FUNCTION fnMQInterface(qManagerName IN VARCHAR2
  ,qNamePut IN VARCHAR2
  ,qNameGet IN VARCHAR2
  ,qBufferClob IN OUT CLOB
  ,qTransactionName IN VARCHAR2
  ,qMessageId OUT VARCHAR2
  ,qReturnMsg OUT VARCHAR2)

  RETURN BINARY_INTEGER AS EXTERNAL
  LIBRARY MQINTERFACELIB
  NAME "mqInterface"
  Language C
  WITH CONTEXT
  PARAMETERS(CONTEXT
  ,qBufferClob OCILOBLOCATOR
  ,qBufferClob INDICATOR SHORT
  ,qManagerName
  ,qManagerName INDICATOR
  ,qNamePut
  ,qNamePut INDICATOR
  ,qReturnMsg
  ,qTransactionName
  ,qNameGet
  ,qNameGet INDICATOR
  ,qMessageId
  ,qMessageId INDICATOR
  );

  The C function prototype is as follows:

  int mqInterface(OCIExtProcContext *ctx, OCILobLocator **p_in, short p_in_i,
char* managerName, short managerName_ind, char*
  queueName, short queueName_ind, char* returnMsg, char* transactionName, char*
queueNameGet, short queueNameGet_ind, char*
  messageId, short messageId_ind);

  I have made sure that I use OCILobLocator **p_in as it is an IN OUT parameter.

  Now in the C procedure when I write the following :

  if (p_in_i == OCI_IND_NOTNULL)
  {
  return 0;
  }
  else
  {
  return 1;
  }

  It always returns 1. This means that the indicator shows that the CLOB has no
value.

  Please tell me why the CLOB is being passed as null, as I make sure that it
contains some value. Morever when I make the parameter as IN then the value is
passed properly.
The main reason for all this is that I want to pass a CLOB, read from it and
then write something into  it in the C program using OCILobRead() and
OCILobWrite(). How can this be done by using a single IN OUT CLOB to external
routine?

  Thanks in advance
  Mayank Kaloni

      and we said...
The indicator for an IN OUT parameter is going to be a POINTER, short * p_in_i.

Your function prototype should in fact be:

  int mqInterface
    (
   OCIExtProcContext  *,  /*   1 : With-Context  */
       OCILobLocator **,  /*   2 : QBUFFERCLOB  */
               short  *,  /*   3 : QBUFFERCLOB (Indicator) */
                char  *,  /*   4 : QMANAGERNAME  */
               short   ,  /*   5 : QMANAGERNAME (Indicator) */
                char  *,  /*   6 : QNAMEPUT  */
               short   ,  /*   7 : QNAMEPUT (Indicator) */
                char  *,  /*   8 : QRETURNMSG  */
                char  *,  /*   9 : QTRANSACTIONNAME  */
                char  *,  /*  10 : QNAMEGET  */
               short   ,  /*  11 : QNAMEGET (Indicator) */
                char  *,  /*  12 : QMESSAGEID  */
               short  *   /*  13 : QMESSAGEID (Indicator) */
    );

 int mqInterface(OCIExtProcContext *ctx,
                 OCILobLocator **p_in,
                 short p_in_i,    <<<===== wrong
                 char* managerName,
                 short managerName_ind,
                 char* queueName,
                 short queueName_ind,
                 char* returnMsg,
                 char* transactionName,
                 char* queueNameGet,
                 short queueNameGet_ind,
                 char*  messageId,
                 short messageId_ind);  <<<===== wrong

I would suggest that having the C input arguments in a different ORDER then the
plsql ones is confusing at best (a very bad practice at worst).  I would change
that immediately.  It made it very very hard to read and understand and will
only lead to more errors like this.

If you have access to my book, i have an extensive chapter on External Routines,
with best practices included.  Some of your OUT parameters don't have indicators
(bad practice) and you are not passing LENGTHS for the varchars!!  Thats a
buffer overflow/underflow just waiting to happen!

Review &
      Followup

      Rating: 5
Advice
      taken!  January 06, 2002
Reviewer:  Mayank
      Kaloni  from London
Hi Tom,
       Thanks a lot. I will certainly implement your advice regarding the pl/sql
and external routine parameters. The code looked a bit dirty as it is still in a
development stage.
     One more cause of problem was that for IN OUT parameters I was using
**parameter, and that parameter as such to OCILobWrite() and OCILobRead() when I
should have passed *parameter as they expect a pointer and not a pointer to a
pointer.

Thanks
Mayank Kaloni

      Rating: 5
What's the name
      of the book?  January 06, 2002
Reviewer:  Mayank
      Kaloni  from London
Hi Tom,
      I forgot to ask you the name of the book. Please do send me the name of
the book. I must say that Oracle 8.0.5 documentation is grossly insufficient in
light of what you just told me.

Thanks and Regards
Mayank Kaloni

          Followup:

Just goto:

http://asktom.oracle.com/
Click on my face ;)

January 07, 2002 Dan -- Thanks for the question regarding "SQLJ -
      retreiving rowid-", version 8.1.6.2

      You Asked
This is very strange to me.  We are evaluating SQLJ to see if it will be of any
use to us.  So I tried to write a simple sqlj program. Now, I am not a
programmer, but the knowledge never hurts.  Anyway, I am retreiving rows from a
table, displaying the rowid (or what I think is the rowid) looking up the
result, and displaying again.  As you can see, I doesnt look right..  Any hint
as to what I am doing wrong?  Thanks. There has got to be some mask that I am
missing.....

 Your site is great, part of my daily routine.
 select unit_type, rowid from unit_type
     2>
UNIT_T ROWID
------ ------------------
CO     AAA7k5AAHAAAwPPAAA
AREAMG AAA7k5AAHAAAwPPAAB
DIV    AAA7k5AAHAAAwPPAAC
DPOMG  AAA7k5AAHAAAwPPAAD
4 rows selected.
hpn1(tcr1)# pg RowidTst.sqlj

import sqlj.runtime.*;
import sqlj.runtime.ref.*;
import java.sql.*;
import oracle.sqlj.runtime.*;
import oracle.sqlj.runtime.Oracle;
import oracle.jdbc.driver.*;
import oracle.sql.*;

#sql iterator TabIter3 (String, ROWID );
public class RowidTst {

  public static void main(String[] args) {
  int whatsid = 0;
     try {
     Oracle.connect(RowidTst.class,"connect.properties");
     OracleConnection c =
        (OracleConnection) DefaultContext.getDefaultContext().getConnection();
     System.out.println("Connected.");
  DefaultContext.setDefaultContext( new DefaultContext(c));

   TabIter3 tabIter3;

ROWID jabba = null;
String jabba2 = null;
 #sql tabIter3 =
      { select unit_type, rowid from unit_type };
  while (true)
        {
        ROWID xrowid = null;
        String xunit_type = " ";
        #sql { fetch :tabIter3 into :jabba2, :jabba };
        if ( tabIter3.endFetch() ) break;
        System.out.println ("Rowid " + jabba + " Unit type " + jabba2  );

        #sql {select unit_type ,rowid
              into :xunit_type, :xrowid  from unit_type
                where rowid =:jabba };
                System.out.println ("Unit type  " + xunit_type
                + " Rowid " + xrowid);
        }

// Close the database connection
   #sql { commit work };
   #sql { rollback work };
c.close();
System.out.println("Disconected...");
System.out.println("I B DONE");

} catch (SQLException e) {
         e.printStackTrace();
}
}
}
hpn1(tcr1)# java RowidTst
Connected.
Rowid oracle.sql.ROWID@6b13c7 Unit type CO
Unit type  CO Rowid oracle.sql.ROWID@3d93f4
Rowid oracle.sql.ROWID@3ca5f1 Unit type AREAMG
Unit type  AREAMG Rowid oracle.sql.ROWID@329f3d
Rowid oracle.sql.ROWID@749757 Unit type DIV
Unit type  DIV Rowid oracle.sql.ROWID@7bd6a1
Rowid oracle.sql.ROWID@47ee05 Unit type DPOMG
Unit type  DPOMG Rowid oracle.sql.ROWID@5b9e68
Disconected...
I B DONE
hpn1(tcr1)#

      and we said...
oracle.sql.ROWID is an "object".  It has a default way to "print itself" like
all objects do (classname@pointer).  It's the default way objects print.

You can call stringValue() on the rowid -- but it returns it in HEX, not base64.

So, if you need a rowid to find a row -- use the oracle.sql.ROWID type (and you
can print the hex representation of it).  If you want to see it as a string --
simply use rowidtochar().

Here is an example:

ops$tkyte@ORA9I.WORLD> create or replace and compile java source named
"RowidTst"
  2  as
  3  import sqlj.runtime.*;
  4  import sqlj.runtime.ref.*;
  5  import java.sql.*;
  6  import oracle.sqlj.runtime.*;
  7  import oracle.sqlj.runtime.Oracle;
  8  import oracle.jdbc.driver.*;
  9  import oracle.sql.*;
 10
 11    #sql iterator TabIter3 ( ROWID, String );
 12
 13  public class RowidTst {
 14
 15    public static void xmain() throws SQLException
 16    {
 17       TabIter3 tabIter3;
 18
 19       ROWID  rowidObject = null;
 20       String rowidString = null;
 21
 22       #sql tabIter3 = { select rowid, rowidtochar(rowid) from dual };
 23
 24       while (true)
 25       {
 26          #sql { fetch :tabIter3 into :rowidObject, :rowidString };
 27
 28          if ( tabIter3.endFetch() ) break;
 29
 30          System.out.println("RowidObject = " + rowidObject );
 31          System.out.println("RowidObject.stringValue() = " +
rowidObject.stringValue() );
 32          System.out.println("RowidString = " + rowidString );
 33       }
 34       System.out.println("I am finished");
 35    }
 36  }
 37  /

Java created.

ops$tkyte@ORA9I.WORLD> show errors
No errors.
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> create or replace procedure rowidtst
  2  as language java name 'RowidTst.xmain()';
  3  /

Procedure created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> set serveroutput on size 1000000
ops$tkyte@ORA9I.WORLD> exec dbms_java.set_output( 1000000 );

PL/SQL procedure successfully completed.

ops$tkyte@ORA9I.WORLD> exec rowidtst
RowidObject = oracle.sql.ROWID@fa9e5965
RowidObject.stringValue() = 414141414464414142414141414956414141
RowidString = AAAADdAABAAAAIVAAA
I am finished

PL/SQL procedure successfully completed.

Review &
      Followup

      Rating: 5
sqlj
      rowid  January 07, 2002
Reviewer:  Dan
      Malumphy  from Cleveland, OH USA
Perfect, exactly what was needed.

      Rating: 4
Some
      Corrections  January 07, 2002
Reviewer:  Max
      Salviejo  from Cleveland, OH USA
oracle.sql.ROWID is not an object
oracle.sql.ROWID is a class by definition

An object is
oracle.sql.ROWID lookMom;

Where "lookMom" is an object of the type class oracle.sql.ROWID
Its an instance of that class type.

I believe, the reason why the ROWID "object" its not being outputted is because
the designer of oracle.sql.ROWID an extension of the class "oracle" (which is an
extension by default of the class "Object") did not implement the method
toString().

Simply if you extend ROWID class to define toString()

class NewRowID extends oracle.sql.ROWID
{
          public NewRowID()
          {
                  // CALL BASE CLASS CONSTRUCTOR super();
          }

          public String toString()
          {
                  return super.toString() + ??? someObject ????
          }
}

I have not looked into oracle package deeply, but the gist is there I believe.

The 'oracle.sql.ROWID@41324AKF' the section after the @ is hash code.

January 07, 2002 ls -- Thanks for the question regarding "How to
      show a cursor is not closed?", version 8.1.7

      You Asked
Hi Tom again

I would like to show one of developers the effect of not closing cursors, I have
this code

create or replace procedure ax
as
  cursor cEmp is select ename from emp;
  vTmp varchar2(20);
begin
  open cEmp;
  loop
  fetch cEmp into vTmp;
  exit when cEmp%notfound;
  end loop;
end;
/

Here I explicitly leave cEmp open since I have not used stmt close cEmp;

so I run it

SQL> r
  1  create or replace procedure ax
  2  as
  3  cursor cEmp is select ename from emp;
  4  vTmp varchar2(20);
  5  begin
  6  open cEmp;
  7  loop
  8  fetch cEmp into vTmp;
  9  exit when cEmp%notfound;
 10  end loop;
 11* end;

Procedure created.

SQL> exec ax

PL/SQL procedure successfully completed.

From another session I check v$open_cursor

SQL> select sql_text from v$open_cursor;

SQL_TEXT
------------------------------------------------------------
SELECT ENAME   FROM EMP
select sysdate from dual
select sql_text from v$open_cursor

does this mean cEmp is still open?

Nevertheless I recreated the procedure with close cEmp;
and ran again as above, I still yield same result so is the way I am trying to
accomplish wrong?

Note: I have run select sysdate from dual after each execution of ax

How can I show this developer?

      and we said...
PLSQL is very forgiving.  Since cEmp is a local variable -- and it goes out of
scope, it closed it for you.  If this were a cursor variable returned to a
client -- it would be a different story (if the client didn't close them).  you
would eventually accumulate lots of open cursors and get the dreaded ora-01000
"max open cursors".

You can show them an example of why to close it (other then common sense I
suppose?  Other then good programming practice?)

ops$tkyte@ORA815.US.ORACLE.COM> create or replace package demo_pkg
  2  as
  3          procedure p;
  4  end;
  5  /

Package created.

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> create or replace package body demo_pkg
  2  as
  3
  4  cursor c is select count(*) from dual;
  5
  6  procedure p
  7  is
  8          n number;
  9  begin
 10          open c;
 11          fetch c into n;
 12          dbms_output.put_line( 'p, n = ' || n );
 13  end;
 14
 15  end;
 16  /

Package body created.

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> exec demo_pkg.p
p, n = 1

PL/SQL procedure successfully completed.

ops$tkyte@ORA815.US.ORACLE.COM> exec demo_pkg.p
BEGIN demo_pkg.p; END;

*
ERROR at line 1:
ORA-06511: PL/SQL: cursor already open
ORA-06512: at "OPS$TKYTE.DEMO_PKG", line 4
ORA-06512: at "OPS$TKYTE.DEMO_PKG", line 10
ORA-06512: at line 1

by using a stateful package.

Review &
      Followup

      Rating: 5
stateful
      package  January 06, 2002
Reviewer:  Kulguru

Tom, what do you mean by  a stateful package.

Do you mean package which is once called for a session and then is cached in the
library cache.

          Followup:

No, I mean a package that maintains a state -- as this example does.

Notice that the cursor is defined in the package -- outside of any
function/procedure.  Its scope is global, it maintains a state from call to
call.

      Rating:
      4
Why  January 06, 2002
Reviewer:
      Kulguru
Tom

At what stage we will hit the ora-01000
"max open cursors".

using your given example.

          Followup:

Hopefully never with my example.  There is only one cursor!

      Rating: 3
no
      change  January 07, 2002
Reviewer:  Kulguru

Tom

Well, then maintaining a state mean something which does not undergo a change.

Because in your case the cursor is static and is outside of any function or
proceudre, and will  not change.

          Followup:

maintaining a state means what it says -- we maintain a state, a set of
values....

The cursor is "stateful", it does not get "reset" on each call to the database.
The cursor is not "static", its scope is at the session level instead of the
call level as is "normal"

    Copyright © 2003 Oracle Corporation, All rights reserved.


Recent Archives Advanced Search FAQ by Category Most Popular Hot Articles Your Questions [ Listing ]
Subject | Article | Last Updated (East coast time)
Efficient use of BLOB(s) | article only | Jan 08, 2002 12:08 pm
How to distribute datafiles | article only | Jan 08, 2002 02:09 pm
Explicit Row Locking from PL/SQL function | article only | Jan 08, 2002 02:15 pm
How to check Oracle server operating system from SQL or PL/SQL? | article only | Jan 08, 2002 06:59 pm
SQL date interval literal question | article only | Jan 09, 2002 12:25 pm
Trigger Question | article only | Jan 09, 2002 05:33 pm
result sets from stored procedures | article only | Jan 10, 2002 07:29 am
Difference using Auto-increment col and using Attribute key | article only | Jan 10, 2002 07:37 am
Parallel Insert | article only | Jan 10, 2002 08:07 am
intermedia not working for "," | article only | Jan 10, 2002 10:17 am
Different kinds of loops( control structures) | article only | Jan 10, 2002 10:35 am
Data Trasfer from Production To TEst | article only | Jan 10, 2002 11:53 am
Java Script with PL/SQL | article only | Jan 10, 2002 01:18 pm
View question | article only | Jan 10, 2002 01:32 pm
extracting ddl | article only | Jan 10, 2002 02:27 pm
Statspack & dbms_job | article only | Jan 10, 2002 03:32 pm
Trigger to trap who did what via sql_text | article only | Jan 10, 2002 07:34 pm
Minute difference up to one minute from dates | article only | Jan 11, 2002 01:24 pm
Incremental backup off a split mirror image of data files | article only | Jan 11, 2002 02:00 pm
PL Performance | article only | Jan 11, 2002 03:20 pm
PL/SQL Calling Stored Procedure | article only | Jan 11, 2002 04:16 pm
RE: [Fwd: Ask Tom Submission] importing tables into a different tablespace | article only | Jan 12, 2002 10:10 am
use of cursor parameters | article only | Jan 12, 2002 10:30 am
Help in sqlplus | article only | Jan 12, 2002 03:06 pm
SQL Statement | article only | Jan 12, 2002 05:41 pm
performance issues between triggers or procedures w/ jobs | article only | Jan 12, 2002 06:02 pm
Questions!!! | article only | Jan 12, 2002 07:45 pm
Rollup and Cube | article only | Jan 13, 2002 09:49 am
Data mart questions | article only | Jan 13, 2002 09:56 am
Assigning alias names to the grand totals resulting from Rollup in a Group By | article only | Jan 13, 2002 01:04 pm
Data may be inserted into complex cviews when the statement affects only one of the tables in the join. | article only | Jan 14, 2002 07:38 am
9i: gather_schema_stats, 'GATHER STALE' vs. 'GATHER AUTO' | article only | Jan 14, 2002 07:50 am
Too much Indexes... | article only | Jan 14, 2002 11:27 am
create global temporary table foo as subquery produces no rows ! | article only | Jan 14, 2002 12:30 pm
No. of rows returned by XML APIs in PLSQL | article only | Jan 14, 2002 02:02 pm
R.I. across schemas? | article only | Jan 14, 2002 04:09 pm
row(s) 1 - 36 of 36
January 08, 2002 Brent -- Thanks for the question regarding "Efficient use of BLOB(s)", version 8.1.7
You Asked
I currently have a situation where I am creating a table that will be used to
store all personnel images.  Each employee will have both a high and low
resolution photo stored.  I want to store each photo as a BLOB.  As you can
image this table will eventually become very large, so my concern is how to
properly structure it so that it works most efficiently.  I am unsure whether to
create one record per employee with both a low and high resolution image column
or create two records per employee, one record per image type.  On another note
when inserting an empty blob into a field, does it take up a default amount of
tablespace?
and we said...
blobs are just pointers, these pointers point to other segments stored outside
of the table.

These other segments can be in different tablespaces if you like.

read
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76940/toc.htm
You'll have one record/employee with both images. Each image can be stored in a
tablespace separate from the table and themselves.
January 08, 2002 Gu -- Thanks for the question regarding "How to distribute datafiles", version 8.1.6
You Asked
Hi Tom,

We have a new server IBM RS/6000 7025-6F1 with AIX 4.3.3. The disk size is 18GB
for all 14 disks. The disk for OS and Oracle software is mirrored and both have
seperate Wide/Fast-20 SCSI I/O Controller. FOr the rest 12 disks, we use IBM SSA
160 SerialRAID Adapter(14109100). We will use RAID 0+1. Now the questions are:

1. What is the better way to do RAID 0+1? Strip 5 disks and mirror or strip 2
disks and mirror them? Should we have 2 none RAID disks?

2. Our DB is in no archive mode and exported every week day. How to distribute
all datafiles? We will locate password file, parameter file and init.ora file to
the disk that ORACLE_HOME reside; put system segment, data segment, index
segment, rollback segment to RAID 0+1 disks; put temp segment, redolog file and
export dump files to none RAID disks. For rollback segment, I am not sure where
is the better place. Could you explain in detail?

Thank you in advance.

Best Regards,
Gu Wang
and we said...
1) it depends, go with what you feel comfortable with.  The size of the stripe
set is up to you (i would actually ask the hardward vendor for their advice on
the best performing configuration with their hardware)

see also
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:359617936136
2) why??? do you want to lose a days work?  I can assure you that running in
this mode is a 100% way to lose data.

Also, export is a very very very poor backup tool.  I would STRONGLY encourage
you (like REALLY strongly) to rethink your position.  Use archive log mode, use
hot backups, use export once or twice a week more as a way to test the integrity
of your database then as a backup tool.  Keep at least 3-5 backups available for
recovery purposes.

I really wish you would rethink this, unless you don't care about losing data
and can afford to take a very very long time to recover from any errors (if you
are able to recover at all).
Review & Followup
Rating: 4
January 08, 2002
Reviewer: Tianhua Wu from Atlanta, GA
Why not use RAID 1+0 (or RAID 10), it provide much beter protection than raid
0+1.

Followup:
excellent explanation from
http://www.backupcentral.com/high-availability.html
The RAID ?hierarchy? begins with RAID 0 (striping) and RAID 1
(mirroring).  Combining RAID 0 and RAID 1 is called  RAID-0+1 or
RAID-1+0, depending on how you combine them.  (RAID 0+1 is also called
RAID-01, and RAID-1+0 is also called RAID-10.)  The performance of
RAID-10 and RAID-01 are identical, but they have different levels of
data integrity.

RAID-01 (or RAID 0+1) is a mirrored pair (RAID-1) made from two stripe
sets (RAID-0); hence the name RAID 0+1, because it is created by first
creating two RAID-0 sets and adding RAID-1.  If you lose a drive on one
side of a RAID-01 array, then lose another drive on the other side of
that array before the first side is recovered, you will suffer complete
data loss.  It is also important to note that all drives in the
surviving mirror are involved in rebuilding the entire damaged stripe
set, even if only a single drive was damaged.  Performance during
recovery is severely degraded during recovery unless the RAID subsystem
allows adjusting the priority of recovery. However, shifting the
priority toward production will lengthen recovery time and increase the
risk of the kind of the catastrophic data loss mentioned earlier.

RAID-10 (or RAID 1+0) is a stripe set made up from N mirrored pairs.
Only the loss of both drives in the same mirrored pair can result in any
data loss and the loss of that particular drive is 1/Nth as likely as
the loss of some drive on the opposite mirror in RAID-01.  Recovery only
involves the replacement drive and its mirror so the rest of the array
performs at 100% capacity during recovery.  Also since only the single
drive needs recovery bandwidth requirements during recovery are lower
and recovery takes far less time reducing the risk of catastrophic data
loss.

January 08, 2002 Tejas -- Thanks for the question regarding "Explicit Row Locking from PL/SQL function", version 8
You Asked
Dear Sir,

How to explicitly Lock/unlock the row(s) of a table ?
I want to hold the lock until I explicitly release
(the lock should not be released on commit/rollback)

Please explain with some example.

Thanks

Tejas
and we said...
commit and rollback always unlocks a row.  No avoiding it.

To explicitly LOCK -- select .... where ... FOR UPDATE;

that'll lock it from other updates.

Commit/rollback = locks on rows are gone.

If you want to implement your OWN locking scheme, you can use dbms_lock (see the
supplied packages guide).  This lets you create locks that can be gotten and
released on demand and persist across commits.
Review & Followup
Rating: 4
January 08, 2002
Reviewer: Tejas
I think, dbms_lock is not actually locking any row(s).

If one user holds the lock in exclusive mode then other user
can not hold the "same" lock. But it does not prevent the direct update of the
row (by using update table set ...).

Please let me know, What I understood is true or not ?

Could I prevent these row(s) from updating by other users, while associated lock
(using dbms_lock) is held by an user ?

Followup:
You can, if you write you OWN data access layer (eg: an API).

Instead of allowing:

   update t set x = ... where pk = ....

you write a procedure

procedure p( p_pk in number, p_x in varchar2 )
is
begin
   dbms_lock -- get a lock named after the primary key
   update the row
end;

don't let people do updates, let them use an API.

January 08, 2002 Daryl -- Thanks for the question regarding "How to check Oracle server operating system from SQL or PL/SQL?", version 8.1.7
You Asked
During the installation of some application software, we build the necessary
schema objects in Oracle if they don't exist. Additionally, we point out any
potential problems to the user doing the install. Some of these problems vary
with the Oracle server platform. For example, the INSO filters for interMedia
were not available for Oracle on Linux prior to 8.1.7. (So we need to suggest a
workaround or a limitation for those people installing on Linux Oracle 8.1.6.)

I've not been able to see how to do this from SQL or PL/SQL. Can you suggest
anything? (I've already written a Java stored procedure that returns
System.getProperty("os.name") along with the PL/SQL wrapper for it. However, I'd
prefer a "pure" PL/SQL or SQL option, since this stuff is only run during the
installation.)
and we said...
Here is a small cut and paste from my book that describes how to do this:

DB_VERSION and PORT_STRING

The DB_VERSION routine was added in Oracle 8.0 in order to make it easier for
applications to figure out what version of the database they were running in. We
could have used this in our CRYPT_PKG (see the DBMS_OBFUSCATION_TOOLKIT section)
for example to tell users that attempted to use the DES3 routines in an Oracle
8.1.5 database that it would not work (instead of just trying to execute the
DES3 routines and failing). It is a very simple interface as follows:

scott@TKYTE816> declare
  2      l_version        varchar2(255);
  3      l_compatibility varchar2(255);
  4  begin
  5      dbms_utility.db_version( l_version, l_compatibility );
  6      dbms_output.put_line( l_version );
  7      dbms_output.put_line( l_compatibility );
  8  end;
  9  /
8.1.6.0.0
8.1.6

PL/SQL procedure successfully completed.

And provides more version detail then the older function PORT_STRING:

scott@TKYTE816> select dbms_utility.port_string from dual;

PORT_STRING
---------------------------
IBMPC/WIN_NT-8.1.0

Using the port string, not only would you have to parse the string, but you
cannot tell if you are in version 8.1.5 versus 8.1.6 versus 8.1.7. DB_VERSION
will be more useful for that.  On the other hand, the PORT_STRING does tell you
what operating system you are on.
Review & Followup
Rating: 5
Another Option? January 08, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
Tom:

I typically use this to get the version(s):

SELECT PRODUCT, VERSION FROM SYS.PRODUCT_COMPONENT_VERSION;

Are there any pro's/con's to using this method vs. the package call?

Also, is it possible to get a patchset release level?  For example, I have
installed patchset 8.1.7.2.5 but the version will be reported as 8.1.7.2.1 I
understand that that is the version number of the server, but would like to find
the patchset release level...

- Mark

Rating: 4
Close, but where are the mappings? January 08, 2002
Reviewer: Daryl from Texas
This is CLOSE to what I need. When my server is running on Windows, I get:

SQL> select dbms_utility.port_string from dual;

PORT_STRING
-------------------------------------------------
IBMPC/WIN_NT-8.1.0

Under Solaris, I get:

SQL> select dbms_utility.port_string from dual;

PORT_STRING
-------------------------------------------------
SVR4-be-8.1.0

Hmmmm....how do I know that's SOLARIS, and not some other System V R4-based
UNIX? Is there a list of mappings of port string codes to OS names? Searching
Metalink provided no clues. (I already own your book, btw, so I appreciate the
pointer to it. I just missed that one.)

Followup:
There is not as far as I know but it looks like this page is starting to collect
them -- if everyone who has an OS that isn't represented yet posts em, we'll get
them....

Rating: 5
Interesting Results January 08, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
Daryl:

This is what I get on a couple of my boxes:

(select dbms_utility.port_string from dual;)

AIX/8.1.7:
PORT_STRING
--------------------------
IBM AIX/RS600 V4 - 8.1.0

Solaris/9.0.1:
PORT_STRING
--------------------------
SVR4-be-8.1.0

I don't have any other *nix platforms to test on... I wonder if hp/ux would show
up as 'be' as well?

Does 'be' denote Solaris?

Rating: 5
HP-UX January 08, 2002
Reviewer: Brandon Lyon from Rock Island, IL
Here is the HP-UX (64 bit) response (since someone asked):

HP-UX 11.0 PORT_STRING
---------------------
HP9000/800-64.0.8.1.0

Rating: 5
hp ux answer January 08, 2002
Reviewer: dan malumphy from Cleveland, OH USA

SQL> select dbms_utility.port_string from dual
  2  /
:

PORT_STRING
--------------------------------------------------------------------------------
HP9000/800-7.0-8.1.0

SQL>
this is
uname -a
HP-UX hpn1 B.11.00 U 9000/800 621309343 unlimited-user license
running 8.1.6.2

Rating: 5
HP January 08, 2002
Reviewer: Benny from Irvine
PORT_STRING
---------------------------------------
HP9000/800-7.0-8.1.0

Rating: 3
quick question.. January 08, 2002
Reviewer: neeti from TX USA
Is there a function in oracle to return the sid for
the same session inside a plsql procedure ?

Followup:
The sid:

select sid from v$mystat where rownum = 1;

If you mean the Oracle_sid

select instance_name from v$instance;

Rating: 5
Linux January 08, 2002
Reviewer: Daryl from Texas
Oracle on Linux (Intel) reports:

LINUXI386/LINUX-2.0.34-8.1.0

Rating: 4
my systems January 09, 2002
Reviewer: Amit from Timbaktoo
my systems show this:

PORT_STRING
----------------------------------
Linuxi386/Linux-2.0.34-8.1.0

PORT_STRING
--------------------
IBMPC/WIN_NT-8.1.0

Thanks,
AB

Rating: 5
For Tru64 UNIX version 5.1 on Compaq Alpha GS80 January 09, 2002
Reviewer: Johan Snyman from Johannesburg, South Africa
DEC Alpha OSF/1, r1.1-8.1.0

The Oracle version is 8.1.6.2

Rating: 5
I guess not many people saw this one coming... January 08, 2003
Reviewer: Frank from The Netherlands
SQL> select dbms_utility.port_string from dual;

PORT_STRING
--------------------------------------------------------------------------------
MACOSX/OSX-10.2-Darwin-6.0-BE

Who'd have thought.... :-)

Rating: 5
July 10, 2003
Reviewer: Kid
then, what about this ???

select dbms_utility.port_string from dual;

PORT_STRING
--------------------------------------------------------
DOS 5.0

January 09, 2002 Yves -- Thanks for the question regarding "SQL date interval literal question", version 8.1.7
You Asked
Hi Tom.

The "WHAT" parameter (procedure) for my DBMS_JOB.SUBMIT call accepts a varchar2
string as input which is the actual interval for the job.  I am not using an
interval when calling the submit procedure, as my "WHAT" procedure is computing
its next date for custom scheduling reasons.

My question is this:  Whithin my procedure, how do I convert a varchar string
like 'Trunc(sysdate) + 2 + 1/24' to a date?

I need this conversion to happen for my custom scheduling logic.  After the
"WHAT" procedure is through, I return the next date for the job as an OUT Date
parameter.

Thanks,

Yves
and we said...
If I had a string
   x := 'trunc(sysdate)+2+1/24';

and wanted to evaulate it -- i would:

  execute immediate 'select ' || x || ' from dual' INTO my_date_variable;

My_date_variable would have the answser in it.
January 09, 2002 Qingchen -- Thanks for the question regarding "Trigger Question", version 8.1.7
You Asked
I create a trigger on the server side. The trigger is:

create or replace trigger test
before insert on emp
for each row
begin
if :new.hiredate>='01-JAN-02' then
raise_application_error(-20100,'This is an invalid date');
end if;
end;

Emp is the demo table Oracle provide.

SQL> desc emp
Name Null? Type
------------------------------- -------- ----
EMPNO NUMBER(4)
ENAME CHAR(10)
JOB VARCHAR2(20)
MGR NUMBER(4)
HIREDATE DATE
SAL NUMBER(7,2)
COMM NUMBER(7,2)
DEPTNO NUMBER(2)

The trigger works fine if I tried to insert new record under SQLPLUS. It will
fire the trigger if hiredate >=01-JAN-02.
Howerver I can still insert record using Form GUI interface without any error
message even if the hiredate >=01-JAN-02.
My question is: Why the trigger is not fired if user insert data through form?
and we said...
Oh, I would have hoped that we would have learned our lessons from Y2k
already!!!

Please -- never ever again use 2 digits for a year.

Code the trigger as:

if ( :new.hiredate >= TO_DATE( '01-01-2002', 'dd-mm-yyyy' ) )
then
   raise....

Also, never use implicit conversions -- use an explicit format as I did.  If you
can, avoid using JAN, use 01 (better if you ever need to move to a different
language).

When you go into sqlplus, do

select to_char( hiredate, 'dd-mon-yyyy' ) from emp

to see what is ACTUALLY in there, you might be surprised to find 1902 or 1903
etc in there.

ALWAYS (repeat always) use 4 digits!!
January 10, 2002 Sandip -- Thanks for the question regarding "result sets from stored procedures", version
You Asked
Does Oracle support procedures (functions) that return
multiple result sets to ODBC/OLEDB client? If answer is yes,
please give an example of Stored procedure and relevant VB code.

I will truly appreciate your help.

Thanks
and we said...
see
http://osi.oracle.com/~tkyte/ResultSets/index.html
Review &
      Followup

      Rating: 5
Putting it
      together  January 09, 2002
Reviewer:  Harrison
      Picot  from Haymarket VA (Cuba but colder) USA
Hi Tom:

I have tried putting your paper on ResultSets together
with your (similar) articles on reference cursors, and
I can copy your stuff and get it to work just like
you show it (in the reference cursors article, selecting by supplying one of
four column values), but I am not getting it working in real life:  I have:

CREATE OR REPLACE CONTEXT my_ctx using tree.cssearch

CREATE OR REPLACE PACKAGE tree AS

TYPE rc is REF CURSOR;
[snip]
PROCEDURE cssearch (
   p_people_id IN people.PEOPLE_ID%TYPE DEFAULT NULL,
   p_proname IN people.PRONAME%TYPE DEFAULT NULL,
   p_surname IN people.SURNAME%TYPE DEFAULT NULL,
   p_family IN people.FAMILY%TYPE DEFAULT NULL,
   o_result_set OUT tree.rc
);
END tree;
CREATE OR REPLACE PACKAGE BODY tree AS
[snip]

PROCEDURE cssearch (
   p_people_id IN people.PEOPLE_ID%TYPE DEFAULT NULL,
   p_proname IN people.PRONAME%TYPE DEFAULT NULL,
   p_surname IN people.SURNAME%TYPE DEFAULT NULL,
   p_family IN people.FAMILY%TYPE DEFAULT NULL,
   o_result_set OUT tree.rc)

AS
      l_query  varchar2(512)
               default 'select
               people_id, proname || '' ''|| midname || '' '' || surname name,
               TO_CHAR(birthday,''DD-MON-YYYY'') birthday,
               b_city || '',''|| b_state || '' '' || b_country w_born
               from people where 1 = 1 ';

      cursor l_template is select * from people;
      l_rec  l_template%rowtype;
    BEGIN
       if ( p_people_id is NOT NULL ) then
           dbms_session.set_context( 'MY_CTX', 'PEOPLE_ID',
                                     '%'||lower(p_people_id)||'%');
           l_query := l_query ||
                ' AND people_id LIKE
                  sys_context( ''MY_CTX'', ''PEOPLE_ID'' ) ';
       end if;

       if ( p_proname is NOT NULL ) then
           dbms_session.set_context( 'MY_CTX', 'PRONAME',
                                     '%'||lower(p_proname)||'%');
           l_query := l_query ||
                ' AND proname LIKE
                  sys_context( ''MY_CTX'', ''PRONAME'' ) ';
       end if;

       if ( p_surname is NOT NULL ) then
           dbms_session.set_context( 'MY_CTX', 'SURNAME',
                                     '%'||lower(p_surname)||'%');
           l_query := l_query ||
                ' AND surname LIKE
                  sys_context( ''MY_CTX'', ''SURNAME'' ) ';
       end if;

       if ( p_family is NOT NULL ) then
           dbms_session.set_context( 'MY_CTX', 'FAMILY',
                                     '%'||lower(p_family)||'%');
           l_query := l_query ||
                ' AND family LIKE
                  sys_context( ''MY_CTX'', ''FAMILY'' ) ';
       end if;

      OPEN o_result_set FOR l_query;
   END cssearch;
END TREE;

No errors.

Package body created.

but when I try to get the result set out, I get any number of errors like:

hpicot@SARAH> exec :c:=tree.cssearch(p_surname => 'p')
BEGIN :c:=tree.cssearch(p_surname => 'p'); END;

*
ERROR at line 1:
ORA-06550: line 1, column 12:
PLS-00306: wrong number or types of arguments in call to 'CSSEARCH'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored

If you could explain using your reference cursor example (not printing it out as
you do, but returning a result set, and recovering that set like I was trying to
do,  it would be a big help to me and likely, others.  This really does make
queries where you ask a user to supply any details they have, very nice (or
would be if I made it work).

Thanks,

Harrison

          Followup:

Well, you have an OUT parameter:

PROCEDURE cssearch (
   p_people_id IN people.PEOPLE_ID%TYPE DEFAULT NULL,
   p_proname IN people.PRONAME%TYPE DEFAULT NULL,
   p_surname IN people.SURNAME%TYPE DEFAULT NULL,
   p_family IN people.FAMILY%TYPE DEFAULT NULL,
   o_result_set OUT tree.rc)

It is a procedure -- not a function in your example.  Simply:

SQL> variable c refcursor
SQL> exec tree.cssearch( p_surname => 'p', o_result_set => :c )SQL> print c

      Rating: 4
why
      dbms_session.set_context  January 11, 2002

Reviewer:  Kulguru
what cannt I declare a variable and assign the value  say 'surname'  and then
use it to set query

what is the advantage of using dbms_session.set_context

January 10, 2002 Tet Cheng -- Thanks for the question regarding
      "Difference using Auto-increment col and using Attribute key", version
      Version 7.2/7.3 or 8.1.5

      You Asked
Hi Tom,

    I am doing some Data Modelling and my target database is
Oracle.  Unlike other Databases like SQLServer and Access,
Oracle does not define a Auto-Increment Column.  I may need to
use an Auto-Incrementing column as a Primary key for a table but
I can also choose another attribute of datatype Varchar2(30)
which is unique also in the table.  Can you tell me why Oracle
does not implement Auto-increment Columns as a DataType and the
advantages and disadvantages of using both the auto-increment
column and the attribute as primary keys?

Thanks

Tet Cheng

      and we said...
We offer a sequence which has a little more flexibility and
control.  To get an autoincrement column, I would:

create table T ( x int primary key, .... );
create sequence t_seq;
create trigger t_trigger before insert on T for each row
begin
   if ( :new.x is null ) then
      select t_seq.nextval into :new.x from dual;
   end if;
end;
/

to automatically populate when a value for X was not supplied
(allowing me to easily override the "auto" part of it) or I
would:

create table T ( x int primary key, .... );
create sequence t_seq;

and then:

insert into t ( x, ... ) values ( t_seq.nextval, .... );

The nice thing about sequences is when you are populating a
parent/child table -- you have immediate access to t_seq.CURRVAL
which returns the value of the last NEXTVAL you selected.

As for "..and the advantages and disadvantages of using both the
auto-increment column and the attribute as primary keys?"  I
don't fully understand that as an "auto increment" column is in
fact an attribute as well....

Review &
      Followup

      Rating:
      5
  November 12, 2001
Reviewer:
      A reader

      Rating: 3
Can't
      incrementation of sequence be controlled?  January 09, 2002

Reviewer:  Godwin  from Ghana
Hi Tom,
With this example given
i.e create table T(x int primary key...);
create sequence t_seq;
and then;
insert into t(x,...) values (t_seq_nextval, ...);
Now i did this but after i inserted like 3 rows into table t
and later deleted all the 3 rows in table t, I again insert another row but this
time the sequence continued from 4 instead of starting again from 1 since all
the previous 3 rows were deleted.
How can i ensure that the sequence starts from 1 again when the rows have been
deleted.

          Followup:

You cannot, you do not want it to, it would be a totally inefficient thing to
do.

Sequences do not, will not, cannot give you a "gap free" set of numbers.

Sequences do one thing - they give you a unique number in a highly scalable
fashion.

      Rating:
      3
  January 10, 2002
Reviewer:  Saif

Hi GodWin
You can also do it as following

create trigger t_trigger
before insert on table_name
for each row
begin
select max(nvl(col_seq,0))+1
into :new.x
from table_name ;
end ;
/
If any number value is already present in it, it will autometically generate
next number in sequence. If there is no value, as in your case, it will generate
first sequence number and with this method, there is no chance to loose any
sequence number
I think, this will also help you.

AM I right Tom?

          Followup:

try that in a multi user environment some day and see what happens. (hint -- if
you and I insert at the same time, we'll generate the SAME primary key value).

Do this only if you want to build the least scalable, slowest possible system.
If scaling and performance are not any concern to you -- this is perhaps
acceptable.

Also, insert three rows, delete where x = 2; and see the resulting "gap".

The quest for the "holy grail" of a gap free sequence of numbers in a relational
database that is suppose to perform and scale has always (and forever will)
confuse the heck out of me.

January 10, 2002 Chandra -- Thanks for the question regarding
      "Parallel Insert", version 8.1.7.1

      You Asked
Hi Tom
Thank you for the serivice.
I have a large transaction table to be loaded( 60 Millions rows) in a data
warehousing environment which is taking around 19 hours in a regular PL/SQL
inserting one record at a time and commiting for every 20000 records. From
research I found that we can do the same thing using "bulk loading" option which
are
1. Serailly
2. Parallelly
I started using the following methods.
_____________________________________________________________________
CREATE OR REPLACE PROCEDURE WVDBA.bulk_load_serial AS
BEGIN
  INSERT /*+ APPEND */
    INTO wvdba.gift_detail_temp (
         POST_SEQ_NBR,
         EMPNO,
         ENAME)
  SELECT
         A.PST_POST_SEQ_NR,
         B.EMPNO,
         C.ENAME
 FROM  STAGE.DONOR_INC_DTL A,
       STAGE.POSTING_HIST  B,
       wvdba.ACCT_DONOR_XREF C
 WHERE A.PST_POST_SEQ_NR = B.POST_SEQ_NR (+)
   AND A.INC_DONOR_ACCT  = B.DONOR_ACCT (+)
   AND A.INC_DONOR_ACCT  = C.PRAISE_ACCT_NUM
   AND A.INC_AMT != 0;
  COMMIT;
END;
______________________________________________________________________
This process is taking around 3 hours which is good.
IS there anything I should worry about like
   1. Size of the rollback segements
   2. TEMP tablespace size etc.,
I also understand that We can use PARALLEL insert instead of SERAIL
  INSERT .
Do I need to partition the target table so that the engine would
   write into serveral areas on disk and make use of Parallel querry
   option?

Please let me know on these topics. Please bear with me if I am more elaborative
or less elaborative
Thanks,
Chandra

      and we said...
read:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c21dlins.htm#365
to understand the direct load insert and parallel direct load insert.

and

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1669403535933
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2417875549841
for advice on what to do with those indexes if you have any before you do this
insert into...

January 10, 2002 sushil -- Thanks for the question regarding
      "intermedia not working for ","", version 8.1.7

      You Asked
Hi Tom,

I have checked through your examples for printjoins but this does not
seems to be working for ",".

Is there any way to remove "," from the stopwords list.
I tried setting attribute but not working.

tks
sushil.

      and we said...
Sure it does -- you are probably doing the query wrong.

the comma is a query operator by default.  You must escape it in order to search
for it.  For example:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( data varchar2(50) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 'How,Now,Brown,Cow' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 'How Now Brown Cow' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2      ctx_ddl.create_preference('my_lexer','BASIC_LEXER');
  3      ctx_ddl.set_attribute('my_lexer','printjoins',',');
  4  end;
  5  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_ind_ctx on t(data) indextype
is ctxsys.context parameters('lexer my_lexer');

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where contains( data, 'How' )
> 0;

DATA
--------------------------------------------------
How Now Brown Cow

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where contains( data,
'How\,Now\,Brown\,Cow' ) > 0;

DATA
--------------------------------------------------
How,Now,Brown,Cow

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where contains( data,
'{How,Now,Brown,Cow}' ) > 0;

DATA
--------------------------------------------------
How,Now,Brown,Cow

ops$tkyte@ORA817DEV.US.ORACLE.COM>

That shows the printjoin does in fact work. if you run this search:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where contains( data,
'How,Now,Brown,Cow' ) > 0;

DATA
--------------------------------------------------
How Now Brown Cow

 the comma was interpreted as an operator in the query -- not as a character
to be considered part of the search string

Review &
      Followup

      Rating:
      4
Tks  January 10, 2002
Reviewer:
      A reader
Tks tom.

Infact I had like {,} .

January 10, 2002 Sam -- Thanks for the question regarding
      "Different kinds of loops( control structures)", version 8.1.7

      You Asked
Tom

I use a lot of implicit cursors( well, I visit your website everyday, I should
atleast do that); and I use  a lot of cursor for loops, which are simple and
very easy to code.

1.But I have very few times or never coded(interestingly)  a do while loop can
you give an example of the while loop. Do we have to give an explicit exit
command for the while loop.

Can you tell me about it .

2.Is it true that with 8i cursors have a new attribute called
cursor%bulkrowcount. Do implicit cursors also have this attribute.

Thank you

      and we said...
1) we do not have a "do .... while" loop there is a "while <condition> loop ...
end loop"

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a77069/03_struc.htm#655
explains it and has examples.

No, you need not give an explicit exit.  However, make sure the <CONDITION>
always has a chance to evaluate to false, else you just coded an infinite loop.

2) read:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a77069/04_colls.htm#28245
it is valid ONLY for implicit cursors.
January 10, 2002 Ashok -- Thanks for the question regarding "Data
      Trasfer from Production To TEst", version 8.1

      You Asked
I need to transfer Data from Production to Test database to support test and
development of new applications ... I need to know the most efficient way of
doing this so that integrity constraints can be taken care of and  I need to
tranfer a selective amount of data .

I am aware of these  methods

1. Take a export from Production and Import it into Test database ...
   But in this method programming control is not possible

2. Using the UTL_FILE write a data to file and agiain read it from the same file
using a UTL_FILE utiilty

3. Pro*C programs which connects to both the database and does this thing

4.HAving a db link between the two database and just selecting and trasfering
the data

Please let me know the pros and cons of each methid

Thanks and Regards

Ashok

      and we said...
1) i don't know what you mean by "programming control". if you mean it cannot be
selective, there is in fact a query=  parameter to export.

My personal favorite is the sqlplus copy command.  Its in the "none of the
above" category.  Doesn't need dblinks, no need to write code, very fast -- and
can be selective.

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a82950/ch8.htm#1028839
...
Review
      & Followup

      Rating: 2
How do you do
      copy without having dblinks  January 10, 2002

Reviewer:  Murali  from MA, USA
Can you just elaborate on that.

          Followup:

read the link I convienently provided in the answer.

      Rating: 3
Thanks
        January 10, 2002
Reviewer:  ashok
       from Pg
Tom ,
I want to understand how do u take care of integrity constraint .. for example

If I am trasferring the data of table A and it refers to table b
how to resolve this problem

Thanks a lot

Ashok

          Followup:

disable the constraint, copy the data, re-enable it.

      Rating:
      5
hmmm...  January 11, 2002
Reviewer:
      Bricklen Anderson  from BC, Canada
that link WAS rather conveniently placed...

;-)

I'm sure glad that there are people out there that are willing to help others
out with their questions and problems.

Cheers Tom,

Bricklen

January 10, 2002 Bhavani -- Thanks for the question regarding
      "Java Script with PL/SQL", version 8.1.7

      You Asked
Hi Tom,

We are using Oracle Web Application Server as our reporting tool.  Where can i
find the documentaion which explains clearly how to integrate Java Script with
PL/SQL (HTP and HTF Packages) / How to pass variables from java script to PL/SQL
and vice versa etc. etc..

I found some documents in the Net, but they are not very useful really.

Thank you Tom for your patience and valuble replies.

Sincere Regards

Bhavani

      and we said...
I'm always confused by this question.  You do not really "pass" variables to and
from java script and PLSQL.

PLSQL is a server side language.  It is running in the database.  It generates
an HTML page.  This HTML page might contain some code called java script in it.
That is not really relevant however, it's all just a printed page from the
perspective of PLSQL.

Once the page is generated, it is given to a web browser.  The web browser knows
nothing about PLSQL, it cannot tell really of the page came from the operating
systems file system or from plsql (nor does it care).

The browser gets the page and upon finding some javascript in it, it "compiles"
it and runs it.  The javascript is run long after the PLSQL finished the page
and went away.  There is no PLSQL for the javascript to "call".  When in PLSQL,
there is no javascript to "call" (the browser hasn't gotten the page yet).

The two run in totally different places, totally.  Pretend the page that PLSQL
generated was actually just a file stored in the file system.  Javascript would
have nothing to interact with -- same it true here.

Now, PLSQL, when writing the javascript, can easily encode values into the page.
 You do this with simple string concatentation:

 htp.p( '<script>
         <!-- Comment out script for old browers
         //onClick event handler
         function do_submit() {
              document.theForm.q.value = "' || some_plsql_variable || '"
                    + document.theForm.elements[0].value;
              document.theForm.submit();
         }
        //-->
        </script>' );

so, there is PLSQL writing javascript and the java script will incorporate the
value of SOME_PLSQL_VARIABLE in it.

In a way this shows how javascript will pass values back to PLSQL.  This script
is used to submit the form named "theForm" in the current document.  Javascript
is setting the value of the field "q" in that form to some specific value.  If
that form is submitted to a PLSQL routine - -that plsql routine will recieve q
with that value.

Hope that helps....

Review &
      Followup

      Rating:
      5
  January 11, 2002
Reviewer:  Igor
       from France
Hi

Could you extend this part:

"If that form is submitted to a PLSQL routine - -that plsql routine will recieve
q with that value."

        Thanks

                                     Igor

January 10, 2002 Cesar -- Thanks for the question regarding "View
      question", version 8.1.6

      You Asked
Hi Tom,

I hope you can give me your opinion

Example:

create table information
(code number, mount number)
/

insert into information values (1, 4567)
/
insert into information values (2, 500)
/
insert into information values (3, 2365)
/
insert into information values (4, 17)
/
insert into information values (5, 1973)
/

select * from information

     CODE     MOUNT
--------- ---------
        1      4567
        2       500
        3      2365
        4        17
        5      1973

+ I need this view:

 CODE  MOUNT  B500  B200  B100  B50  B20  B10  B5  B1
----- ------ ----- ----- ----- ---- ---- ---- --- ---
    1   4567     9     0     0    1    0    1   1   2
    2    500     1     0     0    0    0    0   0   0
    3   2365     4     1     1    1    0    1   1   0
    4     17     0     0     0    0    0    1   1   2
    5   1973     3     2     0    1    1    0   0   3

+ Where

  B500 = trunc(MOUNT/500)
  B200 = trunc((MOUNT-B500*500)/200)
  B100 = trunc((MOUNT-B500*500-B200*200)/100)
  B50  = trunc((MOUNT-B500*500-B200*200-B100*100)/50)
  B20  = trunc((MOUNT-B500*500-B200*200-B100*100-B50*50)/20)
  B10  = trunc((MOUNT-B500*500-B200*200-B100*100-B50*50-B20*20)/10)
  B5   = trunc((MOUNT-B500*500-B200*200-B100*100-B50*50-B20*20-B10*10)/5)
  B1   = trunc((MOUNT-B500*500-B200*200-B100*100-B50*50-B20*20-B10*10-B5*5)

+ Now my view looks very very bad, What do you think?

select
  code,
  mount,
  trunc(mount / 500) B500,
  trunc((mount - trunc(mount / 500) * 500) / 200) B200,
  trunc((mount - trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200) / 100) B100,
  trunc((mount -
    trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200 -
    trunc((mount - trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200) / 100) * 100) / 50)
B50,
  trunc((mount -
    trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200 -
    trunc((mount - trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200) / 100) * 100 -
    trunc((mount -
    trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200 -
    trunc((mount - trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200) / 100) * 100) / 50) *
50) / 20) B20,
  trunc((mount -
    trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200 -
    trunc((mount - trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200) / 100) * 100 -
    trunc((mount -
    trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200 -
    trunc((mount - trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200) / 100) * 100) / 50) *
50 -
    trunc((mount -
    trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200 -
    trunc((mount - trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200) / 100) * 100 -
    trunc((mount -
    trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200 -
    trunc((mount - trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200) / 100) * 100) / 50) *
50) / 20) * 20) / 10) B10,
  trunc((mount -
    trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200 -
    trunc((mount - trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200) / 100) * 100 -
    trunc((mount -
    trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200 -
    trunc((mount - trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200) / 100) * 100) / 50) *
50 -
    trunc((mount -
    trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200 -
    trunc((mount - trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200) / 100) * 100 -
    trunc((mount -
    trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200 -
    trunc((mount - trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200) / 100) * 100) / 50) *
50) / 20) * 20 -
    trunc((mount -
    trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200 -
    trunc((mount - trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200) / 100) * 100 -
    trunc((mount -
    trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200 -
    trunc((mount - trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200) / 100) * 100) / 50) *
50 -
    trunc((mount -
    trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200 -
    trunc((mount - trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200) / 100) * 100 -
    trunc((mount -
    trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200 -
    trunc((mount - trunc(mount / 500) * 500 -
    trunc((mount - trunc(mount / 500) * 500) / 200) * 200) / 100) * 100) / 50) *
50) / 20) * 20) / 10) * 10)/ 5) B5,
    trunc(mod(mount, 5)) B1
from information
/

Thanks in advance

      and we said...
first -- thank you thank you thank you for giving me a create table and
inserts!!! that makes is soooo much easier.

You can use inline views to simplify your query.  Each inline view builds on the
prior one:

SQL> select i.*,
trunc((MOUNT-B500*500-B200*200-B100*100-B50*50-B20*20-B10*10-B5*5)) b1
  2    from
  3  ( select i.*,
trunc((MOUNT-B500*500-B200*200-B100*100-B50*50-B20*20-B10*10)/5) b5
  4      from
  5  ( select i.*, trunc((MOUNT-B500*500-B200*200-B100*100-B50*50-B20*20)/10)
b10
  6      from
  7  ( select i.*, trunc((MOUNT-B500*500-B200*200-B100*100-B50*50)/20) b20
  8      from
  9  ( select i.*, trunc((MOUNT-B500*500-B200*200-B100*100)/50) b50
 10          from
 11  ( select i.*, trunc((MOUNT-B500*500-B200*200)/100) b100
 12      from
 13  ( select i.*, trunc((mount-b500*500)/200) b200
 14      from
 15  ( select code, mount, trunc(mount/500) b500
 16      from information
 17  ) i) i) i) i) i) i) i
 18  /

      CODE      MOUNT B500 B200 B100 B50 B20 B10  B5  B1
---------- ---------- ---- ---- ---- --- --- --- --- ---
         1       4567    9    0    0   1   0   1   1   2
         2        500    1    0    0   0   0   0   0   0
         3       2365    4    1    1   1   0   1   1   0
         4         17    0    0    0   0   0   1   1   2
         5       1973    3    2    0   1   1   0   0   3

Review &
      Followup

      Rating:
      4
Thanks!!!  January 10, 2002
Reviewer:
      Cesar Salas  from Mexico

      Rating: 5
Very good
      solution  January 10, 2002
Reviewer:
      Clemens  from Dortmund, Germany
Hi Tom,

what about using mod?

select code, mount,
    trunc(mount/500) b500,
    trunc(r500/200) b200,
    trunc(r200/100) b100,
    trunc(r100/50) b50,
    trunc(r50/20) b20,
    trunc(r20/10) b10,
    trunc(r10/5) b5,
    mod(r10,5) b1
     from
  ( select i.*, mod(r20,10) r10
      from
  ( select i.*, mod(r50,20) r20
      from
  ( select i.*, mod(r100,50) r50
       from
 ( select i.*, mod(r200,100) r100
      from
  ( select i.*, mod(r500,200) r200
      from
  ( select code, mount, mod(mount,500) r500
      from information
  ) i) i) i) i) i)
  /

Using (with some integer n)
mod(r50,10)
= mod( n*20+mod(r50,20), 10)
= mod( n*20,10)    + mod(r50,10)
= 0        + mod(r50,10)
and similar equations we get:

select code, mount,
    trunc(mount/500) b500,
    trunc(r500/200) b200,
    trunc(mod(r500,200)/100) b100,
    trunc(r100/50) b50,
    trunc(r50/20) b20,
    trunc(mod(r50,20)/10) b10,
    trunc(r10/5) b5,
    mod(r10,5) b1
      from
  ( select code, mount,
    mod(mount,500) r500,
    mod(mount,100) r100,
    mod(mount,50) r50,
    mod(mount,10) r10
      from information)
  /

Best regards,
Clemens

January 10, 2002 -- Thanks for the question regarding "extracting
      ddl", version 8.1.7

      You Asked
The imp show=Y in Oracle 8.1.7 breaks up DDLs into a
fixed length line. This causes some words to be broken down into
meaningless pieces.
e.g.
 "CREATE TABLE "DEF$_AQCALL" ("Q_NAME" VARCHAR2(30), "MSGID" RAW(16), "CORRID"
 "" VARCHAR2(128), "PRIORITY" NUMBER, "STATE" NUMBER, "DELAY" DATE, "EXPIRATI"
 "ON" NUMBER, "TIME_MANAGER_INFO" DATE, "LOCAL_ORDER_NO" NUMBER, "CHAIN_NO" N"
 "UMBER, "CSCN" NUMBER, "DSCN" NUMBER, "ENQ_TIME" DATE, "ENQ_UID" NUMBER, "EN"
 "Q_TID" VARCHAR2(30), "DEQ_TIME" DATE, "DEQ_UID" NUMBER, "DEQ_TID" VARCHAR2("
 "30), "RETRY_COUNT" NUMBER, "EXCEPTION_QSCHEMA" VARCHAR2(30), "EXCEPTION_QUE"
 "UE" VARCHAR2(30), "STEP_NO" NUMBER, "RECIPIENT_KEY" NUMBER, "DEQUEUE_MSGID""
 " RAW(16), "USER_DATA" BLOB) USAGE QUEUE  PCTFREE 10 PCTUSED 40 INITRANS 1 M"
 "AXTRANS 255 LOGGING STORAGE(INITIAL 262144 NEXT 262144 MINEXTENTS 1 MAXEXTE"
 "NTS 505 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TA"
 "BLESPACE "TOOLS" LOB ("USER_DATA") STORE AS  (TABLESPACE "TOOLS" ENABLE STO"
 "RAGE IN ROW CHUNK 8192 PCTVERSION 0 NOCACHE  STORAGE(INITIAL 262144 NEXT 26"
 "2144 MINEXTENTS 1 MAXEXTENTS 505 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS "
 "1 BUFFER_POOL DEFAULT))"
 "CREATE INDEX "DEF$_TRANORDER" ON "DEF$_AQCALL" ("CSCN" , "ENQ_TID" )  PCTFR"
 "EE 10 INITRANS 2 MAXTRANS 255 STORAGE(INITIAL 262144 NEXT 262144 MINEXTENTS"
 " 1 MAXEXTENTS 505 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL D"
 "EFAULT) TABLESPACE "TOOLS" LOGGING"

That meanas we cannot just delete the double quotes and execute them.
How can we reformat them ?

      and we said...
imp userid=u/p .... indexfile=foo.sql

edit foo.sql and it'll all be there nice and neat (with REM's in front of it but
just c/REM // and they go away....
Review &
      Followup

      Rating: 5
Good Tip, thank
      you Tom  January 10, 2002
Reviewer:
      A reader  from Switzerland

      Rating: 3
I may be
      wrong...  January 10, 2002
Reviewer:
      A reader  from ca, usa
I may be wrong, but I'm under the impression that using "indexfile" only
produces the table and index DDL, not all the constraints, sequences etc.?? If
so, a very useful tip I found on this site some time ago (can't find it now)
from Conner was to user the strings command. That way you get everything
possible from the dump file - each command on a single line.

Quote:
Rather then indexfile, it can sometimes be very useful to use 'strings' (unix)
on an "exp rows=n" dump file - mainly because the create commands are not broken
over multiple lines.  This makes removing the storage clause (with something
like sed 's/STORAGE(.*)//g' easier.

January 10, 2002 Sonya -- Thanks for the question regarding
      "Statspack & dbms_job", version 8.1.7

      You Asked
I have installed statspack and would like to schedule the snapshot Monday-Friday
starting at 9 a.m. until 11 p.m., but run it in two hour intervals.  I haven't
used dbms_jobs before, so I am somewhat lost as to where to begin.  Do I write a
procedure to call dbms_job.submit?  Any guidance will be appreciated.

      and we said...
you wouldn't happen to have my book would you?  If so, I have a full section on
this in the back of the book.  It shows how to do this sort of custom
scheduling.

By far the easiest way is to have the job run every two hours -- and just have
it look at the current date/time and see if it should run.  For example, if the
first line of the job was

  if ( to_char(sysdate,'hh24') NOT between 9 and 23 OR
       to_char(sysdate,'Day' ) in ( 'Sat', 'Sun' ) )
  then
      return;
  end if;

then you can just schedule it via:

variable n number
begin
   dbms_job.submit( :n, 'your_procedure;', sysdate,
                    'trunc(sysdate,''hh24'')+2/24' );
end;
/

(you would run that in sqlplus)
Review &
      Followup

      Rating: 4
Statspack &
      dbms_job  January 10, 2002
Reviewer:  Sonya
      Whtie  from Warner Robins, GA
I refer to your web site many times and found the reponse to my question very
helpful and informative.

January 10, 2002 Ringo -- Thanks for the question regarding
      "Trigger to trap who did what via sql_text", version 816

      You Asked
Tom,

I am using the below trigger to trap who did what (the sql_text) to the below
table. But it only capture itself! In other words, I did't see other, even I try
insert update on other session. Am I missed something in the where clause?

Thanks in advance for your help..

Quote
========
declare
  cursor c_find_record is
    select a.sql_text,
         b.username,
         b.osuser,
         b.machine,
         b.terminal ,
         sysdate
    from v$sqltext a, v$session b, dual c
    where a.sql_text like '%TABLE_NAME%'
      and b.sql_hash_value=a.hash_value
      and a.address=b.sql_address;
 BEGIN
    FOR v_data in c_find_record LOOP
        insert into sys.t_orgrel (sql_text, user_name, osuser, machine,
terminal, log_date)
        values ( v_data.sql_text, v_data.username, v_data.osuser,
v_data.machine, v_data.terminal, v_data.sysdate);
    END LOOP;
 END;

      and we said...
well, the ONLY thing this trigger could capture would be the sql some OTHER
session is executing.

Your session will be executing this query, hence in v$session, your sql_address
will be the address of this query.  Only if some other session just HAPPENED to
be executing some sql against this table would you capture that sql.

You really are looking for new features of 9i (what you are trying to do is
fairly futile in 8i and before, the necessary information just isn't there for
you, you cannot capture the SQL reliably -- which means you cannot capture the
sql really, you'll always be "guessing").

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2293525330079
...
Review
      & Followup

      Rating: 4
trigger to find
      out who did what to table  January 10, 2002

Reviewer:  Ringo  from Hong Kong
Thanks Toms, looks like we need to guess for a while as still need to test out
9i for another 2-3 months, before we can roll it out. By the way, any
possibilities to download your presentation of 9i?

We are having difficuity to view it real time and we hate to miss it!

          Followup:

http://asktom.oracle.com/~tkyte/searchdb.zip

just page forward as I say "next slide please"

January 11, 2002 Partha -- Thanks for the question regarding
      "Minute difference up to one minute from dates", version 8.1.6

      You Asked
Tom,

I have follwing dates and i want's to display difference in minutes,
when i use follwing query , i'm not getting why?

E_COMPLETION_TIME = '12/04/2001 11:32:00'

E_BEGIN_TIME = '12/04/2001 11:31:00'

select TRUNC(MOD(("LMF"."EVENT_DETAILS"."E_COMPLETION_TIME" -
"LMF"."EVENT_DETAILS"."E_BEGIN_TIME")*1440,60)) Minutes,
from event_details;

Actually i should get one minute difference in the result, but i'm getting 0
why?

Can you please give me the explaination.

Thanks in advance.

      and we said...
It is small errors introduced in the mixed mode arithmetic. It sees "ints",
"reals", large scale, small scale numbers and rounding errors creep in.

If you use 24*60 (which I personally find more readable), the math works.  If
you through in a little extra math to force the constant to a real, it works:

ops$tkyte@ORA815.US.ORACLE.COM> select x, y from t;

X                   Y
------------------- -------------------
12/04/2001 11:32:00 12/04/2001 11:31:00

ops$tkyte@ORA815.US.ORACLE.COM> select
  2  to_char( (x-y)*1440, '9.99999999999999999999999999999999999999999' )
  3  from t;

TO_CHAR((X-Y)*1440,'9.9999999999999999999999
--------------------------------------------
  .99999999999999999999999999999999999999940

ops$tkyte@ORA815.US.ORACLE.COM> select
  2  to_char( round((x-y)*1440,5), '9.99999999999999999999999999999999999999999'
)
  3  from t;

TO_CHAR(ROUND((X-Y)*1440,5),'9.9999999999999
--------------------------------------------
 1.00000000000000000000000000000000000000000

ops$tkyte@ORA815.US.ORACLE.COM> select
  2  to_char( (x-y)*24*60, '9.99999999999999999999999999999999999999999' )
  3  from t;

TO_CHAR((X-Y)*24*60,'9.999999999999999999999
--------------------------------------------
 1.00000000000000000000000000000000000000000

ops$tkyte@ORA815.US.ORACLE.COM> select
  2  to_char( (x-y)*2*1440/2, '9.99999999999999999999999999999999999999999' )
  3  from t;

TO_CHAR((X-Y)*2*1440/2,'9.999999999999999999
--------------------------------------------
 1.00000000000000000000000000000000000000000

See the (x-y)*1440 is just a little smaller than one (you normally won't see
that)

Consider:

ops$tkyte@ORA815.US.ORACLE.COM> select TRUNC(MOD((x - y)*1440,60)) from t;

TRUNC(MOD((X-Y)*1440,60))
-------------------------
               .000000000

ops$tkyte@ORA815.US.ORACLE.COM> select MOD((x - y)*2*1440/2,60) from t;

MOD((X-Y)*2*1440/2,60)
----------------------
           1.000000000

ops$tkyte@ORA815.US.ORACLE.COM> select MOD(round((x - y)*1440,5),60) from t;

MOD(ROUND((X-Y)*1440,5),60)
---------------------------
                1.000000000

ops$tkyte@ORA815.US.ORACLE.COM> select MOD((x - y)*24*60,60) from t;

MOD((X-Y)*24*60,60)
-------------------
        1.000000000

rounding to 5 decimal places (biggest number of decimals in a day with seconds),
can correct this as well.
Review & Followup

      Rating: 5
Minute
      Difference up to one  January 11, 2002
Reviewer:
      A reader

January 11, 2002 Charles -- Thanks for the question regarding
      "Incremental backup off a split mirror image of data files", version 8.1.7

      You Asked
Hi Tom,

I want to run an incremental rman backup off a split mirror. The split volumes
will be mounted on a host other than the production database server. I want to
run the rman backup off the other host, so we dont incur performance hits on the
production server. Is this doable? When I tried doing this, performed the
following steps.
1. Alter tablespace xx begin backup;
2. Split mirrors
3. Alter tablespace xx end backup;
4. Alter database backup controlfile to ..
5. Split volumes moved to second host.
6. startup mount
7. rman target sys/... blah blah
RMAN> register database;
8. RMAN> list incarnation of database;
List of Database Incarnations
DB Key Inc Key DB Name DB ID CUR Reset SCN Reset Time
------- ------- -------- ---------------- --- ---------- ----------
18431 18432 TEST 1741522178 YES 1 17-OCT-01
8. RMAN> List backup of database;
RMAN-00601: fatal error in recovery manager
RMAN-03012: fatal error during compilation of command
RMAN-03028: fatal error code: 600
RMAN-03013: command type: list
RMAN-00600: internal error, arguments [6960] [] [] [] []
This should have returned no rows, not an internal error.

The goal is to make an incremental backup off a split mirror set of disks.  Is
this supported?

Thanks,
Charles

      and we said...
Before you split the mirrors, you should also issue an ALTER SYSTEM SUSPEND;

From the backup and recovery guide:

<quote>
Making Backups in a Suspended Database

After a successful database suspension, you
can back up the database to disk or break the mirrors. Because suspending a
database does not guarantee immediate termination of I/O, Oracle recommends
that you precede the SUSPEND statement with a BEGIN BACKUP statement to
place the tablespaces in hot backup mode.

You must use conventional operating system backup methods to back up split
mirrors. RMAN cannot make database backups or copies because these operations
require reading the datafile headers. After the database backup is finished or
the mirrors are re-silvered, then you can resume normal database operations
using the RESUME statement.

Backing up a suspended database without splitting mirrors can cause an extended
database outage because the database is inaccessible during this time. If
backups are taken by splitting mirrors, however, then the outage is nominal. The
outage time depends on the size of cache to flush, the number of datafiles, and
the time required to break the mirror.
</quote>

That quote was taken from:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76993/osbackup.htm#940
So, I you won't be using many RMAN features on the split mirror database.  You
can use RMAN to basically "copy the file".  You'll be taking advantage of the
recovery catalog but thats about it.  RMAN will just be copying files.   You can
also use RMAN with a standby (data guard) database to offload backups to another
machine.

see

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76990/usingrma.htm#441865
for details on backing up a split mirror database with RMAN.
Review &
      Followup

      Rating:
      5
  April 15, 2002
Reviewer:  Raj
       from New York
Tom,

This was a very useful reply. I realize now that RMAN and split-mirror backups
are for different purposes. RMAN is great for doing incremental backups and also
to minimize the backup volume and time. But I have never used split mirror
backups. In what kind of environments will it be useful. Is it primarily useful
for a zero transaction datawarehouse or a regular OLTP environment. What are the
advantages of going for a split mirror backup strategy.

Thank you verymuch.

January 11, 2002 Marcelo -- Thanks for the question regarding "PL
      Performance", version 8i

      You Asked
Hi Tom,
please advice...

Considering the structures below, which is better and why.
The samples return just one line, but think about a similar code returning
thousands of lines.

1.
declare
  tab varchar2(30) := 'ANY TABLE';

begin
  for vC in (select table_name, n
               from user_tables, (select sum(data_length) n
                                    from user_tab_columns
                                   where table_name = tab)
              where table_name = tab) loop
      dbms_output.put_line(vC.table_name || ' - ' || vC.n);
  end loop;
end;

2.
declare
  tab varchar2(30) := 'ANY TABLE';
  tot number;

begin
  for vC in (select table_name
               from user_tables
              where table_name = tab) loop
      select sum(data_length)
        into tot
        from user_tab_columns
       where table_name = tab;
  end loop;
  dbms_output.put_line(tab || ' - ' || tot);
end;

Thanks for your help,
Marcelo

      and we said...
My mantra:

if you can do it in a single statement, DO IT.

I vote for #1.  It requires less context switching between the database kernel
and the plsql runtime.

January 11, 2002 -- Thanks for the question regarding "PL/SQL
      Calling Stored Procedure", version 8.0.5.1

      You Asked
Tom,

We have an 8.0.5.1 database.  I'm trying to call a stored procedure in an update
trigger, with the name and/or owner of the procedure unknown at compile time
(the values will be known in the :new variables).  I would rather not use
dynamic sql within the trigger since the code in the stored proc is quite large.

Trigger text:

begin
  if (:new.col1 = 'T') then
     :new.col2.stored_proc_owned_by_col2;
  else
     :new.col3.stored_proc_owned_by_col3;
  end if;
end;

I know you can't bind the owner or part of the stored proc name
in the pl/sql.  Is there any way to call the procedure without
dynamic sql?

Thanks, Dana

      and we said...
You NEED to use dynamic sql.  The size of the code in the stored proc is totally
not relevant here.

You need to dynamically invoke some routine.  By definition you do not know at
compile time WHAT routine -- hence it is dynamic.  We cannot compile the above
code because all code that is compiled is static -- it is known at compile time
what is going to be called (so we can setup dependencies, check permissions,
etc).

You need to use dbms_sql in this case if you are going to go down this path.
Review &
      Followup

      Rating: 3
Only the owner
      is dynamic  January 11, 2002
Reviewer:  Dana
      Fong  from Emeryville, CA USA
Tom:

In my case, the contents of the stored proc is static,
but the owner of the proc is dynamic.  If I use dbms_sql utilites to bind the
owner and execute the proc I get the ora-01031, invalid SQL statement.

I need to reference the owner of the proc, since the proc grants privileges as
that owner.  Any way to pull this off, even with dynamic SQL?

Thanks.

          Followup:

You cannot BIND those things, you must concatenate them.

  execute immediate 'begin ' || :new.owner || '.procedurename; end;';

You can only use binds where a character string could be placed.  For example,
we know that this is OK:

   begin owner.procedure( :x ); end;

since:

   begin owner.procedure( 'hello' ); end;

is OK, :x could be 'hello' and that would be syntactically ok.  However, we know
that:

   begin  :x.procedure( 'hello' ); end;

is NOT ok since:

   begin 'OWNER'.procedure( 'hello' ); end;

is not valid syntax.  You cannot bind identifiers (we need different PLANS,
different security checks, different everything when identifiers change!)

January
      12, 2002 Aurokiam -- Thanks for the
      question regarding "RE: [Fwd: Ask Tom Submission] importing tables into a
      different tablespace", version

      You Asked
I am trying to export tables from
big database to a smaller database
by adding more tablespaces in small database.
I have a situation where the objects on small database
are all on one tablespace.

I am just going to truncate the tables
and then import from big database to small database.
How do I change my objects to different tablespace after
truncating the
table.

Please suggest me any information which would be of help.

      and we said...
If I understand, you have a "big" database with tables in many
tablespaces and in the target database - you wish all objects to
go into a single tablespace.  If this is correct, read on.  If
not -- please clarify the question.  In general, the following
shows how you can use imp to imp objects into any given
tablespace (including importing all objects into a single
tablespace).

The easiest method in my experience is to precreate the objects
in the target database.  Take the DDL scripts you have and
global change the tablespaces to be the single tablespace you
want everything in.  Run this DDL in the target database.  Then,
run the import with "ignore=y".  This will ignore object
creation errors (which you will get since the objects already
exist).

Another method is to import using a user from which 'unlimited
tablespace' has been revoked and explicit quotas are used.  You
would assign to this use a DEFAULT and TEMPORARY tablespace,
revoke unlimited tablespace from them, and give them unlimited
quotas on their default and temporary tablespaces.  In this way,
IMP has no choice but to create all objects in the users default
tablespace.  If you want, you can get 'fancy' and make it so
that tables goto one tablespace and indexes to another by doing
2 imps -- one with INDEXES=NO to get the table data in, altering
the users default tablespace and adjusting their quotas
appropriately and then doing an import indexes=y rows=n to get
the indexes in.

Here is an example of how to export data from one tablespace and
then import it into another.  I used one database with 2
different accounts but the concept applies to 2 databases as
well.

SQL> REM here is the user we will IMPORT into
SQL> create user demo_imp identified by demo_imp;
User created.

SQL> grant connect, resource to demo_imp;
Grant succeeded.

SQL> REM *this is key* if you do not do this
SQL> REM quotas do not work...
SQL> revoke unlimited tablespace from demo_imp;
Revoke succeeded.

SQL> REM Here is the tablespace we wish to IMPORT into:
SQL> alter user demo_imp quota unlimited on new_tablespace;
User altered.

SQL> REM This is their temporary tablespace
SQL> alter user demo_imp quota unlimited on temp;
User altered.

SQL> REM make their default and temporary assignments...
SQL> alter user demo_imp default tablespace new_tablespace
temporary tablespace temp;
User altered.

SQL> REM We will export from SCOTT.  Scott has his data in the
USERS
SQL> REM Tablespace.  IMP would by default try to create objects
in USERS

SQL> connect scott/tiger
Connected.
SQL> select segment_name, segment_type, tablespace_name from
user_segments;

SEGMENT_NAME                   SEGMENT_TYPE
TABLESPACE_NAME
------------------------------ ------------------
------------------------------
DEPT                           TABLE              USERS
EMP                            TABLE              USERS
BONUS                          TABLE              USERS
SALGRADE                       TABLE              USERS
ACCOUNT                        TABLE              USERS
RECEIPT                        TABLE              USERS
IMAGE                          TABLE              USERS
PK_DEPT                        INDEX              USERS
PK_EMP                         INDEX              USERS
IMAGE_PK                       INDEX              USERS

10 rows selected.

SQL> host exp userid=scott/tiger "tables=(emp,dept)"

SQL> REM So, now lets imp into demo_imp and see what happens...

SQL> connect demo_imp/demo_imp
Connected.
SQL> host imp userid=demo_imp/demo_imp full=y

SQL> select segment_name, segment_type, tablespace_name from
user_segments;

SEGMENT_NAME                   SEGMENT_TYPE
TABLESPACE_NAME
------------------------------ ------------------
------------------------------
EMP                            TABLE              NEW_TABLESPACE
DEPT                           TABLE              NEW_TABLESPACE
PK_EMP                         INDEX              NEW_TABLESPACE
PK_DEPT                        INDEX              NEW_TABLESPACE

The data got put into the tablespace we wanted.

A third option is to make sure the database you are going to
import into has no tablespaces with the same name as the one you
exported from.  for example -- After I did the above, I exported
the data from demo_imp and dropped the tablespace
"new_tablespace".  then I created yet another account and
imported the data as follows:

SQL> host exp userid=demo_imp/demo_imp "tables=(emp,dept)"

SQL> connect tkyte/tkyte
Connected.

SQL> drop user demo_imp cascade;
User dropped.

SQL> drop tablespace new_tablespace;
Tablespace dropped.

SQL> grant connect, resource to another_user identified by
another_user;
Grant succeeded.

SQL> alter user another_user default tablespace USERS temporary
tablespace TEMP;
User altered.

SQL> host imp userid=another_user/another_user full=y

SQL> connect another_user/another_user
Connected.

SQL> select segment_name, segment_type, tablespace_name from
user_segments;

SEGMENT_NAME                   SEGMENT_TYPE
TABLESPACE_NAME
------------------------------ ------------------
------------------------------
EMP                            TABLE              USERS
DEPT                           TABLE              USERS
PK_EMP                         INDEX              USERS
PK_DEPT                        INDEX              USERS

So, since we no longer had the tablespace "new_tablespace" to
import into -- IMP was forced to import into the users default
tablespace.  We did not need to revoke unlimited tablespace in
this case -- we just needed NOT to have the tablespace
"new_tablespace"...

You cannot change the tablespace of an already created object in
releases of Oracle less then 8.1.  In Oracle8i, release 8.1 you
can move objects easily via:

alter table emp move tablespace new_tablespace;
alter index emp_idx rebuild tablespace new_tablespace;

The alter index rebuild works in 7.3 and up though.  Tables
cannot be moved this way in 7.3/8.0.

Review &
      Followup

      Rating: 5
Saved me a lot
      of time  November 09, 2001
Reviewer:
      Vincent  from Herndon, VA USA
Your second option saved me a lot of time.  I no longer have to write a script
which will change tablespace setting from the ddl file that will be generated by
imp with INDEXFILE option.

      Rating: 3
Moving tables
      into another tablespace(v8.1.5.0)  January 12, 2002

Reviewer:  Amarsey  from Ghana
Tom will this code apply to the ff question? And how do i
execute the script at the SQL PROMPT?

Q.I have already created tables in my own user account but the tables were
stored in the SYSTEM tablespace. Now i have created my own tablespace to so that
i can move my tables from the SYSTEM Tablespace into my own. How do i go about
it since i undertand it is not a good practice to store objects in the SYSTEM
tablespace.
Thanks a lot.

          Followup:

what is an "ff" question.

to execute the script from the sqlplus propmt, put the commands in a file (say
x.sql) and run

SQL> @x.sql

read:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:47812348053
it shows how to move tables.

January 12, 2002 Andrew -- Thanks for the question regarding "use
      of cursor parameters", version 8i

      You Asked
Tom

Please could you provide guidelines on the use of cursor parameters. I have read
that using them allows better resource sharing.
e.g.
cursor c1 (v_hire_date in emp.hire_date%type) is
select * from emp where hire_date = v_hire_date;

is better than

cursor c1 is
select * from emp where hire_date = v_hire_date;

How can the same same benefit be achieved with ref cursors and implicit cursors
like:

for rec in (select * from emp where hire_date = v_hire_date) loop
.. whatever...
end loop;

Thanks for a brilliant site!

Regards
Andrew

      and we said...
They are identical, those two cursor definitions are the same.

The first one is useful if you put the cursors into a package:

create package my_cursors
as
   cursor c1 ( v_hire_date in date ) is
   select * from emp where hire_date = v_hire_date;

   ....
end;
/

since v_hire_date isn't in scope, you need to parameterize it.  In a local
block, you need not do this:

create procedure p
as
   v_hire_date    date;

   cursor c1 is select * from emp where hire_date = v_hire_date;
begin

here, since v_hire_date is in scope, you need not parameterize it.  In theory --
the cursor c1 in the package could be reused more often because procedure p1,
p2, p3, ... pn could all use it where as the cursor in procedure p can ONLY be
used by P (obviously, it is only known to p).

I personally am not a huge fan of the package approach.  If you use this you
have to remember that (for example) P1, P3, P5 and P7 use it.  If you are
working on the code for P1, you have to be careful not to call P3, P5, P7 or any
piece of code that might call them as YOU are using that cursor and they are
GOING TO WANT TO use that cursor.  It becomes a shared resource and you have
these implicit dependencies between these bits of code.  I would rather let each
have their own cursor and avoid such situations.

I use implicit cursors -- the last one you have there, the cursor for loop,
whenever I can.  Meaning the only time I have an explicit cursor is when I am
forced to use a ref cursor.
January
      12, 2002 Reader -- Thanks for the
      question regarding "Help in sqlplus", version 8i

      You Asked
Ps refer to the script below

VARIABLE x_ref_Cur  REFCURSOR;
set feedback off;
set verify off;
set heading off;
BEGIN
open :x_ref_Cur for  select  rownum||'='|| name  name from oe_order_sources
where name like '%AUTOLOAD%';
END;
/
print :x_ref_Cur

accept ord_srcnum  PROMPT 'Select the order source:     '

declare
  type tbl is table of  varchar2(100) index by binary_integer;
  tbl1 tbl;
  type a is ref cursor;
begin

 open :x_ref_Cur for   select   name   from oe_order_sources where name like
'%AUTOLOAD%';
 fetch :x_ref_Cur  bulk collect into  tbl1;
 Close :x_ref_Cur ;

    dbms_output.put_line(tbl1('&ord_srcnum'));
 end;
/
--wroks fine --but

I am writing a sql plus interface in which i ask the user to select the order
source he wants .In place of writing the order source at prompt i ask him to
enter the number corresponding to order source.
after the user has selected the number i want to contiune processing with that
order source.Why do i need to open the cursor again in the seconf pl/sql block
.Can i directly bulkcollect without opeing the cursor again.

      and we said...
Think of a cursor like a glass of water.  When you opened it, you filled up the
glass.  each fetch to display the row was like taking a drink.  Eventually you
emptied the glass.

the glass is empty.  When the cursor has been fetched from it is "empty".

Consider what would happen if it did not go "empty" -- it would be an infinite
loop, we would get to the end and start all over again.

It is like reading a file, when you hit EOF, you close it and open it to re-read
it (or "rewind" it but to rewind a cursor, we close/open)
January 12, 2002 Neena -- Thanks for the question regarding "SQL
      Statement", version 8.1.6

      You Asked
Hi Tom,

I have the follwong data in a table.

1          100
2          2
3          30

4          4      -- only this row in the result set
4          44     | -- not needed in the result set
4          444    |

5          1      | -- not needed in the result set
5          3      |
5          5      -- only this row in the result set

6          40
6          6      -- only this row
6          60

7          7
8          80
.
.
.
.

Now I am trying to write a query to get the below results ( in case of
duplicates in the first column, get the row with both the columns are equal)

1          100
2          2
3          30
4          4
5          5
6          6
7          7
8          80
.
.
.

Please help me.

sorry for any inconvinience.

Thanks,
Neena.

      and we said...
select c1, min(c2)
  from t
 where c1 = c2
    or not exists ( select null from t t2 where t2.c2 = t.c1 )
 group by c1;

Before the group by you would have every row where c1=c2 and every row where
there was a C1 that didn't have a matching C2 somewhere.

After the group by you would be left with the minimum C2 by C1, resulting in
what you want.
Review & Followup

      Rating: 3
follow
      up  January 12, 2002
Reviewer:  Neena Chetty
       from chicago, USA
Tom,

Thanks for the quick response.

I ran your query. But it is not returning the desired result. Here I am giving
the table data and also the result set.

test-home.work> select * from t;

        C1         C2
---------- ----------
         1        100
         2          2
         3         30
         4          4
         4         44
         4        444
         5          1
         5          3
         5          5
         6         40
         6          6
         6         66
         7          7
         8         88
         9          8

15 rows selected.

test-home.work>
test-home.work> select c1, min(c2)
  2    from t
  3   where c1 = c2
  4      or not exists ( select null from t t2 where t2.c2 = t.c1 )
  5   group by c1;

        C1    MIN(C2)
---------- ----------
         2          2
         4          4
         5          5
         6          6
         7          7
         9          8

6 rows selected.

test-home.work>

Here, the resultset is missing these rows.

1    100
3    30
8    88

I guess this is due to the row with c1=5 has both 1 and 3 as c2 and c1=9 has c2
as 8.

My table will have 700K rows. Is it OK with "or not exists" as per performace.
Can we do this with PL/SQL. If so, how?

Thank you very much for ur help.

Neena.

          Followup:

whoops -- missed a predicate in the subquery:

select c1, min(c2)
  from t
 where c1 = c2
    or not exists ( select null from t t2 where t2.c2 = t.c1 AND t2.c1 =
t.c1 )
 group by c1
/

      Rating: 3
Example needs
      some modification  January 12, 2002
Reviewer:
      Chip  from Denver, CO USA
SQL> select * from t;

        C1         C2
---------- ----------
         1        100
         2          2
         3         30
         4          4
         4         44
         4        444
         5          1
         5          3
         5          5
         6         40
         6          6
         6         60
         7          7
         8         80

14 rows selected.

SQL> select c1, c2
  2    from t
  3   where c1 = c2
  4      or not exists (select null from t t2 where t2.c2 = t.c1);

        C1         C2
---------- ----------
         2          2
         4          4
         5          5
         6          6
         7          7
         8         80

6 rows selected.

SQL> select c1, c2
  2    from t
  3   where c1 = c2
  4      or not exists (select null from t t2 where t2.c1 = t.c2);

        C1         C2
---------- ----------
         1        100
         2          2
         3         30
         4          4
         4         44
         4        444
         5          5
         6         40
         6          6
         6         60
         7          7
         8         80

12 rows selected.

SQL> select c1, min(c2)
  2    from t
  3   where c1 = c2
  4      or not exists (select null from t t2 where t2.c1 = t.c2)
  5  group by c1;

        C1    MIN(C2)
---------- ----------
         1        100
         2          2
         3         30
         4          4
         5          5
         6          6
         7          7
         8         80

8 rows selected.

SQL>

          Followup:

yup, missed a predicate, see above.

      Rating: 4
Faster
      Example  January 12, 2002
Reviewer:  Chip
       from Denver, CO USA
With the additional predicate, the group by clause is not needed.

select c1, c2
  from t
 where c1 = c2
    or not exists
     ( select null
         from t t2
        where t2.c2 = t.c1
          and t2.c1 = t.c1
     )

If table t had lots of rows, an index on c1, c2 should help query performance
(especially the not exists clause).

          Followup:

Insert into t values ( 1, 101 );

Now, it is needed as 1 100 and 1 101 would both come back.

      Rating: 5
Thank
      You  January 12, 2002
Reviewer:  Chip
       from Denver, CO USA
Thanks, you have helped my understanding of the not exists clause.  Your
examples are great :)

If columns c1 & c2 are unique, then this query returns the same results as your
example:

select c1, c2
  from t
 where c1 = c2
    or not exists
     ( select null
         from t t2
        where t.c1 = t2.c1
          and (  t.c1 = t2.c2
              or t.c2 > t2.c2
              )
     )

Your example using min() and group by would always work since the group by
consolidates any duplicate minimums.

      Rating: 3
Another
      approach  January 15, 2002
Reviewer:
      S.K.Krish  from ATL USA
select c1,decode(count(c1),1,min(c2),c1) from t
group by c1

I'm not sure about the performance of this statement. Just i have tried.

January 12, 2002 Bricklen -- Thanks for the question regarding
      "performance issues between triggers or procedures w/ jobs", version 8.1.7

      You Asked
Hi Tom, not a problem per se, just a question:

I have a couple of tables that are receiving inserts in the order of 10000 -
15000 rows every fifteen minutes.
I need to monitor those inserts in pretty close to real time for certain
threshold value increases (ie. if the new values surpass a set value).
I had triggers set up to do this, which worked fine, but also tried the same
with jobs that called procedures to monitor the data every couple of minutes (in
lieu of the triggers - not both at once).

My question then is this: is there a preferred way of doing something like that?
Is one method more taxing on performance, or could it be down the road when the
inserts start to get larger?

(I would guess that the jobs would incur less I/O, but that's why I'm asking).

Thanks Tom.

Cheers,

Bricklen

      and we said...
If the trigger based solution was fast enough, that is the approach I would
take.  It is the most straigthforward and requires the least coordination of
processes.

If the trigger based solution was not fast enough, I would have the LOAD process
look for these exceptions -- I would code something up in C (preferably) or Java
(if forced to) that would efficient load and look for the exceptional data.
Definitely use array binds to mass insert the data as well.
Review &
      Followup

      Rating: 5
re: procedure
      and trigger performance  January 12, 2002

Reviewer:  Bricklen Anderson  from BC, Canada

wow! that was quick!
thanks for the informative answer.  You not only answered the question well, you
gave me some more info to look into for future reference.

Cheers,

Brick

January 12, 2002 OCP -- Thanks for the question regarding
      "Questions!!!", version 8.1.6

      You Asked
Hi Tom,

1.For testing purpose i have installed Sun Solaris 8.0(Intel)
  can i install oracle 8i EE(Sun Sparc)..(Just for testing   installation)

2. How can users access objects of some other user without using the
owner.objectname...Ex dual table....everybody can use it without giving the
owner name...

3. Can u give me an example of sending text message to cell phone..

      and we said...
1) NO.  sparc is a risc architecture, intel is cisc.  They are totally
incompatible at the binary level.  Totally different instruction sets.

2) there is a public synonym created for DUAL by sys:

create public synonym dual for sys.dual;

3) nope.  how you do this differs by cell phone.  If you tell me HOW you send a
text message to your cell phone, I'll tell you how to do it from Oracle.
Sometimes it is as easy as sending an email or executing some URL.  Other times
you have to use a WAP gateway.  In any case, we can do it -- how depends.
Review &
      Followup

      Rating:
      4
thnx  January 14, 2002
Reviewer:  OCP
      Guy  from ny

January 13, 2002 Sam -- Thanks for the question regarding "Rollup
      and Cube", version 8.1.7

      You Asked
Tom

Why are rollup and cube called n dimensional group by options.

Can you show the difference between rollup and cube with a small example.

Thank you

      and we said...
sigh, take your subject:

rollup and cube

plug it into my search and read away.....  Publishing this one just as a
reminder to others, please try the search -- interMedia is pretty good at
finding relevant articles!

The first hit you'll get will be "Difference Between ROLLUP and CUBE"
January 13, 2002 Ron -- Thanks for the question regarding "Data
      mart questions ", version 9.0.1

      You Asked
Tom,

I am planning to implement a data mart instance. The mart will consist of 2 main
fact tables containing 2 years worth of data which is approx 30Gb/400m rows
each.

Including indexes and summary tables I would expect the total space for the data
to be around 200 Gb.

There will be a hundred users of which I would estimate 25 may be concurrent at
any one time.
70 percent of the queries will be relatively simple and directed at the most
recent monthly partitions. 25 percent will be more complex  and 5 percent will
be fairly heavy hitting.

What should I factor in when estimating appropriate memory sizings and required
I/O rates? Are you aware of any guidelines/resources for this - the Tuning
manual doesn't help much in this respect.

I am thinking that as many of the queries will be constrained for a specific
month (especially the current one) that I will partition the main fact tables by
month giving partitions of 30Gb/24 = 1.25Gb. Is this too small for partition?

Thanks for all the effort you put into this site - its excellent.

regards

Ron Dooley

      and we said...
Well, you need to figure out what sort of IO rates you need -- that is just
math.  You have the "percentages" and know the amount of IO they'll be doing,
you should be able to figure out about how much IO bandwidth you need to support
that.

As for memory -- that depends on how you set it up.  set the sort_area_size to
64k and you'll use the disk tons to sort and very little ram.  set the
sort_area_size to 100m and the opposite will be true.  It is purely a function
of HOW MUCH ram you want to use.  Some people would say you want a 200m PGA for
a data warehouse (for sorting, hashing, etc -- thats actually the default in
9i), others would go much smaller.  It is totally up to you and how much memory
you want to provide.  You would need a MINIMUM of 128m for the SGA and 128m for
the end users.  The sky is the limit from there.

(benchmarking, that is something I would strongly encourage you to do.  not
benchmarking like a "race" but benchmarking to say "with Xmeg of ram and Y users
and Z disks we get this result.  Vary X,Y and Z to get various baselines.  If
you do not benchmark, it will always be a best guess)

1.25gig is not too small for a partition, not at all.
January 13, 2002 Jan -- Thanks for the question regarding
      "Assigning alias names to the grand totals resulting from Rollup in a
      Group By", version Server 8.1..5

      You Asked
Hi Tom
First, thank you very much for your solution to my previous
question which was headed 'TABLE types in Oracle PL/SQL.  It
worked beautifully.

This time I have a question about the new ROLLUP feature of the
GROUP BY clause.  Is there any way to assign alias names to the
totals produced by the ROLLUP?  Basicaly we want to be able to
pick up the grand totals passed back by a procedure and display
them on a Delphi form.

Currently our stored procedure produces a list of accounts,
along with their corresponding budget figure, actual amount and
the variance.  This works fine.  But the programmer now wants me
to pass back the grand total for each as well.  It would appear
that this could be achieved by adding the ROLLUP to the GROUP BY
clause, so that the grand totals are passed back using the same
cursor.  However, the programmer uses the alias names to refer
to each item, how will the programmer identify the grand totals?

Please find below a copy of the procedure:

procedure LIST_ACTUAL_BUDGET(
         p_value    in     number,
     list_cur    in out    cur_type) is
     begin
           open list_cur for select
            aa.account_id    ACC_ACCOUNT_ID,
            aa.ACCOUNT_CODE ACC_ACCOUNT_CODE,
            aa.DESCRIPTION ACC_DESCRIPTION,
            vb.LOCK_VN GLB_LOCK_VN,
            sum(decode(gross_amount,null,0,gross_amount))
ACC_ACTUAL,
            sum(decode(budget_amount,null,0,budget_amount))
ACC_BUDGET,
            sum(decode(gross_amount,null,0,gross_amount)) -
sum(decode(budget_amount,null,0,budget_amount)) ACC_VARIANCE
        from    account aa,
            v_budget vb,
            v_actuals va
        where    aa.account_id    = vb.account_id (+)
        and    aa.account_id    = va.account_id (+)
        and    aa.CHART_ID    in
            (select chart_id
             from chart_of_accounts
             where entity_id = p_value)
        group by aa.account_id,
            aa.ACCOUNT_CODE,
            aa.DESCRIPTION,
            vb.LOCK_VN
        order by aa.account_id;
end LIST_ACTUAL_BUDGET;

**PLEASE NOTE:  v_budget and v_actuals are views, whereas
chart_of_accounts and account are tables

Any suggestion/s you may have would be most welcome.  Thanks in
advance.

Regards

Jan

      and we said...
I believe what you are looking for is the "grouping" function.
It will set a 0/1 flag to allow you to detect when a row is the
result of a ROLLUP or CUBE operation.  You would have to add a
column or 2 (or more) to your query to retrieve these values for
inspection by the client application.  Here is an example I set
up and then in anticipation of your next question (hey -- why
doesn't rollup work in PLSQL), I'll offer up how to let rollup
work in plsql (see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:186812348071
for why it doesn't work)

ask_tom@OSI1.WORLD> variable P_SAL number
ask_tom@OSI1.WORLD> exec :P_SAL := 1000

PL/SQL procedure successfully completed.

ask_tom@OSI1.WORLD> select deptno, job, sum(sal),
  2             decode(grouping(job),1,1,NULL) job_rollup,
  3             decode(grouping(deptno),1,1,NULL) deptno_rollup
  4    from emp
  5   where sal > :P_SAL
  6   group by rollup(deptno,job)
  7  /

    DEPTNO JOB         SUM(SAL) JOB_ROLLUP DEPTNO_ROLLUP
---------- --------- ---------- ---------- -------------
        10 CLERK           1300
        10 MANAGER         2450
        10 PRESIDENT       5000
        10                 8750          1
        20 ANALYST         6000
        20 CLERK           1100
        20 MANAGER         2975
        20                10075          1
        30 MANAGER         2850
        30 SALESMAN        5600
        30                 8450          1
                          27275          1             1

12 rows selected.

Notice how the grouping() function returns a 1 when a row
represents a rollup of that column.  I've used decode to return
1 or NULL (would normally return 0 or 1 -- the zeroes made the
output hard to read).  Using this fact, it becomes trivial to
find the rollup rows.   You'll find though that when you goto
put this into a procedure:

ask_tom@OSI1.WORLD> variable x refcursor
ask_tom@OSI1.WORLD> set autoprint on

ask_tom@OSI1.WORLD> begin
  2  open :x for
  3  select deptno, job, sum(sal),
  4             decode(grouping(job),1,1,NULL) job_rollup,
  5             decode(grouping(deptno),1,1,NULL) deptno_rollup
  6    from emp
  7   where sal > :P_SAL
  8   group by rollup(deptno,job);
  9  end;
 10  /
begin
*
ERROR at line 1:
ORA-06550: line 8, column 11:
PLS-00201: identifier 'ROLLUP' must be declared
ORA-06550: line 3, column 1:
PL/SQL: SQL Statement ignored

It fails.  We currently must use dynamic sql to execute this
type of query.  It would look like this:

ask_tom@OSI1.WORLD> begin
  2  open :x for
  3  'select deptno, job, sum(sal),
  4              decode(grouping(job),1,1,NULL) job_rollup,
  5              decode(grouping(deptno),1,1,NULL) deptno_rollup
  6     from emp
  7    where sal > :BV1
  8    group by rollup(deptno,job)'     using :P_SAL;
  9  end;
 10  /

PL/SQL procedure successfully completed.

    DEPTNO JOB         SUM(SAL) JOB_ROLLUP DEPTNO_ROLLUP
---------- --------- ---------- ---------- -------------
        10 CLERK           1300
        10 MANAGER         2450
        10 PRESIDENT       5000
        10                 8750          1
        20 ANALYST         6000
        20 CLERK           1100
        20 MANAGER         2975
        20                10075          1
        30 MANAGER         2850
        30 SALESMAN        5600
        30                 8450          1
                          27275          1             1

12 rows selected.

ask_tom@OSI1.WORLD>

Notice we can still support bind variables and all using the
USING clause.  This is as efficient as a "static" ref cursor.

Review &
      Followup

      Rating: 5
quotation mark
      and difference between static sql and dynamic sql  January
      13, 2002
Reviewer:  A reader
It fails.  We currently must use dynamic sql to execute this
type of query.  It would look like this:

ask_tom@OSI1.WORLD> begin
  2  open :x for
  3  'select deptno, job, sum(sal),
  4              decode(grouping(job),1,1,NULL) job_rollup,
  5              decode(grouping(deptno),1,1,NULL) deptno_rollup
  6     from emp
  7    where sal > :BV1
  8    group by rollup(deptno,job)'     using :P_SAL;
  9  end;
 10  /

PL/SQL procedure successfully completed.

Tom

The difference between the above code where you used dynamic sql, and when you
failed with the same code , is the you are using quotation around the sql
statement.

How is the use of quotations making the sql dynamic sql.

Thank you

          Followup:

the query is in a string, the query is not known to PLSQL until runtime.  The
above is the same as:

declare
   some_plsql_variable varchar2(512) := 'select deptno .... group by
rollup(...';
begin
   open :x for some_plsql_variable using :p_sal;
end;

since some plsql variable can contain ANY value.... it is dynamic sql.  the
plsql compiler doesn't "see" that query at compile time, its hidden in the
string.

January 14, 2002 Sam -- Thanks for the question regarding "Data
      may be inserted into complex cviews when the statement affects only one of
      the tables in the join.", version 8.1.7

      You Asked
"Data may be inserted into complex cviews when the statement affects only one of
the tables in the join."

Tom, can you show us how in a view , the statement can effect only one of the
tables in the join.

Does it mean that the insert can be done only into one table at a time  in the
view.

2."In complex views,  for update statements all columns changed are extracted
from  a key -preserved table . In addition , if the view is created wiht the
check option  clause, join columns and columns taken from the tables that are
referenced more than once in the view are not part of the update."

-What is a key preserved table.
-why are join columns not part of the update

3."In complex views, for delete statements, there is only one key preserved
table in the join.this table may be present more than once in the join , unless
the view has been created witht the check option."

can you explain the above...

4.why should all the columns where values are inserted must come from  a key
preserved table in case of of a complex  view.

      and we said...
searching for

key preserved

on this site, i find:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:548422757486
which explains the concept (and once you understand the concept, the rest is
pretty easy)....

1) so, here is the example of the insert:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t1 ( t1_x int primary key, t1_y
date );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t2 ( t2_x int primary key, t2_y
date );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view v
  2  as
  3  select * from t1, t2 where t1.t1_x = t2.t2_x;

View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into v values ( 1, sysdate, 1, sysdate
);
insert into v values ( 1, sysdate, 1, sysdate )
*
ERROR at line 1:
ORA-01776: cannot modify more than one base table through a join view

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into v (t1_x, t1_y ) values ( 1,
sysdate );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into v (t2_x, t2_y ) values ( 1,
sysdate );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

you see -- we cannot insert into both t1 and t2 using this view WITHOUT HELP (we
can write an instead of trigger and provide the logic to insert into both
underlying tables).  We can directly insert into either of t1 or t2 as
demonstrated however.

2) See the above link for "what is a key preserved..."

As for the part about the join columns -- well, only one table at a time can be
modified via the view, and if you update the join columns -- that row would not
appear in that view anymore (you changed the join column values -- that row
doesn't exist anymore, the check condition fails).  Hence in a view with check
option the join columns cannot be modified.  You can see this via:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select column_name, updatable, insertable,
deletable
  2  from user_updatable_columns where table_name = 'V'
  3  /

COLUMN_NAME                    UPD INS DEL
------------------------------ --- --- ---
T1_X                           YES YES YES
T1_Y                           YES YES YES
T2_X                           YES YES YES
T2_Y                           YES YES YES

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view v
  2  as
  3  select * from t1, t2 where t1.t1_x = t2.t2_x
  4  WITH CHECK OPTION;

View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select column_name, updatable, insertable,
deletable
  2  from user_updatable_columns where table_name = 'V'
  3  /

COLUMN_NAME                    UPD INS DEL
------------------------------ --- --- ---
T1_X                           NO  NO  NO
T1_Y                           YES YES YES
T2_X                           NO  NO  NO
T2_Y                           YES YES YES

All we did was recreate the view with check option here....

3) if there are two key preserved tables in a view (our example has 2 key
preserved tables), we cannot delete from it cause Oracle doesn't know WHICH
table to delete from and it won't do both.

ops$tkyte@ORA817DEV.US.ORACLE.COM> delete from v;
delete from v
            *
ERROR at line 1:
ORA-01752: cannot delete from view without exactly one key-preserved table

Now, if we drop the primary key constraint:

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t1 drop constraint SYS_C004852 ;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> delete from v;

1 row deleted.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t2;

      T2_X T2_Y
---------- ---------
         1 13-JAN-02

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t1;

no rows selected

T1 remains key preserved but T2 is no longer key preserved.  Hence we can delete
from the view and the act of deleting from the view will delete from T1.

4) hopefully, once you read about key preserved -- this will be obvious.... We
need to make sure that the row will appear at most "once" in the view itself --
otherwise the very act if modifying the row is ambigous....

Review &
      Followup

      Rating:
      4
  January 14, 2002
Reviewer:
      A reader
in this case  why we don't use insted_of trigger

          Followup:

go for it.  never said you couldn't -- only that you would HAVE to if you wanted
it to be fully insertable, updatable, deletable.

I did say:

...
you see -- we cannot insert into both t1 and t2 using this view WITHOUT HELP
(we
can write an instead of trigger and provide the logic to insert into both
underlying tables).  We can directly insert into either of t1 or t2 as
demonstrated however.
.....

January 14, 2002 Jan -- Thanks for the question regarding "9i:
      gather_schema_stats, 'GATHER STALE' vs. 'GATHER AUTO'", version 9.0.1

      You Asked
Tom,

what's the difference between 'gather stale' and 'gather auto'? How does auto
determine which objects to analyze? Is the *_tab_modifications still being used?
Auto seems to pick up on changes much faster than stale...
If all tables are set to 'monitoring', which one would be preferable?

From the documentation:
'GATHER AUTO' - gather all necessary statistics utomatically.  Oracle implicitly
determines which objects need new statistics, and determines how to gather those
statistics.  When 'GATHER AUTO' is specified, the only additional valid
parameters are ownname, stattab, statid, objlist and statown; all other
parameter settings will be ignored.  Also, return a list of objects processed.
'GATHER STALE' - gather statistics on stale objects as determined by looking at
the *_tab_modifications views. Also, return a list of objects found to be stale.

Thanks, jan

      and we said...
I'll have to research further to find what the real difference is -- but it
appears that auto does use the tab modifications table, from the doc:

<quote>
ALTER_DATABASE_TABLE_MONITORING Procedure

This procedure enables or diables the DML monitoring feature of all the tables
in the schema, except for snapshot logs and the tables, which monitoring does
not support. Equivalent to issuing ALTER TABLE ... MONITORING (or NOMONITORING)
individually. It is recommended to enable monitoring if you use
gather_database_stats or gather_schema_stats with the GATHER AUTO or GATHER
STALE options.
</quote>

Review &
      Followup

      Rating:
      5
*_tab_modifications changes visible much
      faster...  January 13, 2002
Reviewer:  jan van
      mourik  from Houston, TX
Some further observations....
- I did notice that I get results in the *_tab_modifications table very fast
now, after a couple of minutes... In 8i it takes a couple of hours before you
see changes in this view. Is this just Windows behaviour (I'm testing on XP
actually) or the same on unix?
- When you add a new table (or index), 'list auto' will show that table in the
objlist right away. Don't know when it will re-analyze when monitoring hasn't
been set on that table though..

jan

          Followup:

Its changed -- you can even flush it if you want manually in 9i.

What I've found is:

Both use _tab_modifications views .

The differences are  :

* GATHER AUTO : Oracle determines the best estimate percent for object .
Oracle also uses a default parallelism degree based on init.ora parameters .

* GATHER STALE : You should provide estimate-percent and degree parameter

January 14, 2002 atul -- Thanks for the question regarding "Too
      much Indexes...", version 8.0.4.4

      You Asked
Hi.....

I have a table > 20,00000 records...
Developers build lot of indexes on this table..
Now i know that many indexes on single table is not ok,we r using
RULE BASED OPTIMIZER..
so unknowingly droping any index will cause performance problem as we r running
in OLTP...

Here is list of indexes,could u tell me which indexes i can remove safely..

Uniqueness                Index Name                       Column Name
---------- ---------------------------------------- -------------------------
NONUNIQUE                         INDEX_1           PRODUCT_CD
                                                    VARIATION_CD
                                                    PROD_DT
                                                    VEH_STATUS

NONUNIQUE                         INDEX_2           PRODUCT_CD
                                                    VARIATION_CD
                                                    INSP_DT
                                                    VEH_STATUS

                               Index Keys Summary

Uniqueness                Index Name                       Column Name
---------- ---------------------------------------- -------------------------

NONUNIQUE                          INDEX_3           PRODUCT_CD
                                                    VARIATION_CD
                                                    DESPATCH_DT
                                                    VEH_STATUS

NONUNIQUE                          INDEX_4           PRODUCT_CD
                                                    VARIATION_CD
                                                    PROD_DT
                                                    INSP_DT
                                                    VEH_STATUS

NONUNIQUE                 IND_CHACDSRL              CHA_CD
                                                    CHA_SRL

NONUNIQUE                 ind_COLORCD               COLOUR_CD

NONUNIQUE                 IND_DESPDT                DESPATCH_DT

                               Index Keys Summary

Uniqueness                Index Name                       Column Name
---------- ---------------------------------------- -------------------------

UNIQUE                    ind_ENGINE_NO             ENG_CD
                                                    ENG_TYPE
                                                    ENG_YM
                                                    ENG_SRL

NONUNIQUE                 IND_EXPWONO               EXP_WONO

NONUNIQUE                 IND_INSPDT                INSP_DT

                               Index Keys Summary

Uniqueness                Index Name                       Column Name
---------- ---------------------------------------- -------------------------

NONUNIQUE               IND_LISRLNO                   LI_SRL_NO

NONUNIQUE               IND_PRODDT                    PROD_DT

NONUNIQUE               IND_PRODSHFT                  PROD_SHIFT

UNIQUE              PK_CHA_MASTER_CHASSIS_NO             CHA_CD
                                                         CHA_TYPE
                                                         CHA_YM
                                                         CHA_SRL

lAST index is a primary key...

Regards,
atul

      and we said...
none.  all.  some...

There is NO WAY I could answer this.  None of these indexes are "included" in
the other (eg: there is no index on A,B,C and some other index on A,B -- the
index on A,B could be dropped if it were non-unique as the index on A,B,C could
be used in its place).

YOU are the only one that can answer this.   You need to look at the queries
being used to see if any are useless.  You need to look at the cardinality (eg:
if "select count(*), count(distinct exp_wono) from table" returns something
like:

200,000   20

that index is most likely USELESS -- unless of course EXP_WONO is very skewed
and you only query those values that have few rows in the table)....

Review &
      Followup

      Rating: 3
indexes are
      bad?  January 14, 2002
Reviewer:  Edgar
       from Latvia
Hello Tom!

I would like to know why many indexes on a single table are bad? Another
question that I have is, I read in Oracle documentation and on your site (it was
somthing about locks) that every foreign key should have index, can you please
explain what kind of index does this have to be index that includes all foreign
keys or I have to create a single index for each foreign key?

          Followup:

Every index you have adds time to the process of inserting and deleting from the
table (we have to maintain them) and if you update an indexed column - it'll
affect the performance of that as well.

Indexes are not bad, they are not evil, they (like virtually everything in this
world) can be abused and overused.  A little sugar in the coffee might be a good
thing, 10 spoonfuls that would be bad.

As for the foreign keys, you just want to have an index that has the foreign
keys on the leading edge.  Suppose you have a table with foreign keys:

a      -> t1
a,b    -> t2
a,b,c  -> t3

a single index on (a,b,c) would be sufficient, you do not need an index on a,
(a,b), and (a,b,c)

January 14, 2002 Yves -- Thanks for the question regarding "create
      global temporary table foo as subquery produces no rows !", version 8.1.7

      You Asked
Why does create global temporary table foo as subquery produce no data in the
temporary table:

select count(*) from user_tables;
drop table foo;
create global temporary table foo as
select * from user_tables;
select * from foo;
select count(*) from foo;

Produces:

  COUNT(*)
----------
        58

Table supprimée.

Table créée.

aucune ligne sélectionnée

  COUNT(*)
----------
         0

No rows in the temporary FOO table !

Thank you for help.

(and thank you for your book Expert one-on_one).

      and we said...
Because DDL is processed conceptually like this:

begin
    COMMIT;
    begin
       do the ddl;
       COMMIT;
    exception
       when others then
           ROLLBACK;
           raise;
    end;
end;

So, there is a commit right after the DDL.  Since the default for a global
temporary table is "on commit delete rows", the rows disappear!

Consider:

ops$tkyte@ORA8I.WORLD> create global temporary table t1 as select * from dual;

Table created.

ops$tkyte@ORA8I.WORLD> select * from t1;

no rows selected

ops$tkyte@ORA8I.WORLD> create global temporary table t2 ON COMMIT PRESERVE ROWS
as select * from dual;

Table created.

ops$tkyte@ORA8I.WORLD> select * from t2;

D
-
X

ops$tkyte@ORA8I.WORLD>
Review & Followup

      Rating: 3
Ok, now I find
      the response is obvious !  January 14, 2002

Reviewer:  A reader

      Rating:
      5
Excellent  May 09, 2003
Reviewer:  X
       from USA

January 14, 2002 Arvind -- Thanks for the question regarding "No.
      of rows returned by XML APIs in PLSQL", version 8.1.7

      You Asked
Hi Tom,
I have question regarding XML APIs. Is there a way to get how many rows are
returned by any statement in that fetch ?
Ex:
package main is
flag number :=0;
procedure XML_data( po_result_set CLOB);
end;
/
package body main is

Procedure XML_data( po_result_set CLOB) is
lv_string     VARCHAR2(1000) := NULL;
queryCtx    DBMS_XMLQuery.ctxType;
begin
    lv_string := '    SELECT     HCSR.HCSR_ID,HCSR.HCSR_SERIES,HCSR.HCSR_YEAR,
                    HVST.HVST_ID,HVST.HVST_NAME,HVST.HVST_DESCRIPTION
            FROM         HQO_CENSUS_SERIES HCSR,
                    HQO_SERIES_STATES HSST,
                    HQO_VALID_STATES HVST
            WHERE     HCSR.HCSR_ID = HSST.HCSR_ID
                    AND HSST.HVST_ID = HVST.HVST_ID';
    if (flag = 0) then
              queryCtx := DBMS_XMLQuery.newContext(lv_string);
        flag := 1;
    end if;
    DBMS_XMLQuery.setRaiseNoRowsException(queryCtx,true);
    DBMS_XMLQuery.setMaxRows(queryCtx,pi_max_rows);
    po_result_set := DBMS_XMLQuery.getXML(queryCtx);
              WHEN OTHERS THEN
        DBMS_XMLQuery.closeContext(queryCtx);
        flag := 0;
end;

Suppose I am calling above written procedure in a loop by keeping handle open
till all rows fetched. So in the last fetch there may be less number of rows
than pi_max_rows that I have set in setMaxRows. So how to get that number ?

Thanks for any help.

Regards
Arvind Kapil

      and we said...
I asked Sean Dillon, our local XML guy, and he said...
----------------------------------------------------------

Hi Arvind,

Yeah, you're looking for a DBMS_XMLQuery function called GETNUMROWSPROCESSED.

FUNCTION GETNUMROWSPROCESSED RETURNS NUMBER
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 CTXHDL                         NUMBER                  IN

If you don't have this procedure in your DBMS_XMLQuery, just download the latest
production XDK from Oracle Technet:

http://technet.oracle.com/tech/xml/content.html
Here's an example of it in action:

SQL> create or replace package main is
  2    procedure xml_data( p_sql         in varchar2,
  3                        p_max_rows in number );
  4  end main;
  5  /

Package created.

SQL> create or replace package body main is
  2
  3    procedure print_xml(p_xml in out nocopy clob) is
  4      l_xml  varchar2(30000);
  5      l_line varchar2(2000);
  6      l_amt  pls_integer := 30000;
  7      l_idx  pls_integer := 1;
  8    begin
  9      for i in 1 .. ceil(dbms_lob.getlength(p_xml)/l_amt) loop
 10        l_xml := dbms_lob.substr(p_xml, l_amt, l_idx);
 11        loop
 12          exit when l_xml is null;
 13          l_line := substr(l_xml, 1, instr(l_xml, chr(10))-1);
 14          dbms_output.put_line('|' || l_line);
 15          l_xml := substr(l_xml, instr(l_xml, chr(10))+1);
 16        end loop;
 17      end loop;
 18    end print_xml;
 19
 20    procedure xml_data( p_sql         in varchar2,
 21                        p_max_rows in number )
 22    is
 23      l_ctx    dbms_xmlquery.ctxType;
 24      l_xml    clob;
 25    begin
 26      dbms_lob.createtemporary( l_xml, true, dbms_lob.session );
 27      l_ctx := dbms_xmlquery.newContext( p_sql );
 28
 29      -- setup the context
 30      dbms_xmlquery.setRaiseNoRowsException( l_ctx,true );
 31      dbms_xmlquery.setMaxRows( l_ctx, p_max_rows );
 32
 33      -- get the xml fm the context
 34      l_xml := dbms_xmlquery.getXML( l_ctx );
 35
 36      -- print out the xml result set
 37      print_xml( l_xml );
 38
 39      -- determine the no of rows processed
 40      dbms_output.put_line( '' );
 41      dbms_output.put_line( 'No. of rows processed: ' ||
 42                            dbms_xmlquery.getnumrowsprocessed( l_ctx ) );
 43
 44      dbms_xmlquery.closeContext(l_ctx);
 45    exception
 46      when others then
 47      dbms_xmlquery.closeContext(l_ctx);
 48    end xml_data;
 49
 50  end main;
 51  /

Now we've created the package, we'll call it passing the value "5" as the max
rows:

SQL> begin
  2    main.xml_data( 'select e.empno, e.ename, e.job, d.dname
  3                     from emp e inner join dept d
  4                       on e.deptno = d.deptno',
  5                   5 );
  6  end;
  7  /
|<?xml version = '1.0'?>
|<ROWSET>
|   <ROW num="1">
|      <EMPNO>7369</EMPNO>
|      <ENAME>SMITH</ENAME>
|      <JOB>CLERK</JOB>
|      <DNAME>RESEARCH</DNAME>
|   </ROW>
|   <ROW num="2">
|      <EMPNO>7499</EMPNO>
|      <ENAME>ALLEN</ENAME>
|      <JOB>SALESMAN</JOB>
|      <DNAME>SALES</DNAME>
|   </ROW>
. . .   (i clipped out records here, you get the idea)
|   <ROW num="5">
|      <EMPNO>7654</EMPNO>
|      <ENAME>MARTIN</ENAME>
|      <JOB>SALESMAN</JOB>
|      <DNAME>SALES</DNAME>
|   </ROW>
|</ROWSET>
No. of rows processed: 5

PL/SQL procedure successfully completed.

Now we call it w/ "20" rows max, and notice the number of rows processed at the
end of the output:

SQL> begin
  2    main.xml_data( 'select e.empno, e.ename, e.job, d.dname
  3                     from emp e inner join dept d
  4                       on e.deptno = d.deptno',
  5                   20 );
  6  end;
  7  /
|<?xml version = '1.0'?>
|<ROWSET>
|   <ROW num="1">
|      <EMPNO>7369</EMPNO>
|      <ENAME>SMITH</ENAME>
|      <JOB>CLERK</JOB>
|      <DNAME>RESEARCH</DNAME>
|   </ROW>
|   <ROW num="2">
|      <EMPNO>7499</EMPNO>
|      <ENAME>ALLEN</ENAME>
|      <JOB>SALESMAN</JOB>
|      <DNAME>SALES</DNAME>
|   </ROW>
. . .   (i clipped out records here, you get the idea)
|   <ROW num="14">
|      <EMPNO>7934</EMPNO>
|      <ENAME>MILLER</ENAME>
|      <JOB>CLERK</JOB>
|      <DNAME>ACCOUNTING</DNAME>
|   </ROW>
|</ROWSET>
No. of rows processed: 14

PL/SQL procedure successfully completed.

Hope that helps!

_sean_
January 14, 2002 Robert -- Thanks for the question regarding "R.I.
      across schemas?", version 8.1.7.2

      You Asked
Tom, is there any issues to be aware of when enforcing R.I. across schemas? i.e.
would it be OK to segregate logical table structures into their own schemas for
organization and management reasons rather than place them in one giant schema?
Would there be performance or other trade offs? Just wondering if there is any
benifit or negative impact. As some DB's grow in table numbers, this would be
easier for us to manage within separate schemas.
 Bob Maggio

      and we said...
Well, as long as you grant REFERENCES on the tables across the schemas when
needed, there will be no functional difference, no performance implications.

So, there will just be additional grants....  Also, if the tables are all owned
by different people, you'll have to fully qualify the table names in most all
cases...

other then that, there is nothing bad about it.
Review &
      Followup

      Rating: 4
R.I accros
      shemas  January 14, 2002
Reviewer:  Yossi
       from Israel
Tom ,
I am facing a design problem regarding the single schema vs. multiple schemas.
Our application team wants a different schema for each one of the applications.
(10 applications 10 schemas. written in Java using JDBC)
Some of the tables are or can be the same.(for example
users,error_type,languages ...).
My point of view as a DBA to put all the applications in the same schema, each
application will have a prefix. The tables that can be share will be use by all
the applications.
Which design is better? Performance, mainteneces, storage volume, connection
pooling (our main problem)
What do you think about it?
Thanx

          Followup:

I like separate schemas.  Its always seemed counterintuitive to me to have to
prefix all table names with the application -- seems easier to use
application.table_name.

Also, it lets me control access better.  Application1 will have to be granted
exactly what it needs against application2's tables (if any).  In a single
schema -- everything is wide open.

Storage -- connection pooling -- a single schema verses many schemas -- should
not make a bit of difference.


Subject | Article | Last Updated (East coast time)
Domain Lookup | article only | Jan 15, 2002 08:59 am
How can i do a multiple group by? | article only | Jan 15, 2002 10:32 am
Script to kill sessions | article only | Jan 15, 2002 11:20 am
intermedia text search | article only | Jan 16, 2002 07:42 am
combining multiple nowait locks into one statement | article only | Jan 17, 2002 02:29 pm
functionality of LGWR in Rollback of a transaction | article only | Jan 18, 2002 09:16 am
Store procedure -- getting OUT parameters in JDBC calls. | article only | Jan 19, 2002 09:49 am
Performance monitoring for pl/sql procedures | article only | Jan 19, 2002 10:46 am
cannot audit by session? | article only | Jan 19, 2002 06:07 pm
Select * into an Array | article only | Jan 19, 2002 06:19 pm
Internationalization | article only | Jan 19, 2002 06:28 pm
host command | article only | Jan 20, 2002 10:04 am
Compress in export | article only | Jan 20, 2002 11:51 am
Dependencies | article only | Jan 20, 2002 03:10 pm
Cannot drop tables | article only | Jan 20, 2002 03:47 pm
Database backup and recovery plan. | article only | Jan 20, 2002 08:25 pm
Prefix or Suffix in a field value | article only | Jan 21, 2002 09:47 am
selecting not null cols and values in a PL/SQL table from a given heap table. | article only | Jan 21, 2002 10:44 am
Locally managed tablespace - Why extent size does not match | article only | Jan 21, 2002 11:11 am
row(s) 1 - 19 of 19
January 15, 2002 Mike -- Thanks for the question regarding "Domain Lookup", version 8.1.7
You Asked
I have a unnormalized table that represents a type of financial transaction.
For importing purposes into our financial system (PeopleSoft) I need to
"de-pivot" the table.  You have given several examples of pivoting the table via
the decode function but in my case I need to take a "wide" table and make it
"long".  For example:

SQL> select * from x_a;
<b> -- so I have a wide table that I need to go long </b>
PK                                     COL1       COL2       COL3
-------------------------------- ---------- ---------- ----------
96AFC006525B2E39E034080020C902BB          2                     9

<b> -- here is the structure </b>
SQL> desc x_a
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------

 PK                                                 VARCHAR2(32)
 COL1                                               NUMBER
 COL2                                               NUMBER
 COL3                                               NUMBER

<b> -- here is the lookup table.  I can't hard code the values
 -- since in the full version of the problem I am actually labeling
 -- a calculation between multiple fields.  So, I may call the
 -- label a but it really is the col1+col2 </b>
SQL> desc x_lookup
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------

 LABEL                                              VARCHAR2(32)

SQL> select * from x_a;

<b> -- notice the null in col2 </b>
PK                                     COL1       COL2       COL3
-------------------------------- ---------- ---------- ----------
96AFC006525B2E39E034080020C902BB          2                     9

SQL> select * from x_lookup;

LABEL
--------------------------------
COL1
COL2
COL3

SQL> edit
  1  select a.pk, b.label,
  2  dlookup(b.label, 'system.x_a', 'pk='''||a.pk||'''') lookup
  3  from x_a a, x_lookup b
  4  where dlookupv(b.label, 'system.x_a', 'pk='''||a.pk||'''') is not null
  5* order by pk, label
SQL> /

<b> -- The only real-world use for a cartesian join I know of </b>
<b> -- Notice the null value isn't reported as needed. </b>
PK                               LABEL LOOKUP
-------------------------------- ----- ----------
96AFC006525B2E39E034080020C902BB COL1  2
96AFC006525B2E39E034080020C902BB COL3  9

I created a function dlookup to perform this feat.  It performs well but I
couldn't help but wonder if possibly I was doing this the hard way.  Do you know
of an easier way to "de-pivot" a table than to write your own lookup function?

Here is my solution:
create or replace function dlookup(vField in varchar2, vTable in varchar2,
vWhere in varchar2 default NULL) return varchar2
AUTHID CURRENT_USER
as
  vVar varchar2(4000);
  vQuery varchar2(4000);
begin
  vQuery := 'select '||vField||' from '||vTable;
  if (vWhere is not NULL) then
    vQuery := vQuery ||' where '||vWhere;
  end if;
  execute immediate vQuery into vVar;
  return vVar;
  exception
    when NO_DATA_FOUND then
      return null;
    when others then
      raise;
end;
/
and we said...
what about:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int primary key, c1 int,
c2 int, c3 int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1, 100, null, 200 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select t.x, a.label,
            decode( a.label, 'COL1', c1, 'COL2', c2, 'COL3', c3 ) lookup
  2    from t, ( select substr('COL' || rownum,1,4) label from all_objects where
rownum <= 3 ) a
  3  /

         X LABE     LOOKUP
---------- ---- ----------
         1 COL1        100
         1 COL2
         1 COL3        200

no function, no extra table, all of the rows you want....
Review & Followup
Rating: 3
Close ... January 14, 2002
Reviewer: Mike Wilson from Los Angeles
Unfortunately, I don't know what columns they will want in advance or if the
calculation model, driven by the lookup table columns will change.  It will.  By
having a lookup table that drives the columns I can produce an easy to use
interface for the users so that they can change the behaviour of the view
without me having to recode it if they don't need col2, or whichever, the next
time they run the report.  The users are not coders in this case either so I
wouldn't want to have to recode the view/decode queries when they change their
minds.  If that is the case is my little lookup function the most efficient way
to do this?

Or as the short version, is my code as efficient as it can be?

Thanks for your help again.

Followup:
Don't take this personally -- your approach is very bad.

Running dynamic sql for every row - slooooowwwww.

Running plsql for every row -  sllooooooowwww

Not using bind variables in the query you are running dynamically -- the WORST
THING in the entire world you can do.

Ease of use is one thing, zero performance is another.  Your approach will not
perform even a little bit once you get more then a very very small handful of
rows in your base table.

If you want -- create a lookup table, instead of using:

( select substr('COL' || rownum,1,4) label from all_objects where rownum <= 3 )

Use:

create table lookup ( label varchar2(4) );
insert into lookup values ( 'COL1' );
insert into lookup values ( 'COL2' );
insert into lookup values ( 'COL3' );

Then use a view:

select t.x, a.label,
            decode( a.label, 'COL1', c1, 'COL2', c2, 'COL3', c3 ) lookup
  from t, lookup a
 where decode( a.label, 'COL1', c1, 'COL2', c2, 'COL3', c3 ) is NOT NULL
/

Now we have the same exact results -- only this one will run fairly fast.  If
you change the lookup table -- adding or removing a column -- just UPDATE the
view (that is why I would do away with the lookup table and just use all_objects
to create on the fly the labels and update the view as my needs change)

Rating: 2
I can'nt do well January 14, 2002
Reviewer: Them from Vietnam
Hello
I did as you said but I can't sucessful

Followup:
sorry...  no idea what to tell you as you quite simply provide no information to
me.

Rating: 4
Bind Vars! January 15, 2002
Reviewer: Mike Wilson from Los Angeles
I could actually use bind variables.  Don't know why I didn't think about that.
As for the view, in this case, I would have to have a different view for each
show in our facility (you wouldn't believe the accounting that goes on in show
business) and the calculation models change monthly so at the end of the year I
would have dozens views devoted to calculating one #.  I am willing to trade
speed for ease of maintenance in this case.  The other problem is that I simply
don't know what the fields, the calculations, or the combinations of fields will
be next month the real world version of this is actually a bit more complicated
than the version I've showed here.  My production version has a bound on the
lookup table so performance doesn't go non-linear thankfully.  Thanks for your
time.

January 15, 2002 Miguel -- Thanks for the question regarding "How can i do a multiple group by?", version 8i
You Asked
I'm triying to pass this Access query to Oracle:

SELECT DISTINCTROW ALUMNOS.DNI, ALUMNOS.APELLIDOS, ALUMNOS.NOMBRE,
First(ALUMNOS.ORGANISMO) AS PrimeroDeORGANISMO, First(TABLA_ORGANISMOS_1.CODIGO)
AS PrimeroDeCODIGO,
First([TABLA_ORGANISMOS_1].[DESCRIPCION]) AS ORGANDES,
First(TABLA_ORGANISMOS_1.DESCRIPCION) AS DESORG, TABLA_ORGANISMOS_1.DESCRIPCION,

First(ALUMNOS.CURSOS) AS PrimeroDeCURSOS, First(ALUMNOS.ANTIGUEDAD) AS
PrimeroDeANTIGUEDAD,
First(CURSOS.LOCALIDAD) AS PrimeroDeLOCALIDAD,
First(TABLA_ORGANISMOS.DESCRIPCION) AS DESLOC,
ALUMNOS.GRUPO, First(TABLA_GRUPOS.DESCRIPCION) AS DESGRUPO,
First(CONTADOR.SERVICIO) AS PrimeroDeSERVICIO,
First(CONTADOR.DIRECCION) AS PrimeroDeDIRECCION, First(CONTADOR.CONSEJERIA) AS
PrimeroDeCONSEJERIA
FROM CONTADOR, TABLA_ORGANISMOS AS TABLA_ORGANISMOS_1 INNER JOIN
((TABLA_ORGANISMOS INNER JOIN CURSOS ON TABLA_ORGANISMOS.CODIGO =
CURSOS.LOCALIDAD) INNER JOIN (TABLA_GRUPOS INNER JOIN (ALUMNOS INNER JOIN
ALUM_CUR ON ALUMNOS.DNI = ALUM_CUR.DNI) ON TABLA_GRUPOS.CODIGO = ALUMNOS.GRUPO)
ON CURSOS.CURSO = ALUM_CUR.CURSO) ON TABLA_ORGANISMOS_1.CODIGO =
ALUMNOS.ORGANISMO
GROUP BY ALUMNOS.DNI, ALUMNOS.APELLIDOS, ALUMNOS.NOMBRE,
TABLA_ORGANISMOS_1.DESCRIPCION, ALUMNOS.GRUPO, CURSOS.TIPOCURSO
HAVING (((First(ALUMNOS.ORGANISMO)) Like [forms]![frmMenuInformes]![ORGANISMO])
AND ((First(CURSOS.LOCALIDAD)) Like [forms]![frmmenuinformes]![LOCALIDAD]) AND
((ALUMNOS.GRUPO) Like [forms]![frmMenuInformes]![GRUPO]) AND
((CURSOS.TIPOCURSO)="PF" And (CURSOS.TIPOCURSO)="PF" And
(CURSOS.TIPOCURSO)="PF"))
ORDER BY ALUMNOS.APELLIDOS, ALUMNOS.NOMBRE;

The function first returns the value of the field of the first record of the set
of results returned by a consultation. Simply it returns the value of a field
specified in the first record of the set of results returned by a consultation.
Since the records are returned habitually without a concrete order (until the
consultation includes a clause ORDER BY), the records returned by these
functions will be arbitrary.

We have a Table "Empleados" with this data:

Empleado Nombre Domicilio
--------------------------------
0001     Miguel C/Uruguay nº 24
0002     Pablo  C/Teide nº 9
0003     Tomás  C/Los Bancales nº 4
0004     Elena  C/San Agustin nº 6

SELECT First(Nombre) as First FROM Empleados;

And the return is:

First
------
Miguel

I think it tooks the first entry in the table. I hope this help you to
understand the question.

Does this function exist in Oracle?.

I try it in Oracle like :

SELECT DISTINCT ALUMNOS.DNI, ALUMNOS.APELLIDOS,
ALUMNOS.NOMBRE,ALUMNOS.ORGANISMO,T.CODIGO,
T.DESCRIPCION AS ORGANDES, T.DESCRIPCION AS DESORG, T.DESCRIPCION,
ALUMNOS.CURSOS,ALUMNOS.ANTIGUEDAD,
CURSOS.LOCALIDAD,TABLA_ORGANISMOS.DESCRIPCION AS DESLOC, ALUMNOS.GRUPO,
TABLA_GRUPOS.DESCRIPCION AS DESGRUPO,
CONTADOR.SERVICIO,CONTADOR.DIRECCION,CONTADOR.CONSEJERIA
FROM GCDAT.CONTADOR, GCDAT.TABLA_ORGANISMOS
T,GCDAT.TABLA_ORGANISMOS,GCDAT.CURSOS,GCDAT.TABLA_GRUPOS,GCDAT.ALUMNOS,GCDAT.ALUM
_CUR
WHERE ((TABLA_ORGANISMOS.CODIGO = CURSOS.LOCALIDAD) AND (ALUMNOS.DNI =
ALUM_CUR.DNI) AND
(TABLA_GRUPOS.CODIGO = ALUMNOS.GRUPO) AND (CURSOS.CURSO = ALUM_CUR.CURSO) AND
(T.CODIGO = ALUMNOS.ORGANISMO))
GROUP BY ALUMNOS.DNI, ALUMNOS.APELLIDOS, ALUMNOS.NOMBRE, T.DESCRIPCION,
ALUMNOS.GRUPO, CURSOS.TIPOCURSO
HAVING ((ALUMNOS.ORGANISMO Like 'ME05') AND (CURSOS.LOCALIDAD Like 'AY760') AND
(ALUMNOS.GRUPO Like 'A') AND (CURSOS.TIPOCURSO='PF'))
ORDER BY ALUMNOS.APELLIDOS, ALUMNOS.NOMBRE

It's returns:

HAVING ((ALUMNOS.ORGANISMO Like 'ME05') AND (CURSOS.LOCALIDAD
                                            *
Like 'AY760') AND (ALUMNOS.GRUPO Like 'A') AND (CURSOS.TIPOCURSO='PF'))

ERROR at line 10:
ORA-00979: not a GROUP BY expression

How can i do this? Thank you.
and we said...
Since it is arbitrary, just use MIN or MAX -- it'll return the "first" or "last"
record just as easily.  In Oracle's case -- it won't be arbitrary, you'll get
either the min or max value in the set.
Review & Followup
Rating: 5
This solve my problem January 16, 2002
Reviewer: Miguel Garcia from Canary Island/Spain
Thank you Tom, I don't think it's was so easy.

January 15, 2002 Maya -- Thanks for the question regarding "Script to kill sessions", version 8.0.6
You Asked
Hi,

I need to create a batch job to be run on NT machine,
this script needs to connect the DB and kill all sessions of
specified oracle user. How do you recommend to do it?

Thanks
and we said...
I'd just write a stored procedure instead.

In 8.0, say you had a procedure such as:

create or replace
procedure execute_immediate( p_sql in varchar2 )
is
    cursor_name     pls_integer default dbms_sql.open_cursor;
    ignore          pls_integer;
BEGIN
    dbms_sql.parse(cursor_name, p_sql, dbms_sql.native);
    ignore := dbms_sql.execute(cursor_name);
    dbms_sql.close_cursor(cursor_name);
END;
/

Now, you can write a procedure:

create or replace procedure kill_user( p_username in varchar2)
as
begin
   for x in ( select sid, serial#
                from v$session
               where username = upper( p_username ) )
   loop
      execute_immediate( 'alter system kill session ''' ||
                          x.sid || ',' || x.serial# || '''' );
   end loop;
end;
/

The OWNER of this procedure must be granted:

o ALTER SYSTEM
o SELECT ON V_$SESSION

directly, not via a role.  See
http://osi.oracle.com/~tkyte/Misc/RolesAndProcedures.html
for why that is.
Review & Followup
Rating: 2
Hi tom January 15, 2002
Reviewer: El Mayimbe from new york
I think he mentioned a how to kill session(s) not
just one session.

Followup:
Yes, that is exactly what they asked for (and got) - what is your point?

They asked how to kill all of the sessions of a specified oracle user -- and I
wrote that code (it's a loop see...)

Rating: 5
More to the add to the toolset January 17, 2002
Reviewer: Joe from Australia
Liked the idea of creating the execute_immediate stored procedure, emulate
functionality of later versions

January 16, 2002 sushil -- Thanks for the question regarding "intermedia text search", version 8.1.6
You Asked
Hi Tom,

I have problem with intermedia search.
I have a column which has char "#".
I know that we have to remove this non alphanumeric character
from stopwords list using ctx_ddl.set_attribute('Lexer Name','printjoins','#').

I have done this. After that when queriny like where contains(column_name ,'#')
> 0 it returns many rows.No doubt that it filters most of the records but still
there are many records which does not hold '#'.

Then as per your suggestion last time with issue regarding "," I used query like
where contains(column_name,'\#') > 0 but still the records werenot appropriate.
On using an additional clause of instr(column_name,'#') >0 I am getting
appropriate records. Can u please tell me what needs to be done.

Tks
sushil.
and we said...
Ahh, but do you have a row with "#" as an entire word???

You see, if you run "contains( cname, 'hello' ) > 0", that finds all rows that
contain the word hello.  Do you have a row with '#' as a word?  If you do --
it'll work. If you do not -- contains isn't right for what you are doing --
instr is.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( data varchar2(50) );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 'How#Now#Red#Cow' );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 'How Now Red Cow' );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 'How # Now Red Cow' );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec ctx_ddl.drop_preference('my_lexer');
PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2      ctx_ddl.create_preference('my_lexer','BASIC_LEXER');
  3      ctx_ddl.set_attribute('my_lexer','printjoins','#');
  4  end;
  5  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_ind_ctx on t(data) indextype
  2  is ctxsys.context parameters('lexer my_lexer');

Index created.

no here are all of the tokens we have parsed as "words"

ops$tkyte@ORA817DEV.US.ORACLE.COM> select token_text from DR$T_IND_CTX$I;

TOKEN_TEXT
----------------------------------------------------------------
#
RED
COW
HOW
HOW#NOW#RED#COW
NOW

6 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where contains( data, 'How' )
 > 0;

DATA
--------------------------------------------------
How Now Red Cow
How # Now Red Cow

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where contains( data, '#' ) >
0;

DATA
--------------------------------------------------
How # Now Red Cow
Review & Followup
Rating: 4
Doubt January 14, 2002
Reviewer: Ak from India
Hi,
 SQL> drop table t;

Table dropped.

SQL> create table t ( data varchar2(50) );

Table created.

SQL> insert into t values ( 'How#Now#Red#Cow' );

1 row created.

SQL> insert into t values ( 'How Now Red Cow' );

1 row created.

SQL> insert into t values ( 'How # Now Red Cow' );

1 row created.

SQL> commit;

Commit complete.

SQL> exec ctx_ddl.drop_preference('my_lexer');
BEGIN ctx_ddl.drop_preference('my_lexer'); END;

      *
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00904: insufficient privilege to access object CTXSYS.CTX_DDL
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored

SQL> conn sys/sys;
Connected.
SQL> grant dba to TEST;

Grant succeeded.

SQL> conn TEST/TEST;
Connected.
SQL>  exec ctx_ddl.drop_preference('my_lexer');
BEGIN ctx_ddl.drop_preference('my_lexer'); END;

*
ERROR at line 1:
ORA-20000: ConText error:
DRG-10700: preference does not exist: my_lexer
ORA-06512: at "CTXSYS.DRUE", line 122
ORA-06512: at "CTXSYS.CTX_DDL", line 80
ORA-06512: at line 1

SQL> begin
  2          ctx_ddl.create_preference('my_lexer','BASIC_LEXER');
  3          ctx_ddl.set_attribute('my_lexer','printjoins','#');
  4      end;
  5  /

PL/SQL procedure successfully completed.

SQL>  create index t_ind_ctx on t(data) indextype
  2      is ctxsys.context parameters('lexer my_lexer');
 create index t_ind_ctx on t(data) indextype
*
ERROR at line 1:
ORA-29855: error occurred in the execution of ODCIINDEXCREATE routine
ORA-20000: ConText error:
DRG-10503: table TEST.T cannot be indexed because it has no primary key
ORA-06512: at "CTXSYS.DRUE", line 122
ORA-06512: at "CTXSYS.TEXTINDEXMETHODS", line 34
ORA-06512: at line 1

SQL> alter table t add constraints pk_t primary key (data);

Table altered.(Why should i have primary key here ??)

SQL> create index t_ind_ctx on t(data) indextype
  2     is ctxsys.context parameters('lexer my_lexer');
create index t_ind_ctx on t(data) indextype
*
ERROR at line 1:
ORA-29855: error occurred in the execution of ODCIINDEXCREATE routine
ORA-20000: ConText error:
DRG-50704: Net8 listener is not running or cannot start external procedures
ORA-28575: unable to open RPC connection to external procedure agent
ORA-06512: at "CTXSYS.DRUE", line 122
ORA-06512: at "CTXSYS.TEXTINDEXMETHODS", line 34
ORA-06512: at line 1

SQL> select index_name from user_indexes;

INDEX_NAME
------------------------------
T_IND_CTX

SQL>  select token_text from DR$T_IND_CTX$I;

no rows selected

SQL>  select * from t where contains( data, 'How' ) > 0;
 select * from t where contains( data, 'How' ) > 0
                                       *
ERROR at line 1:
ORA-20000: ConText error:
DRG-10599: column is not indexed

----You can see above,index is available..but it says not indexed..why ??

Can I have your comments on above please ??

Thanks,
  Regards
    Ak

Followup:
umm -- obviously you need a primary key in your release and I do not.

add a primary key to the table.

Rating: 3
Agreed but not fully satisfied January 14, 2002
Reviewer: Sushil Vyas from India
Tom,

What u have said is new and also understood the same.
Now in ur last query when u say contains(data,'#') you get only those rows which
has got '#' sign.What about I getting few more rows additionally which do not
have hash sign at all.
The difference here I see is that I am using more than one printjoins like

ctx_ddl.create_preference('my_lexer','BASIC_LEXER');
ctx_ddl.set_attribute('my_lexer','printjoins','#');
ctx_ddl.set_attribute('my_lexer','printjoins','_');
ctx_ddl.set_attribute('my_lexer','printjoins',',');

Is this a problem, if yes than how do we take care of search when there could be
all these nonalphanumeric characters in a field.

Tks Sushil.

Followup:
Only the LAST printjoins is taking effect.

You want one call:

ctx_ddl.set_attribute('my_lexer','printjoins','#_,');

not three as they just overwrite each other.

Rating: 4
why this error January 16, 2002
Reviewer: Ak from India
Hi tom,
  I tested again after adding primary key ,i am getting this
error again.

SQL> conn TEST/TEST;
Connected.
SQL> desc t
 Name                                                  Null?    Type
 ----------------------------------------------------- --------
--------------------------------
 DATA                                                  NOT NULL VARCHAR2(50)

SQL> drop table t;

Table dropped.

SQL>
SQL>
SQL> create table t ( data varchar2(50) primary key);

Table created.

SQL> insert into t values ( 'How#Now#Red#Cow' );

1 row created.

SQL> insert into t values ( 'How Now Red Cow' );

1 row created.

SQL> insert into t values ( 'How # Now Red Cow' );

1 row created.

SQL> exec ctx_ddl.drop_preference('my_lexer');

PL/SQL procedure successfully completed.

SQL>  begin
  2          ctx_ddl.create_preference('my_lexer','BASIC_LEXER');
  3          ctx_ddl.set_attribute('my_lexer','printjoins','#');
  4      end;
  5  /

PL/SQL procedure successfully completed.

SQL> create index t_ind_ctx on t(data) indextype
  2      is ctxsys.context parameters('lexer my_lexer');
create index t_ind_ctx on t(data) indextype
*
ERROR at line 1:
ORA-29855: error occurred in the execution of ODCIINDEXCREATE routine
ORA-20000: ConText error:
DRG-50704: Net8 listener is not running or cannot start external procedures
ORA-28575: unable to open RPC connection to external procedure agent
ORA-06512: at "CTXSYS.DRUE", line 122
ORA-06512: at "CTXSYS.TEXTINDEXMETHODS", line 34
ORA-06512: at line 1

SQL> select index_name from user_indexes where index_name like 'T_IND_CT%';

INDEX_NAME
------------------------------
T_IND_CTX

SQL> select token_text from DR$T_IND_CTX$I;

no rows selected

SQL>  select * from t where contains( data, 'How' )
  2   > 0;
 select * from t where contains( data, 'How' )
                                       *
ERROR at line 1:
ORA-20000: ConText error:
DRG-10599: column is not indexed

SQL> SELECT COUNT(*) FROM T;

 COUNT(*)
---------
        3
please comment.

Ak

Followup:
uh-huh,  so did you search this site for

ORA-28575

the error you are getting -- it'll show you how to configure this.  You just
don't have the normal setup for interMedia completed.

January 17, 2002 Douglas -- Thanks for the question regarding "combining multiple nowait locks into one statement", version 8.1.5
You Asked
Tom, the transaction layer of our custom application often has
to lock rows in several tables for a particular transaction.
Say 50 rows in table A, 10 rows in table B, and 1 row in table C
all at once for one atomic transaction.  Currently, these are
done with 3 separate statements, one select xyz from table for
update nowait for each table before the transaction proceeds.
Is this overhead we shouldn't even be concerned about in the
first place?  The developers are wondering if we can combine the
statements somehow.  One way is a join, if you
select * from A,B,C where A.id=5 and B.id=10 and C.id=20 you
seem to get the right number of locks, but you end up with a
cursor with a cartesian product of rows that is unnecessarily
large.   What should we do?  Leave it the way it was?

Thanks again,
Dc.
and we said...
Leave it the way it was.  There are no efficiencies to be
gained, especially since you are doing the cartesian product.

If you want to reduce the round trips and just need to "lock"
the data, use a stored procedure like this:

ops$tkyte@8i> create table a ( id int );
Table created.

ops$tkyte@8i> create table b ( id int );
Table created.

ops$tkyte@8i> create table c ( id int );
Table created.

ops$tkyte@8i> begin
  2      for i in 1 .. 100 loop
  3          insert into a values ( mod(i,10)+1 );
  4          insert into b values ( mod(i,20)+1 );
  5          insert into c values ( mod(i,30)+1 );
  6      end loop;
  7      commit;
  8  end;
  9  /

PL/SQL procedure successfully completed.

ops$tkyte@8i> create or replace
  2  procedure lock_data( p_a_id in number,
  3                       p_b_id in number,
  4                       p_c_id in number )
  5  as
  6      type myArray is table of number index by
binary_integer;
  7
  8      array    myArray;
  9      empty    myArray;
 10  begin
 11      select id
 12             bulk collect into array
 13        from a
 14       where id = p_a_id FOR UPDATE;
 15      dbms_output.put_line( 'Fetched ' ||
 16                             array.count || ' from A' );
 17
 18      array := empty;
 19      select id
 20             bulk collect into array
 21        from b
 22       where id = p_b_id FOR UPDATE;
 23      dbms_output.put_line( 'Fetched ' ||
 24                             array.count || ' from B' );
 25
 26      array := empty;
 27      select id
 28             bulk collect into array
 29        from c
 30       where id = p_c_id FOR UPDATE;
 31      dbms_output.put_line( 'Fetched ' ||
 32                             array.count || ' from C' );
 33  end;
 34  /

Procedure created.

ops$tkyte@8i>
ops$tkyte@8i> exec lock_data( 1, 3, 5 )
Fetched 10 from A
Fetched 5 from B
Fetched 4 from C

PL/SQL procedure successfully completed.

You don't need the assignment of EMPTY to the array if you don't
care about the counts and you of course do not need the
dbms_outputs either.
Review & Followup
Rating: 4
How about using count(*) January 16, 2002
Reviewer: Monish
Hi Tom,

Are there any disadvantages if we code like this:

declare
 tmp number;
begin
 select count(*) into tmp from a where id = p_a_id FOR UPDATE;
.....
end;

Followup:
Yes, it won't work

ops$tkyte@ORA8I.WORLD> select count(*) from emp for update;
select count(*) from emp for update
                         *
ERROR at line 1:
ORA-01786: FOR UPDATE of this query expression is not allowed

Rating: 4
Another Way January 17, 2002
Reviewer: Monish
Hi Tom,

Can we do it like this:

begin
for i in (select id from a where id = p_a_id for update)
loop
    null;
end loop;
end;

Thanks,
Monish

Followup:
sure, it'll be slower then a bulk collect but there is no technical reason why
you could not.

January 18, 2002 titas -- Thanks for the question regarding "functionality of LGWR in Rollback of a transaction", version 8.1.6
You Asked
We know that whenever a transaction makes some changes in buffer cache a redo
entry is generated by server process and it is placed in redo log buffer and
when this transaction commits a commit record is written to buffer and redo
entries upto commit record are flushed to redo files. But
My Question:
a)when a rollback occurs, is redo entries generated before in redo buffer is
deleted and if so , by whom ,server process or LGWR
and we said...
a) no, they are not deleted -- much of the redo you generated may be on disk
already, way before you rollback.  we generate the redo -- it will be flushed.
The transactions final outcome (commit or rollback) is recorded as well.
January 19, 2002 Norazian -- Thanks for the question regarding "Store procedure -- getting OUT parameters in JDBC calls.", version 8i
You Asked
i am building jsp for the co's project. i am using store
procedure in
my programming by passing a parameter to the database. my
question
is how/what is the correct syntax to return the result after
passing
parameter through store procedure method?

for example:

 try {
 CallableStatement cs = db.conn.prepareCall("{call
svr_proc_budget_check("+parameter1+")}");
 cs.execute();
 cs.close();
 }
 catch(SQLException e){
 }

Therefore, i would like to know whether is the above method
correct?
The above method is use to pass the parameter1, but how can i
recieve
the second parameter at the same time?

can you please get back to me as soon as possible as i am
closely meeting the dateline. thank you very much for your
help.

azian
and we said...
Ok, to get parameters in and out of stored procedure we should
ALWAYS use bind variables (even for the inputs -- this is
critical if you want a well performing scalable application).

Lets say you have a procedure like:

scott@8i> create or replace procedure length_of_string
  2  ( the_string in varchar2, the_length out number )
  3  as
  4  begin
  5     the_length := length(the_string);
  6  end;
  7  /

Procedure created.

in the database. The java code to call it would be:

import java.io.*;
import java.sql.*;
import java.util.*;

class InOut
{

public static void main(String args[])
{
    Statement statement = null;

    try
    {
    DriverManager.registerDriver
        (new oracle.jdbc.driver.OracleDriver());

    Connection connection =
      DriverManager.getConnection
        ("jdbc:oracle:thin:@aria:1521:ora8i", "scott", "tiger");

    connection.setAutoCommit(false);

    statement = connection.createStatement();

    CallableStatement cstmt =
        connection.prepareCall(
               "{ call length_of_string( ?, ? ) }" );

    cstmt.setString( 1, "Hello World" );
    cstmt.registerOutParameter( 2, java.sql.Types.INTEGER );

    cstmt.executeUpdate();

    System.out.println( "That string was " +
                         cstmt.getInt( 2 ) +
                        " bytes long..." );

    }
    catch( Exception e )
    {
        e.printStackTrace();
    }

}

}

Note the use of placeholders (question marks) and the calls to

    cstmt.setString( 1, "Hello World" );

to set the i'th IN or IN OUT parameter to a procedure and the
use of:

    cstmt.registerOutParameter( 2, java.sql.Types.INTEGER );

on the IN OUT/ OUT parameters to tell jdbc we are expecting
something back.  After we execute, we use:

    cstmt.getInt( 2 )

or some variation of cstmt.getXXXX to get the int, string,
float, etc...
Review & Followup
Rating: 4
This helps get the jdbc and jsp essentials worked out. April 05, 2001
Reviewer: Harrison Picot from Haymarket, VA USA
There is not really enough good code around for those
of challanged by texts that assume we can visualize the
code from somthing like "Integer (String s) creates a
new integer from a string (well it does, but not if the
word String is in there).  So actual codes samples are
big help, and more is an even bigger help.  Just looking
at "Table 21-1 Valid SQL Datatype-Java Class Mappings"
or "Table 3-2 Default Mappings between SQL Types and Java
Types" makes me wonder how long it takes to get a simple
invoice program working, and having bits of working code
helps.

Rating: 5
Store procedure -- getting OUT parameters in JDBC calls. June 08, 2001
Reviewer: Jim McFadden from Dallas, TX
This is just what I needed to stub out the code for my project. I did not have
the knowledge to code up a stored procedure with OUT parameters using JDBC.
Every other website I went to did not explain both the SQL and the java together
very well. Thanks for the simple implementation.

Rating: 4
What happens if you don't user :bind variables? January 18, 2002
Reviewer: Tom Henricksen from Des Moines, IA
What happens if you don't user :bind variables?

Followup:
read
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2444907911913
The short answer is

a) your system will not scale
b) your performance will be bad
c) your application will fail
d) you will not succeed

January 19, 2002 nagavenugopal -- Thanks for the question regarding "Performance monitoring for pl/sql procedures", version 8.1.7
You Asked
Hi tom,

Is there any mechanism to find the performance of each procedure.

  1. How much CPU time it takes to execute?
  2. How much Space it occupies in the database.
  3. How tables/Procedures it refers.
  4. How to develop a report on these activities.

Regards,
Venu
and we said...
do you have my book?  you'll be interested in the chapter on dbms_profiler I
have in there.

search for

dbms_profiler

on this site for other examples of using that.

That'll answer #1.

#2 is the view dba_object_size

#3 is dba_dependencies

#4 is writing sql ;)
Review & Followup
Rating: 4
Helpful, but I have a question on sum of run times vs total run time January 18, 2002
Reviewer: Jon from Hartford, CT
I have your book and read the DBMS_PROFILER section but I have a question.

Why is there such a discrepancy between summing the time on plsql_profiler_units
and the value in plsql_profiler_runs?

14:13:25  ==>  column secs  format 999.99
14:32:05  ==> select runid,
14:32:10   2         substr(run_comment,1, 30) as run_comment,
14:32:10   3         run_total_time/1000000000 as secs
14:32:10   4    from (select a.runid, sum(a.total_time)  run_total_time,
b.run_comment
14:32:10   5            from plsql_profiler_units a, plsql_profiler_runs b
14:32:10   6           where a.runid = b.runid group by a.runid, b.run_comment )

14:32:10   7   where run_total_time > 0
14:32:10   8   order by runid asc;

RUNID RUN_COMMENT    SECS
----- ----------- -------
   26 batch         80.51

14:33:30  ==> select runid,run_total_time/1000000000 secs from
plsql_profiler_runs;

RUNID    SECS
----- -------
   26  395.74

Where was the other 315 secs spent?  Does it indicate a problem?

Followup:
The timings are collected using some unit of time, typically only granular to
the HSECS.

That means that many discrete events that take less then 1/100th of a second,
appear to take ZERO seconds.

Many discrete events that take less then 1/100ths of a second may appear to take
1/100th of a second.

Add up lots and lots of discrete events that take less then 1/100 of a second
and you can introduce a lot of errors.

The total run time from the plsql_profile_runs however is only off by an
aggregate of +- 1/100th of a second.

This is the difference between "analog" and "digital" -- think of the
plsql_profile_runs as more of an analog thing, the other table is more
"digital".  To get the aggregate run time -- we looked at the clock, saved that
time, ran the procedure, then looked at the clock again -- we got 395.74
seconds.

To get the individual line times -- we looked at the clock before each line,
execute the line, look at the clock again -- each observation can add +- 1/100th
of a second or so of error to the total value when summed later (don't forget,
included in the aggregate runtime is the time in the profile code itself, time
spent not in YOUR code but in Oracle's code -- thats not in the details either,
not all of it anyway).

So, what you are seeing is the aggregate error introduced by executing many 10's
or 100's (or even millions in your case, 395 seconds is a long time).  lines of
code and having each one be potentially off by as much as 1/100th of a second.

So, it indicates a "problem" -- one that cannot be solved since we are measuring
discrete events with a clock that has a certain resolution.  You just need to be
aware of it.

This is the reason why in a TKPROF report you can sometimes have CPU time larger
then ELAPSED time -- it is the same issue.

January 19, 2002 ls -- Thanks for the question regarding "cannot audit by session?", version 8.1.7
You Asked
Hi Tom

I have been testing auditing in Oracle these couple of days, I have problem
audit by session

I did

sys@DEV817>audit table by session;

Audit succeeded.

sys@DEV817>SELECT * FROM sys.dba_stmt_audit_opts;

USER_NAME            PROXY_NAME                     AUDIT_OPTION
            SUCCESS    FAILURE
-------------------- ------------------------------
---------------------------------------- -------
                                                    TABLE
            BY ACCESS  BY ACCESS

it syas access I dont understand why...

Also what is the difference between

audit_trail = "db"
audit_trail="true"

if true were used what are the auditing options, by db or os?
Finally is it possible to audit table by an user and access/session instead of
audit table by user OR session/access?
and we said...
<quote sql lang ref manual>

...
BY ACCESS

causes Oracle to write one record for each audited statement.

If you specify statement options or system privileges that audit data
definition language (DDL) statements, Oracle automatically audits by access
regardless of whether you specify the BY SESSION clause or BY ACCESS clause.

For statement options and system privileges that audit SQL statements other than
DDL, you can specify either BY SESSION or BY ACCESS. BY SESSION is the default.
...
</quote>

So, DDL related options (such as TABLE is) is done by statement - by access --
which makes sense if you think about it.

You can only create "T" once per session (and if you create T two times -- you
really have created two TOTALLY different T's, same in the name only -- they are
NOT the same).  And if you create T1 and T2 -- you need two audit records, it
CANNOT suffice to have just one.

true and db are the same with audit_trail.  if auditing is on, and set to true,
its the same as DB.

You can only audit table by ACCESS due to the above.
Review & Followup
Rating: 5
After thinking about it it makes sense yea January 19, 2002
Reviewer: A reader
Thank you Tom

A question, what does

ON DEFAULT do? Reading the doc but my English is not good enough to understand
it

Followup:
It sets up the DEFAULT auditing that will be applied to any object created after
you run this command.  It is like altering the default tablespace of a user --
any object they create from that point on will by default be created in that
tablespace.  If you later alter that user and change their default tablespace
again, only objects they create from that point on forward are affected (their
old tables are NOT moved, just as the old AUDIT rules would not be changed).

I chopped out the doc on this and removed some intervening text -- hopefully
this makes for a better read:

<quote>

ON DEFAULT

establishes the specified object options as default object options for
subsequently created objects. Once you have established these default auditing
options, any subsequently created object is automatically audited with those
options. The default auditing options for a view are always the union of the
auditing options for the view?s base tables. You can see the current default
auditing options by querying the ALL_DEF_AUDIT_OPTS data dictionary view.

If you change the default auditing options, the auditing options for previously
created objects remain the same. You can change the auditing options for an
existing object only by specifying the object in the ON clause of the AUDIT
statement.

.....

DEFAULT Example

The following statement specifies default auditing options for
objects created in the future:

AUDIT ALTER, GRANT, INSERT, UPDATE, DELETE
ON DEFAULT;

Any objects created later are automatically audited with the specified options
that apply to them, provided that auditing has been enabled:

o If you create a table, Oracle automatically audits any ALTER, GRANT, INSERT,
UPDATE, or DELETE statements issued against the table.

o If you create a view, Oracle automatically audits any GRANT, INSERT, UPDATE,
or DELETE statements issued against the view.

o  If you create a sequence, Oracle automatically audits any ALTER or GRANT
statements issued against the sequence.

o  If you create a procedure, package, or function, Oracle automatically audits
any ALTER or GRANT statements issued against it.

</quote>

January 19, 2002 -- Thanks for the question regarding "Select * into an Array", version 8.1.7
You Asked
hi

is it possible issueing select into in pl/sql into an pl/sql table (I want to
store a whole table rows into an array)??

tried it but got tons of errors!
and we said...
see
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a77069/04_colls.htm#28329
it is easy....
January 19, 2002 Sanjai -- Thanks for the question regarding "Internationalization", version 8.1.7
You Asked
Hi Tom,

I need to know if we can create a table in oracle database that can store data
in multiple languages.

For example.

Can I create a employee table that can store data for a employee in english,
french, spanish, chinese etc. If yes than how to do that and if not than how can
we get around this.

I have a requirement where the UI is supporting mulitple languages and I need to
store this data. Can i do it in one table.

Thanks for your help and time.
and we said...
UTF8 is the character set.

This:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76966/toc.htm
is the document you want to read from front to back.
January 20, 2002 rajesh -- Thanks for the question regarding "host command ", version 6i
You Asked
hi tom

i had exported two tables using parameter file method as
file=c:\export.dmp
userid=riks/rick
rows=y
compress=y
grants=y
tables=cashes,glday
querry="where day_dt>=to_date('01-dec-2001','dd-mon-yyyy')"

and invoked as

exp parfile=c:\export.txt

and this work well

now i need to do this same thing using developer 6i

by using the command host and passing a date as parameter to export the table.
i can able to execute exp using host command as bellow
host ('exp riks/riks file='c:\export.dmp',no_screen');
but i cannot able to use the key word querry  with above method.

plese help me
and we said...
You'll need to figure out how to properly escape the string on your OS.  This:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:478275606639
covers some ways.  The way I might approach this?  use text_io to write a
parfile to c:\temp and use the parfile command.  That way you don't have to
figure out how to escape the quotes and any other character that might give the
shell heartburn in the future.
January 20, 2002 pauline -- Thanks for the question regarding "Compress in export", version 8.1.7.2
You Asked
Tom,
As I know that compress=y in the export will consolidate table's extents into
first extents. So you suggest that always use compress=N.

I have the impression that if use locally managered tablespace, the table being
created in that tablespace will always use the initial extent from that
tablespace even we specify some different initial extent in the table creation
statement.

I am just thinking about this question: If use compress=y in export
for the table which was created in locally managered tablspace, after
import ,the table will get the consolidated initial extent or still
use the initial extent size of that locally managered tablespace ?

Please clarify it.
Thanks.

Pauline
and we said...
With an LMT -- ever extent will either be:

o uniformly sized (my preference)
o "auto" sized by the system using some undocumented method for sizing.

If you use a uniform size of 1m and you do an export with compress=y and the
initial extent requested was 100m, we will allocate 100 1m extents to satisfy
your "initial" request.  We will take the INITIAL, NEXT and MINEXTENTS -- decide
what they would be in a DMT and preallocate at least that much space in an LMT
for you.
Review & Followup
Rating: 3
January 20, 2002
Reviewer: a reader from NY, U.S.A
Tom,
So if in the case of uniformly sized locally managed tablespace, if tablespace
is uniform size of 1m and the table's extents reached to 100M,at this moment, we
do export with compress=y or take the default from compress,
oracle will allocate 100 1m extents to satisfy  "initial" request--100M, right?
Please confirm it. Thanks.

Followup:
yes. correct.

January 20, 2002 -- Thanks for the question regarding "Dependencies", version 8i+
You Asked
Why dependendencies are not broken on functions and views? If I changed function
body but function signature remains the same, why all dependent objects care?
Same for the views. For a huge database application a significant portion of
upgrade downtime is caused by recompilation of invalid objects, and since
dependencies are not broken on views, virtually every database object gets
recompiled.

I would make even stronger assertion that even if function/view signature is
changed by adding more arguments/columns, the dependent objects shouldnt be
invalidated. As long as 90% of the database application changes are adding new
columns, then the upgrade inconveniences become minimal.
and we said...
This is the reason you should always use PACKAGES for all real code and avoid
standalone functions and procedures for everything except examples, demos and
tests.

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:286816015990
for an example of this behavior.

This is the way to make it so that when the signature doesn't change, the
dependent objects do not go invalid.

As for "why do dependent objects go invalid if the signature hasn't changed" --
well, it is the act of going invalid (you compile that procedure) that
invalidates the other objects.  The signature isn't available, isn't checked,
doesn't really come into play at that point in time.  You are forgetting about
security (so that view goes invalid.  All dependent objects MUST go invalid as
well -- we need to compile them - and compile that view to make sure all access
necessary is still there.  A simple REVOKE will not only invalidate but may make
it impossible to successfully compile).

Ok, so you can use packages to protect functions/procedures from cascading
invalidations -- what about views then?  Well, some people use packaged cursors
(packages to the rescue once more).  You can put the cursor "spec" in the
package spec and the body in the cursor body.  The spec NEED NOT be dependent on
the view -- the body will be.
Review & Followup
Rating: 4
January 20, 2002
Reviewer: A reader
Sir,

Will you please provide an example of "packaged cursor"
and how to use it in place of view.

Thanks

Followup:
You don't use it IN PLACE of a view, you use it to hide the dependency on the
view in a package body.

See
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a77069/05_ora.htm#40356

Here is a very simple example where the packaged spec for the cursors cannot go
invalid:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view v as select * from
dual;

View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package my_pkg
  2  as
  3          type c1_rowtype is record
  4          (  dummy varchar2(25)
  5          );
  6
  7          cursor c1( p_dummy in varchar2 ) return c1_rowtype;
  8  end;
  9  /

Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package body my_pkg
  2  as
  3
  4          cursor c1( p_dummy in varchar2 ) return c1_rowtype is
  5          select *
  6            from v
  7           where dummy = p_dummy;
  8
  9  end;
 10  /

Package body created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> @invalid
ops$tkyte@ORA817DEV.US.ORACLE.COM> break on object_type skip 1
ops$tkyte@ORA817DEV.US.ORACLE.COM> column status format a10
ops$tkyte@ORA817DEV.US.ORACLE.COM> select object_type, object_name, status
  2  from user_objects
  3  where status = 'INVALID'
  4  order by object_type, object_name
  5  /

no rows selected

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop view v;

View dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view v as select 'xxxx'
dummy from dual;

View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> @invalid
ops$tkyte@ORA817DEV.US.ORACLE.COM> break on object_type skip 1
ops$tkyte@ORA817DEV.US.ORACLE.COM> column status format a10
ops$tkyte@ORA817DEV.US.ORACLE.COM> select object_type, object_name, status
  2  from user_objects
  3  where status = 'INVALID'
  4  order by object_type, object_name
  5  /

OBJECT_TYPE        OBJECT_NAME                    STATUS
------------------ ------------------------------ ----------
PACKAGE BODY       MY_PKG                         INVALID

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter package my_pkg compile body;

Package body altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> @invalid
ops$tkyte@ORA817DEV.US.ORACLE.COM> break on object_type skip 1
ops$tkyte@ORA817DEV.US.ORACLE.COM> column status format a10
ops$tkyte@ORA817DEV.US.ORACLE.COM> select object_type, object_name, status
  2  from user_objects
  3  where status = 'INVALID'
  4  order by object_type, object_name
  5  /

no rows selected

ops$tkyte@ORA817DEV.US.ORACLE.COM>

as long as the view signature does not change.  The spec is simply not dependent
on the view.

January 20, 2002 Stephen -- Thanks for the question regarding "Cannot drop tables", version 8.1.7
You Asked
We recently upgraded from 8.0.5 to 8.1.7.  Working with the previous tables
seemed to be fine, but when I tried to create new tables I got ORA-18008.  I
create user OUTLN with DBA privileges (perhaps I should have not have done so)
and was then able to create new tables and carry out any operation on them
except drop them!  I get the follow when I attempt it:

SQL> drop table xx;
drop table xx
           *
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-00942: table or view does not exist

Table xx does exist.
and we said...
did you follow ALL of the recommended steps when upgrading this database?  I
don't think so.  For example, it sounds like

Rem
Rem    NAME
Rem      c0800050.sql - upgrade Oracle RDBMS from 8.0.5 to the new release
Rem

that script was never run.  If it were, it would have created the outln user and
various tables/views in that schema.

You need to revisit how you did this upgrade and read:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a86632/toc.htm
to ensure you did the proper steps (i know you missed at least one!)

Please read that BEFORE just running scripts -- also, since you've created this
user, that script will actually FAIL (quite a mess at this point)!  It'll fail
cause it will attempt to execute:

set serveroutput on

DECLARE
  user_exists EXCEPTION;
  outln_user number;
  outln_tables number;
  extra_outln_tables number;
  DDL_CURSOR integer;
BEGIN
  select count(*) into outln_user from user$ where name='OUTLN';

  select count(*) into outln_tables from obj$ where name in
        ('OL$', 'OL$HINTS') and owner#=
        (select user# from user$ where name='OUTLN');

  select count(*) into extra_outln_tables from obj$ where name not in
        ('OL$', 'OL$HINTS') and type#=2 and owner#=
        (select user# from user$ where name='OUTLN');

  DDL_CURSOR := dbms_sql.open_cursor;
  IF outln_user = 0 THEN
        dbms_sql.parse(DDL_CURSOR, 'create user outln identified by outln',
                 dbms_sql.native);
        dbms_sql.parse(DDL_CURSOR,
                 'grant connect, resource, execute any procedure to outln',
                 dbms_sql.native);
        dbms_sql.parse(DDL_CURSOR, 'create table outln.ol$ ( '||
          'ol_name           varchar2(30), ' ||
          'sql_text          long,  ' ||
.....

but that create user will fling an exception....

If the ONLY thing you've done to workaround this is create the OUTLN user, drop
it and follow the upgrade steps as documented.  If you've done other things to
workaround "strange issues", you might need to undo them as well.

Suggest a FULL backup now.  Then read.  Then fix.
Review & Followup
Rating: 5
January 28, 2002
Reviewer: Stephen Tillman from Wilkes-Barre, PA USA
Your review really helped.  Our system was even more screwed up than I had
thought when I sent in the question (e.g. it did not let me drop user outln,
which I had created outside the script).  Essentially the review told us that we
should stop trying to patch things, scrap what we had done before, and do a
complete reinstall.  Fortunately I had exported the 8.0.5 database before we
tried the initial install, so I did not lose much.  I have not tried everything
I need yet, but the things I have tried are working well, which they did not do
before.  Thank you once again.

January 20, 2002 -- Thanks for the question regarding "Database backup and recovery plan.", version 9.0.1
You Asked
Hi,
We would like to implement our Backup and Recovery Plan for our new Oracle 9i
database on UNIX using RMAN. Our current backup and recovery plan is based on OS
scripts for our other 8i databases.

Can you outline a robust plan for us. We have done reading of the RMAN
manuals... appears a bit dispersed. We are a 24x7 shop and have database in
archive log mode. The size of the database is around 200 MB.

Requirements:

i) Full database backup eveynight( need help as to incremental or not)
ii) Backup of archive logs twice/thrice a day.
iii) Would like to backup directly to tapes, if possible and efficient.
Otherwise we have to do Disk-Disk-Tape. If we do a D-D-T, do we have to remember
the tape details in that case.

Regards..

Irshad.
and we said...
i) for a 200meg database (really -- just 200meg?  my system tablespace is bigger
then that), incremental wouldn't be necessary.

ii) fine

iii) fine as well

RMAN does all of that -- you just need to write the scripts or use OEM to do
this in a "GUI" fashion.

I don't know what you mean by RMAN Manuals and "dispersed".  There are only two
manuals -- a users guide and a reference.  The users guide has a "quick start"
to get your feet wet, and much more detail on how it works.  Then chapter 9 has
examples for you "making whole database backups with RMAN" and "backup up
archived redo logs with RMAN"
Review & Followup
Rating: 3
Backups January 20, 2002
Reviewer: Rahul from NJ
Tom,

How big is your database and what is your backup startegy.
Do you perform rman or physical backups.

Thanks

Followup:
they range from tens to a couple hundred gigs (there is about 6 or 7 of them).
We have a tape library and just do old fashioned scripted backups keeping the
last 5 backups and archives available.  Just never got around to upgrading to
rman -- our backups have been going good enough since version 7.0 -- no need to
break that which isn't broken.  Although with some of the new 9i features --
like block level recovery, restartable backups/restores, and so on -- I'm very
tempted...

Rating: 5
very interesting January 21, 2002
Reviewer: Eric Grancher from CERN, Switzerland
it is very interesting to have a point of view different from (aouah you are
still using scripts, you'd better use RMAN). We are also in the situation where
we use a number of well prepared scripts.

Rating: 3
Reader January 21, 2002
Reviewer: Reader
I am using a HYBRID, conventional once a week ( 2 sets
retention), RMAN every day - before building confidence
with RMAN.

January 21, 2002 mahbuba -- Thanks for the question regarding "Prefix or Suffix in a field value", version 8.1.6.0
You Asked
We have a table named DETAIL_RECORD having c column 'REF_DGNFILE' with about
3400 records.

The field value of REF_DGNFILE are as below:

SQL> SELECT REF_DGNFILE FROM DETAIL_RECORD;

REF_DGNFILE
----------------------------------------
SS1106.CIT
SS1501.CIT
SS1506.CIT
SS1509.CIT
SS1510.CIT
dpc.d33
SS1511.CIT
SS1512.CIT
ss0600.d11
ss0600.cit
SC3.TIF

The structure of the table is as below:

SQL> DESC DETAIL_RECORD
 Name                            Null?    Type
 ------------------------------- -------- ----
 RB_PRMRY                                 NUMBER(10)
 RB_SCNDRY                                NUMBER(10)
 MSLINK                                   NUMBER(10)
 RB_LOCK                                  NUMBER(5)
 RB_FSC                                   NUMBER(5)
 RB_STATE                                 NUMBER(5)
 RB_COMPONENT                             NUMBER(5)
 RB_VERSION                               NUMBER(5)
 RB_OCCURRENCE                            NUMBER(5)
 REF_DGNFILE                              CHAR(40)
 FILETYPE                                 NUMBER(5)
 LOWER_LEFT_X                             NUMBER(10)
 LOWER_LEFT_Y                             NUMBER(10)
 UPPER_RIGHT_X                            NUMBER(10)
 UPPER_RIGHT_Y                            NUMBER(10)

My QUESTION are :

1. My boss told me to update the above mentioned field value with a prefix 'LY_'
for all
   values of this field (as for example, Instead of 'SS1106.CIT', it should be
'LY_SS1106.CIT').

I tried in the following way but could have a luck because of lack of
experiences.

Please,help me out.

SQL> ED
Wrote file afiedt.buf

  1  UPDATE DETAIL_RECORDT
  2  SET REF_DGNFILE = (SELECT 'LY'||REF_DGNFILE FROM  DETAIL_RECORDT
  3* WHERE LENGTH(REF_DGNFILE)>5)
SQL> /
SET REF_DGNFILE = (SELECT 'LY'||REF_DGNFILE FROM  DETAIL_RECORDT
                   *
ERROR at line 2:
ORA-01427: single-row subquery returns more than one row

SQL> ED
Wrote file afiedt.buf

  1  UPDATE DETAIL_RECORDT
  2   SET REF_DGNFILE = (SELECT 'LY'||REF_DGNFILE FROM DETAIL_RECORD
  3* WHERE DETAIL_RECORDT.RB_PRMRY= DETAIL_RECORD.RB_PRMRY)
SQL> /
UPDATE DETAIL_RECORDT
*
ERROR at line 1:
ORA-01401: inserted value too large for column

SQL> ALTER TABLE DETAIL_RECORD
  2  MODIFY(REF_DGNFILE CHAR(150));

Table altered.

SQL>  UPDATE DETAIL_RECORDT
  2    SET REF_DGNFILE = (SELECT 'LY'||REF_DGNFILE FROM DETAIL_RECORD
  3   WHERE DETAIL_RECORDT.RB_PRMRY= DETAIL_RECORD.RB_PRMRY);
 UPDATE DETAIL_RECORDT
*
ERROR at line 1:
ORA-01401: inserted value too large for column

SQL> ALTER TABLE DETAIL_RECORDT
  2  MODIFY(REF_DGNFILE VARCHAR2(1000));

Table altered.

SQL>
SQL>  UPDATE DETAIL_RECORDT
  2    SET REF_DGNFILE = (SELECT REF_DGNFILE FROM DETAIL_RECORD
  3   WHERE DETAIL_RECORDT.RB_PRMRY= DETAIL_RECORD.RB_PRMRY);
  SET REF_DGNFILE = (SELECT REF_DGNFILE FROM DETAIL_RECORD
                     *
ERROR at line 2:
ORA-01427: single-row subquery returns more than one row

2.How to make a prefix , suffix by default in the structure of the field while
creating a table?
and we said...
1) yuck, why char(40),  never use CHAR -- always use varchar2.  CHAR is just a
blank padded varchar2.  It only makes life hard, wastes space.

You want to

update detail_recordt
   set ref_dgnfile = 'LY_' || rtrim(ref_dgnfile);

thats all.

2) create or replace trigger detail_recordt_trigger
   before insert on detail_recordt for each row
   begin
        if ( nvl(substr(:new.ref_dgnfile),'x') != 'LY_' )
        then
            :new.ref_dgnfile := 'LY_' || :new.ref_dgnfile;
        end if;
   end;
Review & Followup
Rating: 4
Prefix or Suffix January 21, 2002
Reviewer: mahbubarahman from Sharjah
Updating is working fine but while creating the trigger for new records I have
faced the following:

Plz, extend your experienced hands to help me.

SQL> create or replace trigger detail_recordt_trigger
  2     before insert on detail_recordt for each row
  3     begin
  4          if ( nvl(substr(:new.ref_dgnfile),'x')= 'LY_' )
  5          then
  6              :new.ref_dgnfile := 'LY_' || :new.ref_dgnfile;
  7          end if;
  8     /
  9  /

Warning: Trigger created with compilation errors.

SQL> INSERT INTO DETAIL_RECORDT(REF_DGNFILE) VALUES('3245-2004');
INSERT INTO DETAIL_RECORDT(REF_DGNFILE) VALUES('3245-2004')
            *
ERROR at line 1:
ORA-04098: trigger 'DETAIL_RECORDT_TRIGGER' is invalid and failed re-validation

1.Please, tell me with examples how to solve the above problem.

2.that means even if I create a New table where I want a
  prefix to be added by default,
  I have to create a trigger for that.
  Is there any other way without trigger where my data
  entry operators need not to include the prefix in
  their entry making their job easier?

Followup:
1) lose the redundant / on line 8.  It should be the word:

   end;

when you get an error like that, simply:

SQL> show errors trigger detail_recordt_trigger

2) your client application could provide it instead.  If you want LOGIC behind
the table, we do that with a trigger.  You can put the logic IN FRONT of the
table by coding it in your application.  Totally 100% up to you.

January 21, 2002 Amit -- Thanks for the question regarding "selecting not null cols and values in a PL/SQL table from a given heap table.", version 8.1.7
You Asked
Hi Tom,

My present prob is:
We have a table with n number of columns, of which the columns of our interest
are named with prefix "H_".

Now my aim is to generate a PL/SQL table with 2 columns (col_name,col_value)
with all such column names ("H_") and their values.

The PL/SQL table should contain the columns "H_" with NOT null values only.

What is the best way to do it?

I was doing this with all_tab_columns and dbms_sql. (dynamic sql)
Can you suggest me a performant solution? Or your way of using dynamic sql for
this?

And yes, the number of "H_" prefixed
columns is not fixed. We can add "H_" cols in new releases.
(thats why I was using All_tab_columns view).

Create table t (
thePK number,
H_xyz varchar2(30),
H_Ayz varchar2(30),
H_xcz varchar2(30),
H_pyz varchar2(30),
H_zyz varchar2(30),
H_lyz varchar2(30),
H_myz varchar2(30),
H_xnz varchar2(30),
colp    number,
colq    number
);

Insert into t(thePK, H_xyz, H_Ayz, H_xcz, H_pyz, H_Zyz, H_Lyz, H_myz, H_xnz,
colp,colq)
values(1,'T1','T2','T3','T4','T5','T6','T7','T8',1,2);

Insert into t(thePK, H_Ayz, H_xcz, H_pyz, H_Zyz, H_Lyz, H_myz, H_xnz, colp,colq)
values(2,'T2','T3','T4','T5','T6','T7','T8',1,2);

Insert into t(thePK, H_xyz, H_xcz, H_pyz, H_Zyz, H_Lyz, H_myz, H_xnz, colp,colq)
values(3,'T1','T3','T4','T5','T6','T7','T8',1,2);

commit;

If I want to create a pl/sql table for thePK = 1 from t then it would contain
all H_ columns because all of them have values.
If I want to create a pl/sql table for thePK=2 from t then it would contain all
H_ columns except "H_XYZ" since it is null in this case.

How to achieve this the best way?

Thanks in advance,

JJ
and we said...
I would use a view, such as this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view v
  2  as
  3  select thePk,
  4         decode( r, 1, 'h_xyz', 2, 'h_ayz', 3, 'h_xcz',
  5                    4, 'h_pyz', 5, 'h_zyz', 6, 'h_lyz',
  6                    7, 'h_myz', 8, 'h_xnz' ) name,
  7         decode( r, 1, h_xyz, 2, h_ayz, 3, h_xcz,
  8                    4, h_pyz, 5, h_zyz, 6, h_lyz,
  9                    7, h_myz, 8, h_xnz ) val
 10    from t, ( select rownum r from all_objects where rownum <= 8 )
 11  /

View created.

Now, since the "H_" columns can change, I suggest you use a procedure like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure create_view
  2  authid current_user
  3  as
  4      l_stmt long default 'create or replace view v as select thePk,';
  5      l_cnt  number;
  6  begin
  7
  8      l_stmt := l_stmt || ' decode( r ';
  9      for x in ( select rownum r, column_name
 10                   from user_tab_columns
 11                  where table_name = 'T'
 12                    and column_name like 'H\_%' escape '\' )
 13      loop
 14          l_stmt := l_stmt || ', ' || x.r || ',' || '''' || x.column_name ||
'''';
 15      end loop;
 16      l_stmt := l_stmt || ') name, decode( r';
 17      for x in ( select rownum r, column_name
 18                   from user_tab_columns
 19                  where table_name = 'T'
 20                    and column_name like 'H\_%' escape '\' )
 21      loop
 22          l_stmt := l_stmt || ', ' || x.r || ',' || x.column_name;
 23          l_cnt  := x.r;
 24      end loop;
 25      l_stmt := l_stmt || ') val from t, ' ||
 26                          ' ( select rownum r from all_objects where rownum
<= ' ||
 27                          l_cnt || ' )';
 28
 29      execute immediate l_stmt;
 30  end;
 31  /

Procedure created.

And just run it after any additional columns are added to T (make it part of
your process).  Then, you can:

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec create_view;

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from v where thePk = 1 and val is
not null;

     THEPK NAME                           VAL
---------- ------------------------------ ------------------------------
         1 H_XYZ                          T1
         1 H_AYZ                          T2
         1 H_XCZ                          T3
         1 H_PYZ                          T4
         1 H_ZYZ                          T5
         1 H_LYZ                          T6
         1 H_MYZ                          T7
         1 H_XNZ                          T8

8 rows selected.

And that is how you would query it.
Review & Followup
Rating: 5
Excellent January 21, 2002
Reviewer: JJ
Thats pretty much I needed. The prescripion is fitting.

thanks,
JJ

Rating: 4
Question on null columns March 05, 2002
Reviewer: A
In how many ways can i select records from a table based on a condition that a
particular column is null or not by NOT USING  'IS NULL' or 'IS NOT NULL'
clauses? . Can u list out some?
U can use nvl() for one..are ther any other ways?

January 21, 2002 Pratap -- Thanks for the question regarding "Locally managed tablespace - Why extent size does not match", version 8.1.6
You Asked
Hi Tom

I created a locally managed tablespace -

CREATE TABLESPACE xyz DATAFILE 'xyz.dbf' SIZE 360M REUSE
EXTENT management local uniform  SIZE 10240

But when I query user_tablespaces it shows the initial and next extent = 16384
Can you please explain why this happens ?
The block size is 8k

Thanks

Pratap
and we said...
extents consist of blocks.  you cannot have a fraction of a block in an extent.
We rounded up to the next block boundary.


Subject | Article | Last Updated (East coast time)
Who's In and for how long | article only | Jan 22, 2002 07:22 am
accessing a higher version database from lower. | article only | Jan 22, 2002 05:45 pm
Statspack Scripts Files | article only | Jan 23, 2002 07:23 am
How does invoker´s rights solve the PL/SQL privs problem | article only | Jan 23, 2002 07:32 am
EXECUTE_QUERY(FOR_UPDATE, NO_WAIT) | article only | Jan 23, 2002 06:20 pm
INSTALL ORACLE 7.3.4 ON WINDOWS 2000 SERVER | article only | Jan 25, 2002 06:45 am
Database Links and LOBs | article only | Jan 25, 2002 04:24 pm
Explaining tablespace | article only | Jan 27, 2002 10:23 am
Approach to implement a complicated query | article only | Jan 27, 2002 11:19 am
Nested Table Question, accessing the columns | article only | Jan 28, 2002 08:09 am
Production and Test Databases | article only | Jan 28, 2002 08:20 am
two way outer join | article only | Jan 28, 2002 08:29 am
Passing BLOBs acroos a DB Link | article only | Jan 28, 2002 08:47 am
Two Similar Databases | article only | Jan 28, 2002 08:51 am
How to use environment variables inside of a pl/sql block. | article only | Jan 28, 2002 12:26 pm
how to query/update a clob column | article only | Jan 28, 2002 12:45 pm
Import causes strange Audit Session behaviour | article only | Jan 28, 2002 01:20 pm
Index Unusable | article only | Jan 28, 2002 02:05 pm
exp imp | article only | Jan 28, 2002 03:50 pm
Matrix type output in sql*plus | article only | Jan 28, 2002 03:53 pm
row(s) 1 - 20 of 20
January 22, 2002 -- Thanks for the question regarding "Who's In and for how long", version 816
You Asked
Hi,
  1) I have a 3 tier app with asp ,iis,oracle816. How do I find out
who's connected and for how long In terms of  Application session and also
database sessions.Because Web environment is stateless,even if some connects to
the application he may not be connected to the database. I think for application
web session I may need to have some tables to capture app logon but for log off
it is difficult ,the use r may have his web session forcefully terminated , How
to catch such events to register in the database tables. Any ideas.

2)I query the v$session . It gives me the the sid, status, logon time etc etc
.What is this status for, its values are active and inactive.
When connected another session from sqlplus just to check.The V$session shows
it's status as Inactive.Why ?Why does it does not have a log off time ?Do u have
any script for this.

Thanx
and we said...
1) no ideas -- it is totally application specific as the application is the
thing with the concept of "logged in" or not.  You have bypassed the database
concept of "logged in or not".

What I do on asktom here is every page view calls dbms_utility.get_time at the
very start and right before its done, I create an audit record in a table that
shows the page and who (sessionid, i don't really know WHO you are), other data
and elapsed time.  I use this to measure performance/page and count real hits
(vs a hit for every image on the page and so on).  Yes, I do an INSERT for each
and every page and YES the database is doing just fine and yes, I highly
recommend this approach for everyone.  The database was built to be inserted
into.

2) status shows whether the session is running a sql statement or not.  There
cannot be a "logoff time" in v$session since the only rows in v$session are
those for sessions that are logged in.  If you are not logged in, you will not
have a row in this v$ table.
Review & Followup
Rating: 4
dbms_utility.get_time January 21, 2002
Reviewer: David Schwartz from New Jersey
Tom, I don't get it. Why do you not just use SYSDATE to get a start & end
date/time. You could always do the calcs on those values to get elapsed time.

It seems to me that the dbms_utility.get_time may give an elapsed time but it
has no reference to any real point in time. (which is useful for tracking user
acrtivity)

Followup:
my pages (fortunately) take significantly less then one second to generate.
SYSDATE is at the second level.  I want an ELAPSED time, not a point in time (i
collect the start time and elapsed time).

Rating: 5
Excellant as usual January 21, 2002
Reviewer: A reader
Thanx for all your answers. I have learned a lot from u.Any suggestions on Books
apart from yours to give 8i dba certification? Planning to give the same after
all these years.

Followup:
I'm not really familar with OCP and the related books personally.  I can say
that an EXCELLENT book is Jonathan Lewis's Practical Oracle8i, building
efficient databases.  You will learn much from that one.

Rating: 5
Excellant as usual January 21, 2002
Reviewer: A reader
Thanx for all your answers. I have learned a lot from u.Any suggestions on Books
apart from yours to give 8i dba certification? Planning to give the same after
all these years.

Rating: 1
move the audit log and trigger it January 22, 2002
Reviewer: roland daane from netherlands
What I did (before 817) was :

move the audit log table to the user tablespace and create a view in the system
which points to the new aud$ table.

Of cause grant all to system on aud$.

Now you can trigger every event using database triggers

Followup:
TOTALLY unsupported.  And, if you've actually done this, you left out half of
the story.

You moved it to USERS.
You ALSO CREATED IT AS SYSTEM.
You created the view as SYS.

You not only have to move the aud$ table, you have to change OWNERS.  AUD$ is
part of the data dictionary, it is owned by SYS.  SYS owned tables cannot have
triggers on them.

I've seen this "technique" *shutdown* (kill, crash, make non-functional) a
database.

this "technique" is highly questionable, a bad idea, not something I would
even consider recommending

That aside, if you read the question, it will not help the user.  They are using
a connection pool.  It logs in ONCE, stays logged in, and services MANY users.
The questioner wants to know how to measure "logged in time" when the
application is the one with the concept of "logged in" and even "user".
Measuring logged in time is TRIVIAL normally -- just audit connect.  However, in
this case -- auditing the Oracle connection is 100% meaningless.  It is the
APPLICATION that has the concept of logged in in this case.

Rating: 5
By V$Session you can get the connect users September 19, 2003
Reviewer: Rahul k Chaudahri from India(Pune)
i done known about 3tir users But may be all users who are connected are shown
in v$session.

   Select serial#, Sid, username from v$session

the STATUS column of the V$session
 ACTIVE          (currently executing SQL),
 INACTIVE,KILLED (Marked to be killed),
 CACHED          (Temporaryily cached for the use by oracle)
 SNIPED          (Session inactive, waiting on the client).

I thing so U may Get some thing from this part

January 22, 2002 aparna -- Thanks for the question regarding "accessing a higher version database from lower.", version oracle 7.3
You Asked
Hello Tom,
Can I access oracle8i database from a oracle7.3 database ?
And is it possible to access oracle8/8i datatypes to be accessed
from an Oracle7.3 database ( e.g. a lob datatype ) .

Thnaks,
Aparna.
and we said...
Yes and No.

Yes you can access 8i from 7.3.

scott@ORA734.US.ORACLE.COM> select * from v$version@ora8i;

BANNER
----------------------------------------------------------------
Oracle8i Enterprise Edition Release 8.1.5.0.0 - Production
PL/SQL Release 8.1.5.0.0 - Production
CORE Version 8.1.3.0.0 - Production
TNS for Solaris: Version 8.1.5.0.0 - Production
NLSRTL Version 3.4.0.0.0 - Production

scott@ORA734.US.ORACLE.COM> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle7 Server Release 7.3.4.0.1 - Production
PL/SQL Release 2.3.4.0.0 - Production
CORE Version 3.5.4.0.0 - Production
TNS for Solaris: Version 2.3.4.0.0 - Production
NLSRTL Version 3.2.4.0.0 - Production

So, that shows the remote database is Oracl8i and the local is
7.3.4

Now, if I access a type 7.3 is unaware of -- I'll recieve:

scott@ORA734.US.ORACLE.COM> select * from t@ora8i;
select * from t@ora8i
              *
ERROR at line 1:
ORA-03115: unsupported network datatype or representation
ORA-02063: preceding line from ORA8I
Review & Followup
Rating: 4
Error January 22, 2002
Reviewer: sivababu from GERMANY
Hello TOM,
     I gone thru this message. We are getting "ORA-03115 unsupported network
datatype or representation"...

We making the full part structure ( like tree ). The error is not comming for
the first position. but it is comming in second position.

Can you pl clear my problem?.

 But the client version and server versions are different.

expecting your mail.

regards,
sivababu

Followup:
and the example to help me reproduce is?  the versions in question are?  give me
a simple create table, inserts and the select to reproduce.

I really cannot "mail" you -- that is not what this forum is for, i have no
emails in the "reviews"

Rating: 2
What do you mean by Yes and NO January 22, 2002
Reviewer: Kulguru
Tom

from your example do you mean that  you can access dynamic views
i.e.datadictionary views from a lower version to higher version, and not regular
tables.

Followup:
No, I mean in a 7.3 database -- the datatype "CLOB" did not exist, it has NO
idea what a CLOB is..

Hence, when I selected from T, with a datatype that 7.3 does not understand,
it'll fail.  I used v$version to show the versions of the databases (and
v$version has types that are common to 7.3 and 8i so it succeeded)

Rating: 3
Unsuppoerted network data type error. January 22, 2002
Reviewer: Sivababu from GERMANY
Hello TOM,
  It is solved now. thanks. I don't know why this error is comming for stored
procedure.
When I'm calling from CENTURA 1.5.1 (front end application) i added two more
parameters which is not existing in the stored procedure. So i got this error.
When i run the new procedure it is working now.

  My client version is 7.3
  My server version is 8.1.6

regards,
sivababu

Rating: 3
Reader January 22, 2002
Reviewer: Reader
Tom,

I just heard a rumor from an Oracle Consulatnt
that as of Oracle RBMS Version 9.2, the
command interpreter SQLPLUS is going away and
evreything is through OEM

Is there a truth to it

Followup:
Don't know, why don't we wait for 9.1 to be finished first.  I won't speculate
on things soooo far in the future.

January 23, 2002 Edmundo -- Thanks for the question regarding "Statspack Scripts Files", version 8.1.7
You Asked
Hi Tom, Thanks for all your previous help.

I have next doubt:

I have installed Oracle 8i - 8.1.7 on AIX RS/6000, and I want to start working
with performance tool statspack, but I did not find the script files
named stat* into $ORACLE_HOME/rdbms/admin .

What happened here, what should  I do to put that files in there ?

I had install Oracle 8i, 8.1.6 and those files are located in that path for this
release. Can I use this script files for 8.1.7.

Thanx for you help.

Regards.
and we said...
They are all called sp* in 8.1.7.

use the new 8.1.7 scripts -- the report is nicer, more info.
Review & Followup
Rating: 4
Statspack Scripts Files May 30, 2001
Reviewer: Edmundo
Thankx for you helpful response.

Rating: 3
reviewer January 23, 2002
Reviewer: Ak from India
 Hi tom,
  In 8.1.5 what is the name of the script ? In your book you
have talked about 8.1.6 and 8.1.7 .
Can you tell me how to use this in 8.1.5.

Thanks,
Regards
  Ak

Followup:
see
http://www.oracle.com/oramag/oracle/00-Mar/index.html?o20tun.html
816 is the first release that came with statspack, that page has a link to a
statspack that can be used with 815 however.

January 23, 2002 Andre -- Thanks for the question regarding "How does invoker´s rights solve the PL/SQL privs problem", version 8.1.6
You Asked
Hi Tom !

In a previous question, I asked:

........................................................

I have both MDA and CORPORATE schemas. MDA has been granted
"ALL" table privileges to access CORPORATE through a role. When
I compile a procedure as MDA, accessing a table in CORPORATE´s
schema I get an error. What can be wrong ?

SQL> show useruser is "MDA"
SQL> desc CORPORATE.DIRECT_CAD Name
Null?    Type
 ------------------------------- -------- ----
 CUR_ACCT_NBR                    NOT NULL NUMBER(11)
 EFF_DATE                        NOT NULL DATE
 ESTAB_DATE                               DATE
 CHNNL_SALES_CODE                         VARCHAR2(2)
 KIT_FLAG_CODE                            CHAR(1)
 FIRST_MOV_DATE                           DATE
 LAST_MOV_DATE                            DATE
 CONNECT_MTH_AVG                          VARCHAR2(5)
 ACTV_CUST_ID                             CHAR(1)

SQL> CREATE OR REPLACE PROCEDURE MDA.TRUNCATE_TABLE ......

   IF p_Eff_Date = '0' THEN
           SELECT max(EFF_DATE)
           INTO DtCurr_RepDate
           FROM CORPORATE.DIRECT_CAD;
           -- Only place CORPORATE.DIRECT_CAD referenced
ELSE...
END TRUNCATE_TABLE;
/

Warning: Procedure created with compilation errors.

SQL> show err

Errors for PROCEDURE MDA.TRUNCATE_TABLE:
LINE/COL ERROR--------
-----------------------------------------------------------------

15/9     PL/SQL: SQL Statement ignored
17/16    PLS-00201: identifier 'CORPORATE.DIRECT_CAD' must be
declared

................................................

You answered:

see
http://osi.oracle.com/~tkyte/Misc/RolesAndProcedures.html
.................................................

Thanks ! A read about this "phenomenon": The PL/SQL block´s
owner must be explicitly granted the privileges, and not through
a role.

However, this same article says that invoker´s rights bypass
this problem.

Could you please elaborate on that ?

Thanks again !!!
and we said...
Prior to Oracle8i, all compiled stored objects executed with the
privileges and namespace of the definer of the object.  That is,
the set of privileges granted directly to the definer (owner) of
the stored object were used at compile time to figure out

ƒá What objects (tables and so on) to access.
ƒá Whether you were in fact allowed to access them.

This static compile time binding went as far as to limit the set
of privileges to on the privileges granted to the definer
directly (eg: no roles were ever enabled during the compilation
or execution of a stored procedure, trigger or view).  This is
predominantly a performance optimization and typically is not a
burden (it is truly a feature).

Beginning in Oracle8i, we have a feature called Invokers rights
which allows us to create procedures that do not follow these
rules.  We can now develop a stored procedure that executes with
the privilege set of the INVOKER (the currently logged in user)
at runtime.  This allows us to create a stored procedure that
might execute properly and correctly for one user (they had
access to all of the relevant objects) but not for another (who
didn¡¦t).  This is because the access to the underlying objects
is not defined at compile time (although the definer must have
access to these objects ¡V or at least objects with those names
in order to compile the PLSQL code) but rather at runtime ¡V
based on the current user.

In your case above however -- it will not solve the issue.  When
we compile a procedure (definer or invoker) we will check that:

ƒá All of the objects it statically accesses (anything not
accessed via dynamic SQL) are verified for existence.  Names are
resolved via the standard scoping rules as they apply to the
definer of the procedure. ROLES are not enabled.

ƒá All of the objects it accesses are verified to ensure that
the required access mode will be available.  That is, if an
attempt to UPDATE T is made ¡V Oracle will verify the definer or
PUBLIC has the ability to UPDATE T without use of any ROLES.

ƒá A dependency between this procedure and the referenced
objects is setup and maintained.  If this procedure SELECTS FROM
T, then a dependency between T and this procedure is recorded

What this means is that a invokers rights routine, at compile
time, is treated exactly the same as a definers rights routine.
This is an area of confusion for many.  They have heard ¡§roles
are enabled in invokers rights routines¡¨ and that statement is
in fact accurate HOWEVER (and this is a big however), they are
not in effect during the compilation process.  That means that
the person who compiles the stored procedure, the owner of the
stored procedure, still needs to have direct access to all
statically referenced tables. The rules spelled out in the
Application Developers Guide on ¡§Privileges Required to Create
Procedures and Functions¡¨ remain in place.  You need direct
access to the underlying objects.

What is changes is the runtime behavior.  Let's say you compiled
your procedure above (after getting direct access).  You grant
execute on it to BOB.  When BOB runs it -- the access to
CORPORATE.DIRECT_CAD will be evaluated under BOB's account --
not MDA's account.  Even though MDA can access it -- it does not
mean that BOB can via this procedure.  Therein lies the MAJOR
difference for IR (invokers rights) over DR (definers rights)
routines.

Now, if you coded the above routine as:

execute immediate 'SELECT max(EFF_DATE)
                     FROM CORPORATE.DIRECT_CAD'
        INTO DtCurr_RepDate;

Then, we would be in business.  MDA would not need access to
corporate.direct_cad at all (ever) but anyone who RAN the
procedure would access to it either directly OR via a role.
Review & Followup
Rating: 2
What the problem? January 23, 2002
Reviewer: Yushiyin from Shanghai,China
1.
ysy1@DB28> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle8i Enterprise Edition Release 8.1.6.0.0 - 64bit Production
PL/SQL Release 8.1.6.0.0 - Production
CORE    8.1.6.0.0       Production
TNS for Solaris: Version 8.1.6.0.0 - Production
NLSRTL Version 3.4.0.0.0 - Production

ysy1@DB28>

2.
ysy1@DB28> conn ysy/ysy@db28
connected.
ysy1@DB28> create table t1 (a varchar2(10));

3.
ysy1@DB28> create user ysy1 identified by ysy1
  2  default tablespace ysy
  3  temporary tablespace temp;

ysy1@DB28> grant create session to ysy1;
ysy1@DB28> grant create procedure to ysy1;
ysy1@DB28> grant select any table to ysy1;

4.
ysy1@DB28> conn ysy1/ysy1@db28
ysy1@DB28> create or replace procedure p1
  2                  as
  3                   num number;
  4                   begin
  5                       select count(*) into num from ysy.t1;
  6                    end;
  7  /
procedure created.

what's the problem?! why not show PLS-00201 ERROR?
(PLS-00201: identifier 'YSY.T1' must be declared.)
One of my database can create the procedure without the problem,but the other
can create the error! what's the problem?

Followup:
you granted select any table directly to ysy1.

that is why you can create the procedure, ysy1 can select from ANY table.

I will guess that in your other database, the user does not have select any
table or, if they do, they have it via a role and as described -- thats not good
enough.

select any table is too powerful for my tastes, suggest you use finer grained
privs.

January 23, 2002 Rama Subramanian -- Thanks for the question regarding "EXECUTE_QUERY(FOR_UPDATE, NO_WAIT)", version 6.0
You Asked
Hi Tom,

I'm using Forms Version 6.0.5.0.2 (Developer 2000 Ver.6.0).
My Database is Oracle Ver.8.1.5.

I have a question on Forms.

When I issue an EXECUTE_QUERY(FOR_UPDATE,NO_WAIT) from one of my
datablocks, I get a standard message (Two Button Alert) from
Forms, as below, if the record I'm trying to access is locked by
someone else.

FORMS - Could not reserve record (2 tries) Keep Trying? -
Yes/No.

Is there any way by which I can trap this message and issue my
own message? Or is there any other way that I can issue an
EXECUTE_QUERY(FOR_UPDATE), and notify the user if the record in
question is not immediately available?

Regards,
Rama Subramanian G
and we said...
You can program your own ON-LOCK trigger on that block that does
the equivalent of what forms does.  Lets say you have a block on
DEPT.  You could code an ON-LOCK trigger on that block like:

declare
    resource_busy exception;
    pragma exception_init( resource_busy, -54 );
  l_deptno   number;
begin
    for i in 1 .. 3
    loop
        begin
            select deptno into l_deptno
              from dept
             where rowid = :dept.rowid
               and deptno = :dept.deptno
               and dname = :dept.dname
               and loc = :dept.loc
               FOR UPDATE NOWAIT;

             RETURN;
        exception
             when resource_busy then
               null;
             when no_data_found then
               message( 'Sorry, row was changed by some other
                         session, please requery it' );
        end;
  end loop;

  message( 'Bummer, someone else has this locked!!!!' );
  raise form_trigger_failure;
end;

The logic is basically the same as what forms does for you
automatically.  It'll try to query the row up with a FOR UPDATE
NOWAIT by rowid using all of the values in the block.  If we get
resource_busy, someone has it locked -- try again.  If we get no
data found -- the rows values have changed since we queried it
-- you lose.  Otherwise we return and the record is locked by
our session.  After trying three times -- we'll give up and
return our own message about the row being locked.
Review & Followup
Rating: 4
execute_query(for_update,nowait) January 23, 2002
Reviewer: Steve Marrochello from Bloomington,il USA
Is there anyway to display which user might have a lock on the records you are
trying to query for an update. We have problems with careless users just going
on lunch breaks without exiting their application. Somebody who tries to access
those records would like to see who is locking the records so that they can have
somebody exit out the offending machine.

Followup:
Well, the DBA can see it -- you have to be blocked in order to see the blocker.
You can generate a LIST of CANDIDATES and see who's been idle the longest (they
are the best candidate).  You would use v$lock to see whose got TM locks on your
objects and then v$session to see whose been idle for a while.

January 25, 2002 ERIC -- Thanks for the question regarding "INSTALL ORACLE 7.3.4 ON WINDOWS 2000 SERVER", version 7.3.4
You Asked
SIR:

MY CUSTOMER WANTS TO INSTALL ORACLE 7.3.4 ON WINDOWS 2000 SERVER.BUT I TRIED
THIS MANY TIMES AND HAD SOME PROBLEMS(JUST LIKE 'can't get service'.....and so
on ).I GET THE MESSAGE FROM OTN NETWORK:
"Oracle 7.3 will not work on Win2000. I tried this some time ago
and had a multitude of problems. So I now use Oracle8i on
Win2000. 7.3 is not certified for Win2000. "

IS IT TRUE?PLEASE TELL ME HOW I CAN FINISH THE JOB WELL BY INSTALLING THE ORACLE
7.3.4 ON WINDOWS 2000.THANKS FOR YOUR HELP.

    best regards;
                     ERIC HUANG    01.22
and we said...
You cannot.

Windows 2000 is a brand new operating system that was written FIVE years AFTER
Oracle7.3 was.

Your customer has to understand that:

windows 3.11 (was the OS in place when 7.3 was first released!  think about
that)
win95
win98
winNT
win2k
winME
winXP

are as different from eachother as Solaris, Linux, HP/UX, and AIX.

It is not possible to run Oracle7.3 on Win2k.
Review & Followup
Rating: 1
I got 7.3.4 to run on Win 2K no problem with a diff installer January 23, 2002
Reviewer: Ryan Dorosh from Canada
I used a year 2000 installer and I can get 7.3.4 to run on Win 2K with no
install problems.

Followup:
Fine, now -- call support and tell them you are having some issue -- Oh, its
totally unsupported...

734 might run in your case, might not run in someone elses case -- in any case
-- it is 100% not supported and 10000% not recommended.  You need to use the
proper operating system for the software.

Rating: 4
Thanks for your help!! January 23, 2002
Reviewer: Eric Huang from Taiwan
sir:

  Thanks for your help!!

       Have a nice day!!

But I Have two new questions,please help me to find the answer:
   1.Can we install oracle 7.3.4 on nt platform first and upgrade to win2000
later?Does it work well in this case?
   2.How can I install OEBU UTILITY of oracle 7.3.4 on the  machine running
oracle 7.3.4 server without installing OEBU before?
Thanks for your help!!
best regards;
ERIC HUANG   01.24

Rating: 5
Hello? Is anyone at home? January 24, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
Oracle 7.3.4 is not supported on Win2000, so installing it on NT and then
migrating NT to 2000 would still result in an unsupported configuration.  If you
(and appararently Mr. Dorosh) think there is no problem with running unsupported
configurations, I wish you luck and hope you never have a problem.

- Mark

Rating: 4
Tom Helps Friends. January 24, 2002
Reviewer: Khalid Hussain from Pakistan
My dear Tom,
I found many answers in my mind which resolve many problems.
Thanks.

Rating: 3
WHERE CAN I GO? January 25, 2002
Reviewer: gre from China
I met the same problems when i tried to instanll
734 personal edition on my WINXP.
 How about 8i for WIN98 on WINXP ? Where can i go
If I want to have a ORACLE  peronal edition set up on my
WINXP?
Thank you for any suggestions in advance!

Followup:
Try 9i -- 9i is the only supported and supportable version of Oracle on this
very brand new, very different operating system.

Rating: 4
installing 9i December 06, 2002
Reviewer: A reader
Hi tom,

 I have ora 8.1.7.4.1 install on win2k,
then I downloaded the trial version of ora 9.0.2
(AS not DB) from OTN

setp 1:  I installed 9ias on same win2k machine.
setp 2:  some how it was unable to install it properly
         so installation aborted.
setp 3:  I tried to install again (in new home) and
         it installed correctly.
step 4:  to make clean installation I uninstall the 9ias
step 5:  Tried to clean windows registary which had
         3 oracle homes ( removed registry entry for 9ias)
         set oracle home_counter
step 6:  shutdwon and stratup the machine.
step 7:  triying to install the 9iAS in new oracle home
          Now in the installer it loads products.jar
         but when I select the product and press next
         it says all the installation must be done by the
         same user (win2k user) and does not allowme to
         install ora 9ias. I have used same (win2k myself)
         user both times.

  do you know how can I install  it again ??

Rating: 5
version - 8.1.7.4.10? September 11, 2003
Reviewer: Sri from India
Dear Tom,

I installed 8i Release 3 (8.1.7.0.0) in my Windows 2000 Server.  After this I
applied the patchset 8.1.7.4.1.  Now when I gave "Select Version From
V$Instance" in sql*plus, it gave me the result "8.1.7.4.1".  After this I
applied the 8.1.7.4.10 patch.  The process went through smooth and didn't give
me any problems.  However, when I restarted the database and queried V$Instance,
it still says "8.1.7.4.1".  Is this the expected behaviour?  If so, how else can
I check (just for curiosity) which is the patch that is installed in my
database.

Thank you.

January 25, 2002 Reji -- Thanks for the question regarding "Database Links and LOBs", version 8.1.6
You Asked
Tom:
    I have two dbs running 8.1.6. I created a DB link from one
    DB to other and when I tried to do a select on a table
where I have CLOB data and it's giving me error.  How do I

remotely copy the lob data from one db to the other?

-Thx
-Reji
and we said...
You cannot select them but you can

insert .. select ...

them.

tkyte@TKYTE816> create database link ORA816.US.ORACLE.COM
  2  connect to scott identified by tiger
  3  using 'aria-dev.us.oracle.com';

Database link created.

tkyte@TKYTE816> select * from dual@ORA816.US.ORACLE.COM;

D
-
X

tkyte@TKYTE816> create table t ( z clob );
Table created.

tkyte@TKYTE816> insert into t select * from
                                  t@ora816.us.oracle.com;
1 row created.

tkyte@TKYTE816> select * from t;

Z
-----------------------------------------------------------------
---------------
*****************************************************************
***************....... (chopped)
*****************************************************************
***************
Review & Followup
Rating: 3
Good input January 24, 2002
Reviewer: Vishnu from USA
Hi tom ,
Is it possible to create a snap shot to refresh the BLOB table data(FROM A
REMOTE TABLE ) in regular intervels or the sheduled DBMS_JOB .Which is a good
solutioin..
thanks .
vis

Followup:
Yes, a snapshot would work well in this configuration.

Rating: 3
Snap shot giving an error....on remote blob fields January 25, 2002
Reviewer: vishnu from USA
Hi Tom
Thanks for the help..
I have a table by name 'MDOC' like below in database DB1.

RM_ID        NUMBER
MANUFACTURER    VARCHAR2(20)
CH_NAME     VARCHAR2(20)
B_BLOB        BLOB
B_MIME        VARCHAR2(20)

IN the above B_BLOB field stores the PDF files ..and b_mime stores the mime
info.

We are in the process of developing an application  based on DB2(same
blobtable,which needs to be refreshed every day). I Have cretaed
 a DBLINK 'LINK1' for connecting to DB1.

Now i have created a snap shot  bpsi_colreg.view_blob_test as
select * from DB1.MDOC@LINK1.. is giving me error.
pl help

Followup:
and the error would be????  (my crystal ball is in the shop this week)

Rating: 3
Sorry For not writing the error January 25, 2002
Reviewer: vishnu from usa
Hi Tom ,
I am sorry for not writing the error
 ORA-12014: table 'DOC' does not contain a primary key constraint

Thanks
vis

Followup:
umm, does your table in fact have a primary key?  it's required...

Rating: 5
Database Links and LOBs", version 8.1.6 January 25, 2002
Reviewer: Su-mei Y. Chang
Tom, this is wonderful.  Clear, easy and simple.  Appreciate
very much.

January 27, 2002 Tho -- Thanks for the question regarding "Explaining tablespace", version 8.1.5
You Asked
Hi Tom,
I am sorry if this question is stupid. I have read the Oracle doc
several times but don't understand local managed & dict managed tablespace
definitions.Could you explain more clearly?
Thank you very much.
and we said...
A dictionary managed tablespace, the only type of tablespace prior to Oracle8i,
manages extents in a set of database tables.  A locally managed tablespace does
this extent managed in a bitmap in the header of a datafile.  A zero bit means
that space is free, a one bit means it is allocated.

Using the dictionary approach, we can see alot of contention in the database
when you do lots of extends.  Dictionary updates are done serially (so one user
at a time) and the SQL to get the free space can be somewhat time consuming.  We
need to query a free space table to find the BEST free extent that is as big or
bigger then the extent you want.  If we cannot find that, we have to go back and
try to coalesce free space (rows in the free space table that are "adjacent" to
eachother) and try again.  When we finally find one, we have to delete it from
free space and add it into allocated space.

Dictionary managed tablespaces allow for extents of any size which can (and
frequently does) lead to free space fragmentation.  That is -- you might have
500meg of free space in a tablespace but you find that your largest CONTIGOUS
free chunk is 1m and lots are less.  This is a real problem when your next
extents for objects in this tablespace are all greater than 1m.  You might have
500meg free but no object will be able to extend.

Contrast this to a locally managed tablespace.  Free space is managed in a
bitmap at the head of the file.  Instead of serializing ALL space requests for a
database -- we serialize for a shorter period of time at the file.  If you have
more then 1 file, you can have more then 1 space request being processed --
hence it removes contention.  The process of finding free space is faster as
well -- space is typically managed in a UNIFORM fashion in a locally managed
tablespace -- each extent is exactly the same size as every other extent.  No
long search for the "best" fit -- the first fit is the best fit.  So, its faster
at finding space.  Additionally -- free space coalescing happens automagically.
Free up an extent and its bit goes to zero.  If the bit in front of it and
behind it were zero -- we now have 3 zero bits to indicate three free chunks.

Most importantly -- it is IMPOSSIBLE to have free space fragmentation in a
locally managed tablespace with uniform extents.  Since every extent is the size
of every other extent -- ANY extent is the correct size for ANY objects next
extent.  If you have 500m free in a locally managed uniform tablespace -- you
really do have 500meg free.  In a dictionary managed tablespace, that same
tablespace might be "full".

In Oracle8i, use locally managed tablespaces with uniform extents.  Oh yeah, if
someone says "but that'll cause our objects to have many extents" just say "so
what, who cares".  It is perfectly OK to have objects with many 100's of extents.
Review & Followup
Rating: 4
Nice Explanation June 01, 2001
Reviewer: VJ from Boston, MA USA
This was nice explanation and made the understanding very clear.

Rating: 4
June 06, 2001
Reviewer: Julie from USA

Rating: 4
Explaining tablespaces June 27, 2001
Reviewer: A.Senthil Kumar from Chennai
It was really informative , I have also gone thru the
previous articles and find them very useful.

Rating: 4
Dictionary and locally managed tablespace June 27, 2001
Reviewer: A reader
clearest  explanation read on this point

Rating: 4
Dictionary and locally managed tablespace June 27, 2001
Reviewer: Umesh S. Kasturi from Hubli India
clearest  explanation read on this point

Rating: 5
January 25, 2002
Reviewer: subbarao from india
This is the perfect explanition i ever read. A small clarification  in this. I
will have 2 gb of data in a table, if i use this locally managed tablespace for
this, I want to give 1m as uniform extent. In this case the table may have 2048
extents. Is it ok. In earlier versions they say if number of extents increase
there is performance drain, is there in current versions. Please let us know.

Thanks in advance
v6163

Followup:
Who is "they" -- "they" would be incorrect, inaccurate at best.

Anyway, 2048 extents in a single object is getting high.  2048 extents in a
single tablespace with many objects is just fine.

Rating: 4
Very nice January 25, 2002
Reviewer: Brian Tkatch from Royal Oak, MI USA
1) Is there a reason to *not* use a dictionary managed tablespace in 8i?

2) If one follows the directions set forth in
http://technet.oracle.com/deploy/availability/pdf/defrag.pdf
 then there should be no fragmentation in a dictionary managed tablespace
either. Would there still be a significant difference in between the two?

Followup:
1) not really... i use them exclusively

2) with LMTs, you its enforced, its not a choice, not something to follow -- its
just a fact of life.

LMTs are faster on some operations, like allocating space, truncating tables,
dropping objects...

Rating: 4
best explaination of LMT and DMT January 25, 2002
Reviewer: Partha
Perfect explaination

Rating: 4
Very Nice January 25, 2002
Reviewer: Guillermo Portugal from Mexico
This is the perfect explanition i ever read

Rating: 5
RE: Brian's Feedback January 25, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
Tom:

Not sure which one of you (Brian or yourself) is "backwards"...

Brian's question:
1) Is there a reason to *not* use a dictionary managed tablespace in 8i?

Followup:
1) not really... i use them exclusively

I'm quite sure you meant to say you use LMT's exclusively  - unless you've had a
radical change of mind :) and/or maybe Brian also meant to say "locally managed"
instead of "dictionary"...

Just want to avoid any confusion for the casual readers...

- Mark

Followup:
Correct -- yes, I got that totally 100% backwards, guess I read it too fast.

I use LMT's 100% (except for system which cannot be).

Dictionary managed tablespaces have drawbacks (they can get fragmented, they
allocate and deallocate space slower for example).

Use LMT's.  In 9i, its the DEFAULT type of tablespace.

Rating: 4
January 26, 2002
Reviewer: Subbarao from india
Hi tom,
As i asked u about a clarification on  giving extent size as 1Mb and tabledata
as 2GB getting 2048 extents. So in this case how much size  i need to give for
my extent.
U told that 2048 extents in a single object is getting high. So in any case how
many max extents i can plan for a object. And also i have a tables of size 60GB
and 30GB that will grow in a year, what is the best way to give extents for
this. Please let us know.

Followup:
Try to target 1000 or so as the max number of extents. That is a reasonable
number, one that should accomidate virtually any sized table.

Rating: 5
The size of bitmap in LMT January 26, 2002
Reviewer: A reader
How do we determine the size of the bitmap (in header) in LMT. By default is it
64K? What happens if we get more than 64K extents in that tablespace, does the
bitmap grow?

Followup:
it is 64k/datafile.

There are a lot of BITS in 64k.

January 27, 2002 Ming -- Thanks for the question regarding "Approach to implement a complicated query", version 9.00
You Asked
We are developing a product catalog browser in which the database queries are
complicated. The where_conditions in the query depends on user's choice. We are
thinking about two approaches to implement the query:

  1. Generates the query script in the client side then send the SQL
statement to the database server.

  2. Let the work totally done by the database server, that is, run a
procedure which generate the scripts dynamically (might need to use
dynamic SQL).

Since the performance is the most important in this application, We
would like to know which approach has better performance. Is there any other
better approaches? If we use a procedure/function, how does it return the query
result?
and we said...
I would use this approach:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1288401763279
and see also:
http://osi.oracle.com/~tkyte/ResultSets/index.html
for further examples on using ref cursors.

USE BIND VARIABLES as demonstrated in the example, it is CRUCIAL to your
success!  This cannot be overstated.  Do not use simple string concatenation to
create queries, use bind variables.
January 28, 2002 Ramachandran -- Thanks for the question regarding "Nested Table Question, accessing the columns", version
You Asked
Could you please explain how to access the inner most
data element in a nested table, in a select statment.( This
nested table is basically a table of objects. I want to access
the column defined in the object in the Select statement
directly.)
and we said...
Here is an example -- feel free to followup with a specific
question.  I had to sort of guess at what you were asking
without an example so we'll start from here.  My types are:

ops$tkyte@8i> create or replace type myScalarType as object
  2  (       x int,
  3          y int
  4  )
  5  /
Type created.

ops$tkyte@8i> create or replace type myTableType as table of
myScalarType
  2  /
Type created.

ops$tkyte@8i> create or replace type myDemoType as object
  2  (       a int,
  3          b myTableType
  4  )
  5  /
Type created.

And now I'll create a table of that type:

ops$tkyte@8i> create table demo_table of myDemoType
  2  nested table b store as b_table
  3  /
Table created.

and put 2 rows into it:

ops$tkyte@8i> insert into demo_table
  2  values ( myDemoType( 1, myTableType( myScalarType( 5, 6 ),
  3                                       myScalarType( 7, 8 ),
  4                                       myScalarType( 9,10 ) )
) )
  5  /

1 row created.

ops$tkyte@8i>
ops$tkyte@8i> insert into demo_table
  2  values ( myDemoType( 2, myTableType( myScalarType( 15, 16
),
  3                                       myScalarType( 17, 18
),
  4                                       myScalarType( 19,110 )
) ) )
  5  /

1 row created.

Now, just to see whats there:

ops$tkyte@8i> select * from demo_table
  2  /

         A
----------
B(X, Y)
-----------------------------------------------------------------
-----------------------------------
         1
MYTABLETYPE(MYSCALARTYPE(5, 6), MYSCALARTYPE(7, 8),
MYSCALARTYPE(9, 10))

         2
MYTABLETYPE(MYSCALARTYPE(15, 16), MYSCALARTYPE(17, 18),
MYSCALARTYPE(19, 110))

This shows how to get back just the 2 tables:

ops$tkyte@8i> select d.b
  2    from demo_table d
  3  /

B(X, Y)
-----------------------------------------------------------------
-----------------------------------
MYTABLETYPE(MYSCALARTYPE(5, 6), MYSCALARTYPE(7, 8),
MYSCALARTYPE(9, 10))
MYTABLETYPE(MYSCALARTYPE(15, 16), MYSCALARTYPE(17, 18),
MYSCALARTYPE(19, 110))

And then this shows how to treat ONE of those 2 tables as a
"real" table, one that I can select from, insert into, update
and so on:

ops$tkyte@8i> select *
  2    from THE ( select d.b from demo_table d where d.a = 1 )
  3  /

         X          Y
---------- ----------
         5          6
         7          8
         9         10

ops$tkyte@8i> insert into
  2    THE ( select d.b from demo_table d where d.a = 1 )
  3  values ( 100, 200 )
  4  /

1 row created.

ops$tkyte@8i>
ops$tkyte@8i> update
  2    THE ( select d.b from demo_table d where d.a = 1 )
  3   set x = -x
  4  /

4 rows updated.

ops$tkyte@8i> delete from
  2    THE ( select d.b from demo_table d where d.a = 1 )
  3   where y = 10
  4  /

1 row deleted.

ops$tkyte@8i> select *
  2    from THE ( select d.b from demo_table d where d.a = 1 )
  3  /

         X          Y
---------- ----------
        -5          6
        -7          8
      -100        200
Review & Followup
Rating: 5
passing a record type consisting of a nested table January 28, 2002
Reviewer: Vishal Shah from Atlanta, GA USA
Tom,
Taking your example above, if I have a function that returns a rowtype that
contains a nested table as below

create or replace function demo_func returns demo_table%rowtype as
myrow demo_table%rowtype;
begin
 select * into myrow from demo_table where rownum < 2;
 return myrow
end;
/

how do I access the nested table in the rowtype value returned by the function?

thanks,
Vishal

Followup:
It'll look like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function demo_func return
demo_table%rowtype
  2  as
  3  myrow demo_table%rowtype;
  4  begin
  5   select value(p) into myrow from demo_table p where rownum < 2;
  6   return myrow;
  7  end;
  8  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      x demo_table%rowtype;
  3  begin
  4
  5      x := demo_func;
  6
  7      dbms_output.put_line( 'myDemoType.a = ' || x.a );
  8      dbms_output.put_line( 'There are ' || x.b.count ||
                               ' elements in myDemoType.b' );
  9
 10      for i in 1 .. x.b.count
 11      loop
 12          dbms_output.put_line( 'x.b(' || i || ') = (' ||
                                    x.b(i).x || ',' || x.b(i).y || ')' );
 13      end loop;
 14  end;
 15  /
myDemoType.a = 1
There are 3 elements in myDemoType.b
x.b(1) = (-5,6)
x.b(2) = (-7,8)
x.b(3) = (-100,200)

PL/SQL procedure successfully completed.

Rating: 5
Accessing nested tables August 13, 2002
Reviewer: [das flederwiesel]? from Walldorf/Baden - GERMANY
Looks exactly like the information I have been looking for during the last days.
I am a really newbie, but why does it seem to be so hard to get some more
detailed information about subjects like this? You find so many scripts within
the www, but so many are simply useless. Now I am happy about having found this,
the only one where someone seemed to have thought about what he writes and
why....

Regards,
[das flederwiesel]™

January 28, 2002 Ruben -- Thanks for the question regarding "Production and Test Databases ", version 8.1.7
You Asked
Hi, Tom .

What kind of problems could I face if I install the production and test database
in the same machine (i.e. AIX - Unix) ?

Thanx in advance.
and we said...
consumption of resources is #1.  You have a test instance running on production.
 It consumes RAM, disk, and CPU.

it would be a really bad IDEA in my opinion to put test and production on the
same machine.  Production should be a "stable, well known environment with
little to no change day to day".  Test should be a "fluid, ever changing
environment with many changes day to day".

The are somewhat mutually exclusive environments.
Review & Followup
Rating: 4
Development database January 28, 2002
Reviewer: Abdal-Rahman Agha from Sudan
OK. But what about the Development Database? Can be put in the same machine with
the Production Database? Is it a good idea? Considering that I have very very
high resources.

Followup:
development = test as far as I'm concerned.

My opinion is -- a production machine does ONE thing, runs your production app.
Thats it, nothing else.  Period.

Rating: 3
Development Environment January 28, 2002
Reviewer: Senthil from OH USA
Developemnt database is much fluid than the Test database.
If you want you can put the Development and the Test in the same machine but not
the production database.

Rating: 4
Production and Test DB January 28, 2002
Reviewer: Ruben
Tom, thanx a lot for your explanation.

January 28, 2002 Ravi -- Thanks for the question regarding "two way outer join", version 8.1.7
You Asked
I know how outer join works. Is there any way to outer join the tables both
ways.

select d.deptno, e.deptno, empno, ename
from emp e, dept d
where d.deptno = e.deptno(+)

If we have dept 40 in dept table and not in emp table, the result set will show
dept 40 with null employee details.

If I have dept 50 in employee table and not in dept table, is there any way to
get the result set with both dept 40 and also dept 50 in a single query?

10   10    1111      smith
20   20    2222      clark
30   30    3333      king
40         null      null
null 50    5555      kumar

thanks,
Ravi Kumar.
and we said...
In 9i, its a FULL OUTER JOIN.  It works by automating the following query:

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into emp ( empno, deptno, ename )
values ( 1111, 50, 'tom' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select d.deptno, e.deptno, empno, ename
  2    from emp e, dept d
  3   where d.deptno = e.deptno(+)
  4  UNION ALL
  5  select d.deptno, e.deptno, empno, ename
  6    from emp e, dept d
  7   where d.deptno(+) = e.deptno
  8     and d.deptno IS NULL
  9  /

    DEPTNO     DEPTNO      EMPNO ENAME
---------- ---------- ---------- ----------
        10         10       7782 CLARK
        10         10       7839 KING
        10         10       7934 MILLER
        20         20       7369 SMITH
        20         20       7876 ADAMS
        20         20       7902 FORD
        20         20       7788 SCOTT
        20         20       7566 JONES
        30         30       7499 ALLEN
        30         30       7698 BLAKE
        30         30       7654 MARTIN
        30         30       7900 JAMES
        30         30       7844 TURNER
        30         30       7521 WARD
        40
                   50       1111 tom

16 rows selected.
Review & Followup
Rating: 2
Outer Joins-- some more clarifications January 28, 2002
Reviewer: Raghav from Bangalore, India
suppose assume i have one more column in the dept table as 'Current_Dept' which
can hold the value 'Y' or NULL.
if my query is
  select .....
  from emp e, dept d
  where e.emp_id = d.emp_id(+) and
        d.Current_dept(+) = 'Y'.
Should it return the rows with Current_dept 'Y' or rows with both 'Y'or NULL.
Pls give clarifications imm..
Thanks

Followup:
did you try it out to see?

If there is no row in DEPT for a given EMP_ID/'Y' combination, one will be made
up for you.  The rows with NULL in DEPT will not even be considered.

Rating: 2
Outer joins - constant comparison January 29, 2002
Reviewer: raghav from Bangalore, India
Here my idea is to get the records with 'Current_dept' is equal to 'Y' or NULL.
here i am very much particular about the usage of the outerjoins. Eventhough
same result can be obtained from some other query, my purpose is to see the
usage of the outer joins for constant comparison.
Hope u understood.
Thanks
Raghav

January 28, 2002 George -- Thanks for the question regarding "Passing BLOBs acroos a DB Link", version Oracle 8i
You Asked
We have a DB link between two databases. We want to select a BLOB from a table
in one database, and insert it into another table with a BLOB column in another
database. Can this be done via a DB link; and if not, what is the reason, and
what is the best apporoach to accomplish this.

Thanks,

George Lymbouris
and we said...
it is supported and works

ops$tkyte@ORA817.US.ORACLE.COM> create table t ( x int, y blob );
Table created.

ops$tkyte@ORA817.US.ORACLE.COM> insert into t select * from t@aria8i;
1 row created.

ops$tkyte@ORA817.US.ORACLE.COM> select dbms_lob.getlength(y) from t;

DBMS_LOB.GETLENGTH(Y)
---------------------
              1600000
Review & Followup
Rating: 4
Very Helpful December 20, 2001
Reviewer: R from UK
This message was very helpful and also made it clear to me that for the above to
work one needs to have Oracle 8i client to be set up.

Rating: 4
If it is constant process of updation ....What is the best way to do January 28, 2002
Reviewer: reddy svv from USA
Hi Tom
If it is constant process of updation ....What is the best way to do.Every time
inserting the data into same table or is there any efficiant way for handling
blob fields thru dblinks ..pl help
reddy

Followup:
consider using replication which will move the update across the network to a
background process.

Read the replication guides to see what it can do for you.

January 28, 2002 Kariem -- Thanks for the question regarding "Two Similar Databases", version 8.1.7
You Asked
hi Tom,
can i ask u plz a question?
i have an oracle8 server and i have another DB2 server (both have
the same Database with the same tables)
and i have forms to connect on both (by using ODBC to DB2) in a form i can
use database on oracle or DB2 by just clicking on a button to switch.
The question is can i insert into both databases in the same transaction
that means to logon to both databases and commit the transaction from the form
level? ( i tried to do so in key_commit trigger but i failed)

thanx for ur help.

            eng.kariem Ahmed
and we said...
You would have to do this at the database level.  You would do this using a
transparent gateway
http://www.oracle.com/gateways/
This will give you a transparent two phase commit between Oracle and DB2.

At the forms level, you would have to open two connections ( using EXEC_SQL most
likely) but you would have no two phase commit, you may very well be able to
insert into Oracle and DB2, but then fail during the commit against one or the
other and have data in one but not the other.
Review & Followup
Rating: 4
great thanx January 28, 2002
Reviewer: kariem from egypt
i will try ur solution many thianx to u

January 28, 2002 Sharon -- Thanks for the question regarding "How to use environment variables inside of a pl/sql block.", version oracle 8.1.6.00 for Win NT
You Asked
- platform = oracle 8.1.6 installed on Windows 98

I have a master script (script1.cmd) that sets a bunch of
environment variables and then calls an sql script(script2.sql).

Inside of script2.sql, there are pl/sql blocks which I
would like to use the ENVIRONMENT variables in. How would I
do that?

EG. script1.cmd contents:
=========================
setlocal

set ORACLE_SID=TESTDB
set ORACLE_HOME=C:\ORACLE\ORA81
set KEYWORD=VALID_HOTBKUP_19990131
set SYS_PASS=TEST/TESTPW
start %ORACLE_HOME%\bin\sqlplus %SYS_PASS%
@C:\TESTSCRIPTS\script2.sql

endlocal

EG. script2.sql contents:
=========================
-- called from script1.cmd
-- purpose - to generate a sql script with all the
-- 'alter tablespace <tbs name> begin backup;' commands.

-- Inside of this script, I have a spool <filename>  and a
-- spool off command.
-- As part of the spool file name, I would like to use
-- the environment variable (KEYWORD=VALID_HOTBKUP_19990131)
-- that I had set in the calling program, script1.cmd.

set serveroutput on size 100000
set trimspool on
set line 500
set head off
set feed off

--BUILD 'ALTER TABLESPACE ... BEGIN BACKUP' CMD SCRIPT

spool D:\TESTSCRIPTS\%KEYWORD%_begin_bkup.sql

declare
  dbname  varchar2(30);
begin
  select name
  into   dbname
  from   sys.v_$database;

-- *** this is the spool file name that I would like to
-- *** incorporate the KEYWORD environment variable into
-- *** I thought that I could reference the KEYWORD env
-- *** variable using %KEYWORD%

  dbms_output.put_line('spool '||%KEYWORD%||dbname||'.log');

  -- Loop through tablespaces
  for c1 in (select tablespace_name ts
             from   sys.dba_tablespaces)
  loop
    dbms_output.put_line('alter tablespace '||c1.ts||' begin
backup;');
  end loop;
  dbms_output.put_line('spool off');
end;
/
spool off

=========================================================
=========================================================

When the script is run, the following files is created:

D:\TESTSCRIPTS\VALID_HOTBKUP_19990131_begin_bkup.sql
               ======================

however the contents of this script, generated within the
pl/sql block, the %KEYWORD% is not interpreted as the value
of the KEYWORD environment variable, I was hoping to see
the following contents:

- this line

dbms_output.put_line('spool '||%KEYWORD%||dbname||'.log');

- would be resolved to

spool VALID_HOTBKUP_19990131TESTDB.log
      ======================

but this did now work.

Any help on this matter would be welcome.

Thanks in advance.
and we said...
It is quite impossible.

The environment you set up is perhaps visible to your SQLPLUS
running on YOUR client however the plsql block is being executed
by a dedicated server/shared server running in a totally
different process space, typically on a different machine and
DEFINITELY with a totally different environment.

that and SQLPlus has no mechanism for reading the environment
anyway.

You would pass these IN as parameters, not as environment
variables.  For example:

set ORACLE_SID=TESTDB
set ORACLE_HOME=C:\ORACLE\ORA81
set KEYWORD=VALID_HOTBKUP_19990131
set SYS_PASS=TEST/TESTPW

start %ORACLE_HOME%\bin\sqlplus %SYS_PASS%
@C:\TESTSCRIPTS\script2.sql %ORACLE_SID% %ORACLE_HOME% %KEYWORD%

and then script2.sql would have:

----------------------------------
define ORACLE_SID=&1
define ORACLE_HOME=&2
define KEYWORD=&3

...
   dbms_output.put_line('spool &&KEYWORD..log');
.......

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

in it...
Review & Followup
Rating: 3
Is it possible to use Java January 28, 2002
Reviewer: Dilip from UK
DB: 8.1.7.0.0 on UNIX

Would it be possible to use a Java stored procedure (including pl/sql wrapper)
to get the value of an environment variable from within a PL/SQL block ?

I'm thinking along the lines of the Java code that Tom gave for running o/s
commands from PL/SQL.

Followup:
If you can write the Java to get it -- yes.  However, the same exact caveat
spelled out in my question:

..
The environment you set up is perhaps visible to your SQLPLUS running on YOUR
client however the plsql block is being executed by a dedicated server/shared
server running in a totally different process space, typically on a different
machine and DEFINITELY with a totally different environment.
....

would apply.  The java is running on the SERVER - not on the client. Hence, it
would have access to the SERVERS environment -- it would be a totally different
environment.

January 28, 2002 raj -- Thanks for the question regarding "how to query/update a clob column", version 8.1.6
You Asked
Hi tom, i tried to update/query a clob column like a varchar2 but got an error.
what is the way to query and update a clob column in pl/sql? Thanks.

update q_list set q_string='delete from log_msg where timestamp < sysdate-7'
where q_string='delete from log_msg where timestamp < sysdate-30';

ERROR at line 1:
ORA-00932: inconsistent datatypes
and we said...
ops$tkyte@ORA815.US.ORACLE.COM> create table q_list( q_string clob );

Table created.

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> insert into q_list values ( 'delete from log_msg
where timestamp < sysdate-30' );

1 row created.

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> update q_list set q_string='delete from log_msg
where timestamp < sysdate-7'
  2  where q_string='delete from log_msg where timestamp < sysdate-30';
where q_string='delete from log_msg where timestamp < sysdate-30'
      *
ERROR at line 2:
ORA-00932: inconsistent datatypes

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> update q_list set q_string='delete from log_msg
where timestamp < sysdate-7'
  2  where dbms_lob.substr( q_string, 4000, 1 ) = 'delete from log_msg where
timestamp < sysdate-30';

1 row updated.

ops$tkyte@ORA815.US.ORACLE.COM>

you need to use dbms_lob.substr to "cast" the clob into a string that can be
compared to another string.  In 9i, this step won't be necessary.
Review & Followup
Rating: 5
An observation... January 28, 2002
Reviewer: raj from nj
Hi Tom, Thanks for the quick answer. Just an observation...when I submitted this
question, I didn't get a message confirming that the question was accepted. So I
ended up hitting 'back' button on browser and then re-submit it. But again no
confirmation.. Until I got your 'answer mail', i wasn't sure if my question was
accepted.

January 28, 2002 Mark -- Thanks for the question regarding "Import causes strange Audit Session behaviour", version 8.1.5
You Asked
Tom,
Your website is great service to the Oracle DBA community...
Now my question...
We do a nightly import on one of our reporting servers. We were wondering what
the user activity level was on the server so we turned on audit session. The
next day we looked at DBA_AUDIT_SESSION and saw thousands of records. 95% of
them appear to have been generated by the import (20 records/second in some
cases). I don't remember having this issue before, is this normal behavior? Does
it have something to do with the "become user" aspect of importing?
and we said...
Yes, it would be causing that -- it will constantly set its user, effectively
changing the session identity, as it imports.  Thats to get things created
properly and such.  We switch back and forth between your user and the user
importing into frequently (many times).
Review & Followup
Rating: 5
January 28, 2002
Reviewer: Mark from California
you've confirmed my suspicions...

January 28, 2002 Bo -- Thanks for the question regarding "Index Unusable", version 8.1.6
You Asked
Hi, Tom.
I met following situation recently.
The original storage parameter for Table T is (Initial 5M Next 5M), and
tablespace SPACE.
I resized it this way,
alter table T move tablespace SPACE storage(Initial 10M Next 10M);

Afeter resizing, the Index IDX on Table T become "UNUSABLE".

Could you please explain why this happened.

Thanks,
and we said...
Think about it.

An index is a data structure with key values (the values of the column) and
rowids.  By moving the table, you changed each and every rows rowid --
invalidating each and every index entry in any and all indexes that point to
this table.

After you move a table, you need rebuild the indexes.

I HOPE (i really really hope) you didn't do this move in the totally mistaken
belief that fewer extents are better.  If the table was growing too fast, you
should just have changed the NEXT extent, not rebuilt the entire table.  It is
OK to have hundreds or even into the low thousand of extents.  You should also
consider using locally managed tablespaces -- not dictionary managed as you
appear to be using in 8i as well.
Review & Followup
Rating: 5
January 18, 2002
Reviewer: A reader

Rating: 4
Index Unusable January 28, 2002
Reviewer: Joe Begenwald from Kansas City, MO
Although it is true that moving a table in such a way necessarily invalidates
any indexes, wouldn't you think that the RDBMS itself would know this and
rebuild the indexes as part of the ALTER TABLE...MOVE TABLESPACE command?  There
are DML actions that can and do change rowid values (i.e. block-chaining),
without requiring a manual index rebuild.  I tend to think this should happen
automatically, since a) it leaves all the indexes unusable every time, and b)
when would you ever want to not rebuild the indexes?

Perhaps an ALTER TABLE/MOVE option, like REBUILD INDEXES or DROP INDEXES would
be appropriate?

Followup:
block chaining (i believe you mean migration) does NOT change the rowid.

There are only two operations that I am aware of that do -- an update to a
partition key that changes the partition the data would be in and an update to
an IOT's primary key.

Both are special cases, both are handled as such.  Both generally involve a
SINGLE row and are processed very much like a delete/insert.

alter table move is a high speed "rebuild this table now" command.

Using alter table move is a VERY rare occurence don't you think?  I mean, its
not something we do everyday.

Additionally, many people rebuild the indexes in parallel -- or do many at a
time -- using nologging and other options.

So, I guess I agree it would be a "nice touch", but -- since the frequency with
which you use this command is sooo low and the fact that finding the marked
indexes and rebuilding them is almost trivially easy.....

Also, it would increase the chance of failure of the alter table move command by
many times, either the table is moved AND all indexes rebuilt or not.  I'd
rather have it be s series of commands, lets me know whats happening.

January 28, 2002 -- Thanks for the question regarding "exp imp ", version 816
You Asked
Hi,
    I exp a schema   say scott has tablespace name = users
exp scott/tiger  file=scott.dmp log=scottlog.txt

I now create a new user say myuser in a default tablespace say myuser_tblsp.

when i do the imp
imp system/manager fromuser=scott to user=myuser file=scott.dmp

The objects are created in users tablespace.Why not is myuser_tblsp.
When I explicitly create objects in myuser it does go to myuser_tblsp.

How to import the schema to a new user with its own tablepsace(myuser_tblsp).
If i did not have the above users tablespace in my database the above imp would
have failed.

Thanx
and we said...
When you export, the tablespace information is saved in the create command (eg:
do an IMP userid=system/manager fromuser=scott touser=myuser file=scott.dmp
SHOW=Y and you'll see what I mean)

When IMP runs the create, it leaves the tablespace in the command.  If the
command succeeds, the objects will be created where specified. The DEFAULT
tablespace associated with a user is just that -- a DEFAULT, its not where all
new objects go, its where new objects that don't specify a tablespace go, thats
all.

If IMP runs the command and it fails because of the tablespace -- IMP will
rewrite the CREATE command without a tablespace in it, then the object is
created in the default tablespace.

So, how to fix?

Well, if you granted this myuser DBA or RESOURCE, they also get "unlimited
tablespace".  You will REVOKE unlimited tablespace from them, give them a QUOTA
on myuser_tblsp and do the import (see:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:47812348053
in the section on export/import for all of the details)
Review & Followup
Rating: 4
Farid from iDBA Experts January 28, 2002
Reviewer: Farid Uddin from Toronto
That is Great !

Rating: 3
why is this error ??? January 28, 2002
Reviewer: reader from NY
I have table name refpath_biz
with

satrdate     date
adid         number
adsizeid     number
conternarea  number
ppath_raw    varchar2(2000)
sitevalueid  number
userid_raw  varchar2(2000)
impressopms number
click      number
eventtype  number
eventcount  number

has 8M rows  when I try to export it out
it says

exp-00008 Oracle error 904 occured
ORA-00904 invaild column name

Can You tell me what is the problem ?

Followup:
Most likely you have upgraded your database and did not use the steps outlined
in the migration guide.  The export views are stale -- causing this error.

Without versions, without information, without details -- its virtually
impossible to give you any meaningful suggestions.

Try running the current version of catexp.sql found in $ORACLE_HOME/rdbms/admin
and retrying the operation.  I've created a table named after yours and it
exports just fine.

Rating: 4
January 28, 2002
Reviewer: reader from NY
Yes,
    The database is oracle 8.1.6 and the export utility is
8.1.7
    I don't know if the dba has taken all nessary care for migraton.

 Thanks !!

Followup:
why wouldn't you be using the 816 export tool against 816?

Rating: 5
January 29, 2002
Reviewer: Philipp from Switzerland
It's very useful for me. I teard one's hair about this.

Rating: 5
Excellant Answer with examples January 29, 2002
Reviewer: A reader
Thanx for you answers. U r like Commander DATA of Star Trek but more practical
then him.
Perfect ,precise and to the point.

Rating: 4
Export February 25, 2003
Reviewer: Tom Dausy from Belgium
Found what I was looking for.
Run catexp.sql and export worked perfectly.
Thanks a lot
Tom

January 28, 2002 Christoph -- Thanks for the question regarding "Matrix type output in sql*plus", version 8.1.7
You Asked
Tom,
I am trying to write a query that formats the output as shown below. I would
appreciate if you could provide some help on this.

       SMITH    JONES   MILLER  KING    ALLEN
SAL    800      2975    1300    5000    1600
MGR    7902     7839    7782            7698
DEPTNO 20       20      10      10      30
COMM                                    300

Thanks,
Chistoph
and we said...
Well, sqlplus wasn't really designed to do that, we can do it in SQL:

scott@ORA817DEV.US.ORACLE.COM> column smith format a5
scott@ORA817DEV.US.ORACLE.COM> column jones format a5
scott@ORA817DEV.US.ORACLE.COM> column miller format a6
scott@ORA817DEV.US.ORACLE.COM> column king format a5
scott@ORA817DEV.US.ORACLE.COM> column allen format a5
scott@ORA817DEV.US.ORACLE.COM> column what format a6

scott@ORA817DEV.US.ORACLE.COM> select what, max(smith) smith, max(jones) jones,
  2             max(miller) miller, max(king) king, max(allen) allen
  3    from (
  4  select case when ename = 'SMITH' and r = 1 then to_char(SAL)
  5                  when ename = 'SMITH' and r = 2 then to_char(MGR)
  6                  when ename = 'SMITH' and r = 3 then to_char(DEPTNO)
  7                  when ename = 'SMITH' and r = 4 then to_char(COMM)
  8             else NULL end SMITH,
  9         case when ename = 'JONES' and r = 1 then to_char(SAL)
 10                  when ename = 'JONES' and r = 2 then to_char(MGR)
 11                  when ename = 'JONES' and r = 3 then to_char(DEPTNO)
 12                  when ename = 'JONES' and r = 4 then to_char(COMM)
 13             else NULL end JONES,
 14         case when ename = 'MILLER' and r = 1 then to_char(SAL)
 15                  when ename = 'MILLER' and r = 2 then to_char(MGR)
 16                  when ename = 'MILLER' and r = 3 then to_char(DEPTNO)
 17                  when ename = 'MILLER' and r = 4 then to_char(COMM)
 18             else NULL end MILLER,
 19         case when ename = 'KING' and r = 1 then to_char(SAL)
 20                  when ename = 'KING' and r = 2 then to_char(MGR)
 21                  when ename = 'KING' and r = 3 then to_char(DEPTNO)
 22                  when ename = 'KING' and r = 4 then to_char(COMM)
 23             else NULL end KING,
 24         case when ename = 'ALLEN' and r = 1 then to_char(SAL)
 25                  when ename = 'ALLEN' and r = 2 then to_char(MGR)
 26                  when ename = 'ALLEN' and r = 3 then to_char(DEPTNO)
 27                  when ename = 'ALLEN' and r = 4 then to_char(COMM)
 28             else NULL end ALLEN,
 29         decode( r, 1, 'SAL', 2, 'MGR', 3, 'DEPTNO', 4, 'COMM' ) what,
 30             r
 31   from (select *
 32           from emp
 33          where ename in ('SMITH', 'JONES', 'MILLER', 'KING', 'ALLEN' ) ),
 34        (select rownum r
 35           from all_objects
 36          where rownum <= 4 )
 37         )
 38   group by r, what
 39   order by r
 40  /

WHAT   SMITH JONES MILLER KING  ALLEN
------ ----- ----- ------ ----- -----
SAL    800   2975  1300   5000  1600
MGR    7902  7839  7782         7698
DEPTNO 20    20    10     10    30
COMM                            300

scott@ORA817DEV.US.ORACLE.COM>

Its a bit of a trick -- but it works ;)
Review & Followup
Rating: 5
Matrix type output in sql*plus January 28, 2002
Reviewer: Christoph from El Reno, OK
Thanks for the quick response. Since SQL*Plus is not really designed to do this,
which tool do you recommend? I didn't have much luck with reports either.

Followup:
I know discoverer does this -- how about anyone else out there -- any
suggestions (i don't use too many reporting tools personally)

Rating: 4
January 29, 2002
Reviewer: Tianhua Wu from Atlanta, GA
Really nice to know!


Subject | Article | Last Updated (East coast time)
Is it possible to do bitwise manipulation of a number in PL/SQL | article only | Jan 29, 2002 06:46 am
Benefit of non-unique index than unique | article only | Jan 29, 2002 10:35 am
How many tablespaces should make up a huge partitioned table? | article only | Jan 29, 2002 10:51 am
CPU Usage on client PC is at its maximum. | article only | Jan 29, 2002 10:52 am
Direct (Parallel Load) | article only | Jan 29, 2002 02:07 pm
how to find out the state of SQL_TRACE for a session | article only | Jan 30, 2002 07:02 am
create user/profile | article only | Jan 30, 2002 07:12 am
PL/SQL Server Pages (PSP) | article only | Jan 31, 2002 07:16 am
Viewing Data associated with BLOB Datatype | article only | Jan 31, 2002 06:10 pm
Returning a clob into a variable for update Using Dynamic SQL | article only | Jan 31, 2002 06:24 pm
End-of-communication channel , hostdef not found | article only | Feb 01, 2002 09:11 am
ref. cursor | article only | Feb 01, 2002 09:52 am
bind variables in SQL*Plus without setting the variables in PL/SQL Blocks? | article only | Feb 01, 2002 10:15 am
sql for heirarchical relationship | article only | Feb 01, 2002 02:12 pm
retrieving sql from shared pool | article only | Feb 01, 2002 03:20 pm
Problem with Raise_Application_Error | article only | Feb 01, 2002 03:25 pm
MAX_USERS in oradim and ENTRIES in orapwd | article only | Feb 01, 2002 07:45 pm
negative values of execute to parse in Statspack report | article only | Feb 01, 2002 07:48 pm
Refresh screen | article only | Feb 01, 2002 09:26 pm
recover data after truncating a table | article only | Feb 02, 2002 11:05 am
Case Sensitive Queries | article only | Feb 02, 2002 05:48 pm
Import only tables without associated objects like triggers etc from schema | article only | Feb 02, 2002 05:52 pm
HELP For Help | article only | Feb 02, 2002 05:55 pm
DBMS_JOB.Interval | article only | Feb 02, 2002 05:57 pm
PL SQL Tables | article only | Feb 03, 2002 09:37 am
Questions... | article only | Feb 03, 2002 10:11 am
Backup Questions | article only | Feb 03, 2002 10:12 am
Select max from collection | article only | Feb 03, 2002 02:01 pm
Improve performance while loading data | article only | Feb 03, 2002 02:03 pm
Variable Number of Parameters | article only | Feb 03, 2002 02:12 pm
Some Questions | article only | Feb 04, 2002 09:56 am
shuffling of # or elements before appearing on screen. | article only | Feb 04, 2002 10:12 am
listing databases | article only | Feb 04, 2002 10:15 am
row(s) 1 - 33 of 33
January 29, 2002 Chris -- Thanks for the question regarding "Is it possible to do bitwise manipulation of a number in PL/SQL", version Plus33
You Asked
Is it possible to do bitwise manipulation of a number in PL/SQL
I.e. to have a number and then extract each indiviual bits using
PL/SQL funtionality?
and we said...
In a couple of different ways -- yes.

The easiest is to use an undocumented (but heavily used -- check
out the data dictionary views, they use it all over the place so
it won't be going away anytime soon) function BITAND().
Consider:

scott@ORA734.WORLD> declare
  2      n    number;
  3  begin
  4      n := random.rand_max( power(2,31) );
  5
  5      dbms_output.put_line( 'Number is ' || n ||
  6                            ' ' || to_bin(n) );
  7      for j in 0 .. 30 loop
  8          dbms_output.put( 'The ' || to_char(j+1) ||
  9                           '''th bit is ' );
 10          if ( bitand( n, power(2,j) ) = power(2,j) )
 11          then
 12              dbms_output.put_line( 'on' );
 13          else
 14              dbms_output.put_line( 'off' );
 15          end if;
 16      end loop;
 17  end;
 18  /
Number is 24117 101111000110101
The 1'th bit is on
The 2'th bit is off
The 3'th bit is on
The 4'th bit is off
The 5'th bit is on
The 6'th bit is on
The 7'th bit is off
The 8'th bit is off
The 9'th bit is off
The 10'th bit is on
The 11'th bit is on
The 12'th bit is on
The 13'th bit is on
The 14'th bit is off
The 15'th bit is on
The 16'th bit is off
The 17'th bit is off
The 18'th bit is off
The 19'th bit is off
The 20'th bit is off
The 21'th bit is off
The 22'th bit is off
The 23'th bit is off
The 24'th bit is off
The 25'th bit is off
The 26'th bit is off
The 27'th bit is off
The 28'th bit is off
The 29'th bit is off
The 30'th bit is off
The 31'th bit is off

PL/SQL procedure successfully completed.

o random available at
http://osi.oracle.com/~tkyte/Misc/Random.html
o To_Bin (and other to_*()'s) available at
http://osi.oracle.com/~tkyte/hexdec/index.html
Review & Followup
Rating: 5
more question January 28, 2002
Reviewer: Shane Hu from nyc
Testing bitand and got the following error message. Could not figure out why.
Pls advise. Thanks!

SQL> select bitand(123,2) from dual;
ERROR:
ORA-00932: inconsistent datatypes
no rows selected
SQL>

Followup:
add zero to it.

select bitand(123,2)+0 from dual;

that way, sqlplus will know its a number...

January 29, 2002 Tarek -- Thanks for the question regarding "Benefit of non-unique index than unique", version 8.1.5
You Asked
Hi Tom,

For primary key which index you like best - unique or non-unique? You know both
supports the purpose of PK but for unique index on PK it takes little much time
to be enabled.

I prefer IOT than those processes. Am I doing wrong?

Take care,
Tarek
and we said...
Depends on what I'm doing.

If I believe I'll be disabling, enabling a primary key -- the use of a
deferrable constraint (to get the non-unique index) can make sense.  I see this
as being useful in a data warehouse where I incrementally load "clean" data --
data that has been thoroughly scrubbed.  Then I can enable the primary key
without re-checking all of the data.

In a NORMAL situation however, I don't typically see needing to do this with a
primary key.  So, I guess in 99% (or more) of the situations, I use a
conventional primary key with a unique index.  I would use a non-unique if I
forsaw the need for it, else no.

IOT's are very very interesting to me as well.  They have their uses (i have a
couple of examples here on this site and in my book).  However, for a really
active table with lots of insert/update/delete activity -- they can be slower
then a table + primary key implementation.
Review & Followup
Rating: 5
By definiton January 28, 2002
Reviewer: Kulguru
Primary key means  not null and unique... so then

doesnt

"If I believe I'll be disabling, enabling a primary key -- the use of a
deferrable constraint (to get the non-unique index) can make sense.  I see this
as being useful in a data warehouse where I incrementally load "clean" data --
data that has been thoroughly scrubbed.  Then I can enable the primary key
without re-checking all of the data."

explanation sound contradictory, to the very definition of Primary Key.  What
will happen if someone inserts a duplicate row, and how are we supposed to know
and clean the table of such rows.

Followup:
As I said "where I incrementally load "clean" data -- data that has been
thoroughly scrubbed"

The data is deemed to be correct, it has been through a series of ETL tools, the
correctness/validity of the data has already been ascertained.

This is purely a performance optimization to be used with care and caution.  If
you have many millions/billions of rows, this can be the difference between
getting the job done in a reasonable amount of time and not.  It is used in data
warehouses, here people do not INSERT a row, you bulk load "clean" data.

Rating: 4
"it has been through a series of ETL tools..." January 29, 2002
Reviewer: Curious from Richmond, VA
OK, I'll bite. What does ETL stand for?

Followup:
Extract
Transform
Load

search for

etl warehouse

on www.google.com

Rating: 4
ETL is a TLA January 29, 2002
Reviewer: Jim Kennedy from Beaverton, OR
ETL -> Extract, Transform, Load.  A common TLA in the data warehousing world.
It means to extract the data from a source or sources, transform that data from
the form it is in to the form you want it in - usually also run sanity checks
etc, then Load that data into your data warehouse.

Rating: 5
OK, I'll bite. What does TLA stand for? January 29, 2002
Reviewer: J from MA

Rating: 5
ETLSPSA January 29, 2002
Reviewer: Connor McDonald from Battersea
ETL is the shortened form of the more common extended form of the acronym:
ETLSPSA

"Extract Transform Load Swear Panic Start Again" :-)

January 29, 2002 Kevin -- Thanks for the question regarding "How many tablespaces should make up a huge partitioned table?", version 8.1.7
You Asked
I am going to have a large table (4.5 billion rows) containing two years worth
of data, with one partition for each day (about 730 partitions).  I am going to
roll off this data each day by adding a new partition and dropping the oldest
partition.  I am also limited on space, so I don't want to waste any.  If I
place all of these partitions in one tablespace, how will it affect query
performance?  Just doing an analyze on the empty table seems to be giving the
row_cache_objects latch a workout.  If I should have more than one tablespace,
what would be a good number to have, and how do I avoid wasting space in the
tablespaces?

Thanks,

Kevin
and we said...
The number of tablespaces involved will not impact your space utilitization.
You would be using Locally managed tablespaces with uniform extent sizes.  The
number of tablespaces will not play into figuring out your space needed.  It'll
be the same.

It would probably be extreme to go with a tablespace/partition, you might
consider N partitions/tablespace (say N=10).   Using a single tablespace
wouldn't be good since:

o a tablespace is online or offline.  one tiny disk outage and the entire table
would be offline (you would obviate the highly available component of
partitions)

o you would lose the ability to place objects on "disks" easily.

use a static set of tablespaces -- do not create tablespaces like "ts1, ts2,
..." and later drop ts1 and create ts731, drop ts2 and create ts732.... (see the
book referenced below for great details on WHY you don't want to drop and create
new tablespaces like that over time!  In fact, the book referenced below should
be considered mandatory reading for you with what you are doing)

Over time you'll only be analyzing the individual partition -- you'll never
analyze the entire table so that test wasn't a good one.  You should see how
this will affect the parse times of your queries (and again, the number of
tablespaces won't really affect this).

I asked:

Jonathan Lewis
http://www.jlcomp.demon.co.uk/
Practical Oracle 8i:  Building Efficient Databases
Publishers:  Addison-Wesley
Reviews at:
http://www.jlcomp.demon.co.uk/book_rev.html
to look at issues surrounding this many partitions and he said:

I think 700 is a viable possibility.  The issue is more
one of how they will be used than how many there are.

The inevitable hit comes from the drop, which results
in a physical update on 700 tabpart$ rows, and 700
indpart$ rows per local index - I hope they're all local;
and this hit is done one row at a time - so is a bit nasty,
but with a reasonable buffer and shared pool not too
extreme.

More importantly, there are two types of query
problem.  One which has to visit a lot of partitions
to find very little data, where the logical I/O against
(usually) each index partition is very small but the
total wasted logical I/O is high compared to the
notional I/O that would be done on a simple
table - e.g. 2,100 LIOs instead of 10.

The second problem is one of issues where the
optimiser examines the stats on all partitions
when it should examine stats on a limited
set of partitions - this can lead to parse times
in the 10s of seconds instead of tenths of
seconds.  But then, even halving the number
of partitions still leaves you with a very heavy
parse time.

one extra thought that occurred to me later is that a query
with a lot of tables in it can result in a fairly high
parse time - this issue can be exaggerated if one
of  the tables is partitioned table with lots of
partitions.
Review & Followup
Rating: 5
January 29, 2002
Reviewer: subbarao from India
Dear tom,
We have got so many solutions from your site. Thanks much. We have a scenario
like this, Server capacity is 216GB(12disks of 18 gb each), 1(one) disk
controller and has RAID 5.The database is nearly 100GB and we have nearly 300
tables. I have sagrigated all the tables into verysmall, small, big, verybig. So
for big and very big categories i have planned for 11 partitions each and
keeping in seperate tablespaces, so it comes to 22 tablespaces. and for other
categories i am planning for 4-5 tablespaces. All the tablespaces will be of
LMT( System exceptional). Apart from this there is next phase of the project and
the data will reside in the same database with equivalent space cosuming.
1) In this case of tablespaces it can come to 60-70 in number. Will there be any
problem in having these many tablespaces in cases like maintenance and backups.
2) Till now i didn't plan for a backup strategy.  And also please let us know
how we should proceed for backup strategy also.
3) We are planning to have a datafile of 1GB size, for easy    maintenance at
Operating System level. Please comment on this.
4) As my server has only one disk controller, can I place all my indexes and
data tablespaces together in one disk. I am asking this because documents says
RBS tablespace should be away from data tablespace, redo log files should be
away from data tablespaces and etc... Will this hold good when there is one disk
controller.

Followup:
1) 60/70 tablespaces won't be a problem.

2) RMAN, use RMAN.

3) use RMAN and you'll never touch the files, you can use larger ones.  With 9i,
there is block level recovery, so big files are faster to recover then before.

4) your entire disk system is not very well thought out in my opinion.  RAID 5
is great for availability, not so for writes.  1 controller for everything --
hope you don't have lots of users. You might want to reconsider this hardware
setup and invest a little more money into the disk system.

Rating: 4
January 29, 2002
Reviewer: Tianhua Wu from Atlanta, Ga
The potential problem with lots of datafiles is checkpoint will take lnog time
to finish because Oracle need to update every datafile header.  Startup/shutdown
would take a while too.

January 29, 2002 Chetan -- Thanks for the question regarding "CPU Usage on client PC is at its maximum.", version 7.3.4
You Asked
Why is the CPU Usage at its maximum (close to 100% when viewed through Task
Manager on NT/2000) on a client PC when I execute a count(*) from a relatively
large table (say over 15 million records)?

Update:

I am using SQL*Plus: Release 3.3.2.0.2 on a client having  Pentium 4; CPU speed
being 1.60 GHz and 384 MB of RAM with Win/2000 installed. The Image name, PID,
CPU, CPU Time and Mem Usage when viewed from Task manager for the one having CPU
as 99 are as follows:

Image Name      PID      CPU     CPU Time    Mem Usage
PLUS33W.EXE     1880      99     03:17:11     5316K
and we said...
Sorry, but Oracle 7.3.4 is in no way supported or supportable on this brand new
OS that was not invented or thought of when Oracle7.3 was written.

There are issues with using 7.3 on Win2000 or ANY platform other then what is
supported.

Please read
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2538728488384
(i cannot understand why people run out and upgrade their OS, the thing they
live on but refuse to upgrade the database.  If you want to run really old
software, you have to run lots of really old software).

Please install the Oracle 8i 817 or better client on this machine and see if the
problem persists.  I cannot even attempt to replicate this issue.  Using
sqlplusw with the 8i software -- I have no issues whatsoever similar to this.
Review & Followup
Rating: 3
SQL*Plus 3.3.2.0.2 acting Normal January 28, 2002
Reviewer: Chip from Denver, CO USA
When SQL*Plus 3.3.2.0.2 is run on Windows NT 4 Workstation
(with 200 MHz Pentium Pro CPU), the CPU usage does jump to
100% while SQL*Plus is waiting for results to be returned
from a query.  The CPU usage is low priority so other work
can be done on the computer.  The SQL*Plus 3.3.2.0.2 client has some advantages
over Oracle 8.* versions: while a query is running, the query stays visible in
the SQL*Plus window and the window can be moved on the desktop; when editing a
query (using notepad) the information remains displayed in the SQL*Plus window.
Also, SQL*Plus 3.3.2.0.2 displays a pop-up dialog box to cancel a query while it
is running, while the SQL*Plus 8.1.7.0.0 does not allow the user to cancel a
running query (other than using Task Manager to kill SQL*Plus).  The SQL*Plus
8.1.7.0.0 File menu does have a cancel option, but it is inoperative while a
query is running (although the CPU usage does not spike, the Task Manager shows
Oracle SQL*Plus Not Responding).

Rating: 3
A Reader January 29, 2002
Reviewer: A Reader from Trevose, PA USA
To cancel a running query on SQL*Plus 8.1.7.0.0, click File and then cancel.

Rating: 3
January 29, 2002
Reviewer: Chetan
Thank You, Tom.

(P.S: I do have Oracle 9i installed but was curious about such SQL*Plus Release
3.3.2.0.2 behaviour. So I thought I would ask the MASTER)

Followup:
well, in this case -- never having actually used 7.3 on windows, I didn't know
-- thanks to Chip from Denver for the answer!

Rating: 3
Only an addiction January 29, 2002
Reviewer: Chiappa from Sao Paulo, SP Brazil
Of course, we CAN interrupt a long-running query in sql*plus 8.1.7 , just choose
File menu and the Cancel option.

January 29, 2002 Chandra -- Thanks for the question regarding "Direct (Parallel Load)", version 8.1.7.0
You Asked
Hi Tom,
Thanks for the great help.
Here is my situation. We have a tradinational structure of
        Cursor LOOP
          insert into table
        End Loop
This we are running on a select which returns around 60 Millions of records.
The target table is not partitioned
We are trying to load this using parallel load process with the
following Code. We got an errors saying
Please help me in correct directions.
Thanks,
Chandra

+++++++
Error
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
"BEGIN WVDBA.P_GIFT_DETAIL.LOAD_GIFT_DETAIL; END;
*
ERROR at line 1:
ORA-01562: failed to extend rollback segment number 1
ORA-01650: unable to extend rollback segment LOADROLL by 6400 in tablespace RBS
ORA-06512: at "WVDBA.P_GIFT_DETAIL", line 51
ORA-06512: at line 1
+++++++++++++++++++++
Rollback segment Info
+++++++++++++++++++++
CREATE ROLLBACK SEGMENT LOADROLL
    TABLESPACE RBS
    STORAGE(INITIAL 250M
            NEXT 100M
            MINEXTENTS 2
            MAXEXTENTS 32765
            OPTIMAL 350M)
+++++++++++++++++++++++
Code
+++++++++++++++++++++++
   sql_string := 'SET TRANSACTION USE ROLLBACK SEGMENT LOADROLL';
   EXECUTE IMMEDIATE sql_string;
   INSERT /*+ APPEND */
       INTO wvdba.gift_detail
            ( GIFT_DTL_ID
             ,POST_SEQ_NBR
             ,.......)
             SELECT  wvdba.GIFT_DTL_ID_SEQ.NEXTVAL
              ,A.PST_POST_SEQ_NR
              ,.......
         FROM stage.donor_inc_dtl   A
              ,stage.posting_hist    B
              ,wvdba.ACCT_DONOR_XREF C
        WHERE A.PST_POST_SEQ_NR = B.POST_SEQ_NR (+)
          AND A.INC_DONOR_ACCT = B.DONOR_ACCT (+)
          AND A.INC_DONOR_ACCT = C.PRAISE_ACCT_NUM
          AND A.INC_AMT != 0;
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
and we said...
Suggestion --

avoid the use of "use rollback segment" -- i hate that command.  either size all
rollback to be big enough or find someother way...

Do this:

DISABLE the indexes, mark them unusable.
Do the insert /*+ append */
REBUILD the indexes afterwards (in paralle, nologging, ....) .

That'll use almost NO rollback (the rollback you are generating is for index
maintainence).

Your command is failing because the tablespace RBS is OUT OF ROOM.  It is full,
or the disk it is on cannot auto-extend if you are using that.  See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1669403535933
for a good example of what I mean.
Review & Followup
Rating: 3
Direct Load parallel January 28, 2002
Reviewer: Chandra from USA
Hi Tom,
Thanks for the response.
In fact I was doing
1. Disable F.Keys
2. Index Unusable
3. Direct Load
4. Build Index
5. Enable F.Keys
Do you think the error is due to "use rollback segment" ..
Thanks again,
Chandra

Followup:
I do not believe your indexes were unusable.  It would not have generated any
rollback hardly at all.

Recheck your logic, I believe you'll find that at least one, if not more,
indexes were intact.

Rating: 5
Excellent January 29, 2002
Reviewer: DV from India
Hi Tom,
This is great stuff.
I have two issues on rollback however.
1. A traditional system is designed with smaller but a number of rollback
segments for OLTP and a few larger ones for batch jobs. So keeping all rollback
segments of same size is not clear to me. Also will there be any performance
problems if really large rollback segment is acquired by a OLTP transaction.
2. You actively discourage ( as per your book ) to commit transactions like this
batch update involving 100 million records intermittently. Is it really prudent
to have one big ( really big ) rollback for these kind of transaction. What if
these are bank balance kind of updations that each row is a single transaction?
What if such a big tranasction fails in between, the same amount of time would
be required to rollback, too?
Thanks for your time.
Regards,
DV

Followup:
1) hopefully, 9i with system managed undo will do away with that.  No more
rollback segments.

I believe that rollback should be sized for EVERYTHING from the get go.  all
rollback segments should be created equal, they should all be large enough to:

a) hold the biggest transactions
b) not wrap during the period of time it takes for your longest running query to
execute

most people size rollback for their updates - forgetting that rollback is
crucial for queries.

2) I actively discourage frequently committing -- do the transaction as large as
it can be.  As for the bank update -- what happens if the power fails half way
through and you've been committing?  Most people forget to make their stuff
"restartable" and they end up with a total mess (i point that out in the book).

My mantra on transactions is "they should be as large as they need to be".

Rating: 4
Direct Load (Parallel) January 29, 2002
Reviewer: Chandra from US
Hi Tom & Group,
Thank you very much for the time that you are spending on these issues, which is
very help full.
Let me tell you what we have here.
1. One huge( 6GB) only one rollback segement and we are
   setting the transaction to use this rollback segement.
2. All the indexes on the target table ( Except Primary
   Key)  are made unusable.
3. All the F.Keys of the Target table are made disable.
4. Only this transaction is running and nothing else is
   running on Oracle (Ofcourse I donn't know about other
   "ORACLE" backgroup jobs.
5. The Target tables are NOT PARALLELIZED.
6. No Special variable are set in INIT.ORA
7. I have clearly noticed that the Rollback segement is
   fully occupied ( the job failed after the RBS is 5.99GB,
   where RBS physical size is 6GB )
Again Thank you very much for your and other memebers of your group.
Chandra

Followup:
Except primary key....

60 million updates to an index could generate this much rollback.  All of the
splits, allocates, etc.

disable that index, load the data, re-enable it.  use the exceptions into clause
of the constraint clause to catch dups if you need.

Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x char(20) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select used_ublk from v$transaction;

no rows selected

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2          for x in 1 .. 40000
  3          loop
  4                  insert into t values ( x );
  5          end loop;
  6  end;
  7  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select used_ublk from v$transaction;

 USED_UBLK
----------
       346

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t add constraint t_pk primary
key(x);

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2          for x in 1 .. 40000
  3          loop
  4                  insert into t values ( -x );
  5          end loop;
  6  end;
  7  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select used_ublk from v$transaction;

 USED_UBLK
----------
       834

ops$tkyte@ORA817DEV.US.ORACLE.COM> ops$tkyte@ORA817DEV.US.ORACLE.COM>

360'ish blocks for 40000 rows, 500'ish blocks for the index maintainence.

January 30, 2002 Alfred -- Thanks for the question regarding "how to find out the state of SQL_TRACE for a session", version 8.1.7
You Asked
Hi Tom,

I wrote a little perl-script, which shows me a list of sessions. For this
sessions I can manipulate the SQL TRACE. When TRACE is on for a session, the
session's entry is maked.
The problem is, that I can't tell, if a session's TRACE was set by someone else.
So my question is:

is there a way to find out what the state of SQL_TRACE is for another session
(like dbms_system.set_sql_trace_in_session)?

Servus -- Alfred
and we said...
There isn't one that I know of.  One thing I do is look for a trace file for the
session, if its there, we are *probably* tracing.  Its a good indicator anyway.

You can set it up like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace type myScalarType as object
  2  ( sid  number,
  3    serial# number,
  4    tracefile varchar2(255),
  5    has_tracefile varchar2(1)
  6  )
  7  /
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace type myTabletype as table
of myScalarType
  2  /
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace directory UDUMP as
'/export/home/ora817/admin/ora817dev/udump'
  2  /
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function session_trace
return myTabletype
  2  as
  3      l_data myTableType := myTableType();
  4  begin
  5      for x in ( select b.sid, b.serial#,
  6                        c.value || '/' || d.instance_name || '_ora_' ||
to_char(to_number(a.spid),'fm99999') || '.trc' fn_full,
  7                        d.instance_name || '_ora_' ||
to_char(to_number(a.spid),'fm99999') || '.trc' fn
  8                   from v$process a, v$session b, v$parameter c, v$instance d
  9                  where a.addr = b.paddr
 10                    and c.name = 'user_dump_dest' )
 11      loop
 12          l_data.extend;
 13          l_data(l_data.count) :=
                     myScalarType( x.sid, x.serial#, x.fn_full, 'N' );
 14
 15          if ( dbms_lob.fileexists( bfilename( 'UDUMP', x.fn ) )  > 0 )
 16          then
 18              l_data(l_data.count).has_tracefile  := 'Y';
 22          end if;
 23      end loop;
 24      return l_data;
 25  end;
 26  /
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from table( cast( session_trace as
myTabletype ) );

       SID    SERIAL# TRACEFILE                      H
---------- ---------- ------------------------------ -
         1          1 /export/home/ora817/admin/ora8 N
                      17dev/udump/ora817dev_ora_2863
                      0.trc

         2          1 /export/home/ora817/admin/ora8 N
                      17dev/udump/ora817dev_ora_2863
                      2.trc

         3          1 /export/home/ora817/admin/ora8 N
                      17dev/udump/ora817dev_ora_2863
                      4.trc

         4          1 /export/home/ora817/admin/ora8 N
                      17dev/udump/ora817dev_ora_2863
                      6.trc

         5          1 /export/home/ora817/admin/ora8 N
                      17dev/udump/ora817dev_ora_2863
                      8.trc

         6          1 /export/home/ora817/admin/ora8 N
                      17dev/udump/ora817dev_ora_2864
                      0.trc

         8       3032 /export/home/ora817/admin/ora8 Y
                      17dev/udump/ora817dev_ora_4917
                      .trc

         9          1 /export/home/ora817/admin/ora8 N
                      17dev/udump/ora817dev_ora_2864
                      2.trc

        11          1 /export/home/ora817/admin/ora8 N
                      17dev/udump/ora817dev_ora_2866
                      2.trc

        12          1 /export/home/ora817/admin/ora8 N
                      17dev/udump/ora817dev_ora_2866
                      4.trc

        13          1 /export/home/ora817/admin/ora8 N
                      17dev/udump/ora817dev_ora_2866
                      6.trc

        14          1 /export/home/ora817/admin/ora8 N
                      17dev/udump/ora817dev_ora_2866
                      8.trc

That shows session 8 is PROBABLY tracing.  Use the query:

select b.sid, b.serial#,
       c.value || '\ORA' || to_char(a.spid,'fm00000') || '.trc' fn_full,
        'ORA' || to_char(a.spid,'fm00000') || '.trc' fn
  from v$process a, v$session b, v$parameter c
 where a.addr = b.paddr
   and c.name = 'user_dump_dest'
/

on NT instead....  only works with dedicated server (but then again, tracing
only makes SENSE with dedicated server!)
Review & Followup
Rating: 5
January 29, 2002
Reviewer: Alfred Mickautsch from Pfalzgrafenweiler, Deutschland
Thanks a lot.

Rating: 5
how to find out the state of SQL_TRACE for a session January 29, 2002
Reviewer: John Chung from Santa Ana, CA USA
First off, thank you for the useful script.

One followup question:
The function session_trace can only be compiled by the SYS user.  If one
compiles it with a user account that has the DBA role only, one will get
'PL/SQL: ORA--00942: table or view does not exist
'PLS-00364: loop index variable X is invalid' error, indicating the implicit
cursor is invalid. How can one get around it without invoking the script from
SYS?

John

Followup:
or just have sys grant select on v_$session, v_$process, v_$parameter to the
owner of the procedure.

Or have sys create a view that selects from v$..... ( use the query i have in
the function there, call it say "SESSION_TRACE_VIEW" ) and have sys grant select
on that single view to the owner of the procedure.

Rating: 4
It was very good, but maybe .... January 30, 2002
Reviewer: Chiappa from Sao Paulo, SP Brazil
The proposed way work , for sure, but a friend of mine (an old orassaurus from
v6) suggests :

ORADEBUG SETORAPID, make "current session" whatever you like, and then check for
event 10046 with DBMS_SYSTEM.read_ev .

 I can´t test this right now , but if anyone is interested, try and let us know
!

Rating: 5
RE: It was very good, but maybe... January 31, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
How does this help determine who started the tracing???  That is what the
original poster was after, right?  Am I missing something in the suggestion?

- Mark

Rating: 5
Mea Culpa January 31, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
Cancel my last question - haven't had my coffee yet.  A re-read of the original
poster's question was in order...

- Mark

Rating: 3
DBMS_SYSTEM SQL TRACING OFF March 11, 2002
Reviewer: Venkateswara Reddy from Bangalore, India
Hi tom

Today I have enabled trcing for a particular sessin using
DBMS_SYSTE.SET_SQL_IN_TRACE. But after tracing we have swithced off all the
system (end of the day). Here what I executed for the trcing user using
SID,SERIAL# I din't found after restarging the system. for the same users (os
users) differenct sid's allocated. But in UDUMB directory still trace files are
creating. I would like to know How can I trace off particulare session which
already I have enabled trace after some time.

Thanks  in advance

Reddy

January 30, 2002 Lilian -- Thanks for the question regarding "create user/profile", version 8.1.6
You Asked
Hi Tom:

we will have about 10 new users who can only read our pro. database, what is
best way to create these 10 users?

our pro. uses default profile, can you tell me what is advantage to create our
own one?

thanks as always
an
and we said...
create user username identified by password;
grant CREATE SESSION to username;
grant ROLE_THAT_HAS_SELECT_ON_SPECIFIC_TABLES to username;

I would use a role that has select on the N tables they are allowed to see.
Give them this role and create session and nothing else.

A profile is used to restrict access to resources.  You can limit the amount of
CPU they are allowed to consume, IO and so on.  You would use it to govern their
use of your resources if you want.
Review & Followup
Rating: 3
connect? January 29, 2002
Reviewer: an from DE, usa
Hi Tom:

it's very helpful. but why is grant create session instead of connect?  what are
different between create session and connect?

thank you very much,
an

Followup:
connect has:

sys@ORA817DEV.US.ORACLE.COM> select * from dba_sys_privs where grantee =
'CONNECT';

GRANTEE                        PRIVILEGE                                ADM
------------------------------ ---------------------------------------- ---
CONNECT                        ALTER SESSION                            NO
CONNECT                        CREATE CLUSTER                           NO
CONNECT                        CREATE DATABASE LINK                     NO
CONNECT                        CREATE SEQUENCE                          NO
CONNECT                        CREATE SESSION                           NO
CONNECT                        CREATE SYNONYM                           NO
CONNECT                        CREATE TABLE                             NO
CONNECT                        CREATE VIEW                              NO

8 rows selected.

granted to it.  they don't need all of that, just create session.  It is the
concept of LEAST privileges.  you give them as little as you can to do what they
need and no more.

Rating: 5
Here's a shortcut January 29, 2002
Reviewer: Neil Kodner from Salt Lake City, UT
Tom, in keeping with your 'fewest lines of code' methodology, how about:

grant ROLE_THAT_HAS_SELECT_ON_SPECIFIC_TABLES to <USERNAME> identified by
<PASSWORD>;

This creates the user, grants the role, and sets the password in one statement
(lets hope that the role already has create_session privs).

Followup:
sure, but it is the "undocumented" fewest lines of code.

that was the way to create a user in v6 of the database "grant xxx to yyyy
identified by".

it would work, yes.

January 31, 2002 -- Thanks for the question regarding "PL/SQL Server Pages (PSP)", version Oracle8i 8.1.6.0.0
You Asked
Where is the utility named "loadpsp" found so that I can compile
PSP files?
and we said...
It should be in $ORACLE_HOME/bin.  It is only installed with a
database (not client) install with 8.1.6 (fixed in 8.1.7).

If you have 8.1.6 on NT, you can get it from the installation CD
via:

We just extracted the loadpsp.exe and placed it in the
%ORACLE_HOME%\bin directory.  For example, if c:\orant is
ORACLE_HOME and d: is the CD drive, issue the
following 2 commands if you have the java developer kit:

 cd c:\orant\bin
 jar -xvf
d:\stage\Components\oracle.rdbms\8.1.6.0.0\1\DataFiles\exec.0.0.j
ar   loadpsp.exe

or use winzip to extract the file.
Review & Followup
Rating: 4
Would like to start using PSP January 31, 2002
Reviewer: Yogeeraj from Mauritius
Hi

could you please guide me to any good documentation which could get me started
with it.

I am running Oracle 8.1.7 and IAS release 1.

thanks in advance

best regards
yogeeraj

Followup:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a77069/07_subs.htm#17196
...

January 31, 2002 Thiruvaiyaru -- Thanks for the question regarding
      "Viewing Data associated with BLOB Datatype", version 8.1.6

      You Asked
Tom,

In my queries I use a column  that is BLOB datatype.
(I use the column in the where condition)
Is there a way in SQL*PLUS where I could find out
where the BLOB datatype column is fetching it's results
from. For eg:- I have a column "content_bfile" which
is of datatype BLOB (Binary File LOB). When I do a
select on the column it gives the following error:-
"SP2-0678: Column or attribute type can not be displayed by
SQL*Plus". But when I use the column in the where condition
I get the desired results. How can I see the data
associated with column CONTENT_BFILE.

Thanks
Sai

      and we said...
SQLPlus just refuses to print raw data (be careful with the
following, if you are using telnet or something -- and the file
has control characters -- it can really jam up your session)

We can use dbms_lob.substr to get upto 2000 bytes (any 2000 --
I'm getting the first 2000) and then "cast" them into a
varchar2.  This cast just flips the datatype on the internal
data -- it does no conversion.  What you see is what you get...

select utl_raw.cast_to_varchar2(
          dbms_lob.substr(content_bfile,2000,1)), ....
  from T
/

the utl_raw package may not be installed in your database.  If
not, cd $ORACLE_HOME/rdbms/admin and do an ls *raw*.  You'll
find a .sql and .plb file.  Install the .sql file and then the
.plb file WHEN CONNECTED AS SYS OR INTERNAL using svrmgrl.
Then, you'll have it.

Review &
      Followup

      Rating: 3
numeric
      error  January 31, 2002
Reviewer:  reddy v
       from USA
Hi Tom ,
I am getting the following error b_blob is my blob field..

Invalid SQL statement: select utl_raw.cast_to_varchar2(
dbms_lob.substr(b_blob,2000,1)) from blob_test (WWV-13005)
ORA-06502: PL/SQL: numeric or value error (WWV-11230)

reddy

          Followup:

that just means whatever webdb/portal object you are selecting that into isn't
big enough.  You can see for yourself that it works in general:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( b_blob blob );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          l_raw raw(10000);
  3  begin
  4          l_raw := hextoraw( rpad( to_char(ascii('A'),'fmXX'), 20000,
to_char(ascii('A'),'fmXX') ) );
  5          insert into t values ( l_raw );
  6  end;
  7  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select utl_raw.cast_to_varchar2(
dbms_lob.substr( b_blob, 2000, 1 ) ) from t;

UTL_RAW.CAST_TO_VARCHAR2(DBMS_LOB.SUBSTR(B_BLOB,2000,1))
---------------------------------------------------------------------------------
--------------------------------------------------
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
....

      Rating:
      4
  July 04, 2002
Reviewer:
      A reader

      Rating:
      4
Excellent!  November 12, 2003
Reviewer:
      David

January 31, 2002 Mohamad -- Thanks for the question regarding
      "Returning a clob into a variable for update Using Dynamic SQL", version
      8.1.7

      You Asked
hi
i am using dynamic SQL to update a clob Feild in a table. And as i need to call
this procedure many times. i need to write it using dynamic SQL. The procedure
as it is in static sql gives no error. But when i am using the dynamic SQL it
stops when it reaches the returning statement.

The static SQL :

PROCEDURE "INSERLONGROW" (corps1 IN varchar2, newsID in Number) IS
 l_clob clob;
BEGIN
  UPDATE news
        SET  corpsF_news = empty_clob()
    WHERE  id_news = newsID
    returning corpsF_news into l_clob;

  dbms_lob.writeappend( l_clob, length(corps1), corps1 );
  COMMIT;
END;

The Dynamic SQL:

PROCEDURE "UPDATELONGROW"(NomTable IN varchar2,Nomchamp IN varchar2,NomID IN
varchar2,ValChamp IN varchar2, ValID in Number) IS
 update_st varchar2(100);
 set_st varchar2(100);
 where_st varchar2(100);
 return_st varchar2(100);
 sql_ST VARCHAR2(200);
 l_clob clob;
 len number;
 v_cursor number;
 v_num number;

BEGIN
  update_st := ' UPDATE ' || NomTable ;
  set_st := ' SET ' || Nomchamp || ' = empty_clob() ';
  where_st := ' WHERE ' || NomID ||' = ' ||ValID;
  return_st := ' returning ' || Nomchamp || ' into l_clob';
  sql_st := update_st || set_st || where_st || return_st;

  v_cursor := dbms_sql.open_cursor;
  dbms_sql.parse(v_cursor, sql_st, dbms_sql.native);
  v_num := dbms_sql.execute(v_cursor);
  dbms_sql.close_cursor(v_cursor);
  len:= length(ValChamp);
  dbms_lob.writeappend( l_clob, len , ValChamp );
   COMMIT;
END;

Thank you for your support

      and we said...
Using Native Dynamic SQL this can be accomplished:

ops$tkyte@ORA817.US.ORACLE.COM> create table t
  2  ( x int primary key, y clob )
  3  /

Table created.

ops$tkyte@ORA817.US.ORACLE.COM>
ops$tkyte@ORA817.US.ORACLE.COM> insert into t values ( 1, null );

1 row created.

ops$tkyte@ORA817.US.ORACLE.COM>
ops$tkyte@ORA817.US.ORACLE.COM>
ops$tkyte@ORA817.US.ORACLE.COM> create or replace
  2  procedure update_clob( p_tname       in varchar2,
  3                         p_pkname   in varchar2,
  4                         p_pkval    in int,
  5                         p_clobName in varchar2,
  6                         p_clobVal  in varchar2 )
  7  is
  8      l_clob clob;
  9  begin
 10      execute immediate
 11      'begin
 12           update ' || p_tname || '
 13              set ' || p_clobName || ' = empty_clob()
 14            where ' || p_pkname || ' = :x
 15        returning ' || p_clobName || ' into :y;
 16       end;'
 17      USING IN p_pkVal, OUT l_clob;
 18
 19      dbms_lob.writeappend( l_clob, length(p_clobVal), p_clobVal );
 20  end;
 21  /

Procedure created.

ops$tkyte@ORA817.US.ORACLE.COM>
ops$tkyte@ORA817.US.ORACLE.COM> exec update_clob( 't', 'x', 1, 'y', 'Hello
World' );

PL/SQL procedure successfully completed.

ops$tkyte@ORA817.US.ORACLE.COM> select * from t;

         X Y
----------
--------------------------------------------------------------------------------
         1 Hello World

Make sure to use BIND VARIABLES where ever possible!!!

Review &
      Followup

      Rating:
      5
  June 22, 2001
Reviewer:
      A reader  from france

      Rating: 4
can a clob
      variable be used in 'EXECUTE IMMEDIATE statement?  January
      30, 2002
Reviewer:  mins  from nj usa
Can a clob variable be used in the dynamic sql(because the
sql statment is greater than 40000 characters)

          Followup:

No (a sql statement that is 40,000 characters -- I would be seriously rethinking
that one).

You'll need to use dbms_sql.  there is a parse routine that will do this for
you:

procedure parse(c in integer, statement in varchar2s,
                  lb in integer, ub in integer,
                  lfflg in boolean, language_flag in integer);
  --  Parse the given statement in the given cursor.  The statement is not in
  --  one piece but resides in little pieces in the PL/SQL table "statement".
  --  Conceptually what happens is that the SQL string is put together as
  --  follows:
  --  String := statement(lb) || statement(lb + 1) || ... || statement(ub);
  --  Then a regular parse follows.
  --  If "lfflg" is TRUE then a newline is inserted after each piece.
  --  For further information and for documentation on the rest of the
  --  arguments see the regular parse procedure below.

      Rating: 4
the varchar2s
      ...  January 31, 2002
Reviewer:  mins
       from nj usa
where could I find an example of this ...
how to pass the the sql to the statement??

procedure parse(c in integer, statement in varchar2s,
                  lb in integer, ub in integer,
                  lfflg in boolean, language_flag in integer);

Could you point me to a site where I could look at an exmaple.

Thanks

          Followup:

declare
    l_stmt          dbms_sql.varchar2s;
    l_cursor        integer default dbms_sql.open_cursor;
    l_rows          number  default 0;
begin
    l_stmt(1) := 'insert';
    l_stmt(2) := 'into foo';
    l_stmt(3) := 'values';
    l_stmt(4) := '( 1 )';

    dbms_sql.parse( c             =>   l_cursor,
                    statement     => l_stmt,
                    lb            => l_stmt.first,
                    ub            => l_stmt.last,
                    lfflg         => TRUE,
                    language_flag => dbms_sql.native );

    l_rows := dbms_sql.execute(l_cursor);

    dbms_sql.close_cursor( l_cursor );
end;
/

      Rating: 4
Using Native
      Dynamic SQL this can be accomplished  June 04, 2003

Reviewer:  Andri Kurniawan  from bandung, Indonesia

helpful example on dbms_sql.parse

February 01, 2002 Raman -- Thanks for the question regarding
      "End-of-communication channel , hostdef not found", version 8.0.4

      You Asked
Hi Tom,
     I am back with my new problems. actually what happened, I
had exported one user's schema in imported in other user's
schema. whats happening now, the moment devlopers try any
transactins thru that user, 2 error comes :

1. end-of-communication channel
2. hostdef not found.

I am badly stuck with these probs. Will u please help me to find
out, why and whats happening. It happens sometimes even when
working on SQL*Plus in daily routine.

pls. help me !

regards.

raman.

      and we said...
when you get the 3113 -- that indicates the server
thread/process we were using has "unexpectedly gone away".  In
sqlplus this will undoubtably lead to the hostdef error since
the error causes the backend to disappear.

The underlying cause of a 3113 can be diagnosed typically by:

o inspecting the alert log on the server.  There should be a
message in there typically that tells of the error occuring and
gives the name of a trace file.

o the trace file will contain more details.  Most importantly,
you can see the SQL statement that was executing usually at the
time of the crash.  This can be useful in diagnosing the
statement that is giving us a hard time.  The remainder of the
trace file will not be very useful to you -- but it will be
useful to support which is your next step....

o contact support with the contents of the trace file.  For
errors like this, the information in the trace file can be used
by support/development to track down the cause of this error.
Review &
      Followup

      Rating: 4
Very well
      put  November 09, 2001
Reviewer:  Doug
       from CT, USA
Very clear explanation of an error.  Quick follow up, can you elaborate on why
in SQL PLUS we get the hostdef problem? The backend?  What is hostdef exactly
anyhow?

          Followup:

the hostdef is just an OCI programming structure - it's part of the connection.
The host definition is not found when the host "goes away" like this.

      Rating:
      4
end-of-comunication problem  February 01, 2002

Reviewer:  Arumugam Sivababu  from India
Hello TOM,
      While running the trigger i'm getting end-of-file communication. The same
trigger is running in other users with out any problem. I couldn't find any
problem in the user. I'm not in a position to send my production triggers and
procedures.

Please reply me and expecting your mail.

regards,
sivababu

          Followup:

Umm, see the last step in my answer.  There is nothing i can personally do.  If
you cannot give support the information -- there will be not much they can do
either.  They will need you to generate a tracefile at the very least.

February 01, 2002 arunima -- Thanks for the question regarding
      "ref. cursor", version 8.1.7

      You Asked
Hi Tom,

I want to know how can I pass a ref. cursor from one function to another
function. Also if the ref. cursor contains rows that has multiple columns how
can I use the column values within a function.

for example the ref. cursor has rows from the emp table and I want to use give
$100 raise to the employee who is 'CLERK'.

Hope I could make my question clear.

Thanks a lot

      and we said...
You just pass it -- nothing fancy.

scott@ORA817DEV.US.ORACLE.COM> create or replace package types
  2  as
  3      type rc is ref cursor;
  4  end;
  5  /

Package created.

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> create or replace procedure give_raise( p_cur in
types.rc )
  2  as
  3      l_empno emp.empno%type;
  4      l_job   emp.job%type;
  5  begin
  6      loop
  7          fetch p_cur into l_empno, l_job;
  8          exit when p_cur%notfound;
  9
 10          if ( l_job = 'CLERK' )
 11          then
 12              update emp set sal = sal+100 where empno = l_empno;
 13          end if;
 14      end loop;
 15      close p_cur;
 16  end;
 17  /

Procedure created.

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> create or replace procedure main
  2  as
  3      l_cur types.rc;
  4  begin
  5      open l_cur for select empno, job from emp;
  6
  7      give_raise( l_cur );
  8  end;
  9  /

Procedure created.

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> select empno, sal from emp where job = 'CLERK';

     EMPNO        SAL
---------- ----------
      7369        800
      7876       1100
      7900        950
      7934       1300

scott@ORA817DEV.US.ORACLE.COM> exec main

PL/SQL procedure successfully completed.

scott@ORA817DEV.US.ORACLE.COM> select empno, sal from emp where job = 'CLERK';

     EMPNO        SAL
---------- ----------
      7369        900
      7876       1200
      7900       1050
      7934       1400

scott@ORA817DEV.US.ORACLE.COM>
Review &
      Followup

      Rating: 5
ref.
      cursor  February 01, 2002
Reviewer:  arunima

Thanks a lot Tom. Things start looking so easy after you explain it.

      Rating:
      5
  February 01, 2002
Reviewer:
      Helena  from USA

      Rating:
      5
  August 02, 2002
Reviewer:  Robert
       from USA

February 01, 2002 -- Thanks for the question regarding "bind
      variables in SQL*Plus without setting the variables in PL/SQL Blocks?",
      version 8.1.7

      You Asked
Hi,

Question:
==========
Is there any way we can use bind variables in SQL*Plus without setting the
variables in PL/SQL Blocks?

Scenario:
========
Currently we need to pass variables from a unix script to a sql statement in a
sql*plus session.
However in doing this, the variables are passed in as constants and will
generate many entries in the SGA.

In trying to improve our database performances we have tried to use bind
variables, but every bind variables
in sql*plus must be set in a PL/SQL block and these in turn also gets stored in
the SGA.

Examples:
=========
By passing in variables to the following sql statement

SELECT COUNT(*) FROM DUAL WHERE &var1 = &var1;

can produce the following entries in the SGA

SELECT COUNT(*) FROM DUAL WHERE 1 = 1;
SELECT COUNT(*) FROM DUAL WHERE 2 = 2;
SELECT COUNT(*) FROM DUAL WHERE 3 = 3;
SELECT COUNT(*) FROM DUAL WHERE 4 = 4;
....

However if we tried to use bind variables

SELECT COUNT(*) FROM DUAL WHERE :b_var1 = :b_var1;

We need to do the following to set the variable

SQL> var b1 number;

SQL> begin
  2    :b_var1 := 1;
  3  end;
  4  /

This generates the following entry in the SGA:

SELECT COUNT(*) FROM DUAL WHERE :b_var1 = :b_var1;

but also including

begin   :b_var1 := 1 end;
begin   :b_var1 := 2 end;
begin   :b_var1 := 3 end;
begin   :b_var1 := 4 end;
....

I know we can get around this problem by using Pro*C and Perl but are there any
ways around this in SQL*PLUS

      and we said...
Nope.  Use CURSOR_SHARING=FORCE instead.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter system flush shared_pool;

System altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set cursor_sharing = EXACT;

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> SELECT COUNT(*) FROM DUAL WHERE 1 = 1;

  COUNT(*)
----------
         1

ops$tkyte@ORA817DEV.US.ORACLE.COM> SELECT COUNT(*) FROM DUAL WHERE 2 = 2;

  COUNT(*)
----------
         1

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select sql_text from v$sqlarea where sql_text
like 'SELECT COUNT(*) FROM DUAL%';

SQL_TEXT
---------------------------------------------------------------------------------
--------------------------------------------------
SELECT COUNT(*) FROM DUAL WHERE 1 = 1
SELECT COUNT(*) FROM DUAL WHERE 2 = 2

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set cursor_sharing = FORCE;

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> SELECT COUNT(*) FROM DUAL WHERE 3 = 3;

  COUNT(*)
----------
         1

ops$tkyte@ORA817DEV.US.ORACLE.COM> SELECT COUNT(*) FROM DUAL WHERE 4 = 4;

  COUNT(*)
----------
         1

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select sql_text from v$sqlarea where sql_text
like 'SELECT COUNT(*) FROM DUAL%';

SQL_TEXT
---------------------------------------------------------------------------------
--------------------------------------------------
SELECT COUNT(*) FROM DUAL WHERE 1 = 1
SELECT COUNT(*) FROM DUAL WHERE 2 = 2
SELECT COUNT(*) FROM DUAL WHERE :"SYS_B_0" = :"SYS_B_1"

See how the 3 = 3 and 4 = 4 queries are not there.  They get rewritten using
binds automagically.

See chapter 10 of my book if you have it for some of the caveats of using this
feature (and why its useful in a limited number of cases -- like this one -- you
should NOT use it to fix "developed code", you should FIX the developed code to
use bind variables)

February 01, 2002 arunima -- Thanks for the question regarding "sql
      for heirarchical relationship", version 8.1.7

      You Asked
Hi Tom,

Here is an example:

select   lpad(' ', (level - 1) * 2) || ename as padded_name, job, level
 from emp
 connect by prior empno = mgr
 start with mgr IS null;

PADDED_NAME          JOB            LEVEL
-------------------- --------- ----------
KING                 PRESIDENT          1
  JONES              MANAGER            2
    SCOTT            ANALYST            3
      ADAMS          CLERK              4
    FORD             ANALYST            3
      SMITH          CLERK              4
  BLAKE              MANAGER            2
    ALLEN            SALESMAN           3
    WARD             SALESMAN           3
    MARTIN           SALESMAN           3
    TURNER           SALESMAN           3
    JAMES            CLERK              3
  CLARK              MANAGER            2
    MILLER           CLERK              3

My question is :

I need to write a query wherein I can get the parents of 'CLERKS'. If in the
hierarchy 'CLERK' has a parent as 'ANALYST' then result should display that ot
else 'MANAGER'.

for example i want this result.

ADAM   SCOTT
SMITH  FORD
JAMES  BLAKE
MILLER CLARK

Thanks a lot for your help.

      and we said...
scott@ORA817DEV.US.ORACLE.COM> select ename, (select ename from emp e2 where
empno = e1.mgr )
  2    from emp e1
  3   where job = 'CLERK'
  4  /

ENAME      (SELECTENA
---------- ----------
SMITH      FORD
ADAMS      SCOTT
JAMES      BLAKE
MILLER     CLARK

is one way to do that.

scott@ORA817DEV.US.ORACLE.COM> select e1.ename, e2.ename
  2    from emp e1, emp e2
  3   where e1.mgr = e2.empno
  4     and e1.job = 'CLERK'
  5  /

ENAME      ENAME
---------- ----------
SMITH      FORD
ADAMS      SCOTT
JAMES      BLAKE
MILLER     CLARK

is another...
Review & Followup

      Rating: 2
heirarchy
      relationship  February 01, 2002
Reviewer:
      arunima  from NY,USA
Tom,

If the table looks like this then how will we do it. If in the hierarchy 'CLERK'
has a parent as 'ANALYST' then
result should display 'ANALYST' or else 'MANAGER'.

Since ANALSYT or MANAGER is not a immediate parent of 'CLERK' I was wondering
how to do that.

PADDED_NAME          JOB            LEVEL
-------------------- --------- ----------
KING                 PRESIDENT          1
  JONES              MANAGER            2
    SCOTT            ANALYST            3
      ADAMS1         SALESMAN           4
       ADAMS         CLERK              5
    FORD             ANALYST            3
      SMITH1         SALESMAN           4
      SMITH          CLERK              5
  BLAKE              MANAGER            2
    ALLEN            SALESMAN           3
    WARD             SALESMAN           3
    MARTIN           SALESMAN           3
    TURNER           SALESMAN           3
       JAMES            CLERK           4
  CLARK              MANAGER            2
    MILLER1          SALESMAN           3
    MILLER           CLERK              4

Thanks for your time.

          Followup:

you are not making sense to me.  show an example of what you expect like you did
above (and I then supplied the answer for)

      Rating: 4
heirarchy
      relationship  February 01, 2002
Reviewer:
      arunima  from NY,USA
Tom,

If the table looks like this then how will we do it. If in the hierarchy 'CLERK'
has a parent as 'ANALYST' then
result should display 'ANALYST' or else 'MANAGER'.

Since ANALSYT or MANAGER is not a immediate parent of 'CLERK' I was wondering
how to do that.

PADDED_NAME          JOB            LEVEL
-------------------- --------- ----------
KING                 PRESIDENT          1
  JONES              MANAGER            2
    SCOTT            ANALYST            3
      ADAMS1         SALESMAN           4
       ADAMS         CLERK              5
    FORD             ANALYST            3
      SMITH1         SALESMAN           4
      SMITH          CLERK              5
  BLAKE              MANAGER            2
    ALLEN            SALESMAN           3
    WARD             SALESMAN           3
    MARTIN           SALESMAN           3
    TURNER           SALESMAN           3
       JAMES            CLERK           4
  CLARK              MANAGER            2
    MILLER1          SALESMAN           3
    MILLER           CLERK              4

Thanks for your time.

      Rating: 3
sql for
      heirarchical relationship  February 01, 2002

Reviewer:  arunima  from NY, USA
Sorry Tom, If I was not detailed enough with my question. In this table
structure mentioned below I need to know the parents of CLERKS.

In the heirarchy of each CLERK if there is an ANALYST then my result should
display the ANALYST. If in the heirarchy of CLERK there are no ANALYST then the
result should be MANAGER.

PADDED_NAME          JOB            LEVEL
-------------------- --------- ----------
KING                 PRESIDENT          1
  JONES              MANAGER            2
    SCOTT            ANALYST            3
      ADAMS1         SALESMAN           4
       ADAMS         CLERK              5
    FORD             ANALYST            3
      SMITH1         SALESMAN           4
       SMITH          CLERK             5
  BLAKE              MANAGER            2
    ALLEN            SALESMAN           3
    WARD             SALESMAN           3
    MARTIN           SALESMAN           3
    TURNER           SALESMAN           3
       JAMES            CLERK           4
  CLARK              MANAGER            2
    MILLER1          SALESMAN           3
    MILLER           CLERK              4

This the the result I need.

MILLER   CLARK
JAMES    BLAKE
SMITH    FORD
ADAMS    SCOTT

I am not sure if it is possible at all with sql. Hope I could expalin it better
this time.

Thanks a lot for your time.

          Followup:

scott@ORA817DEV.US.ORACLE.COM> column padded_name format a20
scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> select   lpad(' ', (level - 1) * 2) || ename as
padded_name, job, level
  2   from emp
  3    connect by prior empno = mgr
  4     start with mgr IS null;

PADDED_NAME          JOB            LEVEL
-------------------- --------- ----------
KING                 PRESIDENT          1
  JONES              MANAGER            2
    SCOTT            ANALYST            3
      ADAMS          CLERK              4
    FORD             ANALYST            3
      SMITH          CLERK              4
  BLAKE              MANAGER            2
    ALLEN            SALESMAN           3
    WARD             SALESMAN           3
    MARTIN           SALESMAN           3
    TURNER           SALESMAN           3
    JAMES            CLERK              3
  CLARK              MANAGER            2
    MILLER           CLERK              3

14 rows selected.

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> select ename,
  2            substr( (select min( to_char( level, 'fm0009' ) || ename )
  3                           from emp e2
  4                          where job in ( 'ANALYST', 'MANAGER' )
  5                          start with e2.empno = e1.empno
  6                    connect by prior mgr = empno ), 5 )
  7   from emp e1
  8  where job = 'CLERK'
  9  /

ENAME      SUBSTR((SEL
---------- -----------
SMITH      FORD
ADAMS      SCOTT
JAMES      BLAKE
MILLER     CLARK

scott@ORA817DEV.US.ORACLE.COM>

should get that for you.

      Rating:
      5
excellent  February 01, 2002
Reviewer:
      arunima  from NY, USA
That was awesome. thanks a lot

February 01, 2002 arunima -- Thanks for the question regarding
      "retrieving sql from shared pool", version 8.1.7

      You Asked
Tom,

Is there a way to retreive the sql queries from the shared pool that belong to a
particular user.

For example in my database I have 5 users a, b,c,d,e. I specifically want to
view the sql queries of b user from shared pool.

I know that we can query the v$sqlarea view to view the sql queries from shared
pool but i am not sure how can i group them by users.

Thanks a lot.

      and we said...
There is no such thing as the "owner" of a query in the shared pool -- hence its
name "shared".

The sql in there is not "owned" by anyone --- that would totally defeat the
purpose of it.  The sql in there is owned by EVERYONE.  Hopefully there is not a
1-1 relationship between queries in the shared pool and users.  If there is, you
have a serious problem.

You may be able to peek in v$open_cursor to see what sql they have been
executing.  It will not be an exhaustive list, it'll show some of the sql they
have done.
February 01, 2002
      Punit -- Thanks for the question regarding
      "Problem with Raise_Application_Error", version 8.1.7

      You Asked
Hello Tom,

The following code doesn't seem to work as desired :

create trigger CHK_DAY
before
insert or update or delete
on EMP
declare
    action char(1);
    username varchar2(30);
    logdate date;
begin
    if to_char(sysdate,'fmday') = 'sunday' then
        if inserting then
            action = 'I';
        elsif updating then
            action = 'U';
        else
            action = 'D';
        end if;
        select user, sysdate into username, logdate from dual;
        insert into INVALID_DAY_LOG values(action, username, logdate);
        raise_application_error(-20001,'Cannot manipulate Database on SUNDAY
!!!');
    end if;
end;

In fact, It's been observed that whenever a Trigger includes a
RAISE_APPLICATION_ERROR statement, no DML statement is executed from within the
Trigger. For the above example, could you please tell me how I can maintain a
log of Users trying to manipulate the Database on Sunday - at the same time not
allowing them to successfully carry out DML. (If the RAISE_APPLICATION_ERROR is
not include, the log trail is successful, but also the User's DML would be
successful)

Anoher thing I wanted to ask was about the error message that is displayed at
the execution of a Trigger which includes a RAISE_APPLICATION_ERROR statement.
Oracle displays the specified error message and follows it with the following
message :

"ERROR DURING EXECUTION OF TRIGGER SCOTT.CHK_DAY"

Is this something wrong or is it correct?

      and we said...
do you have my book?  read the chapter on autonomous transactions.  It has the
whole story and shows how to work around this.

In fact, it is surprising that a developer would expect the insert to persist.
I mean -- if the above code allowed your INSERT to be saved - what about the
rest of the transaction?  If we were on the 500'th row of a 600 row insert into
select -- should the prior 500 rows stay?  Should side effects of those 500 rows
stay?  whose to say what stays and what does not stay?  The answer is NOTHING
stays statements are atomic (lots about that with tons of examples in the book
as well)

Shortcut for you, read:

http://osi.oracle.com/~tkyte/autonomous/index.html
....
February 01, 2002
      Patricia -- Thanks for the question
      regarding "MAX_USERS in oradim and ENTRIES in orapwd", version 8.1.7

      You Asked
I am getting confused with these 2 and would appreciate your help in
clarification. My OS is Windows NT 4.0.

Suppose I delete the old password file and also deleted the old service and
recreated using ORADIM -NEW -SID xxx -INTPWD xxx -MAXUSERS 8 ....., now I can
only have 8 sessions connected to the db as sysdba? I tested this (by creating
>8 login sessions) and it is not so, but the new password did take effect. The
ENTRIES value that I specified while creating the password file will become
invalid after I use MAX_USERS in ORADIM?

Also if I have the service OracleServiceSID running, how do I shut down the
database? I tried to issue shutdown from sql*plus but db became frozen, if I
simply shutdown by stopping the service it seemed ok but I did not find any msg
about the shutdown in alert log so was wondering is it a SHUTDOWN NORMAL? In the
registration key I did find Oracle_SID_shutdown to default to i(shutdown
immediate).

Thanks so much.

      and we said...
You can have as many SESSIONS as you would like.
You can have 8 users (8 differently NAMED users) in the password file.

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:670117794561
for a subtle nuance with the password file and passwords.

When you did the shutdown, you probably did a shutdown normal.  It was waiting
for everyone to log off.  A service shutdown may be using ABORT (right away) or
immediate (kick everyone out then shutdown).  YOu configure that with oradim.

Review &
      Followup

      Rating:
      5
  February 01, 2002
Reviewer:
      Patricia  from MD USA
But ORADIM would overwrite those set by ORAPWD, right? Other than that your
response did cleared up the password file and password file users.

A big THANK YOU!

          Followup:

oradim is doing the same thing as orapwd.  The question started with "I delete
the old password file...".  Oradim will recreate the password file for you.
There is nothing to "overwrite" as it was already erased.

February 01, 2002 David -- Thanks for the question regarding
      "negative values of execute to parse in Statspack report", version 8.1.7

      You Asked
Hi Tom,

I worked with a programmer and desinged a schema. Now I am using Statspack to
check how is the performance of the database and applicaiton.

There are two questions that I like to get your opinion

1. The Execute to Parse values is negative as showing below from Statspack
report. How do you interprete this value?

Instance Efficiency Percentages (Target 100%)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            Buffer Nowait %:  100.00       Redo NoWait %:  100.00
            Buffer  Hit   %:   80.74    In-memory Sort %:   99.10
            Library Hit   %:   92.95        Soft Parse %:   91.24
         Execute to Parse %:  -31.89         Latch Hit %:   99.99
Parse CPU to Parse Elapsd %:   87.32     % Non-Parse CPU:   99.92

2. Below is my SGA info and section of Statspack report. I thought I have
allocated enough shared memory. Do I need to increase the shared memory further?

Total System Global Area   75796640 bytes
Fixed Size                    73888 bytes
Variable Size              47779840 bytes
Database Buffers           27852800 bytes
Redo Buffers                  90112 bytes

shared_pool_size = 24000000  in the init.ora file

Shared Pool Statistics        Begin   End
                               ------  ------
             Memory Usage %:   95.02   79.74
    % SQL with executions>1:   36.15   37.14
  % Memory for SQL w/exec>1:   24.03   39.37

Thanks

      and we said...
1) that means someone is parsing statements and NEVER executing them.   They are
just chewing up your CPU, latching the shared pool, killing your performance.

That percentage is computed as:

round(100*(1-:prse/:exe),2)

So, when the :prse > :exe, you get negative numbers.  VERY VERY VERY bad.

2) you have 24m.  that should be sufficient for most applications.

You have 27ish meg for the block buffer cache.  That is considered "small" on
most systems today.  What is your cache hit ratio, if low, you may benefit from
more.  depends on the ram on the system.

Review &
      Followup

      Rating: 4
so why some sql
      statements were parsed but were not executed?  February 01,
      2002
Reviewer:  David  from Irving, Texas
Thanks Tom for your interpretation on the negative values of execute to parse.
According to your percentage equition, how could parse be bigger than exec. I
thought the purpose of parsing is for execution. So the smallest percentage
should be 0, that is one parsing on execution.

          Followup:

There are tools (bad ones) that parse a statement but never execute it.  Or they
parse it to discover what inputs/outputs it takes and then later on parse it
again to execute it (so the parse/execute is 2:1).

It is commonly referred to as "inefficient coding techniques".  I see it with
some 3rd "generic" libraries.  They parse like there is no tomorrow -- works
great in a single user environment -- starts to stink with two users (or more).

Just cause you parsed, dosen't mean you are obligated to execute.

February 01, 2002 Munzer -- Thanks for the question regarding
      "Refresh screen", version 8.1.7

      You Asked
Tom:

I have a web application (PL/SQL and html) that has a main menu.  One of the
fucntions of the main meny has a message to the user with a count of how many
records are waiting for approval.

Problem I am having is that after a user approves a record I take him to the
main menu but the count does not change until I refresh by pressing the refresh
button manually.

DO you know why the page is not getting automatically refreshed, since I am
going from page to page.  Is it because of the stateful environment I am working
in.

ALso, do you know how you can set up something in PL/SQL, html or javaxcript
that will refresh it automatically.

Thank you,

      and we said...
The browser caches these documents.  It cannot tell the difference between a
static page and dynamic page.

You can use a META tag to either expire the page (causing the browser to get a
new one hopefully) or a META tag to refresh the page every N seconds.

See

http://www.htmlgoodies.com/tutors/meta.html
for #1, and

http://www.htmlgoodies.com/tutors/dynamic5.html
for #2

February 02, 2002 P J -- Thanks for the question regarding "recover
      data after truncating a table", version 8.1.7

      You Asked
i have truncated a table by mistake.
Is there any way i can recover the data lost.

      and we said...
If you are running in archive log mode, perhaps.

If you are using RMAN, read about tablespace point in time recoveries.   You can
use that to restore the affected tablespace to the point in time prior to your
truncation.

If you are not using RMAN, you can just restore system, rollback and the
affected tablespace to another MACHINE from your hot/cold backups (taken prior
to the truncate), point in time recover that to BEFORE your truncation, EXP the
affected table from this "mini-instance" (use ANOTHER machine to avoid more
human error on the production machine) and import it into the other instance.

If you are running in no-archive log mode (also known as the "up the creek
without a paddle mode"), you have lost the data.
February 02, 2002 Rafael -- Thanks for the question regarding "Case
      Sensitive Queries", version 8.1.6

      You Asked
Hi Tom,

I'm having a trouble.

How can I make a SELECT query with a WHERE clause that does not
treat VARCHAR in a case sensitive way ?
For example, I have a column in a table whose values are
"Smith", "Jackson" and "Taylor".
I'd like to submit a query such as SELECT * FROM table WHERE
name = "SMITH", or SELECT * FROM table WHERE name = "TaYlOr".

Can you help me?

Thank you very much for the time and attention.

[ ]'s

      and we said...
See

http://osi.oracle.com/~tkyte/article1/index.html
for how I would approach this using a function based index.  You
need Oracle8i EE or Personal for function based indexes, they
are not part of SE.
Review & Followup

      Rating: 4
change the case
      sensitive to non sensitice  February 01, 2002

Reviewer:  Mike  from alpharetta
Tom,
I have a little different case.
I have inherited a database for develop new applications. I have found out that
all the tables have the case sensitive of the table names and the column names.
The queries would not return data unless putting the double quotes there, for
example:

sql> select custid from baseinfor;
select custid from baseinfor
                   *
ERROR at line 1:
ORA-00942: table or view does not exist

sql> select custid from "BaseInfor";
select custid from "BaseInfor"
       *
ERROR at line 1:
ORA-00904: invalid column name

sql> select "CustID" from "BaseInfor";

   CustID
---------
     4690
     1103

sql>

I want to create views for all the tables, and view names and the column shall
not be case sensitive.

Select 'create view '||table_name||' as select * from '||table_name||';' from
user_tables
/

This creates views, which columns are still case sensitive.

And the following does not work too,

Select 'create view '||table_name||' as select '||column_name||' from
'||table_name||';'
From user_tab_columns
/

Could you help?
Thanks

          Followup:

SQL> select decode( COLUMN_ID, 1, 'create view ' || table_name || '(', ',' ) ||
  2         ' ' || column_name ||
  3        decode( column_id, cid, ') as select * from ' || table_name || ';' )
  4    from user_tab_columns,
      (select max(column_id) cid from user_tab_columns where table_name = 'EMP')
  5   where table_name = 'EMP'
  6   order by COLUMN_ID;

DECODE(COLUMN_ID,1,'CREATEVIEW'||TABLE_NAME||'(',',')||''||COLUMN_NAME||DECODE(CO
LUMN_ID,CID,')ASSELECT*FROM'||TABLE_NAME||'
---------------------------------------------------------------------------------
-------------------------------------------
create view EMP( EMPNO
, ENAME
, JOB
, MGR
, HIREDATE
, SAL
, COMM
, DEPTNO) as select * from EMP;

8 rows selected.

      Rating:
      5
  February 02, 2002
Reviewer:  mike
       from alpharetta, ga
Thank a lot.
The table names and columns in The data dictionaries are still in case
sensitive. Besides to alter table... changing table name/column name, what are
other options for making non case sensitive in the SYS views?

          Followup:

creating the tables without quotes in the first case.  It is only when you tell
us to be case sensitive that we are.

By default, we are not.

As there are NO ways to rename a column, the query right above will fix you up.
You'll be able to use no-case or case sensitive at will (you'll have BOTH)

February 02, 2002 Joseph -- Thanks for the question regarding
      "Import only tables without associated objects like triggers etc from
      schema", version 8.1.7.1.0

      You Asked
Hello, Tom:
We have a full exported schema done through
$exp schemaowner_name/password file=filename.dmp direct=y consistent=y
owner=schemaowner_name

Table data and some triggers were changed during testing. We need to restore
data for schema tables only, without restoring triggers.
There are a lot of tables.
How to export older data only from filename.dmp file without bringing back older
triggers et cetera.

We use Oracle 8.1.7.1.0 on Sun2.6 platform.
Thank you for help.
Joseph

P.S. I enjoy reading your expert 'one-on-one Oracle book'. JA

      and we said...
You would to tables= import.

The easiest method would be to create a PARFILE with a list of tables.  You
could use a query against the data dictionary on the source database to do this,
the format of the parfile will be:

tables=(
emp,
dept,
bonus
)

so, you can see how easy it would be to write a query to get the list of
tablenames and edit that a little to generate the parfile.

then

imp userid=u/p parfile=imp.par

(thanks for the feedback on the book)

followup to comments

Yes -- add triggers = n to the EXP command (oversight, sorry about that)

followup to the comment about trigger=n not being part of IMP

so correct, doh!

corrected above, thanks
Review & Followup

      Rating:
      4
  August 14, 2001
Reviewer:
      A reader

      Rating: 3
triggers
      restored ?  August 15, 2001
Reviewer:  Dave
       from UK
Tom, wouldn't the triggers come back with the table data? The question says
"without restoring triggers".

      Rating: 4
Response to
      previous post  August 15, 2001
Reviewer:
      Connor  from UK
True - you could add triggers=n

      Rating: 4
Import without
      Trigger  August 15, 2001
Reviewer:  Randy
       from Chicago
You need to export the tables using exp triggers=n...
and then import. Otherwise it will always have trigger inside the tmp file.

      Rating: 4
triggers on
      import  August 15, 2001
Reviewer:  Dave
       from UK
Yes, I don't think there is a 'triggers' option on imp.

      Rating: 2
Back to
      square  August 15, 2001
Reviewer:  Gururaj
      Kulkarni  from Norcross GA USA
So how do we restore data without triggers ?

      Rating: 5
Simulating
      triggers=n on IMP  August 15, 2001
Reviewer:
      Connor  from UK
I haven't tried this but you could revoke any privs to create triggers and then
import.  You would presumably get a lot of errors but the triggers would not be
done

      Rating:
      2
RESTRICTIONS  January 26, 2002
Reviewer:
      Sreekanth Unnikrishnan  from Bangalore, India
There is no mention of the restrictions in parfiles. Say like if the list of
tables are exceeding a specific number export command will fail. I agree this is
an O/S restriction, but there has to be a workaround rather than firing the
export different times. Do let us know if there is a solution to this.

          Followup:

Well, I found the schema in my database with the largest number of tables and
created a parfile with 236 tables to export in it -- each listed one after the
other.

I did not hit an error.  You'll have to be a little more clear as to how to
reproduce your issue exactly.

      Rating:
      3
Reader  January 26, 2002
Reviewer:
      Reader
EVEN if the restriction of number of tables exceeds, you
do not have to fire export at different times. You can
always do PARALLEL export of tables at the same time and
do PARALLEL import of tables also. This method also gives
you better exp/imp time.

      Rating:
      3
Restrictions  February 02, 2002
Reviewer:
      Sreekanth Unnikrishnan  from Bangalore
I was not elaborate on my earlier comment since I did not want to make this a
full fledged discussion in itself (Ref ur instructions about this forum). We are
doing a data warehousing application for Citibank in Singapore. Here we have
around 2000 odd tables in a single schema. We have around 19 countries and each
country has 2 major schemas. Here the problem is we have to create parfiles with
50 odd tables and run it in parallel. The O/S is UNIX. If the count of tables
exceeded 60 it says parfile too large. Oracle manual says that the size of the
parfile depends on the O/S. I can pass on the oracle error number to you on
Monday. Additionaly what is the maximum number of bytes a parfile in UNIX can
accomodate.

          Followup:

As I said -- I did 236 files without issue, on solaris.

What is the SIZE of your parfile and what is the cut and paste of the entire
error message from EXP?

(the message on the followups is to not ask new questions, talking about
something related to the original question is OK)

February 02, 2002 -- Thanks for the question regarding "HELP For
      Help", version 816

      You Asked
Hi,
   We have 3 tier app ,ASP,IIS,Oracle 816. Any ideas, suggestions on how to
Implement context sensitive help for this application. We have
vertical toolbar which has a help menu. This must give context sensitive help
depending on the Menu selected.The help files are in windows hlp format.
1)  Where is it best to store these windows help file. Is it possible to store
in the Database. Or  should be stored on the web server.
2)Can I store just the URL of the file location in the database then access the
file which is stored on the web server.
3)I do not know much about Internet File system can IFS help in this case. If so
can u please provide an example .Or LDAP may be.

Thanx

      and we said...
First, iFS is just a file system.  It makes files available anywhere via SMB
(windows proprietary networking protocol), ftp, http, and so on.  It is a tool
you can use -- but not any more so than a filesystem

LDAP is a directory service.  I do not necessarily see its usefulness in a help
system.

I would just use URLS, a help link would simply be a URL -- the URL would point
to the "context sensitive help" for the page you are on.  The URLS could point
to static files, or to a procedure that assembles the correct information from
data stored in database tables.  Me, I would use database tables since I don't
use static files for anything.
Review &
      Followup

      Rating: 2
Need more
      Details Please  February 02, 2002
Reviewer:
      A reader
You Said : " ...or to a procedure that assembles the correct information from
data stored in database tables. Me, I would use database tables since I don't
use static files for anything. "
I Ask : Can u please elaborate how can this be done in the database thru a
procedure. Like how do I feed a windows help hlp formate file
in the database. What could be the table structure and datatype. To  my little
understanding u will store the hlp files in the database, correct me If iam
wrong.Or how will u organise this in the database and how to retrieve it.
If using URL for files stored in database how can the URL be linked to the file
stored in db.
Thanx

          Followup:

Well, I cannot imagine using the "windows help format file" -- I haven't seen
one of those in years.  On the web, one should do web things.  On the web, we
use the browser -- we geneate HTML.

I would be storing help text in clobs in the database, formatted in HTML.  I
would be selecting it out and displaying it on the page when requested.  I don't
see how windows hlp files would even be useful in a web environment (only
severely limiting, sort of locks my end users into a proprietary operating
system).

Sorry -- cannot help on this one, I guess you could just throw the hlp files
onto the server and refer to them by url when someone wants them -- how useful
that will be I don't know -- I don't know how a windows hlp file would be able
to retrieve the other linked hlp files.

February 02, 2002 -- Thanks for the question regarding
      "DBMS_JOB.Interval", version 8.1.6

      You Asked
Hello Tom,

I am trying to put a procedure to run three times a day at 8:00AM, 12:00 and
17:00PM. I have the below problem:

                          next_date => sysdate+1+8/24,
           interval => ' decode( sign(to_char(dt,'hh24')-7),
1, decode( sign( to_char(dt,'hh24')-12),1, decode (
sign(to_char(dt,'hh24')-17),1, trunc(dt)+1+8/24, trunc(dt)+17/24 ) ,
trunc(dt)+13/24), trunc(dt)+8/24)'  );

ERROR at line 1:
ORA-23319: parameter value "decode( sign(to_char(dt,'hh24')-7), 1, decode(
sign( to_char(dt,'hh24')-12),1, decode ( sign(to_char(dt,'hh24')-17),1,
trunc(dt)+1+8/24, trunc(dt)+17/24 ) , trunc(dt)+13/24), trunc(dt)+8/24)" is
not
appropriate
ORA-06512: at "SYS.DBMS_JOB", line 57
ORA-06512: at "SYS.DBMS_JOB", line 129
ORA-06512: at line 6

What exactly is the problem?

      and we said...
you need to use '' to get a single ' in a string.

interval => ' decode( sign(to_char(dt,''hh24'')-7), ....
                                      ^^    ^^

I did not check your logic -- but I am wondering -- what is "dt"?  What I'd be
tempted to do here, to make the logic more understandable and the job easier to
submit is create a function:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function
my_next_date(p_date in date) return date
  2  as
  3      l_next_date date;
  4      l_hour      number default to_char(p_date,'hh24');
  5      l_now       date   default trunc(p_date);
  6  begin
  7      execute immediate '
  8      select case when :l_hour between  8 and 11 then :l_now+12/24
  9                  when :l_hour between 12 and 16 then :l_now+17/24
 10                  when :l_hour between  0 and  7 then :l_now+8/24
 11                  else                                :l_now+1+8/24
 12             end
 13        from dual'
 14        into l_next_date
 15        using l_hour, l_now, l_hour, l_now, l_hour, l_now, l_now;
 16
 17      return l_next_date;
 18  end;
 19  /

Function created.

in 8i, need to use dynamic sql to hide the CASE statement from plsql...

And then test the function:

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select dt, my_next_date(dt)
  2    from (select sysdate+(rownum-1)/24 DT from all_objects where rownum <= 30
);

DT                   MY_NEXT_DATE(DT)
-------------------- --------------------
02-feb-2002 13:44:54 02-feb-2002 17:00:00
02-feb-2002 14:44:54 02-feb-2002 17:00:00
02-feb-2002 15:44:54 02-feb-2002 17:00:00
02-feb-2002 16:44:54 02-feb-2002 17:00:00
02-feb-2002 17:44:54 03-feb-2002 08:00:00
02-feb-2002 18:44:54 03-feb-2002 08:00:00
02-feb-2002 19:44:54 03-feb-2002 08:00:00
02-feb-2002 20:44:54 03-feb-2002 08:00:00
02-feb-2002 21:44:54 03-feb-2002 08:00:00
02-feb-2002 22:44:54 03-feb-2002 08:00:00
02-feb-2002 23:44:54 03-feb-2002 08:00:00
03-feb-2002 00:44:54 03-feb-2002 08:00:00
03-feb-2002 01:44:54 03-feb-2002 08:00:00
03-feb-2002 02:44:54 03-feb-2002 08:00:00
03-feb-2002 03:44:54 03-feb-2002 08:00:00
03-feb-2002 04:44:54 03-feb-2002 08:00:00
03-feb-2002 05:44:54 03-feb-2002 08:00:00
03-feb-2002 06:44:54 03-feb-2002 08:00:00
03-feb-2002 07:44:54 03-feb-2002 08:00:00
03-feb-2002 08:44:54 03-feb-2002 12:00:00
03-feb-2002 09:44:54 03-feb-2002 12:00:00
03-feb-2002 10:44:54 03-feb-2002 12:00:00
03-feb-2002 11:44:54 03-feb-2002 12:00:00
03-feb-2002 12:44:54 03-feb-2002 17:00:00
03-feb-2002 13:44:54 03-feb-2002 17:00:00
03-feb-2002 14:44:54 03-feb-2002 17:00:00
03-feb-2002 15:44:54 03-feb-2002 17:00:00
03-feb-2002 16:44:54 03-feb-2002 17:00:00
03-feb-2002 17:44:54 04-feb-2002 08:00:00
03-feb-2002 18:44:54 04-feb-2002 08:00:00

30 rows selected.

to see that it returns the data I wanted it to for any hour of the day and
then schedule the job like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> variable j number;
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2    dbms_job.submit( :j, 'null;', sysdate, 'my_next_date(sysdate)' );
  3  end;
  4  /

PL/SQL procedure successfully completed.

Review &
      Followup

      Rating:
      4
  February 02, 2002
Reviewer:
      A reader
Tom,

Thanks for the reply. "dt" is the variable which is used to default to sysdate.
Since "INTERVAL" FIELD is restricted to 200. Else I get another error indicating
the length is exceeded. Also I have been using ''hh24'' format.But I still have
the same error. Anycase, I will create afunction.

Thanks

          Followup:

dt isn't defaulted anywhere.  That was the issue here, dt could not be resolved.
 The job process simply dynamically executes:

 execute immediate 'select ' || interval || ' from dual' into next_date;

your local variables aren't "there".

The function gets around that pesky 200 byte limit easily as well (and in
general makes for infinitely more readable code!)

      Rating:
      4
DBMS_JOB.interval  September 14, 2003

Reviewer:  Martin Koeb  from Austria
Hello Tom

The idea to make a function to get the wanted daily intervals is very useful -
Thanks.

Best regards
Martin

February 03, 2002 Lakshmi Narasimhan -- Thanks for the question
      regarding "PL SQL Tables", version 8.1.7

      You Asked
HI Tom

Using Varrays to Store the large no. of Data and inserting through FORALL has
any performance implication in Database with RBO optimizer?

      and we said...
don't use it to store LARGE.  100's ok, 1,000's -- getting out of control.  Look
at the RAM you are using and the speed you are getting.

Best to keep the flow of data going -- process 100 rows, insert them, process
100 rows, insert them.  It'll keep the log buffer flushing in the background,
reducing your waits.  Don't do a ton of work -- through it at the database -- a
ton more work and so on.  do a little, save a little, do a little, save a
little.
February 03, 2002 Rahul -- Thanks for the question regarding
      "Questions...", version 8.1.7

      You Asked
Hi Tom,

1. How much space we have for insert & update
   if db_block_size is 8192 substracting the block
    overhead & pctfree is 10 and pctused is 80.....

2.  quote from page 94 of your book
    With asynchronous I/O, dbwr gathers up a batch
    of blocks to be written, and gives them to the
    operating system.....

    Tom can you please clarify
    the blocks are actually written to disk by DBWR or OS ???

3. Is it possible if a user tries to update a record that has   already   been
updated    by some other user who hasn't commited to get a message like resource
busy if he    hasn't used the  option of Select ....for UPDATE NOWAIT...

Tom what surprises me is none of the authors of Oracle books have emphasized the
importance of parsing as how it improves the performance....I am going through
Rich Niemic's book on Performance Tuning , which i feel is pretty good  but he
has not covered the way you have explained....

Good job Tom keep it up !!!!!
I am eagerly waiting for your new book ....

Thanx,
Rahul M.

      and we said...
1) you'll have about 8000 bytes after overhead is taken away.  So, about 8000
bytes.

ops$tkyte@ORA815.US.ORACLE.COM> create table t ( a char(2000), b char(2000), c
char(2000), d varchar2(4000) );

Table created.

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> insert into t values ( 'a', 'b', 'c', 'd' );

1 row created.

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> analyze table t compute statistics;

Table analyzed.

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> select BLOCKS, EMPTY_BLOCKS, AVG_SPACE,
CHAIN_CNT, AVG_ROW_LEN
  2    from user_tables
  3   where table_name = 'T';

    BLOCKS EMPTY_BLOCKS  AVG_SPACE  CHAIN_CNT AVG_ROW_LEN
---------- ------------ ---------- ---------- -----------
         1            0       2086          0        6014

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> truncate table t;

Table truncated.

ops$tkyte@ORA815.US.ORACLE.COM> insert into t values ( 'a', 'b', 'c', rpad( '*',
1972, '*' ) );

1 row created.

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> analyze table t compute statistics;

Table analyzed.

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> select BLOCKS, EMPTY_BLOCKS, AVG_SPACE,
CHAIN_CNT, AVG_ROW_LEN
  2    from user_tables
  3   where table_name = 'T';

    BLOCKS EMPTY_BLOCKS  AVG_SPACE  CHAIN_CNT AVG_ROW_LEN
---------- ------------ ---------- ---------- -----------
         1            0        113          0        7987

ops$tkyte@ORA815.US.ORACLE.COM>

2) the OS does all of the real writing, dbwr ASKS the OS to do the write.  The
physical hardware is always abstracted from us.  We use the supplied read/write
routines of the OS.

3) well, if you read my book -- the section transactions -- you'll find out that
this means you are having some infamous LOST UPDATE problems in your
application.  That might be all right but most of the time, this condition
actually means you have a bug in your application logic (if two people try to
update the same row).  The select for update is the way to do this.

February 03, 2002 OCP -- Thanks for the question regarding "Backup
      Questions", version 8.1.6

      You Asked
Hello Tom,

Just wondering

1.What will happen if during a export if discontinued due to power failure will
the .dmp
file contain information if so like what

2.If you are taking a RMAN backup the Process fails abruptly like as said above
what will be the status of the backup file

Thanks in advance

Ocp Guy.

      and we said...
1) it'll be a corrupt, mostly unusable file.  We'll be able to read whatever was
safely put in there but will "crash" at the end.  Its pretty useless.

2) In 8i, you just restart it and it'll do the backup again.  In 9i, it'll pick
up where it left off when you restart it (resumable backups and restores in 9i)
February 03, 2002 Ajay -- Thanks for the question regarding "Select
      max from collection", version 8.1.7.2

      You Asked
Tom,
I have a simple collection defined as such:

create or replace type t_date_a as table of date

Once this collection is loaded, I need to find the max value.  What attribute
name do I need in the block below?

declare
v_date_a t_date_a;
v_date   date;
begin
  v_date_a:=t_date_a();
  v_date_a.extend(10);
  for i in 1..10 loop
   v_date_a(i):=sysdate+i;
  end loop;

  select max(?)
    into v_date
    from table(cast(v_date_a as t_date_a));
end;
/

Thanks for your help.

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2  v_date_a t_date_a;
  3  v_date      date;
  4  begin
  5    v_date_a:=t_date_a();
  6    v_date_a.extend(10);
  7    for i in 1..10 loop
  8     v_date_a(i):=sysdate+i;
  9    end loop;
 10
 11    select max(column_value)
 12      into v_date
 13      from table(cast(v_date_a as t_date_a));
 14  end;
 15  /

PL/SQL procedure successfully completed.

Column_value is the default name given to simple collections of builtin types
like numbers, dates, varchar2's.
Review &
      Followup

      Rating:
      5
  February 03, 2002
Reviewer:
      A reader  from Boston
Thanks, Tom.  I've been struggling with this for a while.  What table should I
have queried to find the attribute name?

          Followup:

It is in Application Developer?s Guide - Object-Relational Features and other
docs.

You could have created a table/view as well to peek:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function f return t_date_a
  2  as
  3  begin
  4          null;
  5  end;
  6  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view v
  2  as
  3  select * from table( cast( f as t_date_a ) );

View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> desc v
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 COLUMN_VALUE                                 DATE

February 03, 2002 Pawan -- Thanks for the question regarding
      "Improve performance while loading data", version 8.1.7

      You Asked
Tom,
    I am presently working on a Data Warehouse project which is using
Informatica for mapping and loading data into an Oracle database (8.1.7 on HP UX
11.0). The data is in flat files and some of the hundreds of tables in the
Warehouse are HUGE (300-500 million records). The designers have not partioned
the base tables into which this data is being loaded. Their contention is that
since we are using SAN ( storage Area Network) that has multiple disks we don't
need to partion the tables. I am not sure if this is right. Also they have
primary keys on the tables. My question is - what is the best way to speed up
the loading process? What init.ora parameters need to be set? If they agree to
partition then whether it is better to partion by range or by hash? Is it
possible to use parallel loading of a table if yes then How?
    When I question I am told that since they are using Informatica we will have
this kind of performance. I don't understand why one would use a 1/2 million
package and have such horrible performance. Please let me know in yr opinion
what is the most optimum method of loading such huge amount of data.

Thanks in advance.

      and we said...
The designers of this system are not database guys are they.  Well, that is a
rhetorical question -- they aren't.

Partitioning provides many things -- partition elimination can make your queries
go faster in a data warehouse.  many times faster.

Partitioning allows for easy aging of data, rolling windows.  Something
virtually every data warehouse does.

Partitioning allows for higher availability when that soft corruption kicks in
and it will.  better to have a single, small partition offline to be rebuilt
then the entire table.

Partitioning eases adminstrative burdens.  It is a fact that performing
operations on small objects is inherently easier, faster and less resource
intensive then performing the same operations on a large object.

Partitioning makes mass updates -- something you might need to do in a data
warehouse, might not -- faster.  You can use parallel DML, but only if you
partition.

And the list goes on.

We've had this "multiple disk" thing from day 1 in databases.  There has ALWAYS
been more then one disk.  Enter raid striping -- a SANS didn't do anything new
here.  Your designers haven't worked on large data warehouses -- or if they
have, they've never used partitioning and don't understand it.

Loading a huge amount of data -- i would be using sqlldr in parallel direct path
mode.  I would be using parallel index builds with no logging.  It would not
take very long at all.

Review &
      Followup

      Rating: 4
Just a quick
      addition, if I may please  February 03, 2002

Reviewer:  Ravi  from USA
Hi Tom, If I may add to what you said:
What about disabling constraints and dropping indexes before loading so much
amount of data?? And, that way, if you have the comfort of rebuilding the index
during database inactive time (like nights), you can reduce time a lot.

And how about altering table to 'NOLOGGING' before doing dataload because we
have the data in ascii files anyways.

Just a quick addition: Parallel DML can also be done on a non-partitioned table,
right?? The only difference is it will add above the HWM. Right? Please correct
me if I'm wrong.

          Followup:

yup -- parallel direct path loads without logging will get you all that as well.
 The indexes will be usuable and have to be rebuilt.

PDML (parallel updates/deletes) need partitioning.  Parallel INSERT INTO SELECT
* FROM does not.

      Rating: 3
Improve
      performance while loading data  February 03, 2002

Reviewer:  Tony  from Australia
Pawan,
I just joined an organisation which is almost having the same env., as you
mentioned in your scenario(Informatica/ Oracle 8.1.6/HP-UX 11.0). This site was
also not using partitioning for their FACT tables. I can really understand the
people's environment.
In order to change the old believes (when partitioning was not available), you
have to convince them, may be by simulating in some test environment. I had to
do the same, once "THE REAL BENEFITS OF PARTITIONING" (as mentioned by Tom) are
proved, the resistance/belief will be change very quickly.
Just to give an idea, with partitioning some of the queries response time has
improved by even 50%, and even loading time reduced by 50%.
Also keep in mind the benefits of Local and Bitmap indexes. All the best!

      Rating: 4
Thanks a
      lot  February 05, 2002
Reviewer:  Pawan
       from USA
Thanks Tom and Ravi,
      You people have vindicated whatever I hve been saying but the million
dollar question should I take the Boss headlong and show her this answer and
pack my bags.

Thanks again

      Rating: 5
Partitioning
      Rules  February 07, 2002
Reviewer:  Johan
      Snyman  from Johannesburg, South Africa
Another HUGE benefit of partitioning is that one can load new data into a table
with the same structure as the partitioned table, but without any indexes.

Then you create all the indexes the partitioned table have on the newly loaded
table, add an empty partition to the partitioned table, swap the newly loaded
table with the empty partition (within a few seconds) and voila - you have added
millions of indexed rows in no (or very little) time at all.

It gives you the best of both worlds : loading without index maintenance AND
only indexing the newly loaded data.

February 03, 2002 Punit -- Thanks for the question regarding
      "Variable Number of Parameters", version 8.1.7

      You Asked
Can U Please explain me how can I create a procedure or function which can
accept a variable number of parameter values. For example, How can i create a
simple function ADD() which may accept any number of values and return me the
sum. Oracle has in-built functions such as Greatest, Least, etc. which are
capable of accepting any number of parameters - Is that possible for a developer
to create such a function ?

Thanks for ur help

      and we said...
Like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace type numArray as table of
number
  2  /

Type created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function addemup( p_data in
numArray ) return number
  2  as
  3          l_total number;
  4  begin
  5          for i in 1 .. p_data.count
  6          loop
  7                  l_total := nvl(l_total,0) + nvl(p_data(i),0);
  8          end loop;
  9          return l_total;
 10  end;
 11  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select addemup( numArray( 1, 2, 3, 4, 5 ) )
  2    from dual;

ADDEMUP(NUMARRAY(1,2,3,4,5))
----------------------------
                          15

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select addemup( numArray( 1, 2, 3 ) )
  2    from dual;

ADDEMUP(NUMARRAY(1,2,3))
------------------------
                       6

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select addemup( cast( multiset( select
user_id from all_users ) as numArray ) )
  2    from dual;

ADDEMUP(CAST(MULTISET(SELECTUSER_IDFROMALL_USERS)ASNUMARRAY))
-------------------------------------------------------------
                                                         2138

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select sum(user_id) from all_users;

SUM(USER_ID)
------------
        2138

Review &
      Followup

      Rating:
      4
NumArray  February 04, 2002
Reviewer:
      JG  from ndia
Excellet example as always

      Rating: 5
Simply
      Excellent  February 05, 2002
Reviewer:
      shankar  from India
Your code very useful besides being simple.Thanks

      Rating:
      5
  February 05, 2002
Reviewer:  Oleksandr
      Alesinskyy  from Bad Soden am Taunus, Hessen Germany

February 04, 2002 Mahendra -- Thanks for the question regarding
      "Some Questions", version 9.0.1

      You Asked
Dear Tom,

QUESTION - I
-------------------------------------------------------------------
I have a storage seems below.

SR_NO OP_BAL CL_BAL
----- ------ ------
    1    100     99
    2     99     95
    3     95     93

Here you can observe that closing balance of 1'st record must be opening balance
of 2'nd,
and same the closing balance of 2'nd must match the opening balance of 3'rd.

Now I would like to control this chain at database level through Database
Triggers, so any
updation from any tools (SQL*Plus/ FORMS) couldn't disturb this chain.

What workaround is required to achieve this spec?

According to me it should be like.

1) When we update the CL_BAL of any record, we have to log this change into some
intermediate
   storage and remove this logged information in 2'nd step.

2) When we update the OP_BAL of any record, it will pickup the previous record's
CL_BAL and
   perform necessary checks. Here we have to remove the logged information if it
is there in
   the intermediate storage.

3) Reviewing the information gthered in intermediate strorage at certain time
intervals.

Now I am looking for a triggering funcationality like deferred constraints
(Checking while
Commit). If it is possible, we can remove the intermediate storage and the most
importantly,
total control could be at database level.

QUESTION-II
-------------------------------------------------------------------
SQLWKS> create table t1
    2> ( f1 number,
    3>   f2 number
    4> )
    5> /
Statement processed.

SQLWKS> insert into t2 values (1,1)
    2> /
1 row processed.

SQLWKS> commit
Statement processed.
SQLWKS> /

SQLWKS> create or replace trigger trg_bi_t1
    2> before insert on t1
    3> for each row
    4> when (new.f1=1)
    5> begin
    6>   insert into t1 values(3,3);
    7>   -- inserting into same table(t1)
    8> end;
    9> /
Statement processed.

SQLWKS> insert into t1 values (1,1)
    2> /
1 row processed.

SQLWKS> insert into t1 select * from t2
    2> /
ORA-04091: table CNG.T1 is mutating, trigger/function may not see it
ORA-06512: at "CNG.TRG_BI_T1", line 2
ORA-04088: error during execution of trigger 'CNG.TRG_BI_T1'

SQLWKS> select * from t2
    2>
F1         F2
---------- ----------
        1          1

Here table(T2) contains only one row.

I would like to know why "insert into t1 values (1,1)" is working but,
"insert into t1 select * from t2" is not eventhough T2 contains same data.

Waiting for your valuable comments.

Thanks in advance.

Mahendra.

      and we said...
question to you --

if there is a rule that says the closing must equal the next opening -- why
store the closing at all (or why store the opening).  It sounds like a derived
field, make it so.  Why are you trying to store this totally redundant data that
we can retrieve for you quite simply?

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( sr_no int, op_bal int )
  2  /

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1, 100 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 2,  99 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 3,  95 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 4,  93 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select sr_no, op_bal, lead(op_bal) over (
order by sr_no )
  2    from t;

     SR_NO     OP_BAL LEAD(OP_BAL)OVER(ORDERBYSR_NO)
---------- ---------- ------------------------------
         1        100                             99
         2         99                             95
         3         95                             93
         4         93

ops$tkyte@ORA817DEV.US.ORACLE.COM>

As for the mutating table error, you get that with the multi-row insert (all
insert into t select's are possibly multi-rows) because the table will be
mutating.  In the special case of the singleton insert, we know that only one
row is going in, so we can give you a consistent view of it.

See

http://osi.oracle.com/~tkyte/Mutate/index.html
for the general purpose way to avoid this HOWEVER -- i would recommend you
rethink the structures here first (and avoid having to implement this "rule"
which is easier done on select)
Review &
      Followup

      Rating:
      4
error  February 03, 2002
Reviewer:
      Ash  from AUS
I am getting error

SQL> select sr_no, op_bal, lead(op_bal) over (
  2  order by sr_no )
  3  ;
select sr_no, op_bal, lead(op_bal) over (
*
ERROR at line 1:
ORA-00439: feature not enabled: OLAP Window Functions

This is on version 8.1.7.

Do I need to run any sql script for anabling this feature.
Thanks in advance.

          Followup:

You need the Enterprise Edition of Oracle for this.

      Rating: 5
error - ASH
        February 03, 2002
Reviewer:  jim
       from Australia
Ash,
    Do you have the Enterprise Edition?
I know there was an error in the 8.1.6 doco
which said analytical functions were available in the
standard edition but they are not. It's Enterprise
Edition only.

Look for the banner which gets displayed when you
log onto SQL*Plus, you will see the edition displayed.

My banner follows as an example

Regards

Jim
==============================================

SQL*Plus: Release 8.1.7.0.0 - Production on Mon Feb 4 09:51:30 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected to:
Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production
With the Partitioning option
JServer Release 8.1.7.0.0 - Production

      Rating: 2
See
      9i  February 05, 2002
Reviewer:  Oleksandr
      Alesinskyy  from Bad Soden am Taunus, Hessen Germany
I wiah to note, that in 9i analitycal functions are available in Standard
Edition

      Rating: 2
Please excuse
      me  February 05, 2002
Reviewer:  Mahendra

Dear Tom,

Thanks for your fast response.

Your answer is quite logical to my question, but indeed I have represented my
question wrong way to make it sort. Please excuse me. Now let me define the
requirement once again.

Test Case is given below.

CNG@ORA9>drop table MST
  2  /

Table dropped.

CNG@ORA9>create table MST
  2  ( IDN    NUMBER(10)   NOT NULL,
  3    PKT_NO NUMBER(8)    NOT NULL,
  4    WGHT   NUMBER(10,1) NOT NULL,
  5    DTE    DATE         NOT NULL,
  6    CONSTRAINT MST_PK PRIMARY KEY (IDN)
  7  )
  8  /

Table created.

CNG@ORA9>drop table DET
  2  /

Table dropped.

CNG@ORA9>create table DET
  2  ( IDN       NUMBER(10)   NOT NULL,
  3    PKT_NO    NUMBER(8)    NOT NULL,
  4    NW_PKT_NO NUMBER(8)    NOT NULL,
  5    WGHT      NUMBER(10,1) NOT NULL,
  6    DTE       DATE         NOT NULL,
  7    CONSTRAINT DET_PK PRIMARY KEY (IDN)
  8  )
  9  /

Table created.

CNG@ORA9>drop sequence my_seq
  2  /

Sequence dropped.

CNG@ORA9>create sequence my_seq start with 1 increment by 1
  2  /

Sequence created.

CNG@ORA9>insert into mst
  2         (idn, pkt_no, wght, dte)
  3  values (my_seq.nextval, 1, 100, sysdate);

1 row created.

CNG@ORA9>insert into det
  2         (idn, pkt_no, nw_pkt_no, wght, dte)
  3  values (my_seq.nextval, 1, 2, 80, sysdate);

1 row created.

CNG@ORA9>insert into det
  2         (idn, pkt_no, nw_pkt_no, wght, dte)
  3  values (my_seq.nextval, 1, 3, 20, sysdate);

1 row created.

CNG@ORA9>insert into mst
  2         (idn, pkt_no, wght, dte)
  3  values (my_seq.nextval, 2, 80, sysdate);

1 row created.

CNG@ORA9>insert into det
  2         (idn, pkt_no, nw_pkt_no, wght, dte)
  3  values (my_seq.nextval, 2, 4, 75, sysdate);

1 row created.

CNG@ORA9>insert into det
  2         (idn, pkt_no, nw_pkt_no, wght, dte)
  3  values (my_seq.nextval, 2, 5, 5, sysdate);

1 row created.

CNG@ORA9>break on mst_idn on pkt_no on mst_wght
CNG@ORA9>select a.idn mst_idn, a.pkt_no, a.wght mst_wght ,
  2         b.idn det_idn, b.nw_pkt_no, b.wght det_wght
  3    from mst a, det b
  4   where a.pkt_no = b.pkt_no;

  MST_IDN    PKT_NO  MST_WGHT   DET_IDN NW_PKT_NO  DET_WGHT
--------- --------- --------- --------- --------- ---------
        1         1       100         2         2        80
                                      3         3        20
        4         2        80         5         4        75
                                      6         5         5
My Requirement
--------------
Now here I would like to define rule that; DET_WGHT(DET.WGHT) of PKT_NO(2)=80
must
match MST_WGHT(MST.WGHT) of pkt_no(2)=80 in any dam situation.

For that I have coded trigger on MST :

create or replace trigger mst_t1
Before insert or update of wght on mst
   for each row
  when (Nvl(old.wght,0) != Nvl(new.wght,0))
Declare
  mv_wght     number;
Begin
  --
  Select a.wght
    Into Mv_wght
    From det a
   Where a.nw_pkt_no = :new.pkt_No
     And a.idn       = ( Select max(b.idn)
                           From det b
                          Where b.nw_pkt_no = :new.pkt_No
                            And b.idn       < :new.idn );
  --
  If Nvl(:New.Wght,0) != Nvl(Mv_Wght,0) Then
    RAISE_APPLICATION_ERROR(-20001, 'WGHT doesn''t match with
previous:'||:new.pkt_no||' DET:'||Mv_Wght||' MST:'||:New.WGHT);
  End If;
  --
Exception
  When No_Data_Found Then
       Null;
End;

Now if i am trying to update MST.WGHT with some value which doesn't match
previous DET.WGHT, It
will raise an error. i.e.,

CNG@ORA9>update mst set wght=81
  2   where idn = 4;
update mst set wght=81
                   *
ERROR at line 1:
ORA-20001: WGHT doesn't match with previous:2 DET:80 MST:81
ORA-06512: at "CNG.MST_T1", line 15
ORA-04088: error during execution of trigger 'CNG.MST_T1'

One way control is achieved by this coding.

Now what I am looking for is; when I update DET.WGHT of PKT_NO(1) from 80 to 81,
Application Code should force the updating application(FORMS/SQL*PLUS) to update
MST.WGHT of PKT_NO(2) too from 80 to 81.

February 04, 2002 -- Thanks for the question regarding "shuffling
      of # or elements before appearing on screen.", version 8.1.7

      You Asked
hi Tom,

is there a method oracle could pick an element after shuffling for a specified
time period, like we shuffle cards.
 i'm creatng a quiz containing say 1000 questions in  dbase.when one clicks
"pick" on form, oracle first shuffles these 1000 questions(may be by ques#) for
1 secs(or any specified time) within itself and bring up one random pick, so
that one could answer the question, and go for next the same way.
 i first have gone through the archives, tried myself,  but no way.

      and we said...
use random:

scott@ORA817DEV.US.ORACLE.COM> select *
  2    from ( select ename, dbms_random.random from emp order by 2 )
  3   where rownum < 5;

ENAME          RANDOM
---------- ----------
TURNER     -992702630
ADAMS        -4631844
KING         59276727
MARTIN      575438979

scott@ORA817DEV.US.ORACLE.COM> /

ENAME          RANDOM
---------- ----------
ALLEN      -1.388E+09
MILLER     -1.179E+09
TURNER     -1.155E+09
SMITH      -441261935

scott@ORA817DEV.US.ORACLE.COM> /

ENAME          RANDOM
---------- ----------
FORD       -1.767E+09
KING       -1.723E+09
MARTIN     -1.590E+09
SCOTT      -490255047

Review &
      Followup

      Rating: 2
how to do this
      on release 8.0.4  February 04, 2002
Reviewer:
      abeginner2001  from asia
hello tom ,
i've been doing almost the same type of project, but on psoracle rel 8.0.4. how
should i go about .

          Followup:

you can use my random package:

http://osi.oracle.com/~tkyte/Misc/Random.html
and issue:

select ename, random.rand from emp order by 2;

and just fetch the first N rows that interest you.

      Rating: 1
did'nt actually
      get the result  February 04, 2002
Reviewer:
      oralearner  from us
hii tom,
i did'nt uderstand the column random and its contents.
can u explain, how does this # referes to ename.

          Followup:

ummm, search for dbms_random here or read the supplied packages guide.

Its a random number generator.  Order by random, take the first 4 and you get a
random 4 rows.  QED.

February 04, 2002 Brian -- Thanks for the question regarding
      "listing databases", version

      You Asked
Is there any way to retrieve the names of all the
Oracle databases on a specific server?  For instance, server
Oracle1 contains the databases OraSvr and UsgSvr.  Is there any
functionality to return OraSvr and UsgSvr when inputting Oracle1

(kind of like querying SELECT name FROM master.dbo.sysdatabases
in SQL Server)?

      and we said...
Actually --- in SQL Server there is one physical database with
many logical 'databases' inside (you only run 1 instance of
SQLServer).  In Oracle -- we typically recommend you do the
same.  You would not have >1 database on a server and each
database would have many schemas and tablespaces (our logical
equivalent of their 'database' in a database).

Given that you have >1 database on the server AND these
databases really are not connected to each other (unless you
connect them) -- there is nothing in the database per say that
will tell you about the other instances running (they are all
independent of eachother, unlike the SQLServer database where
they are all running in the same server really).

You can use database links to make any Oracle database aware of
any other Oracle database.  You would have to register each
instance with every other instance and then a query on
ALL_DB_LINKS would show you.  This would also give you the
ability to connect to these other databases without having to
log out and log in again as the database link will allow you to
perform cross instance, distributed query and updates (but not
DDL in the remote instances)

Review &
      Followup

      Rating: 5
My search for
      the holy grail has ended.  December 05, 2001

Reviewer:  Duane  from Manchester, New Hampshire
      (USA)
I scoured the ends of the internet looking.. no hoping someone would be able to
answer this question.  I have 6 open Oracle books and have pulled almost all of
my hair out trying to figure this out.  This is a very clear and informative
answer.  The site is easy to use and, as I have found, very useful.  I will
definitely be back if I have any other questions.

      Rating: 5
Some
      clarifications please  February 04, 2002
Reviewer:
      Yogeeraj  from Mauritius
Hello

Thank you to guide me to a document which explains me how to create 2 databases
on the same server. Each working independently.

I want to know all technical aspects about configuration of the listener, how to
start them, etc

thank you very much in advance for your precious time and guidance

Best Regards
Yogeeraj

          Followup:

You would read the OS admin guide for your platform as it varies (NT very
different from Unix).

You would read the Network admin guide for the listener.  Start here:

http://technet.oracle.com/docs/content.html

      Rating: 3
Use oratab
      file  February 04, 2002
Reviewer:  Rob
      Balter  from Pittsburgh, PA
If using unix check the file /etc/oratab. This file is
used to list all instances and their associated $ORACLE_HOME
dirs. It is used by oraenv to set env variables.  It is also
used by dbstart and dbshut scripts. The DBA should update
this file if a new instance is created on the server though
it is not a necessity.

      Rating:
      3
  February 05, 2002
Reviewer:
      A reader
I know for linux it is /etc/oratab.  For Solaris, it is in /var/opt/oracle.


Subject | Article | Last Updated (East coast time)
select into to an array in pl/sql | article only | Feb 05, 2002 11:48 am
Inconsistency for naming functions | article only | Feb 05, 2002 06:37 pm
REBUILDING INDEXES: clustering_factor | article only | Feb 06, 2002 08:12 am
RBO Tips | article only | Feb 06, 2002 08:15 am
Cursors | article only | Feb 08, 2002 10:01 am
Creating a view | article only | Feb 09, 2002 11:45 am
running sqlldr from plsql | article only | Feb 09, 2002 11:48 am
ID vs natural Key | article only | Feb 11, 2002 09:07 am
create index | article only | Feb 11, 2002 12:41 pm
help on using your site | article only | Feb 11, 2002 12:43 pm
RBOVs CBO | article only | Feb 11, 2002 03:31 pm
Oracle Row-Level Security | article only | Feb 11, 2002 04:46 pm
row(s) 1 - 12 of 12
February 05, 2002 -- Thanks for the question regarding "select into to an array in pl/sql", version 8.1.7.2
You Asked
Hi Tom

Is it possible select into an array instead of a variable? Have been looking the
pl/sql guide but without luck

Thanks
and we said...
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a77069/04_colls.htm#28329
....
Review & Followup

      Rating: 4
sorry forgot to
      ask  February 04, 2002
Reviewer:
      A reader
if it´s possible to reference these arrays afterwards, seems not possible with
bulk collect

          Followup:

That does not make sense to me.  What do you mean "reference these arrays
afterwards".  of course you can -- else what good would the bulk collect be?
give me an example of what you mean.

      Rating:
      4
  February 04, 2002
Reviewer:
      A reader
for example

select ename
into bulk collect .........
from emp

then reference each record for updates

          Followup:

ok, select rowid, ename int bulk collect and then update .... where rowid =
rowid(i);

or use the primary key of the table.

They are arrays -- not a cursor!

      Rating: 3
Loop or
      FORALL  February 05, 2002
Reviewer:  AB
       from UK
"A reader"

What you are looking for is either a loop through the array or the use of
FORALL. Eg.

FOR i IN myArray.FIRST .. myArray.LAST LOOP
   DBMS_OUTPUT.PUT_LINE(myArray(i));
END LOOP;

or

FORALL i IN myArray.FIRST .. myArray.LAST
   UPDATE table_x
   SET    column_x = value
   WHERE  column_y = myArray(i);

          Followup:

good point, i didn't think of it that way -- perhaps they are asking how to do
an array update.  forall would be the answer.

      Rating: 3
Seaching string
      in the PL/SQL table  February 05, 2002
Reviewer:
      siva  from GERMANY
Hello TOM,
     I stored set of string values in Pl/Sql table. I want to search the given
string in this list. Is there any way to do it with PL/SQL statements?.
  I can achive this using java stored procedure(Vectors) or using some dll
(Create library ). But i have to use only pl/sql statements.
Thanks in advance

regards,
siva

          Followup:

ops$tkyte@ORA8I.WORLD> create type myArrayType as table of varchar2(30)
  2  /
Type created.

ops$tkyte@ORA8I.WORLD> declare
  2          data myArrayType := myArrayType( 'How', 'Now', 'Brown', 'Cow',
'Hello', 'World' );
  3  begin
  4          for x in ( select column_value
  5                                   from TABLE( cast ( data as myArrayType ) )
  6                                  where column_value like '%llo' )
  7          loop
  8                  dbms_output.put_line( x.column_value );
  9          end loop;
 10  end;
 11  /
Hello

PL/SQL procedure successfully completed.

ops$tkyte@ORA8I.WORLD>

is the easiest way -- it must be a SQL type -- created at the SQL level as
above, not in a package spec.

Else, just loop through it (thats what the vector does) and compare.

      Rating:
      4
Thanks  February 06, 2002
Reviewer:
      sivababu  from India
Hello TOM,
   I will use Loop as u said. This is the easiest way and simplify the coding.
  Thanks very much

regards,
sivababu

February 05, 2002 Pichaimani -- Thanks for the question regarding
      "Inconsistency for naming functions", version 8.0.6

      You Asked
Hi Tom,
   I would like to what is the maximum length we can have for a
function/procedure name. I found the object_name in dba_objects,dba_source allow
upto 128 where as user_source allows only upto a maximum of 30.

      and we said...
30 is the max length for an identifier (any identifier).

A database link name is allowed to be upto 128 characters, hence the descrepency.
Review &
      Followup

      Rating:
      4
  August 19, 2001
Reviewer:  Pichaimani
      Balasubramanian  from San Jose, CA

      Rating: 2
Maximum length
      of an object name  February 05, 2002
Reviewer:
      Krish Ullur  from Nashville, TN, USA
Database Oracle 9.0.1: Cwmlite.sql - for olap services

Sqls like the following error out.

CREATE PUBLIC synonym all_olap_os_osa_privileges FOR os_osa_privileges;

CREATE OR REPLACE VIEW dba$olap_catalog_entity_uses AS ...

ALTER TABLE CWM$CLASSIFICATIONENTRY
  ADD (CONSTRAINT CWM$CLASSIFICATIONENTRY_PK PRIMARY KEY
  (IRID))

Error reported:

*
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-01401: inserted value too large for column

If I shorten the name, it works fine.
(e.g: all_olap_os_osa_privilege instead of all_olap_os_osa_privileges)

But cannot afford to change them as some dependency could get broken and also,
this is Oracle supplied script.

Is there a workaround for my problem?
(other than tampering SYN$ - well, just kidding)

Krish

P.S: Sorry for digging up an old one from archives.

          Followup:

All of those are less then 30 characters.  They are all valid.  There is
something wrong with your database, perhaps it was not upgraded properly -- or
something.  You'll want to contact support as your installation is "broken"

ops$tkyte@ORA9I.WORLD> CREATE PUBLIC synonym all_olap_os_osa_privileges FOR
os_osa_privileges;

Synonym created.

ops$tkyte@ORA9I.WORLD> CREATE OR REPLACE VIEW dba$olap_catalog_entity_uses AS
select * from dual;

View created.

those work just fine.

February 06, 2002 James -- Thanks for the question regarding
      "REBUILDING INDEXES: clustering_factor", version 8.0.3.0.0

      You Asked
After computing statistics on an index, the "clustering_factor"
is available when querying DBA_INDEXES.  A book I have
states: "If the clustering factor is greater than the number of
blocks in the index, then the index is out of balance due to a
large volume of insert and delete operations."  (High
Performance Oracle8 Tuning, Burleson, p. 316, if you have it.)

First, can you give a better explanation of what the
clustering_factor is?  Then, which blocks do you think they
mean: index leaf blocks or index physical blocks?

I have another (same subject).  The book goes on to say "If the
clustering factor is more than 50 percent of the number of rows
in the table that it is indexing, you should consider
(rebuilding) the index."  Here, are they talking about num_rows
from DBA_INDEXES or the actual (counted) number of rows in the
table?

One more.  It says "If the value for the clustering factor
approaches the number of blocks in the base table, then the
index is said to be clustered.  If the clustering factor is
greater than the number of blocks in the base table and
approaches the number of rows in the base table, then the index
is unclustered."  OK: so which one is good and when do we
consider rebuilding?

Thanks for helping.

      and we said...
From the Reference manual:

<quote>
CLUSTERING_FACTOR:

Indicates the amount of order of the rows in the table based
on the values of the index.

o If the value is near the number of blocks, then the table
is very well ordered. In this case, the index entries in a
single leaf block tend to point to rows in the same data
blocks.

o If the value is near the number of rows, then the table
is very randomly ordered. In this case, it is unlikely
that index entries in the same leaf block point to rows
in the same data blocks.
</quote>

I do not believe the clustering factor they talk of is the
clustering_factor in the dba-indexes but rather some metric they
have made up.  Not having the book referred to -- I cannot
confirm this.

If the clustering factor they are talking of is the
clustering_factor from dba_indexes, then I do not see how they
can come to the conclusion that the "index is out of balance"
from that.  It is a factor that looks at the index entries on
blocks and looks at the rowids they point to.  It is telling us
how close together the data in the table these index entries
point to are (how sorted the table is with respect to this
index).

Let's say you have a really small block size and only 3 index
entries fit on a block.  On a given block -- you have the values
1, 2, and 3.  The value 1 points to a block in the first extent
of the TABLE.  The value 2 points to a block in the second
extent of the TABLE.  The value 3 points to a block in the third
extent of the table.  The clustering factor, if all other blocks
are like this block, would be near the number of rows in the
table -- since none of the index entries on this block point to
rows that are physically close to eachother in the table.

If they go on to say -- rebuild the index based on this value --
something is wrong in their thinking if you ask me.  No matter
how many times I rebuild that index -- the values 1, 2 and 3 are
always going to end up in the same index block and they will
still point to blocks that are very far away from eachother.
The clustering factor will not be materially affected by an
index rebuild (it might get marginally smaller as the index is
compacted -- it might get worse as well since more index entries
might fit into a block after the rebuild).  The
clustering_factor is a measure of how physically ordered the
table is with respect to this index!  Not how out of balance an
index is.

Here is an example.  I have a table EMP with 21,000+ rows in it.
 I built this table via:

create table emp
as
select object_id empno, owner||'.'||object_name ename,
       object_type city, mod(object_id,100) deptno,
       status division
  from all_objects
/

create unique index emp1 on emp(empno);
create index emp2 on emp(ename);
create index emp3 on emp(deptno);

Now, lets look at the indexes:

ops$tkyte@DEV816> analyze index emp1 compute statistics;
ops$tkyte@DEV816> analyze index emp2 compute statistics;
ops$tkyte@DEV816> analyze index emp3 compute statistics;

ops$tkyte@DEV816> select index_name, clustering_factor, num_rows

  2  from user_indexes where index_name like 'EMP_';

Index
Name                           CLUSTERING_FACTOR   NUM_ROWS
------------------------------ ----------------- ----------
EMP1                                       19632      21442
EMP2                                         186      21442
EMP3                                       12000      21442

ops$tkyte@DEV816> analyze table emp compute statistics;
ops$tkyte@DEV816> select blocks from user_tables where
                                      table_name = 'EMP';

    BLOCKS
----------
       171

No matter what index I rebuild, or how many ways I rebuild it,
the results of the above will be the same.  What I can "glean"
from the information above is that:

o the data in the TABLE emp appears to be sorted physically by
the columns in the index EMP2 since the clustering factor of
EMP2 is very near the number of blocks.  EMP2 is the index on
OWNER||'.'||OBJECT_NAME from ALL_OBJECTS.  This makes sense if
you run the all_objects view, data tends to come out sorted by
owner, object_name due to the way the view is processed.

o the data in the TABLE emp is very randomly organized with
respect to EMP1 -- the index on empno.  The object_ids are
coming out of that table very randomly.  So, the TABLE data is
not sorted by empno (object_id).

o the data in the TABLE emp is sort of organized physically in
order by DEPTNO.  Its sort of in the middle.

ops$tkyte@DEV816> alter index emp2 rebuild;
ops$tkyte@DEV816> analyze index emp2 compute statistics;
ops$tkyte@DEV816> select index_name, clustering_factor, num_rows
from
  2  user_indexes where index_name like 'EMP_';

Index
Name                           CLUSTERING_FACTOR   NUM_ROWS
------------------------------ ----------------- ----------
EMP1                                       19632      21442
EMP2                                         186      21442
EMP3                                       12000      21442
ops$tkyte@DEV816>

The numbers will always come out the same.

The clustering_factor can only be used to guess if using the
index to read the table lots will cause the table to be read
almost in order or not.  For example:

select * from emp order by ename;

if I use the index emp2 to process that -- the data in EMP
pointed to index entries in the first index leaf block I process
will mostly be on the same block(s).  I will do a physical read
of the table and then many logical IO's of that block before
moving onto the next block.  It will be fairly efficient to
range scan large ranges with TABLE ACCESS BY ROWIDS using emp2.

On the other hand, if I have:

select * from emp order by object_id;

The index EMP1 is not very good for reading that table.  The
first entry in the index will point to block 1, the second to
block 100, the third to block 500 and so on -- to process the N
rows on the first index leaf block, I'll have to do N physical
IO's to get the blocks from disk probably.  Not only that but
they will probably get flushed from the cache since I won't need
them again right away and I'll have to re-read them once again
later.  It will be horribly inefficient to range scan LARGE
ranges with TABLE ACCESS BY ROWIDS using EMP1.

I myself do not really believe in the rule of "rebuild your
indexes".  I like the alter index rebuild command to MOVE
indexes, but I do not in practice rebulid indexes.
Review &
      Followup

      Rating: 4
Is fair to say
      the smaller of clustering factor is the better
      index??  August 31, 2001
Reviewer:  Mike
       from GA, USA

          Followup:

Well, assuming that you have more rows than blocks (eg: rowsize < blocksize) --
MAYBE.  If you have less rows then blocks (rowsize > blocksize) -- not so.

This factor relates to how efficient this index is for "large range scans".  If
the number of rows to be returned via an index range is small -- the cluster
factor does not come into play.  If the number of rows to be returned via an
index range scan is deemed LARGE -- the cluster factor will come into play.  The
optimizer might start choosing a full scan over an index range scan for an index
that does not have a good cluster factor (due to the fact that the resulting
random IO to many single blocks here and there would be slower then just reading
the table in the first place).

So, the cluster factor is used to determine  whether an index is a good
candidate for LARGE range scans.  For normal index usage (get me my small set of
rows) it doesn't come into play as much.

      Rating: 5
Which view can
      show the rowsize?  September 01, 2001
Reviewer:
      Mike  from GA, USA

      Rating: 4
rowsize is
      AVG_ROW_LEN in DBA_TABLES?  September 01, 2001

Reviewer:  Mike  from GA, USA

          Followup:

Thats the average row length in bytes, yes.  Only valid after an ANALYZE.

      Rating: 4
Very
      interesting  October 20, 2001
Reviewer:  Doug
      Cowles  from CT, USA
I've alwasy wondered how indexes can be rebuilt and get larger clustering
factors.

      Rating: 4
REBUILDING
      INDEXES: clustering_factor  February 06, 2002

Reviewer:  Christophe Tsobgny  from France

Hello

As it is almost impossible to ask tom a question being
in Europe and having a different time zone,
i did a search on indexes and improving query
response time.
The article was very useful and helped confirm
my understanding of the clustering factor.
But will it really improve query response time
if one has a good clustering factor ?
i have one query that has a good execution plan
but the response time is always bad.

kind regards

          Followup:

Well, its not a factor of being in Europe or India or anywhere really (i've run
the stats -- I've a pretty even distribution of questions from everywhere).  Its
more related to the fact that about 4,000 distinct people come here everyday and
on a good day I'll take as many as 40 questions....

If you are interested in examples showing the differences with the clustering
factor -- i have some in my book.  However -- i would start more simply with
tkprof and sql_trace and see what the query is doing, see what the waits (if any
are).  Chapter 10 of my book would take you through that in detail,

http://asktom.oracle.com/~tkyte/tkprof.html
would get you started as well

      Rating: 4
Mark of a
      genius  February 06, 2002
Reviewer:  Pawan
       from USA
It was very confusing for me to understand the clustering factor but Tom made it
so simple.

Thanks

      Rating: 4
Better for data
      warehousing than OLTP  February 06, 2002
Reviewer:
      Dave
Here's my two-cents.

I always think of reducing the clustering factor by physical row ordering as a
quick-and-dirty partitioning feature, or at least a complement to partitioning.
Here's a practical example ...

Say you are loading up a large fact table in a data warehouse with 10million
rows per daily load. It would be common practice to use rolling partitions to
provide one partition per day, with local indexing. If users commonly access the
data according to a particular column -- let's say it is by zip code -- then
physically ordering your daily data load by zip code gives a low clustering
factor on the zip_code index and, as Tom says,  encourages large index range
scans. It would probably be impractical to partition by day and then by zip
code, but you get some of the same perfomance enhancement as you might do
through partition elimination. A major benefit is that you can go further and
choose, for example, four attributes that users commonly access the data by, and
physically order the rows by each (ie. insert into ... select ... order by
attribute_1, attribute_2, attribute_3, attribute_4;).

I've reduced the number of blocks read by common queries by a factor of 25 by
doing this, and reduced device I/O measured at the O/S level by a factor of 8.
Not to be sniffed at.

Of course you get into a case of diminishing returns, but it's more true in DW
than in OLTP that you insert once, select many times, and an extra bit of work
in data load can go a long way towards getting response time down.

February 06, 2002 Lakshmi Narasimhan -- Thanks for the question
      regarding "RBO Tips", version 8.1.7

      You Asked
Hi Tom

Sorry i should not disturb you for these kind of anaswers still i am not getting
any help from other resources.

We are Using RBO as our Optimizer and due to implementation problem  we could
not move out of this to change to CBO.

Everybody knows that Index should be proper and  Query should contain the Index
column for Invoking it.  Is there any other Query Optimization methodology to be
used to get the best performance in RBO.  If you can suggest some good Website
that also helpful for me.

thank you once again and sorry for the inconvenience.

      and we said...
read

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76992/optimops.htm#38864
it tells you how the RBO chooses a plan and it is very predicable.

It is unfortunate that for whatever reason you cannot use the CBO.  You are
missing out on TONS of features -- things you paid for but will not be able to
use....

Review &
      Followup

      Rating: 4
But
      ....  February 05, 2002
Reviewer:  Lakshmi
      Narasimhan R  from Dubai UAE
Hi Tom

Many thanks to you for the Answers.  I am fully agreeing with you that we are
missing lot by not using CBO.  But the primary concern is being a Big product,
used for last couple of years right from 7.X versions, all the queries designed
inside all the packages are for satisfying RBO optimization.  If we change the
Optimizer will it be a big cost.  Or is it really does not need much alternation
except analysing the tables.  Can you suggest me on this issue if u feel ur time
is defintely not wasted.

thanks in advance

          Followup:

try it and see -- take a test machine and test it out.  That is what I would do.

      Rating: 3
what about OLTP
      databases  February 05, 2002
Reviewer:  A
      Reader  from USA
Expanding on your answer, what about the case when an OLTP database is huge and
CBO is used? The tables and indexes are analyzed estimate 10% or so. Over a
little period of time, the statistics all become stale (it's OLTP) and the
optimizer plan gets all wrong. For example we have a few tables of 10GB each and
after a few days the optimizer goes for full table scan leaving the index access
path. In that case we are forced to use RBO. Any suggestions on that how we can
perform better.

          Followup:

analyze more frequently?

Use the alter table monitoring command and gather stats on stale objects every
day.

      Rating: 3
Always CBO
      ?  February 05, 2002
Reviewer:  Harri
       from Finland
Is there any situation when RBO is better than CBO? (CBO with analyzed objects
of course)

Let's say we migrate 6 or some early 7 version db to 8i without any
implementations of new features. If sql-statements are tested and optimized with
RBO, is it still always better(or same at least) to analyze tables+indexes and
use CBO?

          Followup:

Well, you need to test the entire application anyway during an upgrade so I
would say "yes", use the CBO.  It will enable you to use new features, your
queries may run faster, you'll be positioned for the future.

      Rating: 4
Maybe another
      option ...  February 05, 2002
Reviewer:
      Dave
You might also look at the OPTIMIZER_INDEX_COST_ADJ initialization parameter to
promote the use of indexes. I suppose you would get a "more RBO" execution plan
than the CBO might choose

      Rating: 4
RBO
      Tips  February 06, 2002
Reviewer:  Bharath
       from Madras
Hi Tom,
       We are using CBO and the DB is analyzed every week. After last weeks
analyze the reports have started to run slower. How to avoid this.

Thanks for your reply..

          Followup:

Perhaps you missed some tables, perhaps some init parameter changed.  Too little
information contained here to diagnose.

Use tools like statspack on a regular basis so you can see your top sql, how
many reads and such they do, what waits are your big ones so you have a history
to compare to.  So you can say "hey look at that -- that query never did X
reads/execution before" or "wow, our waits on XXXXX have gone way up, wonder
why".

Right now, you'll be starting from scratch probably, with no history to compare
to.  At least statspack will give you something to start looking at (the top
SQL's and such)

February 08, 2002 Srimal -- Thanks for the question regarding
      "Cursors", version 8.0.6

      You Asked
Hello Mr Tom,

Sorry to bother you... but I think I have the problem understood and I think I
can find a resolution. The line which says 'where rownum = lcounter' is not
producing any rows for rownum > 1
for example, if I say

create table a (idn_val number, nme_name varchar2(10));
/
insert into a (idn_val,nme_name) values (1,'John')
/
insert into a (idn_val,nme_name) values (2,'Doe')
/

declare
lcounter number;
ltest varchar2(1000);
begin
for lcounter in 1..2 loop
select nme_name
into ltest
from a
where rownum = lcounter;
dbms_output.put_line(ltest);
end loop;
end;

the output should be 'John', 'Doe' but I get

John
declare
*
ERROR at line 1:
ORA-01403: no data found
ORA-06512: at line 6

It is not able to find the data for second row and I am not using exceptions to
handle that. I need to refrain from using rownum directly in that aspect I
guess... The OleDB driver gets a different error message!...

Thank you once again. I will try to write the code to correct the above problem.

Srimal

Hello Mr Tom,

I have a situation to solve. The procedure given below is part of a package and
I use the ref cursor to build the web page. I know that this way of implementing
will give me a
" ORA-01023: Cursor context not found (Invalid cursor number) " error but I need
to be able to get the value from the table in a for loop.

Any ideas to proceed. Do I have to declare the SQL statement as a cursor and
then use the FOR loop?

Thank you in advance

procedure sp_initial_cm(aStart in number, aEnd in number, aClient out getClient)
is
ltest varchar2(4000);
lcounter number;
ltemp varchar2(50);
begin
 ltemp := '';
 ltest := '<table>';
  for lCounter in aStart..aEnd loop
   select    <a column>
   into     ltemp
   from     <a table>
   where     rownum = lcounter;

   ltest := ltest|| '<tr><td>';
   ltest := ltest || ltemp ;
   ltest := ltest ||'</td></tr>';
  end loop;
  ltest := ltest || '</table>';
  open aClient for
  select ltest from dual;
end sp_initial_cm;

Hello again,

I will remove the lines of code that close the cursor!

I am using Active Server Pages to access Oracle and use the recordset to get the
cursor out. In the for loop, if I use

for lCounter in 1..1 loop
...
...
loop

the procedure works and retrieves the data and builds the page,

but if I use
for lcounter in 1..2 loop
...
..

loop
I get the abovementioned error. I use 8.0.6!

I Pray I have it explained better this time.

I get the error.

      and we said...
Ahh -- ok -- well, you have a problem here.

First, read:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:948366252775
it explains how rownum works logically and why "where rownum = 2" will NEVER
return any data.

The problem you have however is that somewhere along the way -- your program is
IGNORING ALL ERRORS from the stored procedure.  Your stored procedure throws
NO_DATA_FOUND.  Your ASP layer is happily INGORING that error and continuing on
as if everything was hunky dory.  It attempts to fetch from the cursor however
the cursor was never opened (we exited the procedure as soon as we hit
no_data_found).  That raises that ASP error.

Therefore, I would say that your method of handling database errors is either:

o non-existent
o fatally flawed.

and you need to look into that ASAP.

Review &
      Followup

      Rating: 3
Thank
      You  January 11, 2002
Reviewer:  Srimal
       from Trenton, USA
Hello Mr Tom,

In this scenario, I did not have to catch an exception because the block should
always have data. The error has to do with me trying to use rownum (erroneously,
I have been having a bad week) to bring in the values of nme_fname one after the
other... Your approach is required to accomodate my needs (I wanted to avoid
this but I guess there is no other way).

Once again thank you for clearing the doubts that I had

          Followup:

Ahh -- but there was an error, an unexpected error, and you ASP front end
totally and wholly ignored it.  It silently and totally hid it from you.

The really exciting errors in life are unexpected.  What if, instead of a select
failing, that was an insert.  Now, your ASP calls this procedure and SILENTLY
hides form the end user and the rest of the application that this single insert
failed.  Hmmm, that would be really bad.

Your error handling needs to be changed, the fact that this error was totally
hidden from you means that there are probably many other places where this is
true as well and you could be jepordizing the integrity of your transactions by
having the ASP silently ignore such bad things.

Just a warning, no response needed.

      Rating: 4
Thanks
      again  January 11, 2002
Reviewer:  Srimal
       from Trenton, USA
The application has exceptions built in to it. I just meant the scenario above
(used for the example). But, you are right, ASP has a tendency to confuse the
user in case something goes wrong (especially in this case where it took me to a
different thinking process). If I had the exception in the example I was using,
I would have known that the problem in the code was with using the rownum!

Once again, thank you for your help! This was an eye opener. Always expect
errors, even if it is an example or a test scenario!

      Rating: 3
About
      exception  February 08, 2002
Reviewer:  gre
       from Tianjin
CREATE OR REPLACE PROCEDURE
AS
    v1 number;
    v2 number;
    v3 number;
    v4 char(1);
   CURSOR C1 IS
    SELECT
            f0  a_f0,
            f1  a_f1,
            f2  a_f2,
            f3  a_f3,
            f4  a_f4,
            0   S1,
            0   S2,
            0   S3,
            0   s4
     FROM a;

BEGIN

     FOR c1_rec IN C1 LOOP

    SELECT nvl(b_f1,0),
           nvl(b_f2,0),
           nvl(b_f3,0),
           decode(NVL(c1_rec.a_f4),'0','N','Y')
     INTO v1,v2,v3,v4
     FROM B
     where b_fpk=c1_rec.a_f0
                ;
      c1_rec.s1 :=v1;
      c1_rec.s2  :=v2;
      c1_rec.s3  :=v3;
      c1_rec.s4  :=s4;

      insert into c(c_f1,c_f2,c_f3,c_f4,c_f5,c_f6,c_f7)
                values
            (
            c1_rec.f1,
            c1_rec.f2,
            c1_rec.f3,
            c1_rec.s1,
            c1_rec.s2,
            c1.rec.s3,
            c1.rec.s4)

       end loop;
end;

remark: c_f1...f7 are the filelds of the table c
        b_f1...f3 are the fields of the table b
        b_fpk is the pariary key of the table b
        a_f0..f3 are the paimary key of the table a

i compiled it successfuly, but when i run it got the
following errors
ora-01403
and ora-06521

I add the exception to this , it is successfu but i can't insert the data to the
table c.
what i want to do is to set the null values to zero and finish the insert
. Can i have to use exception? if so , how can use it to have the job done?

Thnaks!
gre

          Followup:

you are getting a 1403 -- no data found.  Your select into doesn't get a single
row for some value, no data.

Your ENTIRE procedure would be more simply, efficiently, and effectively coded
as:

insert into c(c_f1,c_f2,c_f3,c_f4,c_f5,c_f6,c_f7)
    SELECT
            f0  a_f0,
            f1  a_f1,
            f2  a_f2,
            f3  a_f3,
            f4  a_f4,
            nvl(b_f1,0),
            nvl(b_f2,0),
            nvl(b_f3,0),
            decode(NVL(a.a_f4),'0','N','Y')
     FROM a, b
   where b.b_fpk(+) = a_f0;

scrap all of that procedural logic, think in SQL and do it the easy way.

(else wrap an exception block around your select into, catch the no_data_found
and ignore it)

February 09, 2002 Sherry -- Thanks for the question regarding
      "Creating a view", version 8.1.6.2

      You Asked
Is it possible to create a view, but alter the datatype of the column in the
view from the datatype of the column in the table?? ie - the column in the table
would be a datatype of LONG, but in the view would be a datatype of
VARCHAR2(2000)??
Thanks -

      and we said...
Nope.  In 8.0 and up, you really should not be using the LONG type -- it is
pretty non-functional.  With a CLOB, this would be trivial.

You would have to do something like this:

ops$tkyte@ORA8I.WORLD> create table t ( x int, y long );
Table created.

ops$tkyte@ORA8I.WORLD> insert into t values ( 1, 'Hello World' );
1 row created.

ops$tkyte@ORA8I.WORLD> create or replace function getlong( p_rowid in rowid )
return varchar2
  2  as
  3          l_data long;
  4  begin
  5          select y into l_data from t where rowid = p_rowid;
  6
  7          return substr( l_data, 1, 2000 );
  8  end;
  9  /

Function created.

ops$tkyte@ORA8I.WORLD> create or replace view v
  2  as
  3  select x, cast( getlong(rowid) as varchar2(2000) ) y
  4    from t
  5  /

View created.

ops$tkyte@ORA8I.WORLD> desc t
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 X                                            NUMBER(38)
 Y                                            LONG

ops$tkyte@ORA8I.WORLD> desc v
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 X                                            NUMBER(38)
 Y                                            VARCHAR2(2000)

ops$tkyte@ORA8I.WORLD> select * from  t;

         X Y
---------- --------------------
         1 Hello World

ops$tkyte@ORA8I.WORLD> select * from  v;

         X Y
---------- --------------------
         1 Hello World

Review &
      Followup

      Rating:
      5
  May 17, 2001
Reviewer:
      A reader

      Rating:
      5
  May 17, 2001
Reviewer:  Sherry L
      Layton  from Denver, Colorado, USA

      Rating: 5
please urgent
      help me with the error message  February 08, 2002

Reviewer:  Kelly Yi  from IL USA
I run your function exactly. The function is created fine.
When I call the function to create the view I get error:
create or replace function getlong( p_rowid in rowid )
return varchar2
as
l_data long;
begin
select PM_ACTIVITY_LOG into l_data
from aradmin.ext_pm_w_ts@rem2.cis.cat.com
where rowid = p_rowid;
return substr( l_data, 1, 2000 );
end;
function created.

create or replace view v
as
select problem_id, cast( getlong(rowid) as varchar2(2000) ) PM_ACTIVITY_LOG
from aradmin.ext_pm_w_ts@rem2.cis.cat.com
/
select problem_id, cast( getlong(rowid) as varchar2(2000) ) PM_ACTIVITY_LOG
                         *
ERROR at line 3:
ORA-06553: PLS-306: wrong number or types of arguments in call to 'GETLONG'

          Followup:

I cannot reproduce.  You give no version info or anything -- hard to say:

scott@ORA815.US.ORACLE.COM> rem on the site ora817dev i:
scott@ORA815.US.ORACLE.COM> rem CREATE TABLE T ( x int, y long );
scott@ORA815.US.ORACLE.COM> rem INSERT INTO T values ( 1, 'hello world' );
scott@ORA815.US.ORACLE.COM>
scott@ORA815.US.ORACLE.COM> create or replace function getlong( p_rowid in rowid
) return varchar2
  2  as
  3     l_data long;
  4  begin
  5    select y into l_data
  6      from t@ora817dev
  7     where rowid = p_rowid;
  8    return substr( l_data, 1, 2000 );
  9  end;
 10  /

Function created.

scott@ORA815.US.ORACLE.COM>
scott@ORA815.US.ORACLE.COM>
scott@ORA815.US.ORACLE.COM> create or replace view v
  2  as
  3  select x problem_id, cast( getlong(rowid) as varchar2(2000) )
PM_ACTIVITY_LOG
  4  from t@ora817dev
  5  /

View created.

scott@ORA815.US.ORACLE.COM>
scott@ORA815.US.ORACLE.COM> select * from v;

PROBLEM_ID
----------
PM_ACTIVITY_LOG
---------------------------------------------------------------------------------
--------------------------------------------------
         1
hello world

This is 815 going to 817 -- works fine.

February 09, 2002 Percy -- Thanks for the question regarding
      "running sqlldr from plsql", version

      You Asked
Can I run SQL*Loader on the server from a database
procedure ? If yes, then How ?

      and we said...
SQLLoader is a separate, command line program.  This is similar
to asking "can I run 'ls' from plsql".  Using an external
procedure (the ability to write a stored procedure in C) or by
using a java stored procedure, we can run external commands like
this.

If you have a smallish amount of data to load (eg: you recieve
the name of a file with a couple of dozen/hundred records in it
and wish to load it), you can also investigate using the
builting UTL_FILE package.  This lets plsql read/write text
files on the server.  If the data is binary in nature, the
builtin datatype BFILE may be used to read binary data as well.

Review &
      Followup

      Rating: 2
Call sqlldr
      from stored procedure  February 06, 2002
Reviewer:
      Jie Yang  from IL, USA
Your answer is at
http://asktom.oracle.com/pls/ask/f?p=4950:8:480167::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:8812348049,%7Bsqlloader%7D%20and%20%7BAND%7D%20and%20%7Bprocedure%7D
But the answer at

http://www.orafaq.com/msgboard/serverutil/messages/2015.htm
has a different view.

Can you take a look and see whether it is possible to implment that so that one
can call sqlldr from a PL/SQL procedure?

          Followup:

tell me, how is my answer:

Using an external procedure (the ability to write a
stored procedure in C) or by using a java stored procedure, we can run external
commands like this.

materially different from their answer:

You can use an external procedure (Oracle Version 8.0.5 and higher) to call
sqlloader from a stored procedure in plsql

????

      Rating: 4
Loader plus
      triggers versus stored procedures plus UTL_FILE  February
      08, 2002
Reviewer:  Alexandre Matthes  from Rio de
      Janeiro - Brasil
Tom,

What is the best (fast) way to load a heavy amount of data weekly ???

Using SQLLoader loading flat files into non related tables and some triggers
filling the related tables from those non related

OR

Using some stored procedures with UTL_FILE reading that same flat files and
loading them to that same related tables

Thanks in advance,

          Followup:

OR

skip the triggers and UTL_FILE stuff and go straight into the real tables?  If I
had to pre-process the data, I would either:

o use sqlldr to go straight into the REAL tables from the get go if possible

o write some C code to process and array insert the data into the REAL tables in
the first place

o direct path load the data into a scratch table and using a stored procedure
that employeed BULK COLLECTS and FORALL inserts -- process the data

February 11, 2002 Marc -- Thanks for the question regarding "ID vs
      natural Key", version 8.1.7

      You Asked
Hi Tom,
first thank you very much for providing your insights to the rest of the Oracle
developer community.

My problem is related to the "Natural Key/Surrogate Key"-discussion. One of the
main problems with Natural Keys is when the key is subject to change, example:
it's time-related. How can I promote the change to the child tables. The only
mechanism that comes to my mind is trigger combined with deferred FK-constraint.
Do you know any other solution?

Thanx in advance
Marc

      and we said...
If the key is subject to change, it is not the key.  Keys are imutable,
never changing.

What you have is a surrogate key, something that needs a unique/not null
constraint.  It is not the primary key.  You should use a sequence or some other
system generated, imutable key as the primary key and reference this in the
child tables.  No need ever for an update cascade.
Review &
      Followup

      Rating:
      3
  February 12, 2002
Reviewer:  Marc
      Blum  from Aachen, Germany

February 11, 2002 Lilian -- Thanks for the question regarding
      "create index ", version 8.1.6

      You Asked
Hi Tom:

i know when create index that will use temp tablespace, but how can i know how
much space will be taken when i create index.  recently when i tried to create
an index for one of our tables that  has about 9 million records, i failed twice
because of ORA-1652: unable to extend temp segment by 63 in tablespace TEMP.
and also the temp tablespace immidiately filled after creation. We do cold
backup every night, temp has 200m,
why i got this error?
how can i estimate an index space?

thanks as always,
an

      and we said...
Well, in Oracle9i with resumable statements, this all goes away -- you can have
the statement get suspended instead of failing.

Temp is supposed to "fill up", we allocate space in there once and reuse it.
After we use it -- we keep it allocated to avoid having to reallocate it later.
This is normal.

You might need as much space as the ENTIRE index will utlimately consume.
Estimate the size of the index and you have your temp space you might need.  Its
a function of your sort_area_size (the bigger that is, the less temp you need).

200m is a pretty tiny temp space for a database with a 9million record table.
February 11, 2002 kit -- Thanks for the question regarding "help on
      using your site", version 8.1.7

      You Asked
Hi tom,

great site - keeps on helping in  to understand terms other manuals cant

One query i have, If i see a topic i like and see your answer, how can I send
you a question on that answer for further clarification

Also, how can I put my input to other peoples questions

Also will you be coming up with a 9i book as number of people have already
started using it

Thanks
KP

      and we said...
Just use the followup link, if it is related to the existing question, I usually
followup.  If it is not relevant to the existing question -- I ignore it.

Use the "was this useful to you" link.

No word on another book yet ;)
Review &
      Followup

      Rating:
      4
  February 20, 2002
Reviewer:  Sasa
       from Belgrade, Yugoslavia
Mr. Tom,

What’s the alternate way to send you a question, when you have huge queue of
questions for a month or longer ?

This is very serious question. Your opinions and advices are VERY important to
all of us.

Thank you.

February 11, 2002 Reader -- Thanks for the question regarding
      "RBOVs CBO", version 8i

      You Asked
Hi
 Using CBO do we need to put hint in sql statemet or leave it to Oracle
optimiser to take  different and correct execution path depending on the no of
rows in table .Ps comment

      and we said...
You need to analyze the tables and run queries as normal.  It should not be
necessary to use hints.
Review & Followup

      Rating: 3
v$sql_plan
      table  February 11, 2002
Reviewer:
      A reader
I always use explain plan utility under Oracle 8i.
Since 9i there is new v$sql_plan view. Oracle documentation says, that this view
gives real execution plan, while execution plan of explain plan can be different
from real.
Can you give an example, how can it happens?
Can you say, how should we use this new feature (v$sql_plan)

          Followup:

v$sql_plan gives the query plan for the sql as it exists in the shared pool.

Suppose you goto v$sql and find the query "select * from emp where empno > 0"

You do an explain plan on that and find:

scott@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain
scott@ORA817DEV.US.ORACLE.COM> select * from emp where empno > 0;
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=14 Bytes=448)
   1    0   TABLE ACCESS (FULL) OF 'EMP' (Cost=1 Card=14 Bytes=448)

so, apparently thats the plan that that shared sql is using right?  Well, not
in this case.  Unbeknownst to you -- the session that parsed that query had the
optimizer goal set to RULE and the plan is really:

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> alter session set optimizer_goal=rule;

Session altered.

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> select * from emp where empno > 0;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'EMP'
   2    1     INDEX (RANGE SCAN) OF 'EMP_PK' (UNIQUE)

So, in this case, Explain plan will give you the plan YOUR session would have
used -- not what was REALLY used.

Just query v$sql_plan as you would the PLAN table (after joining to v$sql by
address and hash_value)

      Rating:
      3
  February 11, 2002
Reviewer:
      Harold  from Managua, Nicaragua
Hello:

I have a question.

If i put the optimizer mode on Choose, what does it mean? When execute the any
statement the optimizer will choose between Rowid, index, full tablescan?? or
will include the rule base execution???

When the DB is set on rule what is the rule??

in others words, the Rule Mode will not use full tablescan or any others ways
that Choose mode will use??

Thanks for your time

          Followup:

choose means we will CHOOSE the optimizer (rule or cost) when we optimize the
query.

See

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76992/optimops.htm#721
for details.

February 11, 2002 Randy -- Thanks for the question regarding
      "Oracle Row-Level Security", version 8.1.7

      You Asked
Hi Tom,
I have developed a web-form application and want to incorporate row-level
security with the following user requirements:

Government - Only a government member may view data marked for government only.

Manager - Only Manager's may view the data.

Everyone - Everyone may view.

What's the best way to satisfy these user requirements?  Should I place a column
in the database that contains government, manager or everyone option for each
row?  Then use FGAC to restrict? Also, what's the best way to assign users to
these 3 groups?  Would I create 3 roles or should I create a user table?  I'm
still pretty new to the Oracle world so I want to be sure before digging in.
Any advice you can give will be greatly appreciated.

Thank you,
Randy

      and we said...
You will need to add a column to mark the data as "govt", "manager", "public".

You would typically use a table of "USER" to "ROLE" -- a database role isn't
really appropriate here.

A LOGON trigger would be used to select the currently logged in users role(s)
into an application context.  Your predicate function would be:

is
begin
   return 'that_new_column = sys_context( ''your_ctx'', ''user_role'' )'
end;

See

http://osi.oracle.com/~tkyte/article2/index.html
for additional information and don't forget to add that_new_column to existing
indexes as approprirate!  (or use partitionnig and partition the data physically
by this column -- that would work great)


Subject | Article | Last Updated (East coast time)
Tuning I/O | article only | Feb 21, 2002 02:21 pm
deletion of rows | article only | Feb 21, 2002 04:38 pm
RE: [Fwd: Ask Tom Submission] Pragma Exception_Init question | article only | Feb 22, 2002 09:00 am
CBO optimizer plan? | article only | Feb 22, 2002 09:22 am
Spell Date Difference as a ## Years ##Months ## Days | article only | Feb 22, 2002 09:24 am
Read Only Database | article only | Feb 22, 2002 09:58 am
Is there a way to see all index names and all fields in the index? | article only | Feb 22, 2002 02:17 pm
Archive Question | article only | Feb 23, 2002 12:22 pm
Sizing Rollback Segment Correctly and Temporary Tablespace | article only | Feb 23, 2002 12:32 pm
Intermedia query result order by date desc | article only | Feb 23, 2002 01:22 pm
Pl/SQL questions | article only | Feb 23, 2002 01:29 pm
partitions | article only | Feb 23, 2002 02:16 pm
do matching using pl/sql table, cursor? | article only | Feb 23, 2002 06:52 pm
update statement on an indexed column | article only | Feb 23, 2002 06:58 pm
sql loader controlfile, parsing the row for multiple values... | article only | Feb 23, 2002 07:10 pm
how good is execute immediate or dynamic sql | article only | Feb 24, 2002 09:22 am
Fixed length output file using utl_file | article only | Feb 24, 2002 09:48 am
statspack | article only | Feb 24, 2002 12:55 pm
About droping column | article only | Feb 24, 2002 03:46 pm
ORA-00904 while creating materialized view | article only | Feb 24, 2002 06:52 pm
Index usage when PK is disabled | article only | Feb 25, 2002 08:07 am
Redo Entries | article only | Feb 25, 2002 08:08 am
Update, index and bind variables | article only | Feb 25, 2002 02:09 pm
procedure | article only | Feb 25, 2002 02:38 pm
row(s) 1 - 24 of 24
February 21, 2002 Christophe -- Thanks for the question regarding "Tuning I/O", version 8.1.7
You Asked
Hello Tom

Thank you for this opportunity.

1 - Index Clustering Factor

    How much performance gain do you think one can expect
    once a table has been properly sequenced with its index ?

    I have 4 tables of approx 98 000 000 rows each.
    They all participate in the same query
    The clustering factor was not good nor was the query response
    time, so i thought i could significantly improve query
    performance by properly sequencing the table data with
    its indexes.
    The query takes between 20ms to up to 5mn to run depending
    on the search criteria.  How much gain can i expect ?

    Is it true to believe that the Clustering factor is the
    Number 1 thing to look into when trying to reduce the cost
    of I/O  - of cource once the query itself is properly tuned

    Is it reasonable to consider pinning these tables in memory ?

Thank you
Christophe
and we said...
It depends (do you have my book -- i go through this in some detail with
examples).

If you access TONS of rows via a specific index, having the data clustered
(sorted by that index value) may pay off.

On the other hand, a full scan might be faster.

Sounds like you have a case whereby sometimes you WANT to use an index for some
values and sometimes NOT.  This is one time when you might NOT want to use a
bind variable for a certain column.  If you are using the CBO (cost based
optimizer) and have computed histograms for the column in question -- you might
want to consider using a hard coded value.  Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( name varchar2(30), gender
varchar2(1) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select object_name, decode(
mod(rownum,100), 0, 'F', 'M' ) from all_objects;

17165 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx on t(gender);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table t
  2  compute statistics
  3  for table
  4  for all indexes
  5  for all indexed columns
  6  /

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> select name from t where gender = 'F';

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=171 Bytes=3078)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=3 Card=171 Bytes=3078)
   2    1     INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE) (Cost=1 Card=171)

ops$tkyte@ORA817DEV.US.ORACLE.COM> select name from t where gender = 'M';

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=16994 Bytes=305892)
   1    0   TABLE ACCESS (FULL) OF 'T' (Cost=3 Card=16994 Bytes=305892)

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

Here, the cluster factor would be pretty good (99 m's in a row, 1 f, 99 m's and
so no).  It is because the data is so skewed that using an index when gender =
'M' is bad but good when gender = 'f'.  Having all of the F's together and all
of the M's together would make a bit of difference.

I have a gut feeling that your issues is NOT clustering related, more likely it
is data skew related.

You need to look at the tool sql_trace and tkprof to see what's really happening
(and chapter 10 of my book goes into depth on these tools as well)
February 21, 2002 Ravi -- Thanks for the question regarding "deletion of rows", version Oracle 8.1.5
You Asked
How to delete all the parent/child records recursively. Starting
from a single table, the recursion may go as much as 4 to 5
levels and span about 50 tables. ?
and we said...
If you defined the relationship using foreign keys with ON
DELETE CASCADE -- it'll just happen.  For example:

create table Child ( ..., x int references parent(y)
                                on delete cascade, ... );

Make sure the foreign keys are indexed (see
http://osi.oracle.com/~tkyte/unindex/index.html
for a script to verify that) else performance will be pretty
bad.

If you did not use on delete cascade -- it'll be upto you to
"start at the bottom" and work your way up (delete the child
before the parent and so on)...
Review & Followup
Rating: 2
On delete Cascade February 13, 2002
Reviewer: Arun Prasath.D from India
Yes,I do accept that On delete Cascade option would delte the child records too
and makes our work easier.

Can you just throw us more light to achieve the same through a procedure
assuming that due to security reasons on delete cascade is not used and we would
like to perform the same action for certain records passed as parameters.

I could find Script for On Update Cascade.Can you provide the same for On Delete
Cascade?

Followup:
security reasons?  thats 100% silly.

If you can write triggers to do it, why why why would using "on delete cascade"
present some sort of "security" issue?

No, I don't have an on delete cascade script since the language fully supports
it natively.  In 8i and up (only 8i and up -- not in 8.0 and before) you can do
it like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table p ( x int primary key );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table c ( x references p );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create trigger p_trigger
  2  before delete on p
  3  for each row
  4  begin
  5          delete from c where x = :old.x;
  6  end;
  7  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into p values ( 1 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into c values ( 1 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> delete from p;

1 row deleted.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

In 8.0 and before, you'll get:

delete from p
            *
ERROR at line 1:
ORA-04094: table OPS$TKYTE.C is constraining, trigger may not modify it
ORA-06512: at "OPS$TKYTE.P_TRIGGER", line 2
ORA-04088: error during execution of trigger 'OPS$TKYTE.P_TRIGGER'

and there is no way around it.

Rating: 5
Arun Prasanth, I agree with Tom. We've the same Case February 21, 2002
Reviewer: Ravi S. Banda from USA
We came across same scenario, Arun. Found it very useful. The approach mentioned
by Tom is the most efficient. "Security" will play LITTLE role in this scenario.

February 22, 2002 Rajendran -- Thanks for the question regarding "RE: [Fwd: Ask Tom Submission] Pragma Exception_Init question", version
You Asked
Hi
I was looking at user defined exceptions and assigning error
code/numbers to exception using PRAGMA EXCEPTION_INIT. for example,

DECLARE
invalidSKU EXCEPTION;
EXCEPTION_INIT (invalidSKU, -20001);
BEGIN
...
END;

Now is there a way to associate an error message with an user defined
error code, in addition to the defining exception ? The idea being that
I should be able to capture the error message using SQLERRM. Also I will
be able define various user exceptions,
an valid error code and an a generic
error message, all at one place maybe one package.

Thanks,
raj.
and we said...
You are sort of mixing 2 concepts up here.

One is the ability to define a user defined exception (invalidSKU above) and the
other is to take an existing Oracle error code (eg: -20001 in your example) and
map that to some user defined exception.

We'll be able to achieve your goal (define exceptions and have known error
messages/codes associated with them) easily though.  You can do this in one
package as well.

First, lets look at what you did above.  Here is a full example:

SQL> declare
  2     invalidSKU EXCEPTION;
  3     pragma EXCEPTION_INIT (invalidSKU, -20001);
  4  BEGIN
  5          raise_application_error( -20001, 'Some Error Message' );
  6  exception
  7          when invalidSKU then
  8                  dbms_output.put_line( 'We caught the invalid SKU exception'
);
  9                  dbms_output.put_line( 'sqlerrm = ' || sqlerrm );
 10                  dbms_output.put_line( 'sqlcode = ' || sqlcode );
 11  end;
 12  /
We caught the invalid SKU exception
sqlerrm = ORA-20001: Some Error Message
sqlcode = -20001

PL/SQL procedure successfully completed.

You made it so that when the SQL error "-20001" is raised, PL/SQL will convert
that into the named exception invalidSKU.  The error message that accompanied
the -20001 (generated by the raise_application_error) is there in the SQLERRM
builtin function.  While we can make it so that when invalidSKU is raised -- the
sqlcode is set to -20001 -- we have no control over the associated error text
(it'll be NULL).

What pragma exception_init is designed for is to convert an Oracle Error code
into a named exception to make programing easier.  Instead of having a big 'when
others' like:

when others then
   if ( sqlcode = some_value ) then
       ...
   elsif ( sqlcode = some_other_value ) then
       ...
   elsif ....
   ....
   end if;

the pragma allows you to have:

when Some_Error then
when Some_Other_Error then
....

Now, how can you define a package that has a bunch of user defined exceptions
with corresponding error codes and messages and how would another programmer
'raise' such an exception?  It might look like this:

SQL> create or replace package exception_pkg
  2  as
  3          invalid_sku  constant number default -20001;
  4          invalid_sku_exception   exception;
  5          pragma exception_init( invalid_sku_exception, -20001 );
  6
  7          another_error constant number default -20002;
  8          another_error_exception exception;
  9          pragma exception_init( another_error_exception, -20002 );
 10
 11          procedure throw( p_exception in number, p_extra_msg in varchar2
default NULL );
 12
 13  end;
 14  /

Package created.

This is my package spec for all of my exceptions.  Here I will assign a numeric
code in the range of -20000 to -20999 (these are the valid sqlcodes you may
control, all others belong to Oracle itself) to each named exception.  I also
define an EXCEPTION for each code and then use pragma exception_init to map the
named exception to the error code.  In the above example, I have defined 2
exceptions.  I have directed PL/SQL to map the error code -20001 to the named
exception "invalid_sku_exception".

This spec also defines a "throw" procedure.  This is what the developers will
call to cause an exception to be raised.  This procedure will take one of the
constants as input and will call the internal routine "raise_application_error"
to raise an exception with a specific error code and message.  Inside the
package body, we will see how we get error messages (text) mapped to specific
error codes.  Additionally, we will see that the parameter p_extra_msg that can
be supplied to the throw procedure will be appended to the error message as
well.

Now, the body of exception_pkg.  It might look like this:

SQL> create or replace package body exception_pkg
  2  as
  3
  4  -- We will create an array of error messages.  Since the PLSQL table
  5  -- type is a sparse array, we can put into it indices like -20001,
  6  -- -20002 and so on and leave the rest of the table blank.  The
  7  -- 'elaboration' code at the bottom of this package will be executed
  8  -- once per session when the package is first referenced.    This
elaboration
  9  -- code will initialize the plsql table of error messages for us
 10
 11  type myarray is table of varchar2(255) index by binary_integer;
 12  err_msgs  myArray;
 13
 14
 15  -- throw is what is used to raise user defined exceptions, those
 16  -- defined in the exception_pkg specification.
 17  --
 18  -- p_exception should be one of the constants found in the specification
 19  -- p_extra_msg is optional.  if supplied, it will be appended to the error
message
 20  --
 21  -- this procedure uses another routine, who_called_me (supplied
separately).
 22  -- who_called_me gets the callers name and line number.  this information
 23  -- is put into the error message as well so we can figure out who threw
 24  -- the exception in the first place easily from the error message.
 25
 26  procedure throw( p_exception in number, p_extra_msg in varchar2 default
NULL )
 27  is
 28       l_owner    varchar2(30);
 29       l_name        varchar2(30);
 30       l_type        varchar2(30);
 31       l_line        number;
 32       l_exception number;
 33  begin
 34       who_called_me( l_owner, l_name, l_line, l_type );
 35       raise_application_error( p_exception      ,
 36                   'Exception at ' || l_type || ' ' ||
 37                    l_owner || '.' || l_name || '(' || l_line || ') ' ||
 38                    err_msgs(p_exception       ) || ' ' || p_extra_msg );
 39  exception
 40       -- we will get this when we have an invalid exception code, one
 41       -- that was not set in the err_msgs array below.  The plsql table
 42       -- access will raise the NO-DATA-FOUND exception.  We'll catch it,
 43       -- verify the exception code is in the valid range for
raise_application_error
 44       -- (if not, set to -20000) and then raise the exception with the
message
 45       -- "unknown error"
 46
 47       when NO_DATA_FOUND then
 48           if ( p_exception between -20000 and -20999 ) then
 49           l_exception := p_exception;
 50           else
 51           l_exception := -20000;
 52           end if;
 53
 54           raise_application_error( l_exception     ,
 55                   'Exception at ' || l_type || ' ' ||
 56                    l_owner || '.' || l_name || '(' || l_line || ') ' ||
 57                    '**UNKNOWN ERROR**' || ' ' || p_extra_msg );
 58
 59  end;
 60
 61  begin
 62       -- Our elaboration code.  For each constant in the package spec we
should
 63       -- have an entry in this plsql table.
 64       -- this code is run once per session when the package is first
'touched'
 65
 66       err_msgs( invalid_sku ) := 'Invalid SKU Code';
 67       err_msgs( another_error ) := 'Another Error Message...';
 68  end;
 69  /

Package body created.

The procedure who_called_me is attached (as well as the source code for this
package).  You could just embed who_called_me right into this package body but
since I use it in so many places, I prefer to just let it standalone.

Now, a developer will throw and catch exceptions like this:

SQL> create or replace procedure demo1
  2  as
  3  begin
  4       exception_pkg.throw( exception_pkg.invalid_sku, '<extra text from
runtime>' );
  5  exception
  6       when exception_pkg.invalid_sku_exception then
  7           dbms_output.put_line( 'Caught the error' );
  8           dbms_output.put_line( 'sqlerrm = ' || sqlerrm );
  9           dbms_output.put_line( 'sqlcode = ' || sqlcode );
 10  end;
 11  /

Procedure created.

They will use the exception_pkg.throw procedure to raise an exception (instead
of using RAISE or raise_application_error directly).  They can also pass in
extra text, for example with the invalid SKU error -- they might pass in the SKU
that was in fact invalid for the end user to see.

This routine also shows how they would catch these errors.  They would use the
named exceptions in the exception_pkg as I have above.  Specifically:

  6       when exception_pkg.invalid_sku_exception then

is how they would refer to the exception.  When we run this procedure in SQLPLUS
we can see that we achieved the desired results:

SQL> exec demo1
Caught the error
sqlerrm = ORA-20001: Exception at PROCEDURE TKYTE.DEMO1(4) Invalid SKU Code
<extra text from runtime>
sqlcode = -20001

PL/SQL procedure successfully completed.

We

o Caught the error with line 6
o See the error message "Invalid SKU Code".  It has some runtime info appended
and at the front it has the actual name and line number of the procedure that
threw the exception.
o it has the error code we defined for this error....

The following demonstrates what happens when this package is used with an
undefined, out of range error code:

SQL> create or replace procedure demo2
  2  as
  3  begin
  4       exception_pkg.throw( 123, '<This is an unknown error>' );
  5  exception
  6       when Others then
  7           dbms_output.put_line( 'Caught the error in OTHERS since this error
was not mapped' );
  8           dbms_output.put_line( 'sqlerrm = ' || sqlerrm );
  9           dbms_output.put_line( 'sqlcode = ' || sqlcode );
 10  end;
 11  /

Procedure created.

SQL> exec demo2
Caught the error in OTHERS since this error was not mapped
sqlerrm = ORA-20000: Exception at PROCEDURE TKYTE.DEMO2(4) **UNKNOWN ERROR**
<This is an unknown error>
sqlcode = -20000

PL/SQL procedure successfully completed.

Since "123" is not a valid error code -- this raises the OTHERS exception
(cannot be mapped to a named exception) and the error message reflects this
Review & Followup
Rating: 4
Good Stuff February 14, 2002
Reviewer: roland Daane from Netherlands
This is really interesting stuff.

I'm using this method of work by inderting the data for the exceptions into an
Oracle table and from that creating the error package.

I'm really interested in the source for the who_called_me procedure!

Followup:
See
http://osi.oracle.com/~tkyte/who_called_me/index.html
for that.

Rating: 5
August 20, 2003
Reviewer: A reader

February 22, 2002 Maya -- Thanks for the question regarding "CBO optimizer plan?", version 8.0.4
You Asked
Hello,

We use CBO (FIRST_ROWS)
When trying to tune SQL statements we found that using the RULE
hint make ,most of the time, a better plan.

1.
I tried that hint on a simple small table with an unique index,
with a statment contains a "where" clause using index columns.
Somehow the RULE hint does not create the expected plan, with an
index scan, not full table scan.

What am I missing here?

here is what I've done:

SQL> CREATE TABLE MYTAB (
  2    USER_ID        VARCHAR2 (7)  NOT NULL,
  3    CR_ID          NUMBER (8)    NOT NULL,
  4    NOTIFICATIONS  NUMBER (8)    NOT NULL)
  5  TABLESPACE USERS;

Table created.

SQL> CREATE UNIQUE INDEX myind1 ON mytab (USER_ID,CR_ID);

Index created.

SQL> set autotrace traceonly explain
SQL> SELECT
  2  *
  3  FROM
  4    mytab
  5  WHERE  USER_ID = 1  and CR_ID = 1 --and
  6  /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=1 Card=1
Bytes=49)

   1    0   TABLE ACCESS (FULL) OF 'MYTAB' (Cost=1 Card=1
Bytes=49)

SQL> SELECT /*+RULE*/
  2  *
  3  FROM
  4    mytab
  5  WHERE  USER_ID = 1  and CR_ID = 1
  6  /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: RULE
   1    0   TABLE ACCESS (FULL) OF 'MYTAB'

2.
For the same table I created an index myind (instead myind1),
which is almost the same but the order of columns is different.
why do I get a different plan from the above, the optimizer
makes an index scan?

SQL> drop index myind1;

Index dropped.

SQL> CREATE UNIQUE INDEX myind1 ON mytab (CR_ID,USER_ID);

Index created.
SQL>  SELECT
  2   *
  3   FROM
  4     mytab
  5   WHERE  USER_ID = 1  and CR_ID = 1;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=1 Card=1
Bytes=49)

   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'MYTAB' (Cost=1
Card=1 By
          tes=49)

   2    1     INDEX (RANGE SCAN) OF 'MYIND1' (UNIQUE) (Cost=2
Card=1)

Thanks,

Maya
and we said...
My experience, given that the statistics on the tables are
current, is the opposite in 8.x -- but anyway... here are the
answers to the questions posed:

1) because user_id is a varchar2(7) and the query:

SQL> SELECT /*+RULE*/
  2  *
  3  FROM
  4    mytab
  5  WHERE  USER_ID = 1  and CR_ID = 1
  6  /

is the same as:

SQL> SELECT /*+RULE*/
  2  *
  3  FROM
  4    mytab
  5  WHERE  TO_NUMBER(USER_ID) = 1  and CR_ID = 1
  6  /

you are implicitly applying a function to the user_id column,
making the use of an index impossible in this case.  You should
ALWAYS avoid implicit conversions -- here is a small example
why:

tkyte@TKYTE816> create table t ( x varchar2(1), y int );
Table created.

tkyte@TKYTE816> insert into t values ( 'a', 1 );
1 row created.

tkyte@TKYTE816> insert into t values ( '1', 2 );
1 row created.

tkyte@TKYTE816> select *
  2    from t
  3   where x = 1 and y = 2
  4  /

X          Y
- ----------
1          2

tkyte@TKYTE816> select *
  2    from t
  3   where y = 2 and x = 1
  4  /
 where y = 2 and x = 1
                 *
ERROR at line 3:
ORA-01722: invalid number

Note in the RBO -- the first query works.  Y=2 is evaluated
AND THEN to_number(x) = 1 is evaluated.  Since when y=2, x=1 --
this works.

On the other hand, the second query fails.  Here to_number(x)=1
is evaluated FIRST and since X='a' for some rows -- the
to_number fails.

Just to show you cannot rely on the order of evaluation of a
predicate in any circumstances -- consider this:

tkyte@TKYTE816> analyze table t compute statistics;
Table analyzed.

tkyte@TKYTE816> select *
  2    from t
  3   where x = 1 and y = 2
  4  /
 where x = 1 and y = 2
       *
ERROR at line 3:
ORA-01722: invalid number

tkyte@TKYTE816> select *
  2    from t
  3   where y = 2 and x = 1
  4  /

X          Y
- ----------
1          2

tkyte@TKYTE816>

The CBO will evaulate the predicate is a totally different
order!  The order of evaluation, since it is by definition NOT
DEFINED in SQL cannot be relied on.  The only correct way to
write the above query safely is:

select * from t where y=2 and x = '1';

Compare STRINGS to STRINGS, DATES to DATES and NUMBERS to
NUMBERS.  Never let an implicit conversion happen!

2) for this one, the optimizer found CR_ID on the leading edge
of the index and was able to use that.  It is NOT using USER_ID
in this index (as evidenced by the fact that it is doing an
index range scan on this UNIQUE index).  That query is alot
like:

select *
  from ( select * from mytab where cr_id = 1 )
 where to_number(user_id) = 1
/

The optimizer is using the index to do the inner query and THEN
it applies the to_number function to the user_id column and sees
if that equals 1.
Review & Followup
Rating: 5
A twist on this February 12, 2002
Reviewer: Steve McKee from Montreal, Quebec, Canada
Hi Tom,

I understand that we cannot rely on the order in which
predicates will be evaluated. However, why can't we rely on the following:

select to_number(x)
from   t
where  y=2;

We have this exact situation and are getting conversion
errors even though we eliminate "invalid" rows in the
where clause. Note that the behaviour is dependent on whether or not there are
statistics on the table(s).

Can you explain why (in some cases) every row is hit with the to_number()
function even though we filter out the invalid rows.

Followup:
can you show us how to reproduce easily?

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x varchar2(5), y int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1, 2 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 'a', 3 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select to_number(x)
  2    from t
  3   where y = 2;

TO_NUMBER(X)
------------
           1

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table t compute statistics;

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select to_number(x)
  2    from t
  3   where y = 2;

TO_NUMBER(X)
------------
           1

Rating: 5
twist - followup February 12, 2002
Reviewer: Steve McKee from Montreal, Quebec, Canada
I apologize in advance if the formatting is a mess.

Here's the query that is failing:

select count(*)
from ppcdrdata pp,
     (
        select to_date(value,'DD-Mon-YYYY HH24:MI:SS') START_TIME
        from   site_data
        where  name='CDE::FROM_DATE'
     ) CDE_START,
     (
        select to_date(value,'DD-Mon-YYYY HH24:MI:SS') STOP_TIME
       from   site_data
       where  name='CDE::TO_DATE'
    ) CDE_STOP
where pp.dws_creation_date >= CDE_START.START_TIME and
      pp.dws_creation_date <  CDE_STOP.STOP_TIME

SQL> /

select to_date(value,'DD-Mon-YYYY HH24:MI:SS') STOP_TIME
                              *
ERROR at line 9:
ORA-01847: day of month must be between 1 and last day of month

The "raw" value of "value" column for rows with
name="CDE::TO_DATE"  and  name="CDE::FROM_DATE"
is:

SQL> select '[ ' || value || ' ]'
     from   site_data
     where  name='CDE::TO_DATE';

'['||VALUE||']'
--------------------------
[ 29-Jan-2002 06:54:04 ]

SQL>  select '[ ' || value || ' ]'
      from   site_data
      where  name='CDE::FROM_DATE';

'['||VALUE||']'
------------------
[ 29-Jan-2000 06:54:04 ]

Note that there are numeric (i.e. non date) values in the "value" field of other
rows.

Here are the table definitions

SQL> desc site_data
 Name                Null?    Type
 ------------------- -------- --------------
 DATA_TYPE           NOT NULL NUMBER(5)
 VALUE                        VARCHAR2(250)
 NAME                         VARCHAR2(250)
 DESCRIPTION                  VARCHAR2(1024)

SQL> desc ppcdrdata
 Name                         Null?    Type
 ---------------------------- -------- ----------------------------
 <hide column name>           NOT NULL NUMBER(16)
 <hide column name>           NOT NULL VARCHAR2(40)
 <hide column name>           NOT NULL NUMBER(2)
 <hide column name>                    VARCHAR2(30)
 <hide column name>                    NUMBER(15)
 <hide column name>                    DATE
 <hide column name>                    VARCHAR2(40)
 <hide column name>                    VARCHAR2(20)
 <hide column name>                    VARCHAR2(40)
 <hide column name>                    NUMBER(1)
 <hide column name>                    NUMBER(10)
 <hide column name>                    NUMBER(10)
 <hide column name>                    NUMBER(1)
 <hide column name>                    NUMBER(3)
 <hide column name>                    VARCHAR2(40)
 <hide column name>                    NUMBER(15)
 <hide column name>                    VARCHAR2(15)
 DWS_CREATION_DATE                     DATE

I have opened a TAR on this and the "final answer" from
Oracle was:

Steve,

Development has determined that this is not a bug.
Here is what was state by the developer:

This is not a bug . We can not guarantee how predicates will be applied.
The best way to guarantee Such queries would work is to
use a DECODE around the datatype column, and return some predetermined
value for non-date values that would be recognised as a data to be
excluded.

Please review the bug tomorrow to see the
select count(*) from ppcdrdata pp,
(select decode( name, 'CDE::FROM_DATE',
to_date(value,'DD-Mon-YYYY HH24:MI:SS'), null ) START_TIME
from site_data
where name='CDE::FROM_DATE') CDE_START,
(select decode( name, 'CDE::TO_DATE',
to_date(value,'DD-Mon-YYYY HH24:MI:SS'), null ) STOP_TIME
from site_data
where name='CDE::TO_DATE') CDE_STOP
where pp.dws_creation_date >= CDE_START.START_TIME
and pp.dws_creation_date < CDE_STOP.STOP_TIME;

We will inactivate this tar with 2 week closure.
Thanks

I'm sure they are correct. And I'm sure their workaround
will work. But I still don't understand
what the execution plan was doing.

Note that they were able to reproduce the problem with the
export data I sent them.

Followup:
That makes sense -- if you take the query:

select count(*)
from ppcdrdata pp,
     (
        select to_date(value,'DD-Mon-YYYY HH24:MI:SS') START_TIME
        from   site_data
        where  name='CDE::FROM_DATE'
     ) CDE_START,
     (
        select to_date(value,'DD-Mon-YYYY HH24:MI:SS') STOP_TIME
       from   site_data
       where  name='CDE::TO_DATE'
    ) CDE_STOP
where pp.dws_creation_date >= CDE_START.START_TIME and
      pp.dws_creation_date < CDE_STOP.STOP_TIME

it can be rewritten in a variety of ways -- one of which would have the
predicates:

where pp.dws_creation_date >= CDE_START.START_TIME and
      pp.dws_creation_date < CDE_STOP.STOP_TIME

getting pushed DOWN into the inline views - so that the

select to_date( .... ) start_time
  from site_data
 where name = 'CDE::FROM_DATE'

is more like:

select to_date( .... ) start_time
  from site_data
 where name = 'CDE::FROM_DATE'
   and pp.dws_creation_date >= start_time (and start_time is really just
to_date)

hence, view merging is causing the predicates to be evaluated in a random order.

One "trick" that will fix this will be to add ROWNUM to the inline views:

select count(*)
from ppcdrdata pp,
     (
        select to_date(value,'DD-Mon-YYYY HH24:MI:SS') START_TIME, rownum r1
        from   site_data
        where  name='CDE::FROM_DATE'
     ) CDE_START,
     (
        select to_date(value,'DD-Mon-YYYY HH24:MI:SS') STOP_TIME, rownum r2
       from   site_data
       where  name='CDE::TO_DATE'
    ) CDE_STOP
where pp.dws_creation_date >= CDE_START.START_TIME and
      pp.dws_creation_date < CDE_STOP.STOP_TIME

Now, the inline views will be materialized and then joined to -- instead of
having the query predicates get merged.  Things like rownum, aggregates, order
bys in the inline views preclude us from being able to merge -- HOWEVER -- the
solution offered by support is the BETTER way to do this!  It always will work.

Rating: 4
just a minor doubt February 13, 2002
Reviewer: A reader from India
Hi Tom, Your comment "Compare STRINGS to STRINGS,
DATES to DATES and NUMBERS to NUMBERS. Never let
an implicit conversion happen!".

Query # A :
select      *
from        ALL_OBJECTS
where       CREATED >= '01-JAN-2001'
and         CREATED <= '31-JAN-2001'

Query # B :
select      *
from        ALL_OBJECTS
where       CREATED >= TO_DATE('01-JAN-2001','DD-MON-YYYY')
and         CREATED <= TO_DATE('31-JAN-2001','DD-MON-YYYY')

In the Query # A, isn't the column CREATED (a date datatype)
being internally converted by Oracle to a string datatype
for comparison i.e. to_char(CREATED,'DD-MON-YYYY')?

'01-JAN-2001' will just be a character string to Oracle
irrespective of the format we have given to it?

A lot of my colleagues prefer / insist on using Query # A.
But will not Query # B cost less as there are no implicit
conversions?

Pls share your insights / opinion with us on this.

Followup:
Your colleagues are lazy.  Your colleagues will get totally burned when someone
changes the NLS_DATE_FORMAT.  Your colleagues are wrong.

We'll implicitly convert the STRING into a DATE for this one, consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from all_objects where created >
'x';
select * from all_objects where created > 'x'
                                          *
ERROR at line 1:
ORA-01858: a non-numeric character was found where a numeric was expected

it didn't do the date to a string.  However, you should NOT rely on that
implicit conversion ever.  Be explicity, hit the keyboard a couple of extra
times to get the TO_DATE with YOUR format there!

Rating: 3
Type casting February 14, 2002
Reviewer: Mikito Harakiri
If 'val' is of type CHAR then the predicate val=1 is ambiguous. It can be
transformed either to

TO_NUMBER(val)=1

or

val=TO_CHAR(1).

Oracle seems to always prefer the first way. Wouldn't the rule "cast constant
expression first" resolve the issue?

Followup:
It would remove the error BUT would introduce massive ambiguities and other
"incorrect" (percieved INCORRECT) behavior.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( val varchar2(5) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( '1' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( ' 1' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where val >= to_char(1);

VAL
-----
1

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where to_number(val) >= 1;

VAL
-----
1
 1

They are not equivalent and most people seem to "expect" the second answer.
When comparing to a number -- convert to a number.  The same with dates.  Since
Strings compare SOOO different then number/dates do, we convert to the more
specific type and then compare.

The rule "cast the constant" would not work since you can easily have:

select * from t where c1 = c2;

and c1 is a string, c2 a number.  Same problem -- no constants.

Back to MY RULE.  Never ever in any language rely on implict conversions.  In
EVERY language I've ever worked with over the years -- I've been burned over and
over by not following that very simple rule.  Just be explicit, you'll write
more correct programs.  They might be a tad "longer", a few extra keystrokes,
but they will be predicable and correct.

You can wish the machine could do it all for you (as you seem to constantly want
it to) but it always comes back to "better SAFE then really really SORRY".

Rating: 3
narrowing vs. widening February 14, 2002
Reviewer: Mikito Harakiri
Correction to the rule: when there is ambiguity between narrowing one type or
widening the other, always use widening, because it gurantees no runtime
surprises. If there is a conflict that prevents consolidating the types, then
report query compilation error instead of nondeterministic failure at runtime
confusing unsuspected end user!

Followup:
talk about confusing!! look at the above results of the two different queries
where one widens and the other narrows.  Tell me -- which one is "correct".
I'll gladly argue the other side, no matter which you pick.

Sure, widening avoids the ERROR but gives what many would say is the wrong
answer (silently, without ERROR -- that I believe to be worse then getting the
error).

Rating: 4
agreed February 25, 2002
Reviewer: Mikito Harakiri
Hmm... Even more convincing example would be:

'2'<'11' vs. 2<11

February 22, 2002 Senthil Kumar -- Thanks for the question regarding "Spell Date Difference as a ## Years ##Months ## Days", version Oarcle 8.1.5.0.0
You Asked
I wants to find date difference for given two date in terms of
Years, Months and Days. I wrote a function like this

-----------

create or replace function sdate(frmdate date,todate date)
return char is
speldate varchar(30);
tdays   number:=0;
tmonth number:=0;
tyear number:=0;
begin
-----------------------------------------------------------------

/*check from_date and to_date are in the same month and year */
-----------------------------------------------------------------

if to_char(frmdate,'MON-YYYY')= to_char(todate,'MON-YYYY') then
    select (todate - frmdate)+1 into tdays from dual;

    if (to_char(todate,'MM')='02' and
        to_char(last_day(todate),'DD')='29') then
          if tdays >= 29 then
             tdays:=tdays -29;
             tmonth:=tmonth+1;
          end if;
    elsif (to_char(todate,'MM')='02' and
           to_char(last_day(todate),'DD')='28') then
           if tdays>=28 then
              tdays:=tdays -28;
              tmonth:=tmonth+1;
           end if;
    elsif to_char(todate,'MM')
               in ('01','03','05','07','08','10','12') then
          if tdays >=31 then
             tdays := tdays-31;
             tmonth := tmonth +1;
          end if;
    elsif to_char(todate,'MM') in ('04','06','09','11') then
          if tdays >=30 then
             tdays := tdays-30;
             tmonth := tmonth +1;
          end if;
    end if;
------------------------------------------------
/* check for to_date in the month end or not */
------------------------------------------------
elsif todate = last_day(todate) then

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

    /* check for if from_date started with first day of month
and
       to_date end with last day of month */
-----------------------------------------------------------------
-
    if to_char(frmdate,'DD')='01' then
       select ceil(months_between(todate,frmdate))
                                        into tmonth from dual;
    else
      select (months_between(last_day(add_months(todate,-1))+1 ,

                          last_day(frmdate)+1))+1
                             into tmonth from dual;
      select (last_day(frmdate)-frmdate)+1 into tdays from dual;
      if (to_char(todate,'MM')='02' and
        to_char(last_day(todate),'DD')='29') then
          if tdays >= 29 then
             tdays:=tdays -29;
             tmonth:=tmonth+1;
          end if;
      elsif (to_char(todate,'MM')='02' and
           to_char(last_day(todate),'DD')='28') then
           if tdays>=28 then
              tdays:=tdays -28;
              tmonth:=tmonth+1;
           end if;
      elsif to_char(todate,'MM') in
                    ('01','03','05','07','08','10','12') then
          if tdays >=31 then
             tdays := tdays-31;
             tmonth := tmonth +1;
          end if;
      elsif to_char(todate,'MM') in ('04','06','09','11') then
          if tdays >=30 then
             tdays := tdays-30;
             tmonth := tmonth +1;
          end if;
      end if;
   end if;
----------------------------
/*check for from_date */
-----------------------------
else
    select (months_between(last_day(add_months(todate,-1))+1 ,
                          last_day(frmdate)+1))
                             into tmonth from dual;
    select (last_day(frmdate)-frmdate) +
           ((todate - last_day(add_months(todate,-1))+1)-1) +1
                             into tdays from dual;
    if (to_char(frmdate,'MM')='02' and
        to_char(last_day(frmdate),'DD')='29') then
          if tdays >= 29 then
             tdays:=tdays -29;
             tmonth:=tmonth+1;
          end if;
    elsif (to_char(frmdate,'MM')='02' and
           to_char(last_day(frmdate),'DD')='28') then
           if tdays>=28 then
              tdays:=tdays -28;
              tmonth:=tmonth+1;
           end if;
    elsif to_char(frmdate,'MM') in
                   ('01','03','05','07','08','10','12') then
          if tdays >=31 then
             tdays := tdays-31;
             tmonth := tmonth +1;
          end if;
    elsif to_char(frmdate,'MM') in ('04','06','09','11') then
          if tdays >=30 then
             tdays := tdays-30;
             tmonth := tmonth +1;
          end if;
    end if;
end if;

if tmonth >= 12 then
   tyear := floor(tmonth/12);
   tmonth:= tmonth - (tyear*12);
end if;
select decode(tyear,0,'',tyear || ' Years ') ||
       decode(tmonth,0,'',tmonth || ' Months ') ||
       decode(tdays,0,'',tdays || ' Days ') into speldate from
dual ;
return(speldate);
end;
/
----------

if i execute this function i get a result like this

SQL> select sdate('10-sep-1995','10-sep-1997') from dual;

SDATE('10-SEP-1995','10-SEP-1997')

-----------------------------------------------------------------
-----------------------------------
2 Years 1 Days

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

If you have any easiest way to find date diff. for given two
date in terms of Yrs,Mons and days, Please send it to me.

With Regards
Senthil Kumar. S
and we said...
Well, I think there is an easier way -- we can do this in SQL in
fact.  Consider:

SQL> select older_date,
  2         newer_date,
  3         years,
  4         months,
  5         abs(
  6           trunc(
  7              newer_date-
                    add_months( older_date,years*12+months )
  8                )
  9            ) days
 10    from ( select
        trunc(months_between( newer_date, older_date )/12)
YEARS,
 11     mod(trunc(months_between( newer_date, older_date )),
            12 )  MONTHS,
 12                  newer_date,
 13                  older_date
 14             from ( select hiredate older_date,
 15                add_months(hiredate,rownum)+rownum newer_date
 16                      from emp )
 17         )
 18  /

OLDER_DAT NEWER_DAT      YEARS     MONTHS       DAYS
--------- --------- ---------- ---------- ----------
17-DEC-80 18-JAN-81          0          1          1
20-FEB-81 22-APR-81          0          2          2
22-FEB-81 25-MAY-81          0          3          3
02-APR-81 06-AUG-81          0          4          4
28-SEP-81 05-MAR-82          0          5          5
01-MAY-81 07-NOV-81          0          6          6
09-JUN-81 16-JAN-82          0          7          7
09-DEC-82 17-AUG-83          0          8          8
17-NOV-81 26-AUG-82          0          9          9
08-SEP-81 18-JUL-82          0         10         10
12-JAN-83 23-DEC-83          0         11         11
03-DEC-81 15-DEC-82          1          0         12
03-DEC-81 16-JAN-83          1          1         13
23-JAN-82 06-APR-83          1          2         14

14 rows selected.

The inner query:

select hiredate older_date,
       add_months(hiredate,rownum)+rownum newer_date
  from emp

Is just used to generate "data" to test with.  This would
normally be "your query" with the two dates in it.

Next, we wrap that query as an inline view to compute the years
and months between the two dates.  the years between two dates
is simply:

 trunc(months_between( newer_date, older_date )/12)

we get the number of months between, divide by 12 and truncate
the results.

To get the months between is almost as easy:

 mod(trunc(months_between( newer_date, older_date )), 12 )

we get the months between as an absolute number and then integer
divide (modulo) by 12.  The leftover (modulo) piece is the
months between without the years.

The DAY component is the "hard" part.  What I did was add the
YEARS and the MONTHS between to the older date:

add_months(older_date,years*12+months)

subtract that from the newer_date:

 newer_date-add_months(older_date,years*12+months)

and then trunc and ABSolute value it.  That gives me the days
between the two dates.

for your specific dates above -- I get a different answer -- I
get:

OLDER_DAT NEWER_DAT      YEARS     MONTHS       DAYS
--------- --------- ---------- ---------- ----------
10-SEP-95 10-SEP-97          2          0          0

which seems to be more accurate.

So, even if you do not use this in SQL -- it would simplify your
procedure to:

SQL> create or replace
  2  function sdate( p_date1 in date, p_date2 in date )
                                              return varchar2
  3  as
  4      older_date date default least( p_date1, p_date2 );
  5      newer_date date default greatest( p_date1, p_date2 );
  6
  7      years  number default
  8        trunc(months_between( newer_date, older_date )/12);
  9      months number default
 10        mod(trunc(months_between(newer_date,older_date)),12);
 11      days   number default
 12        abs(trunc(newer_date-
                add_months(older_date,years*12+months)));
 13
 14      return_string varchar2(255);
 15  begin
 16      if (Years > 0 ) then
           return_string := return_string || years || ' years ';
 17      end if;
 18      if (Months> 0 ) then
           return_string := return_string || months|| ' months
';
 19      end if;
 20      if (Days  > 0 ) then
           return_string := return_string || days  || ' days ';
 21      end if;
 22
 23      return rtrim(return_string);
 24  end;
 25  /

Function created.

SQL> column sdate format a25
SQL> select sdate('10-sep-1995','10-sep-1997') sdate from dual;

SDATE
-------------------------
2 years

SQL>
SQL> select sdate( newer_date, older_date )
  2    from ( select hiredate older_date,
  3              add_months(hiredate,rownum)+rownum newer_date
  4             from emp )
  5  /

SDATE(NEWER_DATE,OLDER_DATE)
-----------------------------------------------------------------
------------------------
1 months 1 days
2 months 2 days
3 months 3 days
4 months 4 days
5 months 5 days
6 months 6 days
7 months 7 days
8 months 8 days
9 months 9 days
10 months 10 days
11 months 11 days
1 years 12 days
1 years 1 months 13 days
1 years 2 months 14 days

14 rows selected.

SQL>
Review & Followup
Rating: 5
Excellent June 06, 2001
Reviewer: Andy from Kuala Lumpur, Malaysia
I was looking for this exact function to compute service for a group of
employees in days, months, and years given a future date.

Thanks, Tom!

Rating: 4
what about time?? February 15, 2002
Reviewer: Ravi Penumaka from Chicago, IL
How to do the time part

I am doing this :

select sysdate,
       a.real,
       floor(a.real/(60*60)) hours,
       floor(mod(a.real, 60*60)/60) mins,
       mod(a.real, 60) secs
from ( select (sysdate - trunc(sysdate))*86400 real from dual ) a

Is there any other better way.

Thanks,
Ravi

Followup:
In 9i, we have INTERVALS (new datatype) that do this easily.

See
http://osi.oracle.com/~tkyte/Misc/DateDiff.html
before that.

February 22, 2002 Manoharan -- Thanks for the question regarding "Read Only Database", version 7.3.4
You Asked
Hi Tom,

   We are running an oracle Database(7.3.4) under AIX system.We have around 100
users accessing the database thru  D2K Applications.We would like to temporarily
stop updating/inserting the transaction thru those applications.End users can
query thru those pplications.We cannot customize the applications now.

  I tried to open  the database in read only mode.But i cannot.I got an error
like database need to recover.could u please advice me to make the whole
database read only?

Thnkx in advance
Jeyaseelan.M
and we said...
Alter tablespace tablespace_name READ ONLY;

do that for each tablespace that contains your data (but not TEMP, ROLLBACK, or
SYSTEM).

It'll be read only.  Alter them back to read/write when you are done.
Review & Followup
Rating: 4
Very useful February 11, 2002
Reviewer: A reader from ca, usa

Rating: 4
Valuable Advice February 11, 2002
Reviewer: Manoharan Jeyaseelan from singapore
Valubale advice

Rating: 4
back log February 18, 2002
Reviewer: Mohammed Ahmed
TOM,

I am forced to communicate like this. I am trying to ask a question since almost
2weeks keeping in view your receiving schedule. I always find it closed due to
back log. Suggest me what to do? shall i ask my question this way?

Followup:
Well, in this particular case -- I was on vacation so asking any questions would
have been totally in vain.  I quite simply wasn't here.

Think about it though.  If I say "I have a back log of questions, more then I
can answer right now" and you ask me another question (and bear in mind, there
are LOTS of people out there -- only one of me) how do you think that would
work?

It would defeat the purpose of me accepting only N questions at a time.  I
wouldn't be able to do this AND do my other job.

Sometimes I travel, I get out, I do seminars, I do presentations, I work with
customers tuning/benchmarking/designing (how else would I have the knowledge to
answer questions).

There are MANY resources on the net out there.  I suggest people look at using
the usenet newsgroups (google provides FREE access to that resource).
otn.oracle.com also has discussion groups.

February 22, 2002 Bob -- Thanks for the question regarding "Is there a way to see all index names and all fields in the index?", version 8.1.7
You Asked
Hi Tom,
Is there a way to see all indexes in a schema AND the fields that comprise the
index? I see the view ALL_INDEXES can give me the name of the index and the
owner, but I couldn't find how to see all the fields in the index. I would want
to see
index name1
   col1
   col2
index name2
   col1
   col2
   col3
etc.
Thanks.
and we said...
See all_ind_columns

This is a nifty query that will show you the info on a "single row"

select substr(a.index_name,1,30) index_name,
       decode(a.uniqueness,'UNIQUE','Yes','No') uniqueness,
max(decode( b.column_position,  1, substr(b.column_name,1,30),
NULL )) ||
max(decode( b.column_position,  2, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position,  3, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position,  4, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position,  5, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position,  6, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position,  7, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position,  8, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position,  9, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position, 10, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position, 11, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position, 12, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position, 13, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position, 14, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position, 15, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position, 16, ', '||
substr(b.column_name,1,30), NULL )) columns
from all_indexes a, all_ind_columns b
where a.owner = :owner
and a.table_name = :tname
and b.table_name = a.table_name
and b.table_owner = a.owner
and a.index_name = b.index_name
group by substr(a.index_name,1,30), a.uniqueness
/

assuming you have 16 or less columns in the index.  and if you don't -- well, i
won't go there ;)
Review & Followup
Rating: 5
Excellent February 22, 2002
Reviewer: Bob from NY in USA
Great useful query. I always get some serious use and knowledge from your site.
Thanks.

Rating: 3
Index use on cursor February 22, 2002
Reviewer: ashok from PA,USA
I have a query like this

 cursor c1(para1 varchar2(10), para2 varchar2(10) ) is
  select * from tab1
  where a = para1
   and  b like para2||'%'

 in the explain plan when i write like this

  select * from tab1
  where a = para1
   and  b like '11'||'%' ;

 it does uses a right indexes which is on column b and a .
  but when
    select * from tab1
  where a = para1
   and  b like ''||'%' ;
 it uses a another index which is on table a

now my dba is saying while workin on pl/sql program query
is working in
   select * from tab1
  where a = para1
   and  b like ''||'%' ; manner

please let me know how can i use index on column b and a

thanks and regards
ashok

Followup:
create an index on tab1(a,b)

Rating: 5
Good example February 22, 2002
Reviewer: Rashid from Washington DC, USA
This is just what I needed for my job. Good job, Tom. Keep up the good work.

February 23, 2002 Harold -- Thanks for the question regarding "Archive Question", version 8.1.7
You Asked
Hello

If i have a physical backup that i took before before i put my DB on archive
mode, this will be a problem if i need to recover the DB or i have to put my DB
on archive mode after the backup???

as follows
1. Shutdown immediate
2. physical backup
3. archive mode
4. open db

or

1. shutdown
2. archive mode
3. open db
4. shutdown immediate
5. physical backup
6. open db

Thanks for your time.

Regards
and we said...
that physical backup taken before switching to archive log mode can be restored
BUT NOT POINT IN TIME RECOVERED.  You can only restore it to the point in time
of the backup itself, you'll have lost all work performend on it since the
backup.

do #2 above.
February 23, 2002 Alan -- Thanks for the question regarding "Sizing Rollback Segment Correctly and Temporary Tablespace", version 8.1.7
You Asked
Hi Tom;

Glad you're back!

I'm working on a very large databases and we have some problems on our rollback
segment. It keeps our erroring out everytime we run our batch processing
routines, unable to extend rollback segment. The batch process goes like this:

    1st. Updates rows in Main Table (fact)
    2nd. Insert rows in Our History Table

All of these statements are in one SQL statement like (update col = value, col2
= value where rowid in (select rowid from table where clause) and insert into
table (COL1,...COL2) select col1,...col2 from table where clause;

The average row length for the main table is 2030 and we're only updating two
columns which both columns has an attribute of varchar2(512) and were expecting
to update for 4 million rows for each batch run. And for the history table we
insert 4 rows per updated row in the main table which would be roughly 13
million rows per batch run. The average row length of history table is 64.

What would be the correct size for the rollback segment and temporary table
space to avoid this problem of unable to extend rollback segment?

And also can you explain to me how oracle handles the rollback segment, let say
I have a table with 2 columns col1 varchar2(512),
col2 number, if I try to update col1, will oracle just keeps the previous copy
of the col1 in the rollback segment or it needs to copy the entire row?

Thank you very much.
and we said...
we put before images in the rbs (along with bookeeping information).  You can
estimate how much RBS you'll need via a technique like this.  Say you wanted up
update the rows in a table that looked like "all_objects".  You might:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t as
  2  select * from all_objects where rownum <= 1000;

Table created.

create a sample of it...

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> update t set object_name =
rpad('x',length(object_name),'x');

1000 rows updated.

do the update...

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select used_ublk
  2    from v$transaction
  3   where addr = ( select taddr
  4                    from  v$session
  5                   where sid = ( select sid
  6                                   from v$mystat
  7                                  where rownum = 1 ) )
  8  /

 USED_UBLK
----------
        15

ops$tkyte@ORA817DEV.US.ORACLE.COM> rollback;

Rollback complete.

and see what you used...  Make sure to have indexes in place:

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx on t(object_name);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> update t set object_name =
rpad('x',length(object_name),'x');

1000 rows updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select used_ublk
  2    from v$transaction
  3   where addr = ( select taddr
  4                    from  v$session
  5                   where sid = ( select sid
  6                                   from v$mystat
  7                                  where rownum = 1 ) )
  8  /

 USED_UBLK
----------
        43

ops$tkyte@ORA817DEV.US.ORACLE.COM> rollback;

Rollback complete.

as you can see -- they make a huge difference!

Temporary tablespace -- that depends on many factors, such as the sort area
size.  I personally put temp into autoextentable tempfiles and let them grow as
big as the disk allows at that time (if I don't have enough disk, i don't have
enough disk).  I'll shrink them later if I want to...
Review & Followup
Rating: 5
Superb February 23, 2002
Reviewer: alan sinsuan from AL, USA
Thanks tom.

February 23, 2002 Daniel -- Thanks for the question regarding "Intermedia query result order by date desc", version 9.0.1
You Asked
Hi Tom,

I want intermedia query result sorted by date desc (last in first out)
but can not afford sorting (20 million rows web application).

table created as:

create table menus
( id   number primary key,
  recipe_xml_text clob,
  date_created date
)

intermedia index created as:

create index xml_idx on menus (recipe_xml_text)
indextype is ctxsys.context
parameters ('section group ctxsys.auto_section_group')

regular index created as:
create index date_created_idx on menus (date_created desc)

query A:
select id,date_created from menus where
contains(recipe_xml_text,'keyword within tags')>0
and date_created <= SYSDATE
and rownum < 101 order by date_created desc;

The query result set did not satisfy the last in first out by date.

query B: added first_rows hint.
select /*+ FIRST_ROWS */ id,date_created from menus where
contains(recipe_xml_text,'keyword within tags')>0
and date_created <= SYSDATE
and rownum < 101 order by date_created desc;

the matched rows came out with latest date first,but if there is no match on the
search text,
the query did not come back right away.

Both queries ended up with the same Execution Plan:
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: FIRST_ROWS (Cost=59 Card=1
          Bytes=210)

   1    0   COUNT (STOPKEY)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'MENUS
          ' (Cost=55 Card=1 Bytes=210)

   3    2       INDEX (RANGE SCAN) OF 'DATE_CREATED_INDEX' (NO
          N-UNIQUE) (Cost=6 Card=1)

Is there a solution for query B to come back right away if no match is found ?

I know it's difficult to combine domain index with regular index (to use xml_idx
and date_created_idx simultaneously),If only the score can be used here,is there
any
way I can manipulate the score (like put more weight on the date_created) so
that
order by score desc will generate the desired (last in first out) result ?

Thank You!
and we said...
Well, your last query there says:

o goto the index and row by row check each row if the row contains that data
(20,000,000 times!!!)

If the "latest" rows don't have matches, this will take a long time.

o the query that uses the text index first AND THEN sorts will be very very fast
if there are a couple of hundred rows.

It is my humble opinion that if a search returns more then say 500 hits, it is a
totally useless search.  It NEEDS to be refined.  No one is EVER going to look
at more then that (goto google or anywhere, you generally get 10 hits/page, 10
pages to click thru... Have you ever gone further then that?)

In fact, goto google and search for Oracle.  They show 10 hits/page.  They
showed me 83 pages -- I am 100% sure there are more then 830 pages out there
with "oracle".  In fact on the very last page they say:

"In order to show you the most relevant results, we have omitted some entries
very similar to the 822 already displayed."

basically saying "refine your SEARCH!!!!"

What I do is this:

o limit the text search to 500 hits.
o order by WHATEVER after that.

sorting 500 rows will take fractions of a second, finding 500 rows will too.  I
then also put up "your search returned WAY too much data, here are 500 good
hits.  Please refine your criteria to get more meaningful results".

You cannot manipulate the score, the score is based on the words in the text and
the search terms.
February 23, 2002 Munzer -- Thanks for the question regarding "Pl/SQL questions", version 8.1.7
You Asked
Tom:

Welcome back.  Hope you enjoyed your vacation.

1.  In Pl/SQL I was trying to check if a variable value exists in a oracle
tables as :

variable :='VA,CA,WA'

FOR x in (select * from table where col1 in  (variable) );

It did not work.  would you use bind variable here. owa_util.bind_variables

How would you do it.

or let us say that the variablke is an array type and I want to find records
that meet the variable value.

2.  Would you do a loop for a select statement that runs as many times as the
array is defined for.

state  types.array

begin
FOR i in 1..state.count LOOP
select * from table wher col1=state(i)
LOOP
..
END LOOP

end;

3.  Can i define a cursor with a parameter that can have either a null value or
'COMPLETE'.
If user does not select flag it should find null values for col1.
If user selects flag ir should find null values and values of 'complete'

CASE  1

SELECT * from table where col1 is null

CASE 2

SELECT * from table where col1 is null or col1='complete'

DO I HAVE T ODEFINE TWO CURSORS FOR THIS AND RUN EACH BASE ON VALUE OF
PARAMETER?

Thank you,
and we said...
1) see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:110612348061
for how to do that using a bind variable.

2) no, see #1

3) well, in this particular case -- since you are using "where col is NULL" all
of the time -- i would just code the query as:

  select * from table where col1 is null or col1 = PLSQL_VARIABLE;

and PLSQL_VARIABLE would either have 'complete' or NULL assigned to it.  The
performance in this case will be the same.

In general, you would use a ref cursor -- see:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1288401763279
for an example.
February 23, 2002 ramesh -- Thanks for the question regarding "partitions", version 8.1.5
You Asked
One of my table is partitioned. It has 20 partitions, partitioned by range. The
table has 60,000,000 records.I want to use two partitions in the same sql query.

I want to select data from two partitions  p1 and p2 in single query
Please give me a example how to use it?

Thanks
and we said...
well, just use a union all

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t partition ( part_98 )
  2  union all
  3  select * from t partition (part_99);
February 23, 2002 Jia -- Thanks for the question regarding "do matching using pl/sql table, cursor?", version 9.0.0
You Asked
Hi Tom,
Here is the scenario, there's a table TEMPLATE_CRITERIA

desc TEMPLATE_CRITERIA
Name                            Null?    Type
 ------------------------------- -------- ----
 TEMPLATEID              NOT NULL NUMBER(38)
 CRITERIONVALUE1                  VARCHAR2(255)
 CRITERIONVALUE2                  VARCHAR2(255)
 CRITERIONVALUE3                  VARCHAR2(255)
 CRITERIONVALUE4                  VARCHAR2(255)
 CRITERIONVALUE5                  VARCHAR2(255)
 CRITERIONVALUE6                  VARCHAR2(255)
 CRITERIONVALUE7                  VARCHAR2(255)
 CRITERIONVALUE8                  VARCHAR2(255)
 CRITERIONVALUE9                  VARCHAR2(255)
 CRITERIONVALUE10                 VARCHAR2(255)
 CRITERIONVALUE11                 VARCHAR2(255)

TEMPLATEID is the primary key, the other columns are criterias for this
template, this table has around 10k records. We need to do a matching, return
the TEMPLATEID of the record which matchs the best. The input to the matching is
an array of ordered values [A],the size of the array is 11, A[1] should compare
to column CRITERIONVALUE1, A[2] should compare to column CRITERIONVALUE2...A[i]
can be null. Basically,the comparation need to loop through the table. We used
to do the matching algorithm through c++ code, matching is done with a scoring
algorithm that uses an array to score matches and select the best template. We
loaded the data from the table to a 2 dimentinal array B[i,j], each B[j] denotes
a new record, B[i,j] denote a criteria value for a given template from the
table. Then use Score and Conflict, S[j] is equal to the count of criteria that
match between the input and a given template(A[i] = B[i,j]); C[j] is equal to
the count of conflicts between the input and a particular template. (B[i,j] is
not null and A[i]<>B[i,j]) Then we choose the record which has the highest
score, if score is even, then choose lower conflict and return the TEMPLATEID.
My question is: since the table is not small, instead of passing data to c++, if
we do this by stored proc, how about the performance? which is better compared
to c++ code? I am thinking of use corsor, pl/sql table (but since pl/sql doesn't
direct support 2 dimentional arry, has to use table of records)... Any other
suggestion? Thanks a lot! Your book is awesome!
and we said...
why cannot you do this in a query?  The database is very very fast (believe it
-- or not).

Sounds sort of like:

select * from (
select templateid,
       decode( criterionvalue1, :bv1, 1, 0 )+
       decode( criterionvalue2, :bv2, 1, 0 )+
...
       decode( criterionvalue11, :bv11, 1, 0 ) score,
       decode( criterionvalue1, NULL, 0, :bv1, 0, 1 )+
       decode( criterionvalue2, NULL, 0, :bv2, 0, 1 )+
...
       decode( criterionvalue11, NULL, 0, :bv11, 0, 1 ) conflict
  from t
 order by 2 DESC, 3) where rownum = 1;

Just make sure to "alter table t cache" so the first full scan will cache the
data and let the database do its job!
Review & Followup
Rating: 4
February 23, 2002
Reviewer: Jia

February 23, 2002 -- Thanks for the question regarding "update statement on an indexed column", version 9i
You Asked
Hi Tom,
What happens when an indexed column in a B-tree index is set in an update
statement, but the value does not change?  Does oracle recognize that the old
and new values are the same, and skip the expensive update of the index entry?
Or does it not check whether the value actually changed, and always update the
index entry?

For example, if c is an indexed column (in a B-tree index) whose current value
is 10, and the following update statement is executed:
   update t set c = 10;

Will oracle check if we are setting the column to its existing value, and skip
the index entry update if we are?

I know it's not good form to update indexed columns if they are not actually
changing, but it happens sometimes in legacy code.

Thanks!
and we said...
It is pretty smart about it.  here is a test showing the amount of redo/rollback
generated when updating 1000 rows

a) without an index on it
b) with an index on it and the update doesn't change the value
c) with an index on it and the update does change the value

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t as select * from all_objects
where rownum <= 1000;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace on statistics;

ops$tkyte@ORA817DEV.US.ORACLE.COM> update t set object_name = object_name;
1000 rows updated.

Statistics
----------------------------------------------------------
...
     296528  redo size
...
       1000  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM> select used_ublk
  2    from v$transaction
  3   where addr = ( select taddr
  4                    from  v$session
  5                   where sid = ( select sid
  6                                   from v$mystat
  7                                  where rownum = 1 ) )
  8  /

 USED_UBLK
----------
        15

1 row selected.

so, 290K of redo, 15 blocks of UNDO..

ops$tkyte@ORA817DEV.US.ORACLE.COM> rollback;
Rollback complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx on t(object_name);
Index created.

now with the index...

ops$tkyte@ORA817DEV.US.ORACLE.COM> update t set object_name = object_name;
1000 rows updated.

Statistics
----------------------------------------------------------
...
     296288  redo size
...
       1000  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM> select used_ublk
  2    from v$transaction
  3   where addr = ( select taddr
  4                    from  v$session
  5                   where sid = ( select sid
  6                                   from v$mystat
  7                                  where rownum = 1 ) )
  8  /

 USED_UBLK
----------
        15

1 row selected.

same as without the index! but now, lets actually change the value

ops$tkyte@ORA817DEV.US.ORACLE.COM> rollback;
Rollback complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> update t set object_name =
reverse(object_name);

1000 rows updated.

Statistics
----------------------------------------------------------
...
     908348  redo size
...
       1000  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM> select used_ublk
  2    from v$transaction
  3   where addr = ( select taddr
  4                    from  v$session
  5                   where sid = ( select sid
  6                                   from v$mystat
  7                                  where rownum = 1 ) )
  8  /

 USED_UBLK
----------
        43

1 row selected.

as you can see, very very different results...

ops$tkyte@ORA817DEV.US.ORACLE.COM> rollback;
Rollback complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off
February 23, 2002 Bricklen -- Thanks for the question regarding "sql loader controlfile, parsing the row for multiple values...", version 8.1.7
You Asked
Hi Tom, thank you again for this great resource, it's been a real help several
times in the past.  I checked your archives, but couldn't find anything that
could solve my problem.  Here goes...

I am trying to set up sql loader as my method of insertion, as well as my parser
-- to work as one step.  I am working on a test control file to parse each row,
and extract various strings of data, which may or may not be there, and could be
in any order in the string.  I've developed some sql that will work (works fine
at the sql prompt), but it only seems to check the row for the first column, and
ignores it for the rest (when used in the controlfile).

here it is, with some test data:

LOAD DATA
INFILE *
INTO TABLE test_table
APPEND
WHEN (1) = '>'
FIELDS TERMINATED BY '|'
TRAILING NULLCOLS
(
  INN
"NVL(SUBSTR(:INN,INSTR(:INN,'IN=',1),INSTR(SUBSTR(:INN,INSTR(:INN,'IN=',1)),'
',1)),'0')",
  OUTT
"NVL(SUBSTR(:OUTT,INSTR(:OUTT,'OUT=',1),INSTR(SUBSTR(:OUTT,INSTR(:OUTT,'OUT=',1))
,' ',1)),'0')",
  MAC
"NVL(SUBSTR(:MAC,INSTR(:MAC,'MAC=',1),INSTR(SUBSTR(:MAC,INSTR(:MAC,'MAC=',1)),'
',1)),'0')",
  LEN
"NVL(SUBSTR(:LEN,INSTR(:LEN,'LEN=',1),INSTR(SUBSTR(:LEN,INSTR(:LEN,'LEN=',1)),'
',1)),'0')",
  TOS
"NVL(SUBSTR(:TOS,INSTR(:TOS,'TOS=',1),INSTR(SUBSTR(:TOS,INSTR(:TOS,'TOS=',1)),'
',1)),'0')",
  PREC
"NVL(SUBSTR(:PREC,INSTR(:PREC,'PREC=',1),INSTR(SUBSTR(:PREC,INSTR(:PREC,'PREC=',1
)),' ',1)),'0')",
  ID
"NVL(SUBSTR(:ID,INSTR(:ID,'ID=',1),INSTR(SUBSTR(:ID,INSTR(:ID,'ID=',1)),'
',1)),'0')",
  PROTO
"NVL(SUBSTR(:PROTO,INSTR(:PROTO,'PROTO=',1),INSTR(SUBSTR(:PROTO,INSTR(:PROTO,'PRO
TO=',1)),' ',1)),'0')",
  RES
"NVL(SUBSTR(:RES,INSTR(:RES,'RES=',1),INSTR(SUBSTR(:RES,INSTR(:RES,'RES=',1)),'
',1)),'0')",
  SYN
"NVL(SUBSTR(:SYN,INSTR(:SYN,'SYN=',1),INSTR(SUBSTR(:SYN,INSTR(:SYN,'SYN=',1)),'
',1)),'0')",
  URGP
"NVL(SUBSTR(:URGP,INSTR(:URGP,'URGP=',1),INSTR(SUBSTR(:URGP,INSTR(:URGP,'URGP=',1
)),' ',1)),'0')",
  SRC
"NVL(SUBSTR(:SRC,INSTR(:SRC,'SRC=',1),INSTR(SUBSTR(:SRC,INSTR(:SRC,'SRC=',1)),'
',1)),'0')",
  SPT
"NVL(SUBSTR(:SPT,INSTR(:SPT,'SPT=',1),INSTR(SUBSTR(:SPT,INSTR(:SPT,'SPT=',1)),'
',1)),'0')",
  DST
"NVL(SUBSTR(:DST,INSTR(:DST,'DST=',1),INSTR(SUBSTR(:DST,INSTR(:DST,'DST=',1)),'
',1)),'0')",
  DPT
"NVL(SUBSTR(:DPT,INSTR(:DPT,'DPT=',1),INSTR(SUBSTR(:DPT,INSTR(:DPT,'DPT=',1)),'
',1)),'0')"
)
BEGINDATA
>INPUT DROP IN=eth0 OUT= MAC=5254402572700060979442e00800 SRC=209.53.156.3
DST=209.53.156.10 LEN=60 TOS=0:x00 PREC=0:x00 TTL=64 ID=1299 DF PROTO=TCP
SPT=2658 DPT=113 WINDOW=5840 RES=0:x00 SYN URGP=0|
>STANDARD CONNECTION IN=eth1 OUT=eth0 SRC=192.168.0.2 DST=216.239.51.101 LEN=60
TOS=0:x00 PREC=0:x00 TTL=63 ID=23564 DF PROTO=TCP SPT=1024 DPT=80 WINDOW=5840
RES=0:x00 SYN URGP=0|
>STANDARD CONNECTION IN=eth1 OUT=eth0 SRC=192.168.0.2 DST=216.239.51.101 LEN=60
TOS=0:x00 PREC=0:x00 TTL=63 ID=35143 DF PROTO=TCP SPT=1025 DPT=80 WINDOW=5840
RES=0:x00 SYN URGP=0|
this is only a test Line to see If it will skip it (although it should, since
there is no > prefixing it.   |
>STANDARD CONNECTION IN=eth1 OUT=eth0 SRC=192.168.0.2 DST=209.53.156.6 LEN=60
TOS=0:x00 PREC=0:x00 TTL=63 ID=22598 DF PROTO=TCP SPT=1026 DPT=22 WINDOW=5810
RES=0:x00 SYN URGP=0|
>STANDARD CONNECTION IN=eth3 OUT=eth0 SRC=194.168.0.5 DST=204.53.146.6 LEN=60
TOS=0:x00 PREC=0:x00 TTL=63 ID=22498 DF PROTO=TCP SPT=1013 DPT=22 WINDOW=5840
RES=0:x00 SYN URGP=0|
>INPUT DROP TOS=0:x00 IN=eth5 DST=203.62.116.110
MAC=234323452345000609442e0234500 SRC=249.54.156.63 LEN=60 PREC=0:x00 TTL=64
ID=1229 OUT= DF PROTO=TCP SPT=2454 DPT=323 WINDOW=5830 RES=0:x00 SYN URGP=0|

the test_table simply has varchar2(40) for all the columns, except MAC, which is
a varchar2(100).

the results, when queried from the test_table:
INN     OUTT MAC LEN    (shortened to fit)
------- ---- --- ---
IN=eth5 0    0   0
IN=eth0 0    0   0
...

so, my questions are then:
- how do I parse the string for certain values:
(eg. IN=yadda,OUT=yoodah,PROTO=doodah, etc.)
- or if what I'm doing is fine, how do I get sqlldr to parse the row again for
each column?
- any possible performance issues with this type of parsing, that you can think
of offhand (recommendations?)?

I'm also working on a function with that line of code in it for each row, to put
it into bind variables for insert, but haven't met with success yet.

Any ideas?

Cheers,

Bricklen

ps. I've also being using your book as a resource, which is what got me started
on using functions for the parsing.
and we said...
Well, the fields are NOT terminated by '|' -- there is NO second field 'outt'.

Looks like you want to use that first field as the bind variable in ALL fields:

TRAILING NULLCOLS
(
  INN
"NVL(SUBSTR(:INN,INSTR(:INN,'IN=',1),INSTR(SUBSTR(:INN,INSTR(:INN,'IN=',1)),'
',1)),'0')",
  OUTT
"NVL(SUBSTR(:INN,INSTR(:INN,'OUT=',1),INSTR(SUBSTR(:INN,INSTR(:INN,'OUT=',1))
,' ',1)),'0')",

....

A simple test in plus will confirm this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> variable inn varchar2(2000)
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec :inn := 'INPUT DROP IN=eth0 OUT=
MAC=5254402572700060979442e00800 SRC=209.53.156.3 DST=209.53.156.10 LEN=60
TOS=0:x00 PREC=0:x00 TTL=64 ID=1299 DF PROTO=TCP SPT=2658 DPT=113 WINDOW=5840
RES=0:x00 SYN URGP=0';

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select
nVL(SUBSTR(:INN,INSTR(:INN,'IN=',1),INSTR(SUBSTR(:INN,INSTR(:INN,'IN=',1)),'
',1)),'0')
  2  from dual;

NVL(SUBSTR(:INN,INSTR(:INN,'IN=',1),INSTR(SUBSTR(:INN,INSTR(:INN,'IN=',1)),'',1)
--------------------------------------------------------------------------------
IN=eth0

1 row selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select
NVL(SUBSTR(:inn,INSTR(:inn,'OUT=',1),INSTR(SUBSTR(:inn,INSTR(:inn,'OUT=',1)) ,'
',1)),'0')
  2  from dual;

NVL(SUBSTR(:INN,INSTR(:INN,'OUT=',1),INSTR(SUBSTR(:INN,INSTR(:INN,'OUT=',1)),'',
--------------------------------------------------------------------------------
OUT=

1 row selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select
NVL(SUBSTR(:INN,INSTR(:INN,'MAC=',1),INSTR(SUBSTR(:INN,INSTR(:INN,'MAC=',1)),'
',1)),'0')
  2  from dual;

NVL(SUBSTR(:INN,INSTR(:INN,'MAC=',1),INSTR(SUBSTR(:INN,INSTR(:INN,'MAC=',1)),'',
--------------------------------------------------------------------------------
MAC=5254402572700060979442e00800

1 row selected.

It is very similar to what I do on:
http://osi.oracle.com/~tkyte/SkipCols/index.html
under "option 2"

or see page 381-3 of my book where I show you can reference ALL/ANY of the other
fields in the control file for any variable.
Review & Followup
Rating: 5
Hmmm... February 23, 2002
Reviewer: Bricklen from Victoria, BC, Canada.
As you may have guessed, I am not a habitual sql loader user.  That is about to
change.  ;-)

Your response was excellent, thank you.  You not only pointed out some niggling
problems that I couldn't figure out, you sent me in the direction to solve the
problems, with some more ideas for future implementations.

Thanks again,

Bricklen

February 24, 2002 sushil -- Thanks for the question regarding "how good is execute immediate or dynamic sql", version 8.1.7
You Asked
Tom,

I am using execute immediate in my package to call a function inside the same
package.

Since procedure to be called is varying and I have got lot of procedures so this
seems to be easy way of doing .In other case I will have to make n no of
if..elsif.. conditions.

When actually looking from performance point of view isn't it bad.
Why I am saying so is because even in dynamic sql statements they are mandatory
to parse which means parsing statements even if they are there in cache.
On the other hand if I am calling the procedures then the statements are not
reparsed if present in cache.

This may be fine with a procedure call in execute statement or dynamic sql but
what about other statements like a complex select.

Tks
sushil
and we said...
I would personally go with STATIC code, the performance difference can be very
large.  Consider this trivial example:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package demo_pkg
  2  as
  3          procedure p;
  4
  5          procedure run_static;
  6          procedure run_dynamic;
  7  end;
  8  /
Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package body demo_pkg
  2  as
  3
  4  procedure p
  5  is
  6  begin
  7          null;
  8  end;
  9
 10  procedure run_static
 11  is
 12  begin
 13          p;
 14  end;
 15
 16  procedure run_dynamic
 17  is
 18  begin
 19          execute immediate 'begin demo_pkg.p; end;';
 20  end;
 21
 22  end;
 23  /
Package body created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table run_stats;
Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table run_stats ( runid varchar2(15),
name varchar2(80), value int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view stats
  2  as select 'STAT...' || a.name name, b.value
  3        from v$statname a, v$mystat b
  4       where a.statistic# = b.statistic#
  5      union all
  6      select 'LATCH.' || name,  gets
  7        from v$latch;

View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_start number;
  3      l_cnt   number;
  4  begin
  5      insert into run_stats select 'before', stats.* from stats;
  6
  7      l_start := dbms_utility.get_time;
  8      for i in 1 .. 1000
  9      loop
 10           demo_pkg.run_static;
 11      end loop;
 12      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 13
 14      insert into run_stats select 'after 1', stats.* from stats;
 15
 16      l_start := dbms_utility.get_time;
 17      for i in 1 .. 1000
 18      loop
 19           demo_pkg.run_dynamic;
 20      end loop;
 21      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 22
 23      insert into run_stats select 'after 2', stats.* from stats;
 24  end;
 25  /
1 hsecs
32 hsecs

PL/SQL procedure successfully completed.

thats pretty dramatic -- but only HALF of the story!  compare the stats (you
must be on a single user system to do this, else other sessions will impact the
following report)

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value-a.value run1,
c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11   order by abs( (c.value-b.value)-(b.value-a.value))
 12  /

NAME                                           RUN1       RUN2       DIFF
---------------------------------------- ---------- ---------- ----------
...
STAT...recursive calls                          165       1030        865
STAT...execute count                             12       1005        993
STAT...opened cursors cumulative                 12       1005        993
STAT...parse count (total)                       12       1005        993
LATCH.shared pool                                88       1135       1047
STAT...redo size                              23964      20972      -2992
LATCH.library cache                             296      19335      19039

48 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

Look at the amount of LATCHING going on there to do that dynamic sql (doesn't
matter if we HARD parse or SOFT parse -- it still is a parse and parses are
expensive!  A soft parse is LESS expensive but still costs something).

Remembering that latches are a serialization device, a lock, you will have to
take into consideration that as you ADD users -- your concurrency will go DOWN
the tubes.  You won't scale, won't run as fast as you could.

Here, I would counsel you to use static code if at all possible.
Review & Followup
Rating: 5
February 21, 2002
Reviewer: Jim from Oz
I had a similar scenario last week.
I wrote a PRO*C application which builds
SQL based on a table name and operation (S,U,D,I)
passed in I thought there was a degree of overhead.

TKPROF showed  execute immediate used nearly 10 times as much CPU time over
static SQL.

Further testing similar to Tom's examples showed
results similar to Tom's findings.

STATIC SQL adds program maintenance however it is
FAR FAR better performing in our case.

End result was using Stored Procs (packaged of course!)and
called by Pro*C app.

Rating: 4
Reader February 21, 2002
Reviewer: A reader
Tom,

The stats from v$mystat and v$latch is very good
for comparison. Do you have a similar technique
for wait stats also

Thanks

Followup:
Not for this script -- no -- but only because I make it run in a single user
environment (and waits are few there, i own the machine).  Adding wait
times/events would be trivial however -- just modify the view to include them

Rating: 3
as usual , good one February 21, 2002
Reviewer: sushil vyas from india
Tks Tom.

Rating: 4
Reader February 23, 2002
Reviewer: A reader
Tom,

Your stats view collects from v$mystat, the SID specific
data. However from v$latch , all the GETS from all Sessions. How could you make
the script more accurate
by collecting GETS pertaining to SID in a multi-user environment. Is it
possible.
I was trying to connect v$latchholder.sid = v$mystat.sid.
Could not get desired results. Could you comment on this

Thanks

Followup:
Well, as I say:

...you must be on a single user system to do this, else other sessions will
impact the following report...

no other way (by the time you goto query this -- you are no longer the
latchholder!  that approach won't work.  We keep stats of the latch gets at the
system level, not at the session level)

Rating: 5
August 21, 2002
Reviewer: Jim
The test case Tom used here is a ripper.
I have slightly changed the scripts for my own use
and they have been brilliant Thanks Tom

Rating: 5
Ideal for learning the differences November 12, 2003
Reviewer: kiddo from san jose, ca
Great response in a nutshell! I was browsing to quickly understand the
differences.

February 24, 2002 Vishal -- Thanks for the question regarding "Fixed length output file using utl_file", version 8.1.7
You Asked
Hi Tom

I want to generate a fixed length output file using utl_file utility.
I have done the same for "pipe" delimited files but was confused how would I do
the same when the ouput file should be fixed length...I know it works in control
files when loading using Sql* Loader...but how can I achieve the same goal when
writing to a output file.

Regards
and we said...
utl_file.put_line( file,
                   rpad( field1, 30 ) ||
                   rpad( field2, 40 ) ||
                   ...
                   rpad( fieldN, 10 ) );

Just use rpad to pad each field out the its appropriate length.
Review & Followup
Rating: 5
February 24, 2002
Reviewer: Vishal Marken from Sydney

Rating: 5
Excellent September 20, 2002
Reviewer: Robert Chin from PA
I just implented this RPAD idea of yours to the unloader on p400 of your "Expert
One-on-One" (adding DESCRIBE_COLUMNS col_max_len). We used to declare pl/sql
records and fetch into it and then write the record content out to file to send
to client...now with this solution it's all dynamic, with a view, it's nearly a
black-box solutions !
Thanks for the solutions !

February 24, 2002 -- Thanks for the question regarding "statspack", version 8.1.7
You Asked
hi tom ,

1. <quote> from statspack whitepaper
   " an increase in the 'redosize' without an
increasing in the number
   of 'transactions per second' would indicate a
changing transaction
   profile"

   what does changing transaction profile mean?

2. <quote> from statspack whitepaper
   "unnecessary soft parses also limit application
scalablity;
    optimally a sql statement should be soft-parsed
once per session,
    and executed many times "

    i always thought that soft parse is finding the
query in shared
    pool. when ever i give any sql it searches for the
statement in
    the shared pool and then executes. am i right?
    can u explain the above quote with an example.

3. what is auxiliary database?
Oracle8i Designing and Tuning for Performance
and we said...
1) it means your transactions are all of a sudden doing MORE work, updating MORE
data, then they used to be.  Their "profile" or "what they do" has changed.

2) see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1391892424274
When I show the performance of the ref cursor -- it is doing a SOFT PARSE, when
I do a static sql query in PLSQL -- it is showing a "parse once, execute many
times".

A soft parse is better then a hard parse but much worse then NO PARSE at all.  A
statement should be parsed ONCE per session and executed as many times as
possible.  If you see that you have a parse count = execute count and parse
count > 1 -- you have a piece of code you could tune.  parse count should be 1,
execute should be >= 1.

3) a test database, another instance (i guess).

I could not find the word auxiliary in the 8i designing and tuning for
performance guide.
Review & Followup
Rating: 3
February 24, 2002
Reviewer: A reader
auxiliary database is in Backup & Recovery Guide

Followup:
yes, thats the definition I was assuming -- a "temp" instance, one that we would
use for TSPITR.

February 24, 2002 Muhammad Aamir -- Thanks for the question regarding "About droping column", version 8.1.5
You Asked
When we drop a column is there any rollback info generated because alter table
drop column is DDL.If not what is purpose of
 checkpoint (no of rows)in drop column command
and we said...
why do you (mistakenly) believe DDL doesn't generate rollback?

It does -- if the machine crashed in the middle of doing DDL, we need to be able
to undo that operation when we recover.

DDL is protected by redo and undo like everything else in the database.
Review & Followup
Rating: 4
please explain February 24, 2002
Reviewer: abeginner2001 from asia
youn said "DDL is protected by redo and undo like everything else in the
database." though ddl cannot be rollbacked.

Followup:
DDL is processed like this (conceptually)

begin
    COMMIT; -- any outstanding work
    begin
       DDL statement;
       COMMIT; -- the DDL statement
    exception
       when others then
            ROLLBACK;  -- any work done by the DDL
            RAISE;     -- reraise the exception back to the client
    end;
end;

DDL tends to do things to the data dictionary -- the data dictionary DRIVES the
rest of the entire system.  The transaction against the data dictionary must be
as short as possible (to avoid bring the system to a total screeching halt).
Therefore, we commit immediately before the DDL statement (so a failed DDL
statement will not rollback your work) and we commit or rollback immediately
after so as to have the transaction affecting the dictionary take as little time
as possible.

DDL cannot be rolled back under YOUR control, this is true -- it either commits
or rollsback automagically -- but it CAN and does in fact rollback.

Rating: 5
ddl And rollback February 25, 2002
Reviewer: muhammad Aamir Siddique from Pakistan
great

February 24, 2002 Stanley -- Thanks for the question regarding "ORA-00904 while creating materialized view", version 8.1.7
You Asked
Hi Tom,
the environment is:
Sun Solaris 5.8 64 bit E10k machine
Oracle8i Enterprise Edition Release 8.1.7.0.0 - 64bit Production

I am trying to create materialized view, the query involves a simple join
between 2 tables.

----------------------------------------------------------------
Example:

SQL>  create materialized view empmv
  2   as select a.rowid, b.rowid, a.name, b.dname
  3   from emptest a, depttest b
  4   where a.dno = b.dno;
 from emptest a, depttest b
      *
ERROR at line 3:
ORA-00904: invalid column name

SQL> desc emptest
Name      Null?                Type
ID      NOT NULL              NUMBER
NAME                          VARCHAR2(10)
SAL                           NUMBER
DNO                           NUMBER

SQL> desc depttest
Name      Null?                Type
DNO      NOT NULL              NUMBER
DNAME                          VARCHAR2(10)
----------------------------------------------------------------

I browsed thru metalink where i saw documentation on the problem listed as
bug-851227 but fixed in release 816.
I could not find information on any patches, if a patch needs to be applied for
release 817.

Could you pls let me know of any patches to be applied to solve this?
Thx
Stan
and we said...
alias the rowid columns

as select a.rowid A_ROWID, b.rowid B_ROWID, a.name, b.dname

eg:

scott@ORA817DEV.US.ORACLE.COM> create materialized view empmv
  2   as select a.rowid , b.rowid , a.ename, b.dname
  3   from emp a, dept b
  4   where a.deptno = b.deptno
  5  /
 from emp a, dept b
      *
ERROR at line 3:
ORA-00904: invalid column name

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> create materialized view empmv
  2   as select a.rowid A_ROWID, b.rowid B_ROWID, a.ename, b.dname
  3   from emp a, dept b
  4   where a.deptno = b.deptno
  5  /

Materialized view created.

scott@ORA817DEV.US.ORACLE.COM>

otherwise there are 2 "rowid" columns in there.
Review & Followup
Rating: 3
Thanks February 25, 2002
Reviewer: A reader
Thanks a lot, was involved too much into detail when your reply showed me the
simple error in the qry.
Thanks!

February 25, 2002 -- Thanks for the question regarding "Index usage when PK is disabled", version 8.1.7
You Asked
Hi Tom,
I would like to know if a query makes use of the index on the pk
i)when pk is disabled
ii)when pk cannot be enabled because of inconsistent data
my query looks some this like this
select * from table1 where table1.pk_col = 123;

Thanks in advance
and we said...
If there be an index, we'll use it.  In order to have a primary key index in
place for a disabled primary key constraint -- the index will have to be
NON-UNIQUE (else we drop the unique index when you disable the constraint)

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_pk_idx on t(x);
Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t add constraint t_pk primary
key(x);

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t modify constraint t_pk disable;
Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1 );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1 );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace on explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where x = 1;

         X
----------
         1
         1

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   INDEX (RANGE SCAN) OF 'T_PK_IDX' (NON-UNIQUE)

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off
Review & Followup
Rating: 5
isnt the purpose of using primary key index defeated February 24, 2002
Reviewer: Kulguru
Tom

A primary key is enforced by the underlying not null and unique constraint, if
we make it non unique, how will the integrity of the data in the column be
guaranteed. This doesnt sound right.

In what circumstances can we consider using non unique indexes for primary key
columns, and how shall we ensure that uniqueness is maintained in primary key
column.

Thank you

Followup:
Since version 8.0 of the database, we can and will enforce a primary key with a
UNIQUE or NON-UNIQUE index.

If you have a primary key constraint -- and it is enabled/enforced, rest assured
the data is unique.  That is OUR job.  Whether we do that with a unique or
non-unique index should not concern you.

Depending on how you create the primary key constraint we will either create a
unique or non-unique index.  If an EXISTING index is in place (be it unique or
not), we will "kidnap" that index and associate it with our primary key (thats
what MY example did -- I created a non-unique index, we used it for the primary
key).

Here is a better example:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int,
  2                   y int,
  3                   constraint t_pk primary key(x) )
  4  /

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select index_name, UNIQUENESS from
user_indexes;

INDEX_NAME                     UNIQUENES
------------------------------ ---------
T_PK                           UNIQUE

that shows that BY DEFAULT, we'll create a unique index on a primary key (this
is done in a schema with NO objects in it, so the index we see is one that is
created by our create table..

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;
Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int,
  2                   y int,
  3                   constraint t_pk_deferrable primary key(x) DEFERRABLE
);

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select index_name, UNIQUENESS from
user_indexes;

INDEX_NAME                     UNIQUENES
------------------------------ ---------
T_PK_DEFERRABLE                NONUNIQUE

that shows a DEFERRABLE constraint will by default create a NON-UNIQUE index
all by itself...

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;
Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int,
  2                   y int );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx on t(x,y);
Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t add constraint t_pk primary
key(x);
Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop index t_idx;
drop index t_idx
           *
ERROR at line 1:
ORA-02429: cannot drop index used for enforcement of unique/primary key

ops$tkyte@ORA817DEV.US.ORACLE.COM> select index_name, UNIQUENESS from
user_indexes;

INDEX_NAME                     UNIQUENES
------------------------------ ---------
T_IDX                          NONUNIQUE

ops$tkyte@ORA817DEV.US.ORACLE.COM>

and that shows that a primary key will "kidnap" an index, even one that is NOT
exactly the same as the primary key -- as long as it can be used to enforce the
primary key (since X is on the leading edge of this index, it can be used)

If you plan on disableing primary keys frequently -- consider a non-unique index
since (as demonstrated above) we do NOT drop the index while the constraint is
disabled.  This'll speed up the re-enable process.

February 25, 2002 atul -- Thanks for the question regarding "Redo Entries", version 8.0.4
You Asked
Hello...

I have question regarding redo entries..
I have a database having > 200 users..
sometimes i get message in alert log that "checkpoint not complete"
So database gets hang..

I know that this is because lot of redo entries r generating..
Some user is firing a job which triggers lot of redo entries..

My question is Is there any script or views frm which i know which user is
firing a job which creates lot of redo entries..

Thanks.
atul
and we said...
It does not mean one user is generating gobs of redo.  It just means you have
insufficient online redo logs configured for your system (you need to either
recreate your logfiles LARGER or add MORE of them).

You can use a query like this:

  1  select c.username, to_char( b.value/1024, '999,999,999' ) || ' kbytes'
  2  from v$statname a, v$sesstat b, v$session c
  3  where a.statistic# = b.statistic#
  4  and b.sid = c.sid
  5  and a.name = 'redo size'
  6  and c.username is not null
  7* and b.value > 0
ops$tkyte@ORA8I.WORLD> /

USERNAME                       TO_CHAR(B.VALUE/102
------------------------------ -------------------
CTXSYS                               32,698 kbytes
WEB$VSORENO                               1 kbytes
PUBLIC_USER                              14 kbytes
PUBLIC_USER                              24 kbytes
PUBLIC_USER                               6 kbytes
PUBLIC_USER                               5 kbytes
PUBLIC_USER                               4 kbytes
PUBLIC_USER                              11 kbytes
PUBLIC_USER                              16 kbytes
WEB$VSORENO                               5 kbytes
PUBLIC_USER                               3 kbytes
PUBLIC_USER                               6 kbytes
WEB$VSORENO                              47 kbytes
PUBLIC_USER                               1 kbytes

14 rows selected.

to see how much redo each currently connected session has generated.
Review & Followup
Rating: 4
Thanks February 24, 2002
Reviewer: atul from india
That's very useful to me now i can know who is generating a lots of redo..
I added sid,serial#,process in ur script so i can get sid and serial no to
kill..

thanks.
atul

Followup:
well, thats rather draconian.   Perhaps -- just perhaps -- what you really need
to do is figure out what they are doing, if it is valid for them to be doing
that and SIZE your redo correctly.

Generating lots of redo is NOT in itself a bad thing.  Killing sessions
arbitrarily, that is a bad thing.

February 25, 2002 Jon -- Thanks for the question regarding "Update, index and bind variables", version 8.1.6
You Asked
We generate packages to do standard data access to our tables.  My  question
relates to the UPDATE part of the data access packages (the generated package
updates by primary key only).

The invoker of the procedure passes in a "map" record and a "change" record.
The "map" record consists of  one boolean field for each column on the table.
It is set to FALSE if no update is to occur, and TRUE if the "change" record
contains a value which needs to be reset on the row.

Up until now, we have used these to build a dynamic SQL statement which is then
executed.  But this means each SQL statement is new and reparsing, etc. must
occur.  The alternative would be to retrieve the record from the database
(remembering this is by PK, so it is fast) and build a SQL statement that would
contain a SET statement for EVERY column, except the primary key column(s).
Columns which are to change  (known from the "map" record) would be set to the
new values, and columns which are not changed would be set to equal the value on
the existing record.  This alternative was challenged on the basis that even if
the columns are being set to their current value, there could be a serious
impact on the indexes, including potentially severe fragmenting if there are a
relatively large number of indexes on the table.

I would like to take advantage of bind variables (read your book and got your
message!) which we could achieve using the alternative update, but I have to
know if this index concern is real or not.

Ignore the overhead of going and getting the record by PK, as we do that in
either case, for another reason.
and we said...
Well, in addition to the index issue (which is true) there is the issue that
your REDO generation will go way up as well.

You are between the proverbial rock and hard place.

Now, I would say that you should be able to use bind variables here
regardless!!!

You will have a finite number of statements -- say a table has an ID and columns
C1, C2, C3.  the only updates you should EVER generate are:

update t set c1 = :c1 where id = :id;update t set c1 = :c2 where id = :id;update
t set c1 = :c3 where id = :id;update t set c1 = :c1, c2 = :c2 where id = :id;
update t set c1 = :c1, c3 = :c3 where id = :id;
update t set c2 = :c2, c3 = :c3 where id = :id;
update t set c1 = :c1, c2 = :c2, c3 = :c3 where id = :id;

You have a finite number SQL statements that will be generated for ANY table
ever (thats the important point here!)  Oracle Forms can take this approach for
example -- you can either update all columns (default) or just the ones that
changed (then it constructs the proper SQL like above)

It would be NICE if you could avoid the softparse, but if you set
session_cached_cursors to some good number, you'll avoid alot of that overhead
as well.

So, use bind variables!

Make sure to generate the update in a predicable fashion (eg: never "set c1 =
:c1, c2 = :c2" and later "set c2 = :c2, c1 = :c1" - do the columns in the same
order)

use session_cached_cursors
Review & Followup
Rating: 5
How will the above lead to index fragmentation, can you please explain briefly January 22, 2002
Reviewer: Kulguru
How  will the above lead to index fragmentation, can you please explain briefly.

Followup:
well look at that.

testing is showing that the index update is ignored -- its not doing it (it
optimized it right out of there).  another case of conventional wisdom out the
window....

So, that (the index) is not a concern -- but the row data in the table, that is,
it'll generate gobs of extra redo.

scott@ORA817DEV.US.ORACLE.COM> update emp set empno = empno;

14 rows updated.

Statistics
----------------------------------------------------------
          2  recursive calls
         20  db block gets
          2  consistent gets
          0  physical reads
       3648  redo size
        830  bytes sent via SQL*Net to client
        554  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
         14  rows processed

scott@ORA817DEV.US.ORACLE.COM> update emp set empno = empno, ename = ename, job
= job, mgr = mgr, hiredate = hiredate, sal = sal, comm = comm, deptno = deptno;

14 rows updated.

Statistics
----------------------------------------------------------
          0  recursive calls
         18  db block gets
          1  consistent gets
          0  physical reads
       5104  redo size
        845  bytes sent via SQL*Net to client
        653  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
         14  rows processed

scott@ORA817DEV.US.ORACLE.COM>

So -- nix the index stuff (thanks for making me really look at it) but remember
the extra redo generated.

Rating: 3
You were clear but the solution is not helpful January 22, 2002
Reviewer: Jon from Hartford, CT
This is viable for a table with a very small number of columns but not
otherwise.  If a table has 2 columns plus a PK, the solutions are (read "1" as
"set c1 = :1"; read "12" as "set c1 = :1, c2 = :2", etc.):

1
2
12

For 3 columns,
1
2
3
12
13
23
123

For 4 columns,
1
2
3
4
12
13
14
23
24
34
123
124
134
234
1234

So for "n" columns, we have (2 to the nth) - 1 statements.

If we have 39 columns, we have 5.498E+11 Statements!!  Are we stuck between the
rock and the hard place?  It seems like it.

Followup:
Yes, if you want to selectively update the columns -- tradeoff -- DISK vs
CPU+RAM.

Me, I would probably opt for the disk hit personally.

One other comment on this.  Will your end users really generate 5.4E11
STATEMENTS (i think not).  there will be some small working set that you will
generate, it'll be small, it'll be finite.  As long as you make the sql in a
predicable fashion -- using bind variables -- you'll be just fine.

Rating: 3
January 22, 2002
Reviewer: Mio
How about

update t
set
c1 = decode(:changed1, 1, :c1, c1),
c2 = decode(:changed2, 1, :C2, C2)
...
/
?

Followup:
that will have the effect of updating every column -- greatly increasing the
REDO.  Yes, you can do it -- but be aware of the consequences.

Rating: 4
Well, yes, but remember this is code generation January 22, 2002
Reviewer: Jon from Hartford, CT
So while the users may only typically update a relatively small portion of the
columns, how does one extract which those are from the data dictionary?

By the way, thanks for checking again on the index update/fragmentation.  That
was in dispute and was the crux of my question.  But of course, now we have the
REDO issue.  So in your opinion, if you had the choice of building the UPDATE
statement and executing it dynamically, or updating all the columns and
incurring the REDO, which would you do?

Followup:
Well, you don't have to get it from the data dictionary -- you don't have to
PRE-GENERATE these, just generate them in a predicable fashion.

You say you pass a map record and change record and build a sql statement based
on that.  That is fine, you should do something like this (psuedo-ish code,
won't necessarily compile)

create package body dyn_update_table_t
as
   g_cursor  number := dbms_sql.open_cursor;

procedure do_udpate( map_rec in ..., changes in ... )
is
   l_stmt long default 'update t set ';

   procedure add_set( map_field in number, cname in varchar2 )
   is
   begin
      if ( map_field = 1 ) then
          l_stmt := l_stmt || field_name || '= :' || field_name ||',';
      end if;
   end;
   procedure do_bind( map_field in number, cname in varchar2, val in number )
   is
   begin
      if ( map_field = 1 ) then
          dbms_sql.bind_variable( ... );
      end if;
   end;

   procedure do_bind( map_field in number, cname in varchar2, val in date )
   is
   begin
      if ( map_field = 1 ) then
          dbms_sql.bind_variable( ... );
      end if;
   end;
   procedure do_bind( map_field in number, cname in varchar2, val in varchar2 )
   is
   begin
      if ( map_field = 1 ) then
          dbms_sql.bind_variable( ... );
      end if;
   end;
begin
   add_set( map_rec.c1, 'c1' );
   add_set( map_rec.c2, 'c2' );
   ...
   add_set( map_rec.cN, 'cN' );

   l_stmt := rtrim(l_stmt,',') || ' where pk = :id';
   dbms_sql.parse( g_cursor, l_stmt );
   add_bind( map_rec.c1, 'c1', changes.c1 );
   add_bind( map_rec.c2, 'c2', changes.c2 );
   ...
   add_bind( map_rec.cN, 'cN', changes.cN );

   dbms_sql.execute( g_cursor );
end;

end;
/

You won't avoid the soft parse, but you'll use binds, the sqlstmts are generated
in a predicable fashion (they will be reused if two people update the same set
of columns) and by using session_cached_cursors, you'll avoid much of the soft
parse overhead.

Now, if I had the CHOICE, and the amount of redo generated was acceptable -- I
would use the static sql suggested by someone else:

update t
set
c1 = decode(map_rec.c1, 1, changes.c1, c1),
c2 = decode(map_rec.c2, 1, changes.c2, C2)
...;

only you can answer 'is the amount of extra redo acceptable'.

Rating: 5
Thanks January 23, 2002
Reviewer: A reader
Thanks, Tom.  I was hoping to avoid the soft parses, too, but as that isn't
practical without incurring the REDO cost, you've offered a very reasonable
alternative.

As always, you've been clear, thorough and PATIENT!!

Rating: 5
Alternative - better, worse or no difference? February 25, 2002
Reviewer: Jon from Hartford, CT USA
Tom,

Could you comment on this alternative suggested by Chris Weiss of PureCarbon and
print in Quest-Pipelines newsletter?
http://www.quest-pipelines.com/newsletter-v3/0202_D.htm
And welcome back - we missed you!

Followup:
It is not any different from what i said:

You will have a finite number of statements -- say a table has an ID and columns

C1, C2, C3.  the only updates you should EVER generate are:

update t set c1 = :c1 where id = :id;
update t set c1 = :c2 where id = :id;
update t set c1 = :c3 where id = :id;
update t set c1 = :c1, c2 = :c2 where id = :id;
update t set c1 = :c1, c3 = :c3 where id = :id;
update t set c2 = :c2, c3 = :c3 where id = :id;
update t set c1 = :c1, c2 = :c2, c3 = :c3 where id = :id;

In fact, it is just an IMPLEMENTATION of what I said.  It is not an alternative,
its the same thing!

Rating: 4
I guess you've answered my question; I saw if as different February 25, 2002
Reviewer: Jon from Hartford, CT USA
I guess I saw his building all the potential bind variables in a declare in
l_sql_string and then executing:

EXECUTE IMMEDIATE l_sql_string
USING p_first_name, p_last_name, p_middle_name, p_dob, p_termination, p_id

as being different than building just the update statement in l_stmt and doing

   dbms_sql.parse( g_cursor, l_stmt );
   add_bind( map_rec.c1, 'c1', changes.c1 );
   add_bind( map_rec.c2, 'c2', changes.c2 );
   ...
   add_bind( map_rec.cN, 'cN', changes.cN );

   dbms_sql.execute( g_cursor );

If you're saying there's no effective difference, I guess that answers my
question.  Thanks.

Followup:
It is "different" -- it's a different implementation of the same thing.  Another
way of doing it -- there must be hundreds of ways to do the same thing.

There is a certain elagance to their approach, a certain readability to the one
I laid out.  Cursory testing shows a very slight performance edge in the
"elegant" approach.

February 25, 2002 Ak -- Thanks for the question regarding "procedure", version 81.5
You Asked
Hi tom,
 1)  I have a problem in following procedure ,i need to assign a
dblink name in a variable at the run time and use that variable
to pull the data from diff site.But when i use it,it doesn't seems
to taking the value of that VARIABLE,instead it is taking that varaible(site)
..itself as a dblink.

2)And another question is...say if the network is down for one site  ,hence it
cann't use a dblink to pull the data,what i want here is
it should log the error. and CONTINUE FOR ANOTHER SITE,IF THIS
ALSO NOT AVAILABLE...,log the error and continue for another site.

3)Another question is ,since now we are changing our application
to Use bind varaible,what we are planning now is ....we will have
one DLL WHICH will be written in 'pro *c' and whereever in vb
we have used sql statement,we will call this dll and pass the sql string to be
executed.Because in vb we tested ,it doesn't seems to
be working,but pro*c does it easily(ofcourse we have tested in ).Your comment
please.

create or replace PROCEDURE test IS
  site CHAR(50):='';
  i number;
  ERROR_MSG VARCHAR2(200);
BEGIN

   for i in 1..3  loop
       if i=1 then
        site:='a';
         else
           if i=2 then
        site:='b';
        else
       if i=3 then
         site:='c';
          END IF;
  END IF;
END IF;

INSERT INTO x SELECT a1,a2
 FROM x1@||site WHERE t=t1'

INSERT INTO y SELECT a3,a4 FROM x2@||site
WHERE SER='3';
commit;
End loop;

exception
 when others then
 error_msg := SUBSTR(SQLERRM, 1, 100)|| siteA ;
 insert into error values(error_msg);
END test ;

Awaiting your response.
Thanks,
 Regards
   Ak
and we said...
1) you need to use dynamic sql.

  execute immediate 'insert into x select a1,a2 from x1@' || site || ' where
t=t1';

2) use exception blocks

   begin
     execute immediate ...
     commit;
   exception
     when others then
            rollback;
            log_error;
   end;

   begin
     execute immediate ...
     commit;
   exception
     when others then
            rollback;
            log_error;
   end;

....

3) VB most certainly supports binding.  Please re-read the docs for VB / ODBC or
whatever API you are using.  You don't need to drop down to pro*c.
Review & Followup
Rating: 4
Binding works in VB February 26, 2002
Reviewer: Ravi from NY,NY
Binding works in VB. See the code below where there two methods - one using
command object (binding) and another using dynamic sql. You will see that the
command object results in 2 hard parses, whereas dynamic sql results in 100
*hard* parses. If you are testing over and over, remember to change the 'i = x
to y' with new values of x & y because the old values will be in the oracle
cache.

This works only with the Oracle provider and not with MSDAORA. The best solution
of course is to move to a SP.

Results from program :
Command object Stats
parse count (total)          205
parse count (hard)           2
execute count  104
Dynamic SQL Stats
parse count (total)          205
parse count (hard)           100
execute count  104

... and the code:
Private Sub Form_Load()

    Dim Cn As New ADODB.Connection
    Dim rs As Recordset
    Dim sSQL As String
    Dim cmd As Command
    Dim statsSQL As String

    statsSQL = "select a.name name, b.value " & _
       "from v$statname a, v$mystat b " & _
       "Where a.statistic# = b.statistic# " & _
       "and a.name in " & _
       " ('parse count (total)', " & _
       " 'parse count (hard)', " & _
       " 'execute count') "

    Cn.Open "Provider=OraOLEDB.Oracle.1;Data source=oradb_sh;User
ID=scott;Password=tiger;"

    sSQL = "select * from emp where empno = ?"

    For i = 6001 To 6100
        Set cmd = New ADODB.Command
        Set cmd.ActiveConnection = Cn
        cmd.CommandText = sSQL
        cmd.CommandType = adCmdText
        cmd.Parameters.Append cmd.CreateParameter(, adVarNumeric, adParamInput,
, i)

        Set rs = cmd.Execute
        If rs.EOF And rs.BOF Then
             ' ensure recordset is parsed
        End If
        rs.Close
        Set rs = Nothing: Set cmd = Nothing
    Next i

    Set rs = Cn.Execute(statsSQL)

    Debug.Print "Command object Stats"
    Do While Not rs.EOF
        Debug.Print rs(0), rs(1)
        rs.MoveNext
    Loop
    rs.Close
    Set Cn = Nothing

    Cn.Open "Provider=OraOLEDB.Oracle.1;Data source=oradb_sh;User
ID=scott;Password=tiger;"

    For i = 6101 To 6200
        sSQL = "select * from emp where empno = " & i
        Set rs = Cn.Execute(sSQL)
        If rs.EOF And rs.BOF Then
             ' ensure recordset is parsed
        End If
        Set rs = Nothing
    Next i

    Debug.Print "Dynamic SQL Stats"
    Set rs = Cn.Execute(statsSQL)

    Do While Not rs.EOF
        Debug.Print rs(0), rs(1)
        rs.MoveNext
    Loop
    rs.Close

End Sub


Subject | Article | Last Updated (East coast time)
Defragmenting | article only | Feb 26, 2002 09:40 am
Dynamic Triggers to audit updated columns of a table. | article only | Feb 26, 2002 12:34 pm
How to avoid the SQL*plus messages like Warning: Procedure compiled with Warnings | article only | Feb 26, 2002 01:05 pm
Tuning sql | article only | Feb 26, 2002 02:28 pm
Intermedia Error | article only | Feb 26, 2002 03:40 pm
passing from one PL/SQL web server procedure to another | article only | Feb 26, 2002 08:14 pm
Prioritizing processes differently in MTS | article only | Feb 27, 2002 08:16 am
Arrays | article only | Feb 27, 2002 08:26 am
How to Read a Folder and use Oracle Tool. | article only | Feb 27, 2002 04:01 pm
Data Retrieval problem. | article only | Feb 27, 2002 04:21 pm
LOOPS | article only | Feb 28, 2002 09:31 am
Recovery | article only | Feb 28, 2002 10:04 am
SYS.OBJ$ detailed describe. | article only | Feb 28, 2002 01:11 pm
Updating a large table | article only | Feb 28, 2002 04:30 pm
Oracle Objects and converting them to Java Objects | article only | Feb 28, 2002 05:55 pm
Can we undo alter table set unuse (column)? | article only | Mar 01, 2002 09:05 am
DBMS_LOCK_ID | article only | Mar 01, 2002 01:47 pm
Need to Store Session Variables in PL/SQL | article only | Mar 01, 2002 02:21 pm
Rebuilding Indexes | article only | Mar 01, 2002 02:37 pm
iAS connection timeout | article only | Mar 01, 2002 02:45 pm
Use of Resumable Space Allocation | article only | Mar 01, 2002 03:39 pm
to_char and without to_char | article only | Mar 01, 2002 03:56 pm
SGA tuning and Net tunning | article only | Mar 01, 2002 04:27 pm
Translating a character to multiple characters | article only | Mar 01, 2002 06:30 pm
Resize Undo Tablespace / Shared-Dedicated Server | article only | Mar 03, 2002 10:11 am
multibale source | article only | Mar 03, 2002 10:29 am
migration of db | article only | Mar 03, 2002 10:50 am
Dealing with NULL parameters in a PL/SQL procedure(or fucntion) called by a CallableStatement in JDBC 2.0 | article only | Mar 03, 2002 12:55 pm
backup | article only | Mar 04, 2002 08:05 am
Optimistic Locking pattern | article only | Mar 04, 2002 10:12 am
consolidate dbfile | article only | Mar 04, 2002 12:45 pm
Table copy with all triggers and constrains | article only | Mar 04, 2002 03:51 pm
row(s) 1 - 32 of 32
February 26, 2002 Sam -- Thanks for the question regarding "Defragmenting ", version 8.1.7
You Asked
"defragmenting the objects which are running out of maximum extents"

Tom

Recently I attended an interview I was asked a question

How do you defragment  an object which is running out of maximum extents?

The way I understand the question is that initially the object was allotedd 100
extents for example, and when it starts reaching the limit , how do we handle
it.

My answer was that , that we export the object and import it , in such a case
the whole object contents get fit into the first extent. Is my answer correct.

What is the appropriate answer for this questin.

Thx
and we said...
The correct answer is -- set max extents unlimited because the number of extents
an object has does not have a material effect on performance and this is a
provable fact!  Then, go about doing infinitely more useful things like helping
developers do their job.

read
"HOW TO STOP DEFRAGMENTING AND START LIVING: THE DEFINITIVE WORD ON
FRAGMENTATION"

i have the link (and more on this subject) here
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:730289259844
You do NOT want things to be in a single extent -- that is pointless.  What I
see happen time and time and time again is this:

o hey, lets reorg (nothing better to do, we must justify our existence)
o exp/move the data out
o drop the objects
o imp/move the data in
o hey, why is the system so SLLLLOOOWWWWW today, why is my application failing
with "no such object" errors....

people make mistakes, they lose indexes, grants, triggers, constraints, etc etc
etc during a "reorg".  They make the problem worse -- not better.  The above
scenario is one I've seen played out time and time and time again.

Even worse -- they missed the most important steps which should be:

o collect measurable metrics that show performance today (eg: how fast do our
common queries return, how many tps are we doing, whats the cache hit ratio, etc
etc etc)

o reorg

o collect measurable metrics that show the performance after the move.

o generate the report the justifies the reorg and qualifies the performance gain
(this by the way is the single most important step, one that not many people
seem interested in doing)

If you don't know what the "reorg" did for you -- in a measurable, reportable
fashion -- WHY DO IT???

Anyway, exp imp would be one way.  I would prefer "alter table T move ....."
followed by an "alter index t_idx1 rebuild unrecoverable parallel ...." for each
index on T.  That way I cannot lose a grant/trigger/constraint/index/etc.

But -- unless you can MEASURE the performance gain and quantify why the reorg
was "good" (or bad -- surprise surprise -- you might find a reorged table and
index to be SLOWER until it fragments itself again!!!!!!), i don't see how you
can justify the down time or the work expended.

Probably not the answer you were expecting (and probably why in an interview I
would never get a job as a DBA...)
Review & Followup
Rating: 5
RE: Defragmenting February 26, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
> probably why in an interview I would never get a job as a DBA...

Yeah, but would you even want to be a DBA in a place like that described???

Also, another point is that if you use LMT's, you are using maxextents unlimited
by default and it cannot be overridden...

- Mark

Rating: 5
Perceived performance gains from re-org March 01, 2002
Reviewer: Simon Holt from Dublin, Eire
Many thanks Tom for the authoritative response and references to a debate I
often have to wade into - I will have yet another reference to point to now!

About 7 years ago, as a fledgling DBA, I had a "Road to Damascus" experience
after reading Cary Millsap's white paper on Storage in Oracle which was the
first time I had seen the "many extents" myth debunked. It's amazing how long
this myth persists...

One of the points that Mr. Millsap raised was that there were often performance
gains to be seen from a re-org (remember we are going back to V6/7 here!)which
were attributed to compacting to single extents, but were actually due to
chained/migrated rows being largely removed because of being re-inserted during
the import. This would remove a double i/o necessary for the previously chained
row, and contribute to a perceived increase in performance.

Of course, if you had done metrics before and after the re-org, you would have
recognised it for what it was :-)

February 26, 2002 Khushru -- Thanks for the question regarding "Dynamic Triggers to audit updated columns of a table.", version 8.1.6
You Asked
I want to audit insert and update actions on the table. The
system in question is still in the alpha stage of development
and hence the table structure keeps on changes. we do not want
to constantly keep on changing the trigger.

The structure of the AUDIT_TRAIL table is

     Table_name         varchar2(30)
     Table_action       varchar2(10)  ;; values insert or update
     Session_id         number
     Tab_Column_name    varchar2(30)
     Old_value          varchar2(4000)
     New_value          varchar2(4000)
     Date_time_stamp    date

Can we solve the problem without using a static PL/SQL Query as
describe in one of your previous solutions ?
and we said...
There is no opportunity to use dynamic sql to access the :new
and :old bind values in a trigger.

What I do in cases like this is use SQL to write SQL.  After you
alter or modify a table -- you would as a matter of procedure,
run a script to generate the trigger for you based on the new
set of columns.  It maintains the trigger code for you.

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:59412348055
for a full implementation of this concept -- very similar
to the table you have above already.
Review & Followup
Rating: 4
February 26, 2002
Reviewer: A reader
How do you include the p_key(rowid)into the audit table from the table that
being audit?

Followup:
the same way you capture the old_value and new_value.  Just modify the code
generator to pass it along when it passes them.

Rating: 4
February 26, 2002
Reviewer: A reader
just modify the code??
I tried to get the column_id from the user_tabs_colums
and including the ID in the insert statement but i cant
get it right.
How do you meen?
Thanks in advance.

Followup:
don't see what column id has to do with it.

take the query:

select '    audit_pkg.check_val( ''&1'', ''' || column_name ||
          ''', ' || ':new.' || column_name || ', :old.' ||
             column_name || ');'
from user_tab_columns where table_name = upper('&1')
/

modify it to something like:

select '    audit_pkg.check_val( ''&1'', ''' || column_name ||
          ''', ' || ':new.' || column_name || ', :old.' ||
             column_name || ', :new.ROWID );'
from user_tab_columns where table_name = upper('&1')
/

then make the audit_pkg take the rowid as the last parameter for each function:

create or replace package audit_pkg
as
    procedure check_val( l_tname in varchar2,
                         l_cname in varchar2,
                         l_new in varchar2,
                         l_old in varchar2,
                         l_rowid in varchar2  );

and record the rowid.

Rating: 4
February 26, 2002
Reviewer: A reader
When i try to make a update i get this message:
ORA-01722: invalid number
ORA-06512: at "x.AUDIT_PKG", line 14
ORA-06512: at "x.AUD#IM", line 6
ORA-04088: error during execution of trigger 'x.AUD#IM'
Line 14:
insert into auditim2(im_id, SID,Timestamp,who, tname,cname,old,new)
values(l_rowid,userenv('sessionid'), sysdate, user, upper(l_tname),
upper(l_cname),l_old, l_new);
at "x.AUD#IM", line 6:
audit_pkg.check_val('IM','anmarkning', :new.anmarkning,
:old.anmarkning,'rowid');

I would be greatfull if you could tell me what is wrong.

Followup:
You know what -- if you don't know what the code is doing or how it does it (and
cannot figure it out) -- it might be best if you didn't use it.

You did not follow the instructions above (else you would have :new.rowid not
'rowid' in the insert statement).  I don't know what else you did wrong.  Just
to put it to bed, here you go.  Just CUT AND PASTE this.  Try to understand what
it does and how it does it.

create table audit_tbl
(   timestamp    date,
    who          varchar2(30),
    tname        varchar2(30),
    cname        varchar2(30),
    old          varchar2(2000),
    new          varchar2(2000),
    rid          rowid
)
/

create or replace package audit_pkg
as
    procedure check_val( l_tname in varchar2,
                         l_cname in varchar2,
                         l_new in varchar2,
                         l_old in varchar2,
                         l_rowid in varchar2 );

    procedure check_val( l_tname in varchar2,
                         l_cname in varchar2,
                         l_new in date,
                         l_old in date,
                         l_rowid in varchar2 );

    procedure check_val( l_tname in varchar2,
                         l_cname in varchar2,
                         l_new in number,
                         l_old in number,
                         l_rowid in varchar2 );
end;
/

create or replace package body audit_pkg
as

procedure check_val( l_tname in varchar2,
                     l_cname in varchar2,
                     l_new in varchar2,
                     l_old in varchar2,
                     l_rowid in varchar2  )
is
begin
    if ( l_new <> l_old or
         (l_new is null and l_old is not NULL) or
         (l_new is not null and l_old is NULL) )
    then
        insert into audit_tbl values
        ( sysdate, user, upper(l_tname), upper(l_cname),
                             l_old, l_new, l_rowid );
    end if;
end;

procedure check_val( l_tname in varchar2, l_cname in varchar2,
             l_new in date, l_old in date,
                     l_rowid in varchar2  )
is
begin
    if ( l_new <> l_old or
         (l_new is null and l_old is not NULL) or
         (l_new is not null and l_old is NULL) )
    then
        insert into audit_tbl values
        ( sysdate, user, upper(l_tname), upper(l_cname),
          to_char( l_old, 'dd-mon-yyyy hh24:mi:ss' ),
          to_char( l_new, 'dd-mon-yyyy hh23:mi:ss' ), l_rowid );
    end if;
end;

procedure check_val( l_tname in varchar2, l_cname in varchar2,
             l_new in number, l_old in number,
                     l_rowid in varchar2  )
is
begin
    if ( l_new <> l_old or
         (l_new is null and l_old is not NULL) or
         (l_new is not null and l_old is NULL) )
    then
        insert into audit_tbl values
        ( sysdate, user, upper(l_tname), upper(l_cname),
                                 l_old, l_new, l_rowid );
    end if;
end;

end audit_pkg;
/

And the script to generate the trigger would be:

set serveroutput on
set feedback off
set verify off
set embedded on
set heading off
spool tmp.sql

prompt create or replace trigger aud#&1
prompt after update on &1
prompt for each row
prompt begin

select '    audit_pkg.check_val( ''&1'', ''' || column_name ||
          ''', ' || ':new.' || column_name || ', :old.' ||
             column_name || ', :new.rowid);'
from user_tab_columns where table_name = upper('&1')
/
prompt end;;
prompt /

spool off
set feedback on
set embedded off
set heading on
set verify on

@tmp

The test I used is:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table dept as select * from scott.dept
where rownum = 1;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> @test dept
create or replace trigger aud#dept
after update on dept
for each row
begin
    audit_pkg.check_val( 'dept', 'DEPTNO', :new.DEPTNO, :old.DEPTNO,
:new.rowid);
    audit_pkg.check_val( 'dept', 'DNAME', :new.DNAME, :old.DNAME, :new.rowid);
    audit_pkg.check_val( 'dept', 'LOC', :new.LOC, :old.LOC, :new.rowid);
end;
/

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> update dept set deptno = deptno+1;

1 row updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> update dept set dname = lower(dname);

1 row updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> update dept set loc = initcap(loc), dname =
upper(dname);

1 row updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> @printtbl8 'select * from audit_tbl';
TIMESTAMP                     : 26-feb-2002 12:29:11
WHO                           : OPS$TKYTE
TNAME                         : DEPT
CNAME                         : DEPTNO
OLD                           : 10
NEW                           : 11
RID                           : AAAGl7AABAAAFSaAAA
-----------------
TIMESTAMP                     : 26-feb-2002 12:29:11
WHO                           : OPS$TKYTE
TNAME                         : DEPT
CNAME                         : DEPTNO
OLD                           : 20
NEW                           : 21
RID                           : AAAGl7AABAAAFSaAAB
-----------------
TIMESTAMP                     : 26-feb-2002 12:29:11
WHO                           : OPS$TKYTE
TNAME                         : DEPT
CNAME                         : DEPTNO
OLD                           : 30
NEW                           : 31
RID                           : AAAGl7AABAAAFSaAAC
....
-----------------
TIMESTAMP                     : 26-feb-2002 12:32:56
WHO                           : OPS$TKYTE
TNAME                         : DEPT
CNAME                         : LOC
OLD                           : NEW YORK
NEW                           : New York
RID                           : AAAGl9AABAAAFSaAAA
-----------------

PL/SQL procedure successfully completed.

Rating: 5
April 07, 2003
Reviewer: A reader

February 26, 2002 Kumar -- Thanks for the question regarding "How to avoid the SQL*plus messages like Warning: Procedure compiled with Warnings", version 8.1.6
You Asked
Hi Tom,

1)I have question and looking for any easy solution. I run .sql files on
databases through SQL*plus and that throw a warning messages like
"warning: Procedure compiled with warnings" .
Due to many dependencies initially these will come latter another script
compiles all of them.

I want to get only ORA- related error messages. Is there any shortcut to get a
clean spool files instead of this warnings.

I can still produce the clean spool file after refreshing the initial spool
files which contain these warnings. Is there any special supression commands
exist to get the clear ORA- error messages in my spool files. My customers
confuse a lot when they see these kind of spool files.

2)How Can I populate any message on SQL*plus screen by using WHENEVERSQLERROR
exit situation?

Appreciate your help.
and we said...
1) The warnings will come out regardless.

If you used database packages and never standalone procedures, not only would
you have much more maintainable code but your code would compile cleanly the
first time.

You would compile all of the specs, then all of the bodys.  No dependencies
between specs (hence they compile cleanly) and the bodies are ONLY dependent on
other specs (which are all valid).  You compile cleanly the very first time.

2) Your script that runs sqlplus can inspect the exit code from sqlplus (you can
exit with a non-zero value upon failure, zero upon success).  Your script can
then display any error message you like.
Review & Followup
Rating: 3
How to avoid warnings in sql*plus messages February 26, 2002
Reviewer: Kumar from San Diego
Well, I understood the situation, but there are many dependency functions
triggers etc. So I descide to generate the intermittent spool file and then
reproduce the wanted name spool file after filtering the warning messages. I can
not shift all to database packages.

Regarding whenver sqlerror, I could able to follow partially the answer from
you. For eg: Take a case user login credentials fails then if I want to set the
screen to exit but after populating the user friendly message like "your login
failed and session now disconnect". Is this kind is achievable? I mean it should
pop up on the screen and when user press any key then the session should exit.
Can you give me a clue or an example issue.
Thanks for the help!!

Followup:
In order to get a "user friendly" installation, you'll really be wanting to use
an installation tool.  SQLPlus is a simple command line interface meant to be
used to run queries and such.

February 26, 2002 muhammad -- Thanks for the question regarding "Tuning sql", version 8.1.7
You Asked
Hi,

After 9i, which uses a choose optimizer . is there any need of tuning sql or
using hint(except first_row ,all)  ,if the table are analyze.

if yes , please explan.

what does the cost represent in explain plan.

Thanks

Muhammad
and we said...
In all releases of every database ever made -- there will be queries you need to
tune.  It is a fact of life.

every release, the optimizer gets smarter.  I doubt however the day will come
when it can be said "you'll never ever have to tune a query".

The cost is an internal number that is assigned to a plan.  For every query,
hundreds of plans may be generated.  Costs are assigned to each step in the
plan.  the plan with the lowest cost wins.
Review & Followup
Rating: 2
February 26, 2002
Reviewer: A reader
i am joining two analyzed table with index column, but the the query is doing
full table scane do i have to use hint to force it to use index.(optimizer is
choose)

what should be i do to solve it.

thanks

Followup:
perhaps, just perhaps, the optimizer is doing the right thing!!!

give us your example, include create tables, indexes and row counts in each.

from the believe it or not category:  indexes do not imply "fastest way to
data"

February 26, 2002 Yogesh -- Thanks for the question regarding "Intermedia Error", version 8.1.7
You Asked
Hi Tom,

I was trying following query for intermedia based search and results were
absolutely shocking.

SELECT
    ID,
    NAME
FROM
    TEST
WHERE
    ID IS NOT NULL AND ((  (contains(BODY,' OREGON ' )>0) OR (contains(BODY,' OR
')>0)) )
/

Above query ends the current DB session after returning end of file
communication channel error.

Another query

SELECT
    ID,
    TITLE
FROM
    TEST
WHERE
    ID IS NOT NULL AND  (contains(BODY,' OR ')>0)) )
/

Returns following error

SELECT
*
ERROR at line 1:
ORA-29902: error in executing ODCIIndexStart() routine
ORA-20000: interMedia Text error:
DRG-50901: text query parser syntax error on line 1, column 2

Plz explain.

Regards
Yogesh
and we said...
Well, for the EOF on communication channel, I'll have to refer you to support.
It seems to be a bug dealing with your invalid query you used.  There may be a
patch for it.

For the second query with just 'OR' -- you need to read about the query language
behind interMedia.  OR is a keyword in the search string itself.  You need to
escape it.

So the search would look like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( id number, name
varchar2(80), body varchar2(4000) );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1, 'a', 'oregon' );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1, 'b', 'or' );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1, 'c', 'hello' );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create index search_idx
  2  on t(body)
  3  indextype is ctxsys.context
  4  /

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t
  2  where ID IS NOT NULL AND ((  (contains(BODY,' OREGON ' )>0) OR
(contains(BODY,' {OR} ')>0)) )
  3  /

        ID NAME  BODY
---------- ----- ------------------------------
         1 a     oregon

Now, why didn't OR come back?  It is one of the words in the default stoplist.
If you want to index common words like that, you'll need to set up your own
stoplist using interMedia as well.

As an aside, it would be better to use interMedia to OR:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t
  2  where ID IS NOT NULL AND contains( body, 'oregon or {or} or hello' )  > 0
  3  /

        ID NAME  BODY
---------- ----- ------------------------------
         1 a     oregon
         1 c     hello
Review & Followup
Rating: 3
Great Explaination February 28, 2002
Reviewer: Yogesh Bhandarkar from INDIA
Got New direction for working.

February 26, 2002 daxu -- Thanks for the question regarding "passing from one PL/SQL web server procedure to another", version 8.1.7
You Asked
Hi! Tom,
I am working on an Oracle webserver PL/SQL package. Is there anyway that I can
pass an array in stored procedure to a another stored procedure by using Oracle
Web Toolkit?
For example, I have a p_1 procedure having a_1 array, and want to pass that to
p_2 procedure by using htp.p('<form name ="f_1" method="post" action="p_2">')

Thanks a lot,

Daxu
and we said...
sure, say you have a package:

create package my_pkg
as
    type array is table of varchar2(4000) index by binary_integer;

    procedure p1;
    procedure p2( p_data in array );
end;
/

then p1 can be coded as:

   ...
   htp.formOpen( 'my_pkg.p2' );
   htp.p( '<input type=hidden name = p_data value=1>' );
   htp.p( '<input type=hidden name = p_data value=2>' );
   ...
   htp.p( '<input type=hidden name = p_data value=N>' );
   htp.p( '<input type=submit>' );
   htp.formclose;

and P2 will get the array -- in p_data.  Just use the same name for more then
one field and we'll send them to a plsql table type for you.
Review & Followup
Rating: 4
February 26, 2002
Reviewer: daxu from usa
Tom,
Thanks a lot, that is very helpful. I have another question,
on your code, it just passes an array of varchar2, if I define the array as a
table of a TABLENAME%rowtype, how do I assign value to p_data by using the
followign code?
htp.p('<input type=hidden name = p_data value=???>');

Thanks,

Daxu

Followup:
you cannot pass rowtypes, only scalars, from the mod_plsql module.

February 27, 2002 Mohit -- Thanks for the question regarding "Prioritizing processes differently in MTS ", version 8.1.x
You Asked
Hi Tom,

I would like to know if there is any way we can assign "priorities" to processes
in an Oracle MTS environment so that the Dispatchers allocate more time and
resources to those processes as opposed to others they may have.

I know that the MTS architecture is mainly meant to be used in an online
environment with a large user base... with the underlying assumption that all
processes are equal, but I was wondering if it is possible to specify some
processes as being more equal than others (kind of "First among Equals"!)

Many thanks for the time you spend resolving all the queries here. I found out
about your site quite by accident and have been a regular fan since!

Bext regards,

Mohit.
and we said...
I don't think you want to prioritize PROCESSES -- but you might want to
prioritize sessions.

See
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76956/part6.htm#435958
prioritizing a process, especially in MTS, would be bad.  From call to call
you'll get different shared servers.  So one time you would be "high priority",
one time "low" -- for the same session.
Review & Followup
Rating: 4
A followup.... February 26, 2002
Reviewer: Mohit Dubey from UK
Many thanks for the prompt and lucid reply, Tom!

The reason why I asked this question was that we are hitting the wall with
maximum allowable sessions on our 8.1.7 db here (the system is an online/batch
mix, leaning a bit towards the batch side) and I do not want to keep on
increasing the SESSIONS/PROCESSES ad infinitum. All processes log in to the same
schema.

I suggested we go in for an MTS/dedicated hybrid where batch processes get in
via the dedicated connect string and the online via the MTS one.

However, some bright minds want to put EVERYTHING on MTS and then assign a
higher priority to the batch processes.

As I see it, the "resource" approach would mean associating all new connections
to a "normal" group, then running a script to get sid/ser# of all running batch
processes and switching them to a high priority group (if they are not in
there).

My gut feel says to go with the hybrid approach rather tahn mess about with
resource groups. Comments?

The Oracle manual (rightly, in my opinion) cautions against using MTS for
batch-type processes. Based on your experience, what is the avg. performance hit
if batch-processes are run in MTS mode as against dedicated?

Thanks once again.

Mohit.

Followup:
batch processes are NOT suitable for MTS (shared server).  They will tend to
monopolized the SHARED server (they are not very good at sharing, batch
processes are GREEDY).

The hybrid approach is the CORRECT answer, couple with resource mgmt if you want
finer grained control.

All you need is a couple of long running statements to take over and monopolize
a SHARED server and your online people are hosed.

If you have my book -- I go into this in some detail (it's one of my first
examples of how to fail -- use MTS for things that take a long time to run).
That should squash all discussion.  It is not performance of statements in as
much as starvation of end users -- they, the humans, get no resources whilst the
batch processes consume them all.  Humans get mad when this happens.

Rating: 5
some more explanation please.. February 26, 2002
Reviewer: Yogeeraj from Mauritius
hello
So is it true that in general, for systems similar to what our friend, Mohit, is
describing here we should implement a hybrid MTS/Dedicated configuration?

But, by going to dedicated server processes, each user will allocate their own
memory on the server instead of sharing memory. This increased memory
consumption can lead to paging which can be a performance killer. In general,
what  are the typical resource management parameters for finer grained control?

By the way, the shortcut to your "new book" does not seem to be working. (
http://asktom.oracle.com/pls/ask/z?p_url=http%3A%2F%2Fwww.wrox.com%2FACON11.asp%3FWROXEMPTOKEN%3D181202ZLbcpHDVSYHk38gI4FRM%26ISBN%3D186100690x&p_cat=BEG_ORACLE&p_company=10
Best Regards
Yogeeraj

Followup:
Yes, batch processes that connect to the database, use the connection pretty
much 100% of the time and disconnect should get a dedicated server (they are in
fact going to make use of that resource heavily, they won't "share" nicely).

End user processes (clients) that connect to the database, use the connection
for brief periods with long periods of inactivity (as you might see in any
application with end user interaction -- its called "think time") can use MTS
nicely.  They "share" the resource.

Now, as for the MEMORY.... Thats interesting.  First -- I am saying only that
BATCH PROCESSES get a dedicated server (not that EVERYONE gets one).  I would
envision on a system like this you have 1,000 "humans" connected and many 10
batch processes (i mean -- how many batches do you run typically -- not
thousands, but rather 10's).  So, we are talking about have a very few dedicated
servers, some shared servers and 1,000s of users.

Also interesting is the belief that MTS will DRAMATICALLY reduce the memory
consumption.  It may reduce it -- but in my experience *not dramatically*.
There are two pieces of memory to consider

o UGA - the user global area
o PGA - process global area

In MTS, the UGA is moved from the PGA (where it resides in a dedicated server
environment) into the SGA.  You configure the LARGE_POOL to be big enough to
hold all of your UGA's (and this is considerable in size).  The sum of the sizes
of the UGA's do not GO DOWN at all in MTS and in fact, you must ensure you have
sized the large pool large enough at startup time in order to support the number
of concurrent sessions!  The UGA is *not* shareable memory -- its your session
state.

Now, that leaves the PGA.  We'll have less PGA's this is true.  How much memory
this saves depends on many factors. For example, sort_area_size is setting that
affects the size of a PGA.  When we sort, we'll allocate UPTO sort_area_size
bytes of memory in the PGA to sort.  As soon as we are done sorting -- the data
is moved into the uga (sort_area_retained_size), and we'll release that PGA
memory and use it for other things.  So, if you have only 10 people sort at the
same time (the premise behind using MTS is you have lots of users, only a
certain % of which are concurrently active at the same time so you can use a
shared server) having 10 shared servers will consume just about the same amount
of memory.

What MTS buys you mainly is LESS processes/threads on the system, less context
switching going on, less for the os to manage.  The memory savings are there,
but they are not as big as people seem to make them be out to be.  Its mostly in
the reduced NUMBER OF PROCESSES that the OS has to manage.

So, I would stand by this answer:

o batch = dedicated server
o online = shared server, if and when the OS has run out of resources.

See
http://asktom.oracle.com/pls/ask/z?p_url=http%3A%2F%2Fwww.wrox.com%2FACON11.asp%3FISBN%3D186100690x&p_cat=BEG_ORACLE&p_company=10

for a link that should work (appears wrox had some session information in the
other url and that session "expired") .  I updated the home page as well.

Rating: 4
Thanks Tom! February 27, 2002
Reviewer: Mohit Dubey from UK
Thanks for the reply Tom. Your comments were on the exact same lines as my
thinking!

I think the people here need a little more convincing, though... so I now plan
to setup a database with MTS enabled and have them run a couple of batch process
and online screens simultaneously.... oh well!

Best Regards,

Mohit.

Followup:
or, just get them my book and have them read it.  maybe, just maybe, once they
understand HOW it works, they will see the light.

At least it is extremely easy to fix if they proceed.  You'll only need to
change a connect string for the batch processes.

Rating: 5
a helpful discussion of MTS. October 02, 2002
Reviewer: Cynthia from Denver, USA

February 27, 2002 Munzer -- Thanks for the question regarding "Arrays ", version 8.1.7
You Asked
Tom:

1.  When you create arrays in PL/SQL you always use:
create package my_pkg
as
    type array is table of varchar2(4000) index by binary_integer;

Do you ever use "table of number" or table of date" also.
or in web applications you always use "varchar2" and convert it in the PL/SQL
program.

2.  If I want in PL/SQL to select data from two tables into a variable, and data
is mixed "varchar2,date and number" how do you declare the variable type
(l_rec).

select a.col1,a.col2,b.col1.b.col2 into l_rec from tab1, tab2 where
tab1.col1=tab2.col1;

Thank you
and we said...
1) i would not say "always", rather "mostly".

In plsql packages, if I am defining a type, I typically use the INDEX BY tables.
 They are somewhat easier to use in PLSQL in most cases.  I don't have to
".extend" them to allocate space.  On the other hand, I'll use a "table of
number" without the index by -- if I want to initialize them easily.  Eg:

  type numCollection is table of number;
  type numArray      is table of number index by binary_integer;

...

  procedure p
  is
      l_data1  numCollection := numCollection(1,2,3,4);  -- valid syntax
      l_data2  numArray := numArray(1,2,3,4);  -- NOT VALID, compile will
fail

So, it depends on what I want to do as to which I use.  As for the datatype --
In the WEB i use ONLY varchar2's.  Why?  Because if the end user is giving me
the data (eg: i have a form like:

   <form action=my_pkg.p>
   Enter 3 numbers please:<br>
   <input type=text name=foo><br>
   <input type=text name=foo><br>
   <input type=text name=foo><br>
   <input type=submit>
   </form>

and I used a table of numbers but the end user entered "A,B,C" -- the mod_plsql
module would fling an exception.  I could not validate the data in my own
procedure since A,B,C cannot be passed to my table of numbers.  Hence, I'll pass
it to a table of varchar2, validate in my routine (make sure they are in fact
numbers) and continue (or give them a nice error page)

2) i always use implicit cursors.  so it would be:

    for l_rec in ( select emp.ename, emp.hiredate, dept.deptno
                     from emp, dept
                    where emp.deptno = dept.deptno )
    loop
       ....

you could use explicit cursors and define:

   l_rec   cursor_name%rowtype;

and then

   open cursor_name;
   fetch cursor_name into l_rec;

Me, I would just use the cursor for loop and let plsql do the work for me.  the
code is easier to write, it runs faster, it is easier to understand (IMHO)
February 27, 2002 M. -- Thanks for the question regarding "How to Read a Folder and use Oracle Tool.", version 8.1.7
You Asked
Hi Tom,

Very Interesting question. I have three Folders 1) App_Forms 2)App_Reps
3)App_Code

Every folder has many files 1 has about 300 Forms in .fmb and 2 has about 270
Reports .rdf and 3 has PL/SQL Code with extension .psql.

My Question is how can I use a Loop or any other way to read all the files in a
folder and start Forms Compiler to convert .fmb to .fmx , .rdf to .rep and to
compile the Code in a Database.

I am using Win NT wth Oracle 8i, Developer 6i.
If I do it one by one, it's gonna take days. can you please provide me any
script to read all the files in a folder and Start oracle tools that would do
it's work.

Other possibility can be a folder can have multiple extension files, like .fmb,
.rdf. .sql etc.

Thanks.
and we said...
C:\> for %f in ( *.* ) do echo %f

do a "help for" at the DOS prompt for details.
Review & Followup
Rating: 2
February 27, 2002
Reviewer: atish from charlotte,nc
http://www.orafaq.com/faqfrm45.htmATCH
Here you can find the complete solution

          Followup:

I sort of assumed a reasonably "smart" human would get from

C:\> for %f in ( *.* ) do echo %f

to

FOR %%F in (*.fmb) DO start /w f45gen32 userid=%1 batch=y module=%%F

readily.

(as that is all that the link provides.  due to the way I format text on asktom,
his link is broken the real link is (you'll have to put it together manually):

http://www.orafaq.com/faqfrm45.htm
 and add # BATCH  after it.

However that complete solution isn't really "complete".  It shows the command
for forms 4.5, not 6i.  It does not do reports.  It will not run your sql for
you.  If you LEARN how to use the "for" command -- it will be easy for you to do
all three (and so much more)

      Rating:
      1
  February 27, 2002
Reviewer:
      A reader
Can you please just send me a script to follow, I don't have solid back ground.

Let's suppose Folder is C:\Ora_App\App_Forms\
In App_Forms there are let's suppose 3 fmb's to convert to .fmx
1) a.fmb
2) b.fmb
3) c.fmb

Please help, Thanks

          Followup:

see the above comments -- it is very straight forward.  all you need to do is
supply the command.  If you don't even know the commands you must use, please
take the time to read a little.

      Rating: 4
Correct
      links  February 27, 2002
Reviewer:
      A reader  from READER
Hi,

The correct link i think will be:

http://www.orafaq.com/faqfrm45.htm(POUNT
SIGN)BATCH

POST:
Hi, Tom,
how come cann't I attache #(POUND SIGN)BATCH to the end of
http://www.orafaq.com/faqfrm45.htm?
I think (POUND SIGN)B maybe a null char OR a space

Thanks

          Followup:

(POUND SIGN)B   (POUND SIGN)b translates into <B>  </b>  here on asktom.  I did
that cause I have to mix HTML with non-HTML all of the time.  I want to bold
stuff (so I need tags) but I cut and paste in a sqlplus prompt with > in it all
of the time.  Hence, I have my "own" tags (only B and b)

February 27, 2002 -- Thanks for the question regarding "Data
      Retrieval problem.", version 8.0.5

      You Asked
Tom,
I have a specific requirement.
I have a table with a large number of records. This table does not have a date
and time column.
However, this table is regularly updated. Now I want to retrieve records which
have been updated only after a particular date, say 20th Feb 2002. How can I do
that?

      and we said...
By pure magic.

There is no way to get the data updated after 20th feb 2002 unless you keep
track of when a record was last updated -- we don't.

You need a date field on there and a trigger to maintain it.
February 28, 2002 Munzer -- Thanks for the question regarding
      "LOOPS", version 8.1.7

      You Asked
Tom:

1.  Is there  a way to exclued null values from a loop.

For example, let me say i have p_stock_number as array and I have 20 of those on
my page but user entered pnly 5 an I only need to store 5.

Is there a way to only define the FOR LOOP to insert the 5 only.

FOR i in 1..p_stock_number.count (here can i say not null)
LOOP

insert ....
END LOOP

or do i have to insert an IF statement after the loop to check if p_stock_number
is null or not.

Thank you,

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          type numArray is table of number;
  3          l_data  numArray := numArray( 1, null, 2, null, 3, null, 4 );
  4  begin
  5          forall i in 1 .. l_data.count
  6                  insert into t select l_data(i) from dual where l_data(i) is
not null;
  7  end;
  8  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

         X
----------
         1
         2
         3
         4

ops$tkyte@ORA817DEV.US.ORACLE.COM>

either use the above approach or stick an IF (p_stock_number(i) is not NULL )
then insert.... in there.

The forall is more efficient -- array inserting in plsql is faster then insert a
row at a time.
Review & Followup

      Rating:
      5
LOOPS  February 27, 2002
Reviewer:
      Munz  from Reston, VA
Tom:

Can i always use FORALL i in 1..p_state.count
and skip LOOP and END LOOP statements
since it skips null values

instead of

FOR i in 1..p_state.count
LOOP
..
END LOOP

          Followup:

No, you cannot always use FORALL in place of LOOP.  The are two totally
different constructs.  Check out the PLSQL guide for details.

FORALL is not skipping nulls!  I just moved the "if check" into the INSERT
statement itself!

  5    forall i in 1 .. l_data.count
  6      insert into t select l_data(i) from dual where l_data(i) is not null;
  7  end;

that block of code will execute l_data.count INSERTS -- each insert will insert
either 0 or 1 rows -- depending on whether l_data(i) is null or not.

      Rating:
      5
  February 28, 2002
Reviewer:
      A reader
Mr Munz,

 FORALL is new feature (bulk loading) in oracle. Read documentation.Please Don't
waste Toms valuable time BY ASKING this question. I am trying from last 4 day's
to ask question? no success.

 declare
             type numArray is table of number;
             l_data  numArray := numArray( 1, null, 2, null, 3, null, 4 );
     begin
             for  i in 1 .. l_data.count loop
                     insert into t select l_data(i) from dual where l_data(i) is
not null;
             end loop;
     end;

February 28, 2002 atul -- Thanks for the question regarding
      "Recovery", version 8.0.4

      You Asked
Sir,

I have question regarding point-in-time recovery..
I read in one book that is as follows..
1)Ur batch job is running (started at 3.00 p.m)
2)Due to some problem u lost one datafile at 5.00 p.m
3)u don't know how much job is completed so u want to go back upto 3.00 p.m
4)U restored lost datafile from backup and restored it.
5)Now if u want to go back,
U issued command "alter database recover until time '2001-02-27:15:00:00';"
6)It will go down upto 3.00 p.m..

Is this possible,Because i think that ..
We have to restored earlier backup ie all backup not only damage datafile beore
3.00 pm and recover using until time..
I read this in recovery book..

So,what is correct i am confused..

2)If my client has 7.3 database using PRO*C 1.6 and now he has shifted to 8.1.7
On unixware 7.1.1.
previously he was using proc16.mk file to compile all his programmes.
Now after shifting to 8i will he be able to compile all his programme by using
proc16.mk and also will he be able to use all previously comiled executable?

3)Our client is in noarchivelog mode.I have export dmp file of the database.
He lost his datafile.And he only have export.dmp file..
So what i did was open his database by droping his datafile.
I didn't drop his tablespace,Database is opening and closing without any
problems.
Now in what way that export dmp will be useful?

If i install oracle fresh with typical option Can i import that export.dmp there
to get back full database?

Thanks.
atul

      and we said...
1)  You would have to restore ALL datafiles from a backup taken prior to 3pm on
that date, not just the lost one.

2) i don't know what proc16.mk is.  The technique I use is documented here:

http://osi.oracle.com/~tkyte/proc_makefile/
3) You will have to drop that tablespace.  Once you offline dropped that
datafile, that tablespace is "as good as gone".

After you drop the tablespace (suggest generating a list of objects contained
therein from DBA_SEGMENTS before you drop), you can create it again (the
tablespace) and import those objects that were dropped.

You do not need to import the entire database, just the affected objects (tables
- the indexes/constraints/grants/triggers/etc will come along for the ride)

Now, if your client doesn't want to lose data again (and they will if you do not
do this), you will IMMEDIATELY put all of their production systems in ARCHIVELOG
mode.

Review &
      Followup

      Rating: 5
For what it's
      worth - proc16.mk  February 27, 2002
Reviewer:
      Helen  from UK
Early versions of Oracle 7, eg V7.1.6, came with two versions of proC ie 1.6 and
2.  proc16.mk makefile for former.  Have inkling was something to do with
transition from V6.  Why used with V7.3 anybodies guess.

      Rating:
      3
Query  February 27, 2002
Reviewer:
      atul  from India
Thanks For Ur export Solution..

But if any other segment in other tablespace is referring to segment in this
tablespace which we are going to drop.
Then will it harm in table to table basis relationship?
As our exported data will be of previous day and other segment are having data
ahead...

Thanks.
atul

          Followup:

When you goto drop the tablespace -- if there are objects in there that are
externally referenced -- you'll be notified.  You'll need to DROP the references
(remember what you did here) and then put them back later.  For example:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create tablespace testing datafile
'/tmp/testing.dbf' size 1m;
Tablespace created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table p ( x int primary key )
tablespace testing;
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table c ( x references p );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop tablespace testing including contents;
drop tablespace testing including contents
*
ERROR at line 1:
ORA-02449: unique/primary keys in table referenced by foreign keys

so it warned us.  using a script I have, we can get the references:

ops$tkyte@ORA817DEV.US.ORACLE.COM> @cons p
ops$tkyte@ORA817DEV.US.ORACLE.COM> column fkey format a80 word_wrapped
ops$tkyte@ORA817DEV.US.ORACLE.COM> select
  2  'alter table "' || child_tname || '"' || chr(10) ||
  3  'add constraint "' || child_cons_name || '"' || chr(10) ||
  4  'foreign key ( ' || child_columns || ' ) ' || chr(10) ||
  5  'references "' || parent_tname || '" ( ' || parent_columns || ');' fkey
  6  from
  7  ( select a.table_name child_tname, a.constraint_name child_cons_name,
  8           b.r_constraint_name parent_cons_name,
  9           max(decode(position, 1,     '"'||
 10                  substr(column_name,1,30)||'"',NULL)) ||
 11           max(decode(position, 2,', '||'"'||
 12                  substr(column_name,1,30)||'"',NULL)) ||
 13           max(decode(position, 3,', '||'"'||
 14                  substr(column_name,1,30)||'"',NULL)) ||
 15           max(decode(position, 4,', '||'"'||
 16                  substr(column_name,1,30)||'"',NULL)) ||
 17           max(decode(position, 5,', '||'"'||
 18                  substr(column_name,1,30)||'"',NULL)) ||
 19           max(decode(position, 6,', '||'"'||
 20                  substr(column_name,1,30)||'"',NULL)) ||
 21           max(decode(position, 7,', '||'"'||
 22                  substr(column_name,1,30)||'"',NULL)) ||
 23           max(decode(position, 8,', '||'"'||
 24                  substr(column_name,1,30)||'"',NULL)) ||
 25           max(decode(position, 9,', '||'"'||
 26                  substr(column_name,1,30)||'"',NULL)) ||
 27           max(decode(position,10,', '||'"'||
 28                  substr(column_name,1,30)||'"',NULL)) ||
 29           max(decode(position,11,', '||'"'||
 30                  substr(column_name,1,30)||'"',NULL)) ||
 31           max(decode(position,12,', '||'"'||
 32                  substr(column_name,1,30)||'"',NULL)) ||
 33           max(decode(position,13,', '||'"'||
 34                  substr(column_name,1,30)||'"',NULL)) ||
 35           max(decode(position,14,', '||'"'||
 36                  substr(column_name,1,30)||'"',NULL)) ||
 37           max(decode(position,15,', '||'"'||
 38                  substr(column_name,1,30)||'"',NULL)) ||
 39           max(decode(position,16,', '||'"'||
 40                  substr(column_name,1,30)||'"',NULL))
 41              child_columns
 42      from user_cons_columns a, user_constraints b
 43     where a.constraint_name = b.constraint_name
 44       and b.constraint_type = 'R'
 45     group by a.table_name, a.constraint_name, b.r_constraint_name ) child,
 46  ( select a.constraint_name parent_cons_name, a.table_name parent_tname,
 47           max(decode(position, 1,     '"'||
 48                  substr(column_name,1,30)||'"',NULL)) ||
 49           max(decode(position, 2,', '||'"'||
 50                  substr(column_name,1,30)||'"',NULL)) ||
 51           max(decode(position, 3,', '||'"'||
 52                  substr(column_name,1,30)||'"',NULL)) ||
 53           max(decode(position, 4,', '||'"'||
 54                  substr(column_name,1,30)||'"',NULL)) ||
 55           max(decode(position, 5,', '||'"'||
 56                  substr(column_name,1,30)||'"',NULL)) ||
 57           max(decode(position, 6,', '||'"'||
 58                  substr(column_name,1,30)||'"',NULL)) ||
 59           max(decode(position, 7,', '||'"'||
 60                  substr(column_name,1,30)||'"',NULL)) ||
 61           max(decode(position, 8,', '||'"'||
 62                  substr(column_name,1,30)||'"',NULL)) ||
 63           max(decode(position, 9,', '||'"'||
 64                  substr(column_name,1,30)||'"',NULL)) ||
 65           max(decode(position,10,', '||'"'||
 66                  substr(column_name,1,30)||'"',NULL)) ||
 67           max(decode(position,11,', '||'"'||
 68                  substr(column_name,1,30)||'"',NULL)) ||
 69           max(decode(position,12,', '||'"'||
 70                  substr(column_name,1,30)||'"',NULL)) ||
 71           max(decode(position,13,', '||'"'||
 72                  substr(column_name,1,30)||'"',NULL)) ||
 73           max(decode(position,14,', '||'"'||
 74                  substr(column_name,1,30)||'"',NULL)) ||
 75           max(decode(position,15,', '||'"'||
 76                  substr(column_name,1,30)||'"',NULL)) ||
 77           max(decode(position,16,', '||'"'||
 78                  substr(column_name,1,30)||'"',NULL))
 79              parent_columns
 80      from user_cons_columns a, user_constraints b
 81     where a.constraint_name = b.constraint_name
 82       and b.constraint_type in ( 'P', 'U' )
 83     group by a.table_name, a.constraint_name ) parent
 84  where child.parent_cons_name = parent.parent_cons_name
 85    and parent.parent_tname = upper('&1')
 86  /
old  85:   and parent.parent_tname = upper('&1')
new  85:   and parent.parent_tname = upper('p')

FKEY
--------------------------------------------------------------------------------
alter table "C"
add constraint "SYS_C005345"
foreign key ( "X" )
references "P" ( "X");

ops$tkyte@ORA817DEV.US.ORACLE.COM> undefine 1

save that to a spool file, alter table C drop the constraint:

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table c drop constraint SYS_C005345;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop tablespace testing including contents;

Tablespace dropped.

now the tablespace can be dropped, reload p and fix up C

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table p( x int primary key );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table "C"
  2  add constraint "SYS_C005345"
  3  foreign key ( "X" )
  4  references "P" ( "X");

Table altered.

February 28, 2002 Doru -- Thanks for the question regarding
      "SYS.OBJ$ detailed describe.", version 8.1.5

      You Asked
Could you explain the meaning of 10 in the type# column of obj$ in relation with
object dependencies and object invalidation?

      and we said...
type 10 objects are non-existent objects used when you reference an object via a
synonym.  We need a object to be dependent on -- an "anti object" if you will --
to ensure correctness.

For example, you create a view:

create view v as
select * from t;

Now, say you don't have a table T but there is a public synonym T for SCOTT.T.

We need to setup a non-existent object in your schema to "represent t" as it is
used by V.  This is done so that if you ever create an object named T yourself
-- we can detect that you "virtually" had an object named T via the synonym and
would invalidate all dependent objects.

So, the act of creating a table T will remove this non-existent object and thus
cause V to go invalid and the next time V is referenced and compiled it will
reference T the table, not T the synonym.

If we did not have this "anti-object", we would not recognize this change (we
just added a table after all, we didn't touch anything about the synonym) and
the view V would continue to use the synonym T until sometime in the future when
it recompiled itself -- so it would be arbitrary if and when dependent objects
caught onto the fact that T is a table, not a synonym in our schema.
Review &
      Followup

      Rating: 4
Definitely
      clearer than the Oracle manual.  November 29, 2001

Reviewer:  Doru Crinta  from Brussels/Belgium

They should make clear the presence of 'side' objects in the database for
dependency policies implementation.
Your answer is covering half of my problem.
Next:
I have schema A with table TEST, partitioned. I have schema B with a package PKG
accessing table A.TEST. Obviously there will be an non-existing object created
in schema B for A.TEST.
The reality shows that, just by adding a partition to A.TEST , I'm invalidating
B.PKG.
The add/drop partition operation is not changing the core object definition.
Could you explain the steps performed to mark B.PKG as invalid?

          Followup:

Why would there be a "non-existing object" in schema B?  I would not expect that
to be the case...  Only if I used a public synonym (which I wouldn't).

Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> grant dba to a identified by a;
Grant succeeded.

ops$tkyte@ORA817DEV.US.ORACLE.COM> grant dba to b identified by b;
Grant succeeded.

ops$tkyte@ORA817DEV.US.ORACLE.COM> @connect a/a

a@ORA817DEV.US.ORACLE.COM> CREATE TABLE t( x int )
  2  PARTITION BY RANGE (x) (
  3    PARTITION p1 VALUES LESS THAN (1) ,
  4    PARTITION p2 VALUES LESS THAN (2) ,
  5    PARTITION the_rest VALUES LESS THAN (MAXVALUE)
  6  )
  7  /
Table created.

a@ORA817DEV.US.ORACLE.COM> grant select on t to b;
Grant succeeded.

a@ORA817DEV.US.ORACLE.COM> create public synonym pub_syn_to_t for t;
Synonym created.

a@ORA817DEV.US.ORACLE.COM> @connect b/b

b@ORA817DEV.US.ORACLE.COM> create synonym local_syn_to_t for a.t;
Synonym created.

b@ORA817DEV.US.ORACLE.COM> create procedure p1
  2  as
  3  begin
  4          for x in ( select * from a.t ) loop
  5                  null;
  6          end loop;
  7  end;
  8  /
Procedure created.

b@ORA817DEV.US.ORACLE.COM> create procedure p2
  2  as
  3  begin
  4          for x in ( select * from pub_syn_to_t ) loop
  5                  null;
  6          end loop;
  7  end;
  8  /
Procedure created.

b@ORA817DEV.US.ORACLE.COM> create procedure p3
  2  as
  3  begin
  4          for x in ( select * from local_syn_to_t ) loop
  5                  null;
  6          end loop;
  7  end;
  8  /
Procedure created.

b@ORA817DEV.US.ORACLE.COM> select object_name, status from user_objects;

OBJECT_NAME                    STATUS
------------------------------ -------
LOCAL_SYN_TO_T                 VALID
P1                             VALID
P2                             VALID
P3                             VALID

b@ORA817DEV.US.ORACLE.COM> select name, type, referenced_owner rowner,
  2             referenced_name rname, referenced_type rtype
  3  from user_dependencies
  4  order by name, referenced_type, referenced_name;

NAME TYPE      ROWNER RNAME                          RTYPE
---- --------- ------ ------------------------------ ---------
P1   PROCEDURE SYS    SYS_STUB_FOR_PURITY_ANALYSIS   PACKAGE
P1   PROCEDURE A      T                              TABLE

P2   PROCEDURE B      PUB_SYN_TO_T                   NON-EXISTENT
P2   PROCEDURE SYS    SYS_STUB_FOR_PURITY_ANALYSIS   PACKAGE
P2   PROCEDURE PUBLIC PUB_SYN_TO_T                   SYNONYM
P2   PROCEDURE A      T                              TABLE

P3   PROCEDURE SYS    SYS_STUB_FOR_PURITY_ANALYSIS   PACKAGE
P3   PROCEDURE B      LOCAL_SYN_TO_T                 SYNONYM
P3   PROCEDURE A      T                              TABLE

9 rows selected.

see, only P2 with the public synonym has this "anti-object"...

b@ORA817DEV.US.ORACLE.COM> @connect a/a

a@ORA817DEV.US.ORACLE.COM> alter table t
  2  split partition the_rest
  3  at ( 3 ) into ( partition p3, partition the_rest )
  4  /
Table altered.

a@ORA817DEV.US.ORACLE.COM> @connect b/b

b@ORA817DEV.US.ORACLE.COM> select object_name, status from user_objects;

OBJECT_NAME                    STATUS
------------------------------ -------
LOCAL_SYN_TO_T                 VALID
P1                             INVALID
P2                             INVALID
P3                             INVALID

b@ORA817DEV.US.ORACLE.COM>

but yes, altering the table will invalid dependent objects.  It does not
look to see if the alter WOULD in fact affect the behavior of the referenced
objects -- only that it COULD (thats the whole point of the dependency
mechanism, mark things invalid that MIGHT be affected, force them to recompile
so we'll discover if they were).

For example, P1, P2, P3 are dependent on T.  What if they had coded:

b@ORA817DEV.US.ORACLE.COM> create procedure p1
  2  as
  3  begin
  4          for x in ( select * from a.t partition (p1) ) loop
  5                  null;
  6          end loop;
  7  end;
  8  /

Procedure created.

instead ( a partition access ).  They are STILL dependent on T (not on T
partition (p1)).  Now suppose you DROPPED partition P1 (instead of splitting
the_rest).  Well, if the objects DIDN'T go invalid, we wouldn't catch that you
are actually referencing something that NO LONGER exists!

So, the invalidation is normal, expected and correct...

      Rating:
      5
Reader  November 29, 2001
Reviewer:
      Reader  from USA
Tom,

The table T is owned by schema (user) , a.
The schema (user) b, has two synonyms,
  public synonym ( This is non-existent )
  private synonym ( This is existent synonym )

Could you clarify why local synonym is existent object, but public synonym is
not..

Also, there is a rumour that using public synonym, will
create non-existent entires in shared pool.
(Example: If user "scott" refers to table "tiger.t" via
          public synonym, then Oracle create non-existence
          entry "scott.t" in the shared pool leading to
          performance degradation due cluttering of objects
          in the shared pool)
If there is truth to it, what is the reason for
tracking non-existent objects and how is it utilized in the management of the
shared pool.

Thanks

          Followup:

The private synonym is "existent" because if I have a private SYNONYM T, i
cannot have a view or table (etc) named T -- i don't need that "non existent"
object, I personally have a real object....

Public synonyms do what you describe.  The non-existent objects are tracked for
the reasons explained in this Q/A.  They are necessary for the dependency
mechanism.

      Rating: 5
Now i know what
      non existent is   November 30, 2001
Reviewer:
      Kamaljeet  from UK

      Rating: 4
negative
      effects?  February 28, 2002
Reviewer:  Chris
       from Germany
is there any negative effect by non existent objects?
how to get rid of those objects?

          Followup:

If you don't use public synonyms, you won't get many "non-existent
anti-objects".  You can get some still (eg: have a view that selects from a
non-existent table.  If you "create table t ...", "create view v as select *
from t", "drop table t", you'll have one..)

You get rid of them mostly by not using public synonyms.

Yes, there can be negative effects -- see:

http://www.ixora.com.au/q+a/0012/05112945.htm
...

February 28, 2002 Robert -- Thanks for the question regarding
      "Updating a large table", version 8.0.6

      You Asked
Hello Tom,

Welcome back.  Hope you had a great vacation.

We need to update a table in our data warehouse that has over 96 million rows
and is stored in about 15 gigabytes.  I copied this table to our sandbox
instance on a test server and tried the following approach which ran for over 28
hours.

declare
    v_counter          number   := 0;
begin
    FOR x in (select decode(substr(FSCL_ACTG_PRD_N,5),
                              '01',FSCL_ACTG_PRD_N -91,
                              '02',FSCL_ACTG_PRD_N -91,
                              '03',FSCL_ACTG_PRD_N -91,
                                   FSCL_ACTG_PRD_N -3) FSCL_ACTG_PRD_N,
              rowid
              from ATMCINV.INVC_PRICE_FACTOR_ATMC)
    LOOP
        update ATMCINV.INVC_PRICE_FACTOR_ATMC a
               set a.FSCL_ACTG_PRD_N = (x.FSCL_ACTG_PRD_N)
               where a.rowid = x.rowid;
        v_counter := v_counter + 1;
        IF v_counter > 2000000 THEN
           commit;
           v_counter := 1;
        END IF;
    END LOOP;
    commit;
end;
/

Although this table has a primary key and 2 non unique btree indexes in
production I did not creat them for the test.  Since the servers and instances
are similar I assume this process will run at least this long in production.  I
have a gzipped fastunload of this table that I could sql*load.  These loads run
about 2 hours through thepipe to gunzip.  However, I am struggling with the
syntax for the decode of the substring in the control file. Is this possible?

-- Generated by ManageIT Fast Unload for Oracle
-- Target Oracle version 800
-- Fastload Format
-- Table ATMCINV.INVC_PRICE_FACTOR_ATMC

-- Output data file not specified by the user
-- Need to fill in the actual datafile name before using SQL*LOADER.

OPTIONS (DIRECT=TRUE)
LOAD DATA
INFILE 'Pipe' "VAR"
TRUNCATE
INTO TABLE "ATMCINV"."INVC_PRICE_FACTOR_ATMC"
REENABLE
TRAILING NULLCOLS

("INV_N" VARCHAR(7),
"INV_ADJ_N" VARCHAR(140),
"INV_LIN_N" VARCHAR(140),
"PRCS_TS" DATE "MM/DD/YYYY HH:MI:SSPM" TERMINATED BY X'7F',
"INV_PRC_FCTR_SEQ_N" VARCHAR(140),
"INV_LIN_TYP_C" VARCHAR(2),
"INV_LIN_TYP_NA" VARCHAR(30),
"INV_LIN_BASE_A" VARCHAR(140),
"INV_LIN_EXT_A" VARCHAR(140),
"PROMO_N" VARCHAR(140),
"STMT_EXT_A" VARCHAR(140),
"ROLL_ALLW_IN_PRC_F" VARCHAR(1),
"INCL_IN_DISC_F" VARCHAR(1),
"ACCM_FOR_FTR_F" VARCHAR(1),
"SRV_CHRG_ALLW_C" VARCHAR(6),
"GL_ACCT_SEG3_KEY_N" VARCHAR(140),
"SLSRP_STAT_C" VARCHAR(30),
"SLSRP_NA" VARCHAR(30),
"MISC_CHRG_C" VARCHAR(3),
"FSCL_ACTG_PRD_N
   decode(substr(FSCL_ACTG_PRD_N,5),
         '01',FSCL_ACTG_PRD_N -91,
         '02',FSCL_ACTG_PRD_N -91,
         '03',FSCL_ACTG_PRD_N -91,
              FSCL_ACTG_PRD_N -3)" VARCHAR(140),
"GL_LOC_C" VARCHAR(3),
"GL_DEPT_C" VARCHAR(3),
"GL_ACTG_N" VARCHAR(6),
"GL_RESC_C" VARCHAR(6),
"GL_PROJ_C" VARCHAR(6),
"GL_PROD_N" VARCHAR(10),
"GL_SUFX_C" VARCHAR(2),
"POST_TO_CHKBK_F" VARCHAR(1),
"DW_PRCS_TS" DATE "MM/DD/YYYY HH:MI:SSPM" TERMINATED BY X'7F',
"ACTV_F" VARCHAR(1))

-- END OF CONTROL FILE

Here is the error it throws:

Error calling once/load initialization
ORA-02373: Error parsing insert statement for table
"ATMCINV"."INVC_PRICE_FACTOR_ATMC".
ORA-00604: error occurred at recursive SQL level 1
ORA-00972: identifier is too long

What do you recommend?

      and we said...
You cannot use SQL functions with the direct path loader until 9i.

Advice:

row by row updates always are incredibably slow as compare to a single update.
Why not just SIZE your rollback correctly and issue:

        update ATMCINV.INVC_PRICE_FACTOR_ATMC a
               set a.FSCL_ACTG_PRD_N = decode(substr(FSCL_ACTG_PRD_N,5),
                              '01',FSCL_ACTG_PRD_N -91,
                              '02',FSCL_ACTG_PRD_N -91,
                              '03',FSCL_ACTG_PRD_N -91,
                                   FSCL_ACTG_PRD_N -3) ;

and be done with it?  If you cannot for some reason do that, find the low and
high primary key values (assuming primary key is a number).  Say the low is 1
and the high is 100,000,000.

Divy up the ranges and issue:

        update ATMCINV.INVC_PRICE_FACTOR_ATMC a
               set a.FSCL_ACTG_PRD_N = decode(substr(FSCL_ACTG_PRD_N,5),
                              '01',FSCL_ACTG_PRD_N -91,
                              '02',FSCL_ACTG_PRD_N -91,
                              '03',FSCL_ACTG_PRD_N -91,
                                   FSCL_ACTG_PRD_N -3)
          where primary_key between :SOME_VALUE and :SOME_OTHER_VALUE;

now you can even run those in parallel (each will get its own rollback segment).

If FSCL_ACTG_PRD_N is indexed, drop the index of course.

Review &
      Followup

      Rating:
      5
Thanks!  February 21, 2002
Reviewer:
      Robert D. Ware  from St. Louis, Mo USA
Tom,

Thank you for the quick response.

You said:
You cannot use SQL functions with the direct path loader until 9i.

How would we use the decode of the substr in a conventional load?  Would the
conventional loader perform similar to the PL/SQL approach?

In all likelihood we will probably opt for temporarily growing the rollback
segment tablespace.  The 96 million rows reside in almost 2 million 8K blocks,
that's an average of 48 rows to the block.  I assume this would require about 16
gigabytes of rollback space for this update to work since every block will get
changed.

          Followup:

You can use decode and substr in a conventional path load, you would have:

...
"SLSRP_NA" VARCHAR(30),
"MISC_CHRG_C" VARCHAR(3),
"FSCL_ACTG_PRD_N"                            <<<===== quote
   "decode(substr(:FSCL_ACTG_PRD_N,5),        <<<----- quote & bind :
         '01',:FSCL_ACTG_PRD_N -91,
         '02',:FSCL_ACTG_PRD_N -91,
         '03',:FSCL_ACTG_PRD_N -91,
              :FSCL_ACTG_PRD_N -3)" VARCHAR(140),
"GL_LOC_C" VARCHAR(3),
"GL_DEPT_C" VARCHAR(3),
"GL_ACTG_N" VARCHAR(6),
....

you were missing some quotes and binds there.

It would probably be faster then plsql.

Doing the Piecemeal UPDATE is still an option -- it simulates in a series of
updates what you tried to do procedurally.

When you update -- CHANGED BYTES are recorded in rollback -- not the entire
block.

      Rating:
      5
Results  February 27, 2002
Reviewer:
      Robert D. Ware  from St. Louis Mo.
Tom

I just wanted to share the following with everyone.

1.) The sqlloader approach ran for 4 hours and 37 minutes whether the functions
are used or not.
2.) The PL/SQL ran 5 hours and 13 minutes.
3.) The straight SQL took 2 hours and 35 minutes

All 3 tests were done after dropping all indexes and disabling the primary key
constraint.  This tesing also used a 26 million row table which consumes 3.5
gigabytes of file space.

I am a bit confused about one thing.  The column I updated is a number field
which has a data_length of 22.  For 26 million rows times 22 bytes, I come up
with a little over 545 megabytes.  Yet the rollback space required for the
straight SQL approach was over 2.5 gigabytes.

          Followup:

There is much book-keeping information in rollback.  It is most noticable with a
small update such as yours since the overhead exceeds the data being updated
itself.  But as you update more and more data in a row -- the percentages
change.  For example:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t
  2  as
  3  select rownum c1, rownum c2, rownum c3, rownum c4, rownum c5, rownum c6,
rownum c7, rownum c8
  4  from all_objects;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from t;

  COUNT(*)
----------
     17165

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table log
  2  as
  3  select rownum c1, rownum c2, rownum c3, rownum c4, rownum c5, rownum c6,
rownum c7, rownum c8
  4   from all_objects
  5   where 1=0;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view
  2  my_used_ublk
  3  as
  4  select used_ublk
  5    from v$transaction
  6   where addr = ( select taddr
  7                    from  v$session
  8                   where sid = ( select sid
  9                                   from v$mystat
 10                                  where rownum = 1 ) );

View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into log (c1) values (null);

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          l_sql  long := 'update t set c1 = -c1';
  3      l_sql2 long := 'update log set XX = :x';
  4          l_used_ublk number;
  5  begin
  6          for i in 1 .. 8
  7      loop
  8                  execute immediate l_sql;
  9                  select used_ublk into l_used_ublk from my_used_ublk;
 10                  rollback;
 11                  execute immediate replace( l_sql2, 'XX', 'c'||i ) using
l_used_ublk;
 12                  commit;
 13                  l_sql := l_sql || ', c' || (i+1) || ' = -c' || (i+1);
 14          end loop;
 15  end;
 16  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from log;

        C1      C2      C3      C4         C5         C6         C7         C8
---------- ------- ------- ------- ---------- ---------- ---------- ----------
       200     217     233     250        275        300        324        347

ops$tkyte@ORA817DEV.US.ORACLE.COM>

so, as you can see -- to update 2 columns instead of one doesn't take 2x the
rolllback.

Always good to see that my "do it in a single sql statement if you can" mantra
is vindicated once again.

      Rating:
      4
Reader  February 27, 2002
Reviewer:
      A reader
Why I can not create the view with any other user
but SYS

SQL>  create user user19 identified by user19;

User created.

SQL> grant dba to user19;

Grant succeeded.

SQL> connect user19/user19;
Connected.
SQL> create or replace view
  2    ublk
  3    as
  4    select used_ublk
  5      from v$transaction
  6     where addr = ( select taddr
  7                      from  v$session
  8                     where sid = ( select sid
  9                                     from v$mystat
 10                                    where rownum = 1 ) );
                                   from v$mystat
                                        *
ERROR at line 9:
ORA-00942: table or view does not exist

SQL> select sid from v$mystat where rownum = 1;

       SID
----------
         7

SQL>

          Followup:

read

http://osi.oracle.com/~tkyte/Misc/RolesAndProcedures.html
then, simply:

grant select on v_$mystat to user19;
grant select on v_$session to user19;
grant select on v_$transaction to user19;

when connected as sys and they will be able to create the view.  I just used the
view to make the example shorter (and in my 817 test instance, I have select any
table ;)  -- not recommended in a "real" database of course)

      Rating:
      4
Reader  February 27, 2002
Reviewer:
      A reader
I think only SYS can create views on base tables and
create public synonym for others to access

          Followup:

see above

      Rating:
      4
Reader  February 28, 2002
Reviewer:
      A reader
Thanks

I thought this applies only to PL/SQL procedures and
not VIEWS.

1. Is v_$mystat a view or considered as table

SQL> select * from dba_views where view_name = 'V_$MYSTAT';

OWNER                          VIEW_NAME                      TEXT_LENGTH
------------------------------ ------------------------------ -----------
TEXT
--------------------------------------------------------------------------------
TYPE_TEXT_LENGTH
----------------
TYPE_TEXT
--------------------------------------------------------------------------------
OID_TEXT_LENGTH
---------------
OID_TEXT
--------------------------------------------------------------------------------
VIEW_TYPE_OWNER                VIEW_TYPE
------------------------------ ------------------------------
SYS                            V_$MYSTAT                               47
select "SID","STATISTIC#","VALUE" from v$mystat

It seems v_$mystat is a view of v$mystat which is a view of
gv$mystat which is a view of  x$ksumysta (base table)

SQL> select * from v$fixed_view_definition where view_name in
('V$MYSTAT','GV$MYSTAT');

VIEW_NAME
------------------------------
VIEW_DEFINITION
--------------------------------------------------------------------------------
GV$MYSTAT
select inst_id,ksusenum,ksusestn,ksusestv from x$ksumysta where bitand(ksspaflg,
1)!=0 and bitand(ksuseflg,1)!=0 and ksusestn<(select count(*) from x$ksusd)

V$MYSTAT
select  SID , STATISTIC# , VALUE from GV$MYSTAT where inst_id = USERENV('Instanc
e')

Thanks

          Followup:

it applies to all compiled stored objects (views, triggers, procedures,
functions, packages, materialized views, etc)

v_$mystat is a view.  v$mystat is actually a synonym.

sys@ORA817DEV.US.ORACLE.COM> select owner, object_name, object_type from
dba_objects
  2  where object_name like '%MYSTAT'
  3  /

OWNER                          OBJECT_NAME                    OBJECT_TYPE
------------------------------ ------------------------------ ------------------
SYS                            GV_$MYSTAT                     VIEW
SYS                            V_$MYSTAT                      VIEW
PUBLIC                         GV$MYSTAT                      SYNONYM
PUBLIC                         V$MYSTAT                       SYNONYM

the synonym v$mystat is (surprisingly for many) a pointer to v_$mystat!!

sys@ORA817DEV.US.ORACLE.COM> select table_owner, table_name from dba_synonyms
where synonym_name = 'V$MYSTAT';

TABLE_OWNER                    TABLE_NAME
------------------------------ ------------------------------
SYS                            V_$MYSTAT

So, v_$mystat is a view that queries an object v$mystat and v$mystat is a
synonym to v_$mystat.

V$/X$ tables are magic....

      Rating:
      4
Reader  February 28, 2002
Reviewer:
      A reader
V$/X$ tables are magic....

Are v$ really tables.

The table_name in DBA_SYNONYMS, is it not common to
both types of objects, VIEWS and TABLES.

SQL> create view v1 as select * from dba_tables;

View created.

SQL> select view_name , owner from dba_views where view_name = 'V1';

VIEW_NAME                      OWNER
------------------------------ ------------------------------
V1                             SYS

SQL> create public synonym s1 for sys.v1;

Synonym created.

SQL> select owner, synonym_name, table_owner, table_name from dba_synonyms where
synonym_name = 'S1'
;

OWNER                          SYNONYM_NAME
------------------------------ ------------------------------
TABLE_OWNER                    TABLE_NAME
------------------------------ ------------------------------
PUBLIC                         S1
SYS                            V1

In this query V1 is listed as table , but it is actually
a view

Thanks

          Followup:

no, v$ tables are not tables.

they are typically based on fixed tables (in memory data structures actually)

they do not live on disk.

      Rating:
      4
Reader  February 28, 2002
Reviewer:
      A reader
Tom,

I re-read your answer

"
v_$mystat is a view
"

Thanks

      Rating: 5
"they do not
      live on disk"  February 28, 2002
Reviewer:
      Connor  from Battersea
I thought the same, but it would appear our good friends at Oracle are becoming
more flexible with what a V$ view can be based on:

SQL> select view_definition from v$fixed_view_definition
  2  where view_name = 'V$OBJECT_USAGE';

no rows selected

SQL> set long 5000
SQL> select text from dba_views
  2  where view_name = 'V$OBJECT_USAGE';

TEXT
---------------------------------------------------------------
select io.name, t.name,
       decode(bitand(i.flags, 65536), 0, 'NO', 'YES'),
       decode(bitand(ou.flags, 1), 0, 'NO', 'YES'),
       ou.start_monitoring,
       ou.end_monitoring
from sys.obj$ io, sys.obj$ t, sys.ind$ i, sys.object_usage ou
where io.owner# = userenv('SCHEMAID')
  and i.obj# = ou.obj#
  and io.obj# = ou.obj#
  and t.obj# = i.bo#

          Followup:

Instead of saying:

they are typically based on fixed tables (in memory data structures actually)
they do not live on disk.

I should have said:

they are typically based on fixed tables (in memory data structures actually)
they typically do not live on disk.

February 28, 2002 sateesh -- Thanks for the question regarding
      "Oracle Objects and converting them to Java Objects", version version
      8.0.5.0.0

      You Asked
Hi Tom,

I am storing Objects into a table and retrieving them in a Java
program. In that I have a Java Class reference to which I am
assigning this fetched Oracle Object using the oracle.sql.STRUCT
structure..
   But the table structure for table answers_table is as
follows:

 ANSWERS_QID                     NOT NULL VARCHAR2(10)
 ANSWERS_UID                     NOT NULL VARCHAR2(20)
 ANSWERS_DESC                             ANSWER

 where answer is of type VARRAY and declared as

    answer VARRAY(500) OF VARCHAR2(100)

  In the Java Program I am trying to retrieve the VARRAY into a
String array and I wrote this code , it's giving some errors..

  import oracle.sql.*;
  import oracle.jdbc.driver.*;

  public  class fetcher{
   /* some other methods goes here which fetches the  Connection
  Object*/
   public String[] getAnswers(String qid,String uid){
     String answers[]=null;
    try{
           Statement st=con.createStatement();
           String strQuery="select answers_desc from
answers_table where answers_qid='"+qid+"' and
answers_uid='"+uid+"'";
         ResultSet rs=st.executeQuery(strQuery);
           while(rs.next()){
   oracle.sql.ARRAY answerArray =(oracle.sql.ARRAY)
rs.getARRAY("answers_desc");
    if(answerArray!=null)
     answers =(String[])answerArray.getArray();
          rs.close();
           st.close();
           return answers;
           }
           catch(SQLException se){

        throw se;
        }

    } // end of method

  }// end of class

   Thanks in advance... One final thing is I am using jdk1.1.8
on Solaris

regards
Sateesh

      and we said...
You understand that "getting some errors" is not very
descriptive.  As I do not know exactly what errors you are
getting -- I can only show an example I know works (since I ran
it).  I used Oracle 8.1.5 with jdk 1.1.8 on Solaris myself.

Here is an example I just wrote.  It is written the way I
strongly suggest you write yours:

  public static void demo( Connection conn,
                           String qid, String uid )
  throws SQLException
  {
      Statement stmt = conn.createStatement();
      PreparedStatement ps =
         conn.prepareStatement("select answers_desc " +
                            "from answers_table " +
                           "where answers_qid = :bv1 " +
                             "and answers_uid = :bv2" );
      ps.setString( 1, qid );
      ps.setString( 2, uid );
      ResultSet rset = ps.executeQuery();

      while( rset.next() )
      {
        ARRAY array = ((OracleResultSet)rset).getARRAY(1);

        String[] values = (String[])array.getArray();

        for( int i = 0; i < values.length; i++ )
            System.out.println( "row " + i + " = '" +
                                 values[i] + "'" );

      }
      rset.close();
      stmt.close();
  }

I beg of you to use BIND VARIABLES!!! Never "glue" in constants
as you are doing here.  Please use a prepared statement and bind
the variables into the statement.
Review &
      Followup

      Rating: 3
Thanks and a
      followup  February 28, 2002
Reviewer:  Dean
      Reynolds  from Nashville, TN
That is very helpful.

The problem I'm having is with the speed of our middle tier.  They can only
process 100 rows a second thru JDBC.  It does not vary much by row size / number
of columns.   We need to return on the order of 3500 rows for a part of our app
and don't want the users waiting for 35 seconds.  I was thinking/hoping that we
could create and return an object much faster than that.  Am I wrong?  I'm
trying to return 1 object rather than 3500 rows.  I even thought of building a
JTable and passing that back.  Any suggestions?  Solaris, 8.1.7, jdk 1.3.1

          Followup:

Well, check to make sure your query is tuned (use tkprof - see

http://asktom.oracle.com/~tkyte/tkprof.html
)  and play with the prefetch size.  Look at this:

$ java JdbcTest
2002-02-28 16:37:03.059: Starting
2002-02-28 16:37:05.596: Stopping fetched 14824

2.5 seconds to fetch 14,824 rows.

$ cat JdbcTest.java
import java.sql.*;
import oracle.jdbc.driver.*;

class JdbcTest
{
  public static void main (String args []) throws SQLException {

    DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());

        Connection conn =
      DriverManager.getConnection ("jdbc:oracle:thin:@aria-dev:1521:ora817dev",
      //DriverManager.getConnection ("jdbc:oracle:oci8:@slackdog.world",
                                   "scott", "tiger");

        Statement stmt = conn.createStatement ();

        ((OracleStatement)stmt).setRowPrefetch (500);
    ResultSet rset = stmt.executeQuery ("select object_name from all_objects");

    System.out.println( (new
       Timestamp(System.currentTimeMillis())).toString() +
                   ": Starting" );

        int cnt = 0;
    while (rset.next ())
        {
                rset.getString (1);
                cnt++;
        }
    System.out.println( (new
       Timestamp(System.currentTimeMillis())).toString() +
                   ": Stopping fetched " + cnt );
}
}

      Rating: 5
I KNEW
      it  February 28, 2002
Reviewer:  Dean
      Reynolds  from Nashville, TN
The query runs in about a second.  The middle tier team is telling me it's a
jdbc problem.

Thanks so much for the code so I can show that it is not the database and not
even a jdbc problem. I've had problems proving that the database is humming
along. Many thanks.

          Followup:

If I had a penny for every time a java guy says "problems in the database".....
man oh man, i could retire yesterday.

      Rating: 4
Are entity
      EJB's used in your middle-tier  March 01, 2002

Reviewer:  Eric Ma  from New Jersey, USA
How does your middle-tier handle the Oracle data?  Using plain JDBC resultset,
or stateless session bean + JDBC, or BMP entity beans?  If entity beans are used
the Java guys have to look into using patterns to optimize the performance.

March 01, 2002 Prakash -- Thanks for the question regarding "Can
      we undo alter table set unuse (column)?", version 8.1.7

      You Asked
Tom,
Can we undo alter table set unuse (column)?
What I mean is how to make those unused columns usable again without droping
them? Can we do that?
Or do we have to drop them and recreate them?
Can I extract data from column, which we set as unused?

Thanks in advance

      and we said...
Nope.  It is as good as gone.

You have to recreate them.

If you are in archivelog mode, you can do a tablespace point in time recovery to
ressurect the data possiblly.
Review &
      Followup

      Rating: 4
very
      precise  February 28, 2002
Reviewer:
      stevenzy  from canada
Hi, Tom,

this is a question that i think about for long time.

thanks a lot for your precise reply!

have a nice day!

      Rating: 3
Re.-Can we undo
      alter table set unuse (column)?   February 28, 2002

Reviewer:  Prakash  from LA,USA
What's the difference between dropping a column and set unuse if we cannot undo
set unused.
Why use set unused?

          Followup:

drop column rewrites the table right there, right then.  It takes a bit of time.

unused -- it is a flag delete.  The column is as good as gone but the entire
table is not rewritten, the space for existing data is not reclaimed.  It is
very fast.

You would use "set unused" to get rid of a column right now and then do the drop
when you had sufficient time to rewrite the entire table (you can drop unused
later).

      Rating: 3
Re.-Can we undo
      alter table set unuse (column)? - FUP  February 28, 2002

Reviewer:  Jim  from Australia
Prakash,

Dropping a column maybe (depending on the size
of the table/column) very resource intensive and time
consuming.

So the set unused is a great way to make the column
nolonger visable very quickly. You can then actually
drop the column to reclaim space etc at a later time
typically when during an outage or off peak time.

      Rating:
      3
TSPITR  March 01, 2002
Reviewer:  Tom
      Harris  from England
Tom,

For TSPITR why does Oracle recommend cloning the entire database prior TSPITR ?.
Surely this takes a lot longer than restoring just the minimum set of
datafiles/recreate control file with only the relevant datafiles and then doing
point-in-time recovery.

          Followup:

where did you read that "advice".

Thats not what the RMAN guides say.

March 01, 2002 David -- Thanks for the question regarding
      "DBMS_LOCK_ID", version 8.1.7.3

      You Asked
Oracle installs the following sequence:
CREATE SEQUENCE SYS.DBMS_LOCK_ID
    START WITH 1073741824
    INCREMENT BY 1
    NOMINVALUE
    MAXVALUE 1999999999
    NOCYCLE
    CACHE 20
    NOORDER
/

What is it's purpose?  Should I be concerned with it wrapping?  What would cause
it to increment?  Can it be changed to wrap?
Thank You!

      and we said...
It is used by:

sys@ORA817DEV.US.ORACLE.COM> select owner, name, type from dba_dependencies
where
  2  referenced_name = 'DBMS_LOCK_ID' and referenced_owner = 'SYS';

OWNER                          NAME                           TYPE
------------------------------ ------------------------------ ------------
SYS                            DBMS_LOCK                      PACKAGE BODY

Even if we generated one per second, every second, it would take 30years to
exhaust itself.

It is used to generate lock names.  If you don't make use of dbms_lock, it won't
even budge.

Don't touch it (or ANYTHING owned by SYS for that matter) without someone from
Oracle support telling you to touch it!

Review &
      Followup

      Rating:
      5
  March 01, 2002
Reviewer:  David
      Busfield  from Arizona, USA
Thanks!

March 01, 2002 Asha -- Thanks for the question regarding "Need
      to Store Session Variables in PL/SQL", version 8.1.7/Portal

      You Asked
Hi Tom,
       Thanks for taking my question and your great idea ofd knoledge
sharing .
        I'm using Portal version 3.0.6.6.5 with Oracle 8.1.7 .
I created couple of repots by coding Pl Sql webtoolkit in WebDB 2.0.
Now I need to create an application with 9 TABS .The first TAB is the default
page in which user makes all selections and each of the following TABS is a
report which will use the selections made in the First TAB as parameters . The
user should be able to come back to the
first page any time and the selections he made before should be
in that page. If you have any examples of this sort either stored procedures or
PSP I'd really appriciate that .I apologize for this long question , I know your
time is so premium but I wanted to
explain it in detail as much as I could.
Thanks in advance,
Asha

      and we said...
The way I do this is to set up a session id.  I either use a cookie to pass this
session id from page to page or I use a hidden field (either approach works
fine).

This session id is primary key into a "state" table.  This state table it just a
database table that you use to store the attributes of a users sessions (your
info from "tab1" for example.

You would create a package like this:

create package state
as
    g_data   state_table%rowtype;

    procedure get_state( p_sess_id in number );
    procedure save_state( p_sess_id in number );
end;
/

create package body state
as

procedure save_state( p_sess_id in number )
is
begin
   update state_table set  c1 = g_data.c1, ... cn = g_data.cn
     where sess_id = p_sess_id;

   if ( sql%rowcount = 0 )
   then
            insert into state_table (sess_id, c1, c2, ... )
            values ( p_sess_id, g_data.c1, ... g_data.cn );
   end if;
end;

procedure get_state( p_sess_id in number )
is
begin
   select * into g_data from state_table where sess_id = p_sess_id;
exception
    when no_data_found then null;
end;

end;
/

Now just call get_state/save_state whenever you need and use the values in
state.g_data.

before you say "but thats IO to the database" -- I'll say -- thats what
databases do best.  It is what I do, even on this application you are using
right here...
Review & Followup

      Rating: 4
Straight and
      simple  March 01, 2002
Reviewer:
      A reader
Hi Tom,
       Thanks a lot for your info .
I'll try that out.

Thanks,
Asha

March 01, 2002 Raj -- Thanks for the question regarding
      "Rebuilding Indexes", version 5

      You Asked
I know this question has been asked earlier and
I am sorry to take up this question slot but I
am confused regarding rebuilding indexes.

If I am interpreting it correctly, you don't
recommend rebuilding indexes at all. I have talked
to two seasoned Oracle dba's and they both
recommend rebuilding indexes at a regular period
of time. We have couple of tables with a million
to two million records but the indexes have never
been rebuilt. Also, we don't have that much
deletion of records going on though we do have
insertions and quite a few selects on those
tables. Do you recommend rebuilding indexes for
such a database.

What would be the implication if we did
have to rebuild the indexes. Would I need lot
of space for doing such a task.

Thanks a lot for your help.

Raj

      and we said...
Ask them for the technical reasons WHY they rebuild.

When they say "performance of course" -- ask them for the performance metrics
they took BEFORE the rebuild and AFTER the rebuild.  They won't have any (no one
ever does, no one seems to think about doing that).  They will say "it just goes
faster trust me".

When they say "to reclaim space of course" -- ask them "but how long does it
take before the index is just a big as it was before the rebuild".  You see they
have not reclaimed any space -- the index will just grow again, eventually
hitting a steady state size that it likes to be.

If you need to rebuild your indexes, you need 2x the space -- you'll have the
old and the new index for a period of time.  If you do it online, you'll need
additional space to hold the changes that are made during the rebuild as well.
Review &
      Followup

      Rating: 3
Is your advice
      against rebuilding a "blanket" one?  March 01, 2002

Reviewer:  Mohit Dubey  from UK
Hi Tom,

Would rebuilding indexes not help in case you want to re-balance the b*tree?
Particularly in case of indexes deriving their values from a monotonically
increasing sequence (as I trust most of the unique/foreign keys are)?

Please throw some more light on this issue!

Mohit.

          Followup:

If you can show me mearsurable performance increases or some other measurable,
material benefit -- by all means rebuild.

99.99999999% of the time, no one measures, they just rebuild everything (making
a really big deal out of it, wasting time, cutting off access, generating
overtime hours, etc....

To HAVE to rebuild it a rarity -- not the norm.  Keep good metrics, show the
before and after effects.  Be ready to prove that the rebuild was worth the
effort (i mean, we have to benchmark ourselves in so many other ways -- why is
this any different).

      Rating: 3
Rebuilding
      Indexes  March 01, 2002
Reviewer:  Rob
       from Chicago
I find that I have to watch bitmap indexes very closely.
Our fact tables get loaded every day and every so often a bitmap index seems to
grow very rapidly. I rebuild the index and it then holds at a steady state. I
see this often if we delete and reinsert a days worth of info. The performance
improvements comes for the inserts on the table not the select using the index.

          Followup:

Agreed -- bitmaps are a special case, easily measurable, definite payback.

Now, about b*trees......

March 01, 2002 Michi -- Thanks for the question regarding "iAS
      connection timeout", version version 8.1.6

      You Asked
Tom,

I'm developing a website using iAS with PL/SQL cartridge and I
have few questions about security:

1)Is there any way to configure session timeout on iAS? By
session time out I mean the amount of time the user can stay
connected to a website without use.
  The parameter "Timeout" at "http.conf" file seems to have
other meaning.
2)Using DAD, the application program doesn't have any concern
about connecting to database. In this case, who controls the
connection timeout to the database? If no cookies and session
timeout specific logic is used, the connection to the database
will be opened during all the time while the website is active?

Thanks,
Michi

      and we said...
1) A user cannot be connected to a website "without use" (idle)
using HTTP and the PL/SQL cartridge.  It is totally stateless by
definition.  Unless I am currently running PLSQL procedure, I
have no connection, no resources allocated on your server.
There is nothing to "timeout".

Since it is stateless, what you are trying to do is not really
possible.  You cannot tell the difference at the webserver
between a "new session" and an "existing session" (because the
concept of an existing session just does not exist).  For
example, I fire up my browser - login and run a page.  I exit
the browser and go to lunch.  Now, am I idle or am I logged out?
 the webserver has no idea.  I come back from lunch and login
again and request another page.  Should we "time you out"
because it was an hour since your last page?  No, not in this
case but the problem is we cannot differentiate this case from
the "idle" case.

Since they consume no resources on your webserver while "idle"
the usual motivations for timing out (releasing resources) does
not apply here.

2) the connection is made when a module fires up.  The
connection will process the request.  The module will be
returned to a pool.  If another user needs a session, this
module may be returned and will be used for them.  This session
is not for a specific user but rather is part of a pool that
everyone will use.  The modules will "go away" by themselves
after a period of time.  While they are idle they are consuming
no resources (no locks).
Review & Followup

      Rating: 4
Trying not to
      complicate  March 01, 2002
Reviewer:  Hetal
       from Washington, DC USA
If the connection is stateless ( which i am aware of ), if after executing
procedure  which is a query page, i go to the query list page. How does the
database keep track when i hit the next button to retrieve another set of
records.
  Also once i logged on , i execute procedure A. Then I execute procedure B. Why
am i not prompted for login again?
  I guess I am trying to see how does OAS keep track of returning user?

          Followup:

The browser caches your username and password if you are using basic
authentication and sends. The BROWSER remembers who you are, not ias.

March 01, 2002 Michelle -- Thanks for the question regarding
      "Use of Resumable Space Allocation", version 9i

      You Asked
Does Resumable Space Allocation work with the Import Utility?

      and we said...
yes.

$ imp help=y

Import: Release 9.0.1.0.0 - Production on Fri Mar 1 15:38:34 2002

(c) Copyright 2001 Oracle Corporation.  All rights reserved.

You can let Import prompt you for parameters by entering the IMP
command followed by your username/password:

     Example: IMP SCOTT/TIGER

Or, you can control how Import runs by entering the IMP command followed
by various arguments. To specify parameters, you use keywords:

     Format:  IMP KEYWORD=value or KEYWORD=(value1,value2,...,valueN)
     Example: IMP SCOTT/TIGER IGNORE=Y TABLES=(EMP,DEPT) FULL=N
               or TABLES=(T1:P1,T1:P2), if T1 is partitioned table

USERID must be the first parameter on the command line.

Keyword  Description (Default)       Keyword      Description (Default)
--------------------------------------------------------------------------
USERID   username/password           FULL         import entire file (N)
BUFFER   size of data buffer         FROMUSER     list of owner usernames
FILE     input files (EXPDAT.DMP)    TOUSER       list of usernames
SHOW     just list file contents (N) TABLES       list of table names
IGNORE   ignore create errors (N)    RECORDLENGTH length of IO record
GRANTS   import grants (Y)           INCTYPE      incremental import type
INDEXES  import indexes (Y)          COMMIT       commit array insert (N)
ROWS     import data rows (Y)        PARFILE      parameter filename
LOG      log file of screen output   CONSTRAINTS  import constraints (Y)
DESTROY                overwrite tablespace data file (N)
INDEXFILE              write table/index info to specified file
SKIP_UNUSABLE_INDEXES  skip maintenance of unusable indexes (N)
FEEDBACK               display progress every x rows(0)
TOID_NOVALIDATE        skip validation of specified type ids
FILESIZE               maximum size of each dump file
STATISTICS             import precomputed statistics (always)
RESUMABLE              suspend when a space related error is encountered(N)
RESUMABLE_NAME         text string used to identify resumable statement
RESUMABLE_TIMEOUT      wait time for RESUMABLE
COMPILE                compile procedures, packages, and functions (Y)
VOLSIZE                number of bytes in file on each volume of a file on tape

The following keywords only apply to transportable tablespaces
TRANSPORT_TABLESPACE import transportable tablespace metadata (N)
TABLESPACES tablespaces to be transported into database
DATAFILES datafiles to be transported into database
TTS_OWNERS users that own data in the transportable tablespace set

Import terminated successfully without warnings.

Review &
      Followup

      Rating: 5
Good to
      know.  January 09, 2004
Reviewer:  Robert
       from Memphis, USA.

March 01, 2002 Farid -- Thanks for the question regarding
      "to_char and without to_char", version 7.3.4.5.0

      You Asked
Hi Tom,

I dont understand the difference in the resulted rows. Can you please explain me
? Actually I am looking for the records where last_update_date is less than
1/7/2002 11:42:24 . I have written these SQL statements.

15:39:21 SQL> select count(*) from tracer.payment_entry_point_po_invc where
last_update_date < '07-JAN-02';

  COUNT(*)
----------
     73077

15:39:33 SQL> select count(*) from tracer.payment_entry_point_po_invc
15:42:52   2  where to_char(last_update_date,'DD-MON-YY:HH:MI:SS') <
'07-JAN-2002:11:42:24';

  COUNT(*)
----------
     17140

15:43:03 SQL> ed
Wrote file afiedt.buf

  1  select count(*) from tracer.payment_entry_point_po_invc
  2* where to_char(last_update_date,'DD-MON-YY:HH:MI:SS')
    < '07-JAN-2:11:42:24'
15:43:11 SQL> /

  COUNT(*)
----------
     17140

Thanks in advance

      and we said...
one compares dates, one compares strings.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from dual where '01-AUG-2001' <
'11-JAN-2001';

D
-
X

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from dual where to_date(
'01-AUG-2001' ) < to_date('11-JAN-2001' );

no rows selected

ops$tkyte@ORA817DEV.US.ORACLE.COM>

The string that begins with '01' is less then the string that begins with '11'.

Please get in the habit of using TO_DATE() on all strings you compare to dates.
Best to be EXPLICIT.

Compare dates to dates,
Compare strings to strings,
compare numbers to numbers

never compare dates to strings, strings to numbers, numbers to dates.  ALWAYS be
explicit in your conversions.
March
      01, 2002 vrishali -- Thanks for the
      question regarding "SGA tuning and Net tunning", version 8.1.6.2.0

      You Asked
Hi Tom,

While tuning SGA -

1.select status,count(*) from sys.v$bh group by status

cr    2
read    1
xcur    32765

is that values are okay? I didn't get much information about
this table and how to tune SGA by using this query.

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

2. Also _shared_pool_reserved_min_alloc - is it good to tune
this parameter or leave its value as default?

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

1.I was reading about protocol.ora and nodelay=s flag.
Doe that flag has to be set in this file.

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

2. Regarding SDU and TDU parameters
Are those parameter always improve the performance for network
trips in database?

Thanks
Vrishali

      and we said...
Here is some info on v$bh

v$bh --> what it is and where is it used?
=========================================

This is a very important view when we are looking at performance

monitoring in a parallel server environment.

v$bh gives the status of local forced writes and forced reads
for every buffer in the buffer cache.

In version 8 as compared to version 7 (the base tables for this
view are the tables x$bh and x$le), the view is defined
internally. The columns are still the same though.

Important columns in this view are as follows:

1. The 'forced_write' column which counts the number of times
DBWR had to   write this this block to disk because this
instance had dirtied the block   and another instance had
requested the lock on the block in a conflicting  mode.

2. The 'forced_read' column which counts the number of times the
block had  to be re-read from disk because another instance had
forced it out of this instance's cache by requesting the PCM
lock on the block in exclusive mode.

3. The 'lock_element_addr' column contains the address of the
lock element  element that is locking this buffer.

   If two buffers have the same lock_element_addr, then they are
being protected by the same lock. Anytime two buffers are
covered by the PCM lock, you can have false collisions between
the buffers.

4. The 'status' column says whether the block is currently :

    a) not currently in use              (FREE)
    b) exclusive                         (XCUR)
    c) shared current                    (SCUR)
    d) in consistent read mode           (CR)
    e) being read from disk              (READ)
    f) in media recover mode or          (MREC)
    g) in instance recovery mode.        (IREC)

Columns 1 and 2 together represent the number of disk i/o's an
instance had to perform on each block in the cache due to
conflicting lock requests by other instances. These i/o's are
wasteful since they occur only due to lock activity
and thus they need to be avoided.

Thus, the columns force_write and force_read basically determine
how gc_files_to_locks need to be allocated to various files and
also serve as an important parameter to determine what the value
of gc_db_locks should be.

2) I would not set any underscore parameters without being told
to by support.  It is not necessary to set this, especially if
you make proper use of the large_pool under MTS.

1) if you feel for some reason you need to set tcp.nodelay=yes,
you would have to do that in the protocol.ora file.  No, you do
not HAVE to set it.

2) No, they do not ALWAYS improve it, they can also destroy it.
They are extreme fine tuning knobs, typically used only in a WAN
environment.

Review &
      Followup

      Rating:
      4
Reader  March 01, 2002
Reviewer:
      A reader
Tom,

Could you tell us what is the significance of
class# in v$bh

Thanks

          Followup:

Class number:
1 data or index blocks
2 sort blocks
3 save undo blocks
4 segment headers
5 save undo segment header blocks
6 free list blocks
7 system undo segment header blocks
8 system undo segment blocks
7+(n*2) undo segment n header block
8+(n*2) undo segment n block

      Rating:
      5
Reader  March 01, 2002
Reviewer:
      A reader
Great and timely information

Thanks

March 01, 2002 Scott -- Thanks for the question regarding
      "Translating a character to multiple characters", version 8.1.7

      You Asked
Do you know a technique to translate a character to multiple characters in a
single select using SQLPLUS?

I'm dynamically creating insert statements based off of existing data in a
table.

select 'insert into CCWS_JOIN_SQL
       (QUERY_NAME,SEQUENCE,LINE) '||chr(10)||
       ' values ('||''''||query_name||''''||','||sequence||
                 ','||''''||line||''''||');' insert_statement
from JOIN_SQL
where query_name = 'LAND_BEHAVIOR_AVIATION'

An example of a row of data that contains a quotation mark that is causing grief
is:

INSERT_STATEMENT
----------------------------------------------------------------------
insert into CCWS_JOIN_SQL
       (QUERY_NAME,SEQUENCE,LINE)
 values ('LAND_BEHAVIOR_AVIATION',72,'  'TRANSITION_LIFT' BEHAVIOR');

I know that if I can get the result to look like below it will work. Which is
why I'm trying to translate a single ' into '||'||'||''''||

insert into CCWS_JOIN_SQL  (QUERY_NAME,SEQUENCE,LINE)

 values ('LAND_BEHAVIOR_AVIATION',72,'
'||'||'||''''||'TRANSITION_LIFT'||'||'||''''||' BEHAVIOR');

I know that translate "translates" one character to one character and will not
solve my quest so I really am looking for any technique the translating
technique (I think is a dead end)

Thank you and yours for providing this valuable resource (first time question
submitter)

      and we said...
this should do it:

ops$tkyte@ORA9I.WORLD> create table t ( query_name varchar2(30), sequence int,
line varchar2(30) );

Table created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> insert into t values ( 'LAND_BEHAVIOR_AVIATION', 72, '
''TRANSITION_LIFT'' BEHAVIOR');

1 row created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> select
  2   'insert into ccws_join_sql (query_name,sequence,line) ' || chr(10) ||
  3   'values ( ''' || replace(query_name,'''', '''''' ) || ''',' ||
  4                    sequence || ',''' ||
  5                     replace(line,'''', '''''' ) || ''' );' insert_stmt
  6    from t;

INSERT_STMT
---------------------------------------------------------------------------------
--------------------------------------------------
insert into ccws_join_sql (query_name,sequence,line)
values ( 'LAND_BEHAVIOR_AVIATION',72,'  ''TRANSITION_LIFT'' BEHAVIOR' );

replace ' with ''

in order to replace ' with '', you have to replace '''' with ''''''
Review &
      Followup

      Rating: 5
Worked
      Great!!Translating a character to multiple characters  March
      04, 2002
Reviewer:  Scott Anderson  from Orlando,
      Florida USA
Couldn't see the forest for the trees

THANX MUCH!!!

March 03, 2002 Neeraj -- Thanks for the question regarding
      "Resize Undo Tablespace / Shared-Dedicated Server", version 9i

      You Asked
Hi Tom,

1. In my database UNDO_MANAGEMENT = AUTO, but the size of

UNDO_TABLESPACE has grown to 6GB. How do i reduce/shrink it ? Do i need to
manage the size of UNDO segements or anything ?

2. Whts the difference between Shared and Dedicated servers ? My application is
deployed on WebLogic-Application Server, should i be using Oracle in a Shared
Mode ? IF yes then:-
    a. Currently its in Dedicated mode, how do i configure it in Shared Mode ?
Do i need to re-install Oracle or recreate the database ?
    b. How should i determine the parameters for shared server (no. of shared
servers, max shared servers, dispatchers and max dispatchers ?

Thanks You,

Neeraj

      and we said...
1) you need to look at your undo_retention_period.  This controls the growth of
the undo tablespace.  If you ask us to save 1 hours worth of undo, we'll grow
the undo tablespace to be big enough to hold at least 1 hours worth of undo at
peak (for the hour you generate the MOST undo).

since you have 6gig now, that means there was some period of time during which
you generated 6gig of undo during your undo_retention_period.  Unless you did
something extraordinary that you will never be doing again, shrinking your undo
space will only result in it GROWING again.

you can shrink your space by creating a new, smaller undo tablespace, altering
the system to use that new one(use scope=both if you are using spfiles, else
make sure to update the init.ora pfile as well), waiting for all active
transactions to finish in the old one and dropping it.  But again, it'll most
likely just grow to be 6gig all over again (which in the grand scheme of things
today is not that big).  I would leave it be.

Read chapter 13 of the ADMIN guide for details on managing an undo tablespace.

2) shared server is like connection pooling.  You have a number of shared
servers (say 25) that service a large community (say 1000) of users.

Web Logic typically uses a connection pool.  They'll have a pool of say 25
connections that they keep open and share amongst all of the web requests.

Just as you would not want to have double buffering going on (eg: it is best to
turn off the unix file system buffer cache on mounted file systems that have
Oracle data/log/control files on them, we buffer, unix buffers -- it just wastes
ram and time), you don't want double connection pooling going on.  In general
(warning ROT -- rule of thumb -- here), you would not use shared server with a
connection pool.  You would stick with dedicated server.

In any case, read the server concepts manual -- it explains the difference
between the two.  Read the Oracle9i net guide for setup.  All docs available
here for 9i:

http://download-west.oracle.com/otndoc/oracle9i/901_doc/index.htm

...

Review &
      Followup

      Rating: 4
Resize Undo
      Tablespace / Shared-Dedicated Server  March 02, 2002

Reviewer:  Neeraj Mehta  from Singapore
Thanks a lot Tom !

      Rating: 3
Resize Undo
      Tablespace / Shared-Dedicated Server  March 02, 2002

Reviewer:  Neeraj  from Singapore
Where can i find the undo_retention_period ? And also do i alter the value of
the same ?

Thank You

          Followup:

sorry - it is just "undo_retention"

it is an init.ora parameter

ops$tkyte@ORA9I.WORLD> show parameter undo_ret

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
undo_retention                       integer     900
ops$tkyte@ORA9I.WORLD>

if you are using a pfile (text based parameter file) edit that.  If you are
using an spfile (stored parameter file, new with 9i) use:

ops$tkyte@ORA9I.WORLD> alter system set undo_retention=1000 scope=spfile;
System altered.

March 03, 2002 --
      Thanks for the question regarding "multibale source", version 8.1.7

      You Asked
dear Tom

I have multi data source like txt,dbf,xls,xml every time i need to bring to the
database so i run sql loader and then run my query is there any way to connect
to that so i can use like ODBC  to connect to that files and run a query from
this sources insted of using every time sql loader

second

i have your book can you tell me what is the new in your new book with
your oracle friends

is there any way

best reg

      and we said...
In Oracle9i there is a new feature called EXTERNAL TABLES.  See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1615330671789
it is not available on Oracle8i or before, it is a new feature.  It allows you
to query any file you would have used sqlldr to load previously.

As for the new book - it is 100% a beginners book.  Expert One on One is an
advanced book.  The difference between the two is night and day.  Here is the
table of contents of the new book - as you can see, it is very introductory,
covering things like "how to use sqlplus, creating a user".  Expert one on one
on the other hand is somewhat (lots) more advanced:

Chapter 1: Getting To Know Oracle
Chapter 2: SQL*Plus and Basic Querying
Chapter 3: Creating and Managing Users and Tables
Chapter 4: The New 9i Sample Schemas
Chapter 5: Architecture
Chapter 6: Processing Statements in Oracle
Chapter 7: Tables
Chapter 8: Indexes
Chapter 9: Mastering SQL*Plus
Chapter 10: PL/SQL
Chapter 11: Procedures, Functions, and Packages
Chapter 12: Transactions and Concurrency
Chapter 13: Security
Chapter 14: Views
Chapter 15: Triggers
Chapter 16: Objects
Chapter 17: Advanced Querying
Chapter 18: Tuning Tools
Chapter 19: A SQL Toolkit

Case Study 1 Debugging PL/SQL
Case Study 2 Finding People

Appendix A: SQL Function Reference
Appendix B: Supplied Packages
Appendix C: Data Dictionary
Appendix D: Installing Sample Schemas
Appendix E: Options and Features

Review &
      Followup

      Rating: 4
Please Tom..put
      some Replication in this Book  March 04, 2002

Reviewer:  Pascal  from Hamburg,Germany
Hi Tom,

Thanks for ur support and a  Great Website..

I would like to see some Replication stuff in ur new Book or is it already on
the Shelves?  ..because Expert 1 -on- 1 is an advanced Oracle stuff , but i
didn't see any Replication...
Plus, ur new Book would still be useful because sometimes people forget to learn
very simple useful things like :
Mastering SQL*Plus

Best regards,

Pascal

March 03, 2002 ajay -- Thanks for the question regarding
      "migration of db", version 8.1.5

      You Asked
hai tom,

after a long time we are in line, ijust want to know how to transfer data from
oracle to sql server.i.e i have  a db of around 700 tables and i need to send
some part of it module like around 150 tables to sql server db, which way i need
to do it. along with the data,procedures,triggers and what ever is there in that
tables.
please let me know thanks

bye
ajay

      and we said...
Well, this be a question for Bill Gates or one of his guys, not Tom Kyte.

Given that SQLServer doesn't support PLSQL -- you will have to recode all
triggers and procedures.

Given that SQLServer doesn't support the same datatypes we do -- you'll have to
inspect each and every table to see how it will or will not work in SQLserver.

You can use:

http://osi.oracle.com/~tkyte/flat/index.html
to "unload" the tables in a format their BCP tool will be able to use.

If you were going from SQLServer to Oracle, we provide a migration toolkit that
will help convert TSQL (their stored procedure language) into PLSQL and their
tables into Oracle tables and automate the movement of the data.

Going to SQLServer, well, thats something we don't facilitate particularly well
for some reason.

You do understand that you will have to re-implement in SQLServer --
SQLServer and Oracle are about as different as two database systems could be.
Your odds of just picking up an Oracle app and dropping it in SQLserver are
about 0%, there will be significant work on your part unless this is a 100% read
only application (and even then, there will be work on your part).

March 03, 2002 Krishna -- Thanks for the question regarding
      "Dealing with NULL parameters in a PL/SQL procedure(or fucntion) called by
      a CallableStatement in JDBC 2.0", version 9.1.0

      You Asked
Hi Tom,

Could you please let me know how to deal with NULL parameters in a PL/SQL
procedure (or function) that is called by using a CallableStatement in JDBC?

For example, a procedure has 4 IN parameters - Param1, Param2, Param3, and
Param4. Values are available for Param1 and Param2. Param3 and Param4 have NULL
values. Param3 has a SQL datatype of NUMBER and Param4 has a SQL datatype of
Varchar2.

Thank you very much.

Krishna

      and we said...
import java.io.*;
import java.sql.*;
import oracle.sql.*;
import oracle.jdbc.driver.*;

class Callable
{

static public void main(String args[])throws SQLException
{
    DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());

    Connection conn= DriverManager.getConnection
                     ("jdbc:oracle:thin:@aria-dev:1521:ora817dev",
                      "scott","tiger");

    CallableStatement cstmt = conn.prepareCall
                              ("begin proc(?,?,?,?); end;");

    cstmt.setString( 1, "parm1" );
    cstmt.setString( 2, "parm2" );
    cstmt.setNull( 3, java.sql.Types.NUMERIC );
    cstmt.setNull( 4, java.sql.Types.VARCHAR );

    cstmt.executeQuery();

    cstmt.close();
    conn.close();
}
}

is one way to do it.
March 04,
      2002 abeginner -- Thanks for the
      question regarding "backup", version 8.0.5

      You Asked
dear Tom
is this necessary to create recovery catalog in database,
other than target database.
 while doing practical we created the catalog in the same database i:e the
default only created a separate schema for it it worked.

2. please help me understand cumulative bkup, is this a part of incremental
bkup.

      and we said...
In the "real world" it is necessary to create the recovery catalog elsewhere.

think about it -- your database crashes, you lost some fails (disk failed).  If
your recover catalog is in the failed database -- now what.  You might as well
NOT have a recovery catalog at all in that case!

A cumulative backup is part of incremental backups -- yes (in fact they are
called cumulative incremental backups).

read

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76990/rmanconc.htm#436128
...
Review
      & Followup

      Rating: 3
ashraf
      jamal  March 03, 2002
Reviewer:
      A reader
what happen if i lose previous backup
for example :
i backup level 1 and then 2 and then 3
when i test i found that level 2 of tape is faulty

          Followup:

Well, the level 3 only backed up blocks changed (in this case) since the level
2.  If you lost the level 2 (needed to take your level 1 to that point in time
when you did the level 2), your level 3 backup is useless -- it is missing all
of the changes between the level 1 and level 2.  Your backup is toast.

      Rating:
      4
backup  March 03, 2002
Reviewer:
      abeginner  from asia
dear Tom,
so that means the catalog can be created in the same database but that will be
of no use .
am i right Tom?
incremental backup must follow a cumulative .
am i right Tom?
please answer.

          Followup:

I'll just say it again:

think about it -- your database crashes, you lost some fails (disk failed).  If
your recover catalog is in the failed database -- now what.  You might as well
NOT have a recovery catalog at all in that case!

The part "you might as well NOT have ..." says it all.

A CUMULATIVE backup is a type of incremental backup.

You start with an incremental backup level 0.
You follow with cumulative incremental backups.

See the above link.  It describes the entire process.

      Rating: 4
meatadata in
      catalog  March 03, 2002
Reviewer:  abeginner
       from asia
what is metadata?

          Followup:

metadata is data about data.

The Oracle data dictionary (all_objects, all_users, etc) is metadata about what
is in the database for example.

The recovery catalog is metadata about what backups you have.

      Rating: 5
incremental 0
      then 1  March 04, 2002
Reviewer:  Yogeeraj
       from Mauritius
hi,

I have already moved part of my database on Oracle 8.1.7 from my Oracle 7.2.3. I
am working on my new backup strategy where i will be using RMAN and a recovery
catalog. I have temporarily created my recovery catalog on the same database and
plan to move it to another database later.

My Incremental 0 takes about 8 GB, Incremental 5 GB, Daily Archived Redolog
backup using RMAN - 100 MB and FULL Database export about 2 GB.

I will be doing all my backups to disk. You will agree with me that restoring
backups from DDS tape drives takes ages! So, until i get my High speed/capacity
HP "ULTRIUM", i have decided to keep a copy of my Incremental backups on disks
and even after copying them to tape.

I am considering of not doing an Incremental 1 instead the 7-hour Incremental 0
- Every night! . Which will involve deleting the old backup and using the same
space to do a new Incremental 0.

Can this be done using only rman command? or do i need to use a mixture of HP-UX
OS command with RMAN commands? How will my recovery catalog know that the old
backup is no longer here? Does breaking of backups into "rman pieces" make my
recovery process more efficient? How would i modify my script to speed up my
backup and keep my catalog updated?
----------------------------------------------------------
run {
allocate channel fs1 type disk format='/BACKUP/fs1/incr0_%u.%p';
allocate channel fs2 type disk format='/BACKUP/fs2/incr0_%u.%p';
allocate channel fs3 type disk format='/BACKUP/fs3/incr0_%u.%p';
allocate channel fs4 type disk format='/BACKUP/fs4/incr0_%u.%p';
set limit channel fs1 kbytes=2000000;
set limit channel fs2 kbytes=2000000;
set limit channel fs3 kbytes=2000000;
set limit channel fs4 kbytes=2000000;
backup incremental level 0 database;}
----------------------------------------------------------

My system is an 24x7 OLTP with low load period between 23:00 to 06:00 a.m.

Thanks very much for your time and guidance

Best Regards
Yogeeraj

          Followup:

As long as you don't overwrite the INCREMENTAL on tape, you should be OK.  If
you overwrite the tape, you are just ASKING to lose data.

You will use rman catalog commands to delete old backups.

You may well find that (when backing up to disk) a full hot backup is the
FASTEST.  incremental backups are slower by design but backup less data.  In
your case (given your decisions above), you'll use LESS disk and go faster if
you just do a full backup instead.

March 04, 2002 Jessica -- Thanks for the question regarding
      "Optimistic Locking pattern", version 9.0.0

      You Asked
Hi Tom,
Our application will implement an Optimistic Locking pattern, that is, c++ code
read concurrently from database and load
an object into memory to process business logic(for example, a shipment object
including line itmes). To prevent write-write
conflict,before trying to commit changes, the application must reread the
objects read earlier and determine whether they
have been changed in the interim. We want to add a additional column to those
tables called version_number or something like
that. Each time the object successfully update, the version is incremented. When
preparing for the update, the application
re_query the objects, add the version field to the WHERE clause and compares the
current version value with the one in its
original copy of the object. To implement the version number increment, someone
suggested trigger, i don't think that's a
good idea because of the amount of tables involved and performance hits. If
handle this via middleware, that means from now
on, all applications that can change the object must increment the version
number using same mechanism if they don't through middleware. I want to listen
to your opinion about this, any option native to Oracle and pros and cons of
both?
Thanks a lot!

      and we said...
Use the trigger -- that way no MATTER WHAT updates the object, the version field
is kept correct and consistent.  You cannot avoid having it maintained.

All it would take is one ill behaved update routine to mess everything up.

Not all logic belongs in the middle tier.  If you ask me -- much of the logic
rightly belongs in the database (including security, data integrity).  For
example, even if your interface ensures that the data is "correct" as input by
the end user -- I would still have a check constraint or trigger on the table
just to make sure.  Today your middle tier is c++, tomorrow -- who KNOWS what
will be accessing this data.  You don't want to reimplement the same logic over
and over and over and over again and again.

Use the trigger.  Hundreds of tables, thousands of tables - doesn't really make
a difference.

This sentence:

If  handle this via middleware, that means from now on, all applications that
can change the object must increment the version  number using same mechanism if
they don't through middleware.

clinches it for me.  You should do it at the source, not at the middleware.
Someday, there won't be the middleware -- you'll be updating this from something
else.  Rather then have to remember each and every time to "do the right thing",
just make sure the right thing is always done.

March 04, 2002 Ted -- Thanks for the question regarding
      "consolidate dbfile", version 8.1.7

      You Asked
Hi Tom,

I want to consolidate 6 datafiles to only 1 datafile
in my MSERTP tablespace.

can you tell me how to do it?

best regards

Ted.

      and we said...
You will create a new tablespace MSERTP_NEW

You will "alter table t move tablespace MSERTP_NEW", "alter index i rebuild
tablespace MSERTP_NEW" for each object in there. (remember to rebuild ALL
indexes on each table you move even if they are in a different tablespace!)

You will then be able to drop and remove the datafiles associated with MSERTP.

Personally -- it is not worth your effort.  one file, six files -- whats the
difference?  I would leave it be.  The only thing that could happen as a result
of this might be that you make a mistake and mess something up.

Review &
      Followup

      Rating: 5
Good
      Advice  March 02, 2002
Reviewer:  Tony
       from Philadelphia
Tom,

I enjoy your straightforward answers because they often focus, like your book,
not on how to do something, but WHY you would do it.  Many IT environments I've
worked in are often devoid of this kind of common sense mentality.

In this specific case, the downside (stuffing the move) clearly outweighs the
upside of being a bit more tidy - great answer.

      Rating: 2
consolidate
      dbfiles  March 04, 2002
Reviewer:
      A reader
Hi Tom,
thanks for your reply.
There are over 500 tables and index in my MSERTP tablespace.
Can I use export tablespace and import to consolidate the dbfiles? After I move
several large tables to another tablespace, the MESRTP tablespace will not be
increase rapidly and I want to save some space on the disk.

best regards.
Ted.

          Followup:

You can -- but you also might mess up and lose everything.

I would use alter table move and alter index rebuild.

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:47812348053
for example.  Everytime you take the data out, drop it and put it back in -- you
stand a chance of making a mistake.  so, don't take it out!

consolidating 6 files into 1 will take no more or less space then leaving it
with 6 files.

March 04, 2002 William -- Thanks for the question regarding
      "Table copy with all triggers and constrains", version 8.1.7

      You Asked
Hi Tom,

   I would like to know the best way to copy a table with all its
constrains,indexes and triggers from one schema to another or from one database
to another. Thanks a lot in advance.

      and we said...
exp userid=user1/p  tables=T  <rows=n if you don't want the data>

imp userid=user2/p fromuser=user1 touser=user2

will do that.


Subject | Article | Last Updated (East coast time)
BIND VARIABLES -- dynamic | article only | Mar 05, 2002 09:33 am
Calling Oracle Stored Procedure in Java - Sample Program | article only | Mar 05, 2002 09:48 am
PL/SQL to Replicate/Duplicate a row that has a unique ID and changing columns! | article only | Mar 05, 2002 10:48 am
Who should do the "commit" | article only | Mar 05, 2002 12:24 pm
using a collection (nested table) in a query join fails when it's inside a proc | article only | Mar 05, 2002 01:22 pm
Bit conversion from CHAR(1) | article only | Mar 05, 2002 04:05 pm
Select Statement to display weekly wages for a given month and year | article only | Mar 05, 2002 04:28 pm
flash back querry | article only | Mar 05, 2002 04:34 pm
Buffer Hit ratio =0 | article only | Mar 05, 2002 07:40 pm
Slow Query | article only | Mar 05, 2002 07:42 pm
objects | article only | Mar 06, 2002 09:15 am
XML Type VS. Relational Schema | article only | Mar 06, 2002 09:25 am
data conversion | article only | Mar 06, 2002 09:44 am
Bind Variables In SP | article only | Mar 06, 2002 10:07 am
Time Stamp | article only | Mar 06, 2002 10:10 am
Problem with Using "in list" in PL/SQL | article only | Mar 06, 2002 10:33 am
Locking | article only | Mar 06, 2002 11:08 am
BIND | article only | Mar 06, 2002 11:16 am
Function based index | article only | Mar 06, 2002 12:01 pm
density | article only | Mar 06, 2002 12:34 pm
How can i create a procedure that receives a structure of vector as a parameter ? | article only | Mar 06, 2002 12:36 pm
Connecting using encrypted password | article only | Mar 06, 2002 12:46 pm
sql query | article only | Mar 06, 2002 01:04 pm
How to start a job when a file is detected | article only | Mar 06, 2002 01:06 pm
pl/sql table manipulation | article only | Mar 06, 2002 04:29 pm
Display data | article only | Mar 07, 2002 07:54 am
Sql Query Help | article only | Mar 07, 2002 09:32 am
password file | article only | Mar 07, 2002 01:00 pm
aggregate column | article only | Mar 07, 2002 03:23 pm
A better way to a select statement on two different tables. | article only | Mar 07, 2002 04:34 pm
Unwanted tables | article only | Mar 08, 2002 08:39 am
backup | article only | Mar 08, 2002 02:10 pm
"Order by" in "Group by" | article only | Mar 08, 2002 02:15 pm
pecular problem -- dynamic sql | article only | Mar 08, 2002 02:37 pm
Type Creation Error | article only | Mar 08, 2002 06:59 pm
implications of authid current_user | article only | Mar 08, 2002 07:06 pm
Populating CLOBS via JAVA streams and Database triggers | article only | Mar 08, 2002 07:08 pm
Snapshot fed by many masters | article only | Mar 08, 2002 07:12 pm
Conversion Of Datatype RAW | article only | Mar 10, 2002 05:20 pm
Retrieve Dates Between Two Dates | article only | Mar 10, 2002 05:25 pm
See info writed in a LONG column | article only | Mar 10, 2002 07:53 pm
How is this possible..? | article only | Mar 10, 2002 08:06 pm
Page 276 Index | article only | Mar 10, 2002 08:11 pm
Array initialization | article only | Mar 10, 2002 08:22 pm
oracle date format | article only | Mar 10, 2002 08:24 pm
Tablespace Fragmentation | article only | Mar 11, 2002 07:59 am
Unicode | article only | Mar 11, 2002 03:40 pm
row(s) 1 - 47 of 47
March 05, 2002 sacin -- Thanks for the question regarding "BIND VARIABLES -- dynamic", version 8.1.6
You Asked
in my application. i need to use dynamic sql in a stored proedure with a huge
where condition...in which i want to use bind variables..

but problem for me is some of the conditions  will come to the where based on
the input i give.

say:

procedure myproc( i1 number, i2 number, i3 char,ocursor refcursor_type)
  stmt varchar2(1000);
is
  stmt := 'select * from emp where';

  if i1 > 15 then

     stmt := stmt || ' emploc = ' || chr(39) || i1 || chr(39);

  end if;

  if i2 is not null then

     stmt := stmt || ' and empsal < ' || chr(39) || i2 || chr(39);

  end if;

  if i3  != 'PENDING' THEN
     stmt := stmt || ' and empstatus = ' || chr(39) || i3 || chr(39);

  end if;

end;

i am giving a very simple, like this there are lot of conditions...
in this case how do i bind the variables, do i have to check all these
conditions in binding as well.. or any other simple way you suggest..

thanks
and we said...
read:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1288401763279
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2742950295172
for two approaches.
March 05,
      2002 Saravana -- Thanks for the
      question regarding "Calling Oracle Stored Procedure in Java - Sample
      Program", version 8.1.7

      You Asked
I have tried running the IndexTableExample included in the oracle demo. It works
fine. Now i have a similar task in project. But i get ORA-1036.

Here is the Procedure that has to be invoked.
---------------------------------------------------
CREATE OR REPLACE TYPE ARRY_TYPE AS TABLE OF VARCHAR2(10);
CREATE OR REPLACE PROCEDURE SPMBTEST3 (
    VAR1    IN        VARCHAR2,
    VAR2    IN        ARRY_TYPE,
    VAR3    OUT        ARRY_TYPE)
IS
BEGIN
  DECLARE
    wk_Subject VARCHAR2(64);
BEGIN
dbms_output.put_line('SPMBTEST3 start');

dbms_output.put_line('SPMBTEST3 var1:'||var1);
dbms_output.put_line('SPMBTEST3 var2:'||var2(1));
    VAR3 := ARRY_TYPE('in2','in3','in4','in5');

dbms_output.put_line('SPMBTEST3 end');
    return;
EXCEPTION
        WHEN OTHERS THEN
            RETURN ;
    END;
END;
-------------------------------------------
Now here is the test program

import java.sql.*;
import oracle.sql.*;
import oracle.jdbc.driver.*;

class SPMBTEST
{
    public static void main (String args []) throws SQLException,
ClassNotFoundException
    {
            DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
        Connection conn = DriverManager.getConnection
("jdbc:oracle:oci8:@nectar","forsrs", "forsrs");
        run( conn );
        conn.close();
    }
    static void run(Connection conn) throws SQLException
    {

        System.out.println ("SPMBTEST_TEST");
        OracleCallableStatement
spmbtest=(OracleCallableStatement)conn.prepareCall("{ call SPMBTEST3( ? ? ? )
}");
        String[] values = { "xy","xd","xe" };
        int maxLen = values.length;
        int currentLen = values.length;
        int elemSqlType = OracleTypes.VARCHAR;
        int elemMaxLen = 10;
        int maxOutLen = 20;

        spmbtest.setString( 1, "12345" );
        spmbtest.setPlsqlIndexTable (2, values, maxLen, currentLen, elemSqlType,
elemMaxLen);
        spmbtest.registerIndexTableOutParameter (3, maxOutLen, elemSqlType,
elemMaxLen);
        spmbtest.executeQuery();
        Datum[] outvalues = spmbtest.getOraclePlsqlIndexTable (3);
        for (int i=0; i<outvalues.length; i++) System.out.println
(outvalues[i].toString());
        spmbtest.close();
    }
}

Could you please clarify on this.

Also please provide some samples on using Oracle TABLE/RECORD Types in stored
procedure.

Can i use Oracle Thin driver to invoke stored procedure have composite data
types like nested TABLE and RECORD Types.

Thank you for Your help in Advance.

Kumar

      and we said...
couple of things wrong with the above:

a)    .... nt)conn.prepareCall("{ call SPMBTEST3( ?, ?, ? ) }");

you were missing the commas in there.  that was the cause of the 1036

b) you are using the "PLSQL index by table" bind method to bind to an Oracle
Object Type.  You can only use (this new api available with 817 and up only)
this to bind to PLSQL Index By Tables.  If you change the PLSQL code to:

create or replace package demo_pkg
as
    type arry_type is table of varchar2(10) index by binary_integer;
end;
/

CREATE OR REPLACE PROCEDURE SPMBTEST3 (
    VAR1    IN        VARCHAR2,
    VAR2    IN        demo_pkg.ARRY_TYPE,
    VAR3    OUT        demo_pkg.ARRY_TYPE)
IS
BEGIN
  DECLARE
    wk_Subject VARCHAR2(64);
BEGIN
    VAR3(1) := 'in2';
    var3(2) := 'in3';
    var3(3) := 'in4';
    var3(4) := 'in5';

    return;
EXCEPTION
        WHEN OTHERS THEN
            RETURN ;
    END;
END;
/

and add the commas -- your example works dandy.

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:986230504001
for how to pass collections (nested tables, varrays) to/fro java and plsql.

As for doing tables of RECORDS -- that's a no-go.  Only PLSQL can send tables of
records to/fro itself.  There are no 3gls that can do tables of records.

Now, collections of object types -- which are pretty much the same as tables of
records -- java can do that.  Suggest you look at the link provided in

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:433419656020
to go down that path.

Review &
      Followup

      Rating: 5
Authoritative
      Guidance Indeed.  March 05, 2002
Reviewer:
      Saravana Kumar  from NEC, Tamachi, Japan
Thanks very much Tom!
For your prompt and Authoritative Guidance.

March 05, 2002 Kirk -- Thanks for the question regarding "PL/SQL
      to Replicate/Duplicate a row that has a unique ID and changing columns!",
      version 8.1.7

      You Asked
What is the RIGHT way to replicate a row of a table in Oracle,
when one or more of the columns MUST change because it is a
primary key (wired to a Sequence with a Trigger)?

This is always for 1 row.  In a nutshell, we have a
record that represents an event...  If it is rescheduled,
we basically copy the original, and mark it rescheduled.
The new record is the newly scheduled event.

I hate admitting MSSQL experience, but we would simply:
Insert the row into a #TempTable
Alter the ID field
Insert the row from the #TempTable into our real table.

3 Lines of pretty simple code, no Field Names, and it
worked even if the table structure changed over time.

I would LOVE to do the same thing in PL/SQL.  Right now,
I have a Permanent Temp table, and have to maintain it.
There must be a better way that is more generic?

      and we said...
Well -- I don't know what you mean by "I have a Permanent Temp table, and have
to maintain it."  what is to maintain???

But -- why take the data out at all?

insert into T
select * from T where id = :some_id;

Maybe an example will help you:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( id number primary key, data
varchar2(30), rescheduled char(1) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create sequence t_seq;

Sequence created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger t_trigger before
insert on t for each row
  2  begin
  3          select t_seq.nextval into :new.id from dual;
  4  end;
  5  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t (data, rescheduled)
  2  select object_name, 'N' from all_objects where rownum <= 100;

100 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          l_id number;
  3  begin
  4          insert into t (data, rescheduled)
  5          select data, 'Y'
  6            from t
  7           where id = 55;
  8
  9          select t_seq.currval
 10            into l_id
 11            from dual;
 12
 13          dbms_output.put_line( 'I just rescheduled appt 55 to ' || l_id );
 14  end;
 15  /
I just rescheduled appt 55 to 101

PL/SQL procedure successfully completed.

I don't understand why you would use (even in SQLSERVER) a temporary table for
such a basic thing?

Review &
      Followup

      Rating: 2
Clarifications
      to Problem...  March 04, 2002
Reviewer:
      Kirk  from FL USA
Some clarifications.

First, the field count is 153 for this table.
It changes every month, sometimes weekly...  Attributes
are still being determined as features are required.

A permanent Temp Table is just XXX_Temp with
the same structure as XXX.

Okay, our trigger fires ONLY when Id is NULL!
(This is because we have external loaders that
use the sequences so they can add related records
elsewhere, etc).  Actually, this is the crux of the
problem (Where ID is Null or ID would be duplicated
would fix the problem).

And I thought I had done a good job of explaining
the situation.  Ouch, did I drop the ball.

I Guess the question could have been: How can I
setup a trigger on an insert to use a sequence if
the field is null or if the data is being appended
from the same table??

          Followup:

I'd be tempted to do something like this then:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package demo_pkg
  2  as
  3      function clone(p_id in number) return number;
  4  end;
  5  /
Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package body demo_pkg
  2  as
  3
  4  g_sql_stmt long default null;
  5
  6  cursor g_cursor is select column_name
  7                       from user_tab_columns
  8                      where table_name = 'T'
  9                        and column_name <> 'ID';
 10
 11  procedure build_query
 12  is
 13  begin
 14      g_sql_stmt := 'insert into t ( id';
 15      for x in g_cursor
 16      loop
 17          g_sql_stmt := g_sql_stmt || ', ' || x.column_name;
 18      end loop;
 19
 20      g_sql_stmt := g_sql_stmt || ' ) select t_seq.nextval';
 21      for x in g_cursor
 22      loop
 23          g_sql_stmt := g_sql_stmt || ', ' || x.column_name;
 24      end loop;
 25      g_sql_stmt := g_sql_stmt || ' from t where id = :id';
 26  end build_query;
 27
 28
 29  function clone( p_id in number ) return number
 30  is
 31      l_id number;
 32  begin
 33      if (g_sql_stmt is null)
 34      then
 35          build_query;
 36      end if;
 37
 38      execute immediate g_sql_stmt using p_id;
 39
 40      if ( sql%rowcount = 1 )
 41      then
 42          select t_seq.currval into l_id from dual;
 43      else
 44          raise_application_error( -20001, 'row with id ' || p_id || ' not
found' );
 45      end if;
 46
 47      return l_id;
 48  end;
 49
 50  end;
 51  /

Package body created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop sequence t_seq;

Sequence dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( id number primary key, data1
varchar2(30), data2 date, data3 number );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create sequence t_seq;

Sequence created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger t_trigger
  2  before insert on t for each row
  3  begin
  4      if ( :new.id is null )
  5      then
  6          select t_seq.nextval into :new.id from dual;
  7      end if;
  8  end;
  9  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t (data1,data2,data3)
  2  select object_name, created, object_id from all_objects where rownum <=
100;

100 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where id = 101;

no rows selected

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_output.put_line( 'going to clone 55
into ' || demo_pkg.clone(55) );
going to clone 55 into 101

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where id = 101;

        ID DATA1                          DATA2          DATA3
---------- ------------------------------ --------- ----------
       101 /130f798d_MetalUtilsTableHeade 28-AUG-01       7440

ops$tkyte@ORA817DEV.US.ORACLE.COM>

It'll adjust as you add/remove columns.  You just need to make sure that the
primary key name, tablename and sequence name don't change....

alternatively, you could do this instead:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package demo_pkg
  2  as
  3          i_am_cloning boolean default FALSE;
  4          cloned_id    number;
  5  end;
  6  /
Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;
Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop sequence t_seq;
Sequence dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( id number primary key, data1
varchar2(30), data2 date, data3 number );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create sequence t_seq;
Sequence created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger t_trigger
  2  before insert on t for each row
  3  begin
  4      if ( :new.id is null OR demo_pkg.i_am_cloning )
  5      then
  6          select t_seq.nextval into :new.id from dual;
  7                  demo_pkg.cloned_id := :new.id;
  8      end if;
  9  end;
 10  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t (data1,data2,data3)
  2  select object_name, created, object_id from all_objects where rownum <=
100;

100 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select * from t where id = 55;
insert into t select * from t where id = 55
*
ERROR at line 1:
ORA-00001: unique constraint (OPS$TKYTE.SYS_C005389) violated

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec demo_pkg.i_am_cloning := TRUE;
PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select * from t where id = 55;
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec demo_pkg.i_am_cloning := FALSE;
PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_output.put_line( 'cloned row has id
= ' || demo_pkg.cloned_id );
cloned row has id = 101

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where id = 101;

        ID DATA1                          DATA2          DATA3
---------- ------------------------------ --------- ----------
       101 /130f798d_MetalUtilsTableHeade 28-AUG-01       7440

      Rating: 4
But if
      only...  March 05, 2002
Reviewer:  Scott
       from Chicago
So this is another situation wherein it'd be just swell if we could,

declare
  t_rec  t%rowtype;
begin
  select *
    into t_rec
    from t
   where id = :some_id;
  t_rec := null;
  insert into t
  values (t_rec);
end;

Do you know whether the appropriate Oracle dev team(s) have ever seriously
looked into implementing this type of functionality.  The select-into-record
functionality coupled with %ROWTYPE record definition is terrific, but the
inability to then turn around and actually use these records without knowing
their exact structure can be painful, as seen above.

cheers,
Scott

          Followup:

I have heard of an unconfirmed rumor that this'll be in 9iR2 -- insert/update
using a whole record.

March 05, 2002 Lucy -- Thanks for the question regarding "Who
      should do the "commit"", version 8.1.6

      You Asked
Hello, Tom,

Currently, in all our applications (Java and stored procedure), the "commit" is
done in the Oracle packages, but some jave code may call 3 stored procedures
while others may only call one of that 3, how can we control the transaction, so
far, we do commit in all 3 procedures,  can Java code control the transaction?
If yes, is it different in the Weblogic connection pool or MTS enviorments.

Thanks

      and we said...
Not only CAN java (more generally the client) control the transaction -- the
client SHOULD control the transaction.

I do not like putting commits in PLSQL procedures/functions in my packages.  It
is not clear to the developer using it that this API commits or not.  Someday
down the road -- you might need to take two or three API's and call them as a
single transation.  If the PLSQL commits -- you cannot, you are out of luck.

I run into this all of the time, IMHO its a bad practice to put the commit in
the API -- only the CLIENT knows when the transaction is done, they are
responsible for committing or rolling it back.

It is no different in connection pooling or MTS -- if you have my book, I
actually cover this topic (transactions, how to use them, what they are and who
should commit and why)
March 05,
      2002 Brian -- Thanks for the question
      regarding "using a collection (nested table) in a query join fails when
      it's inside a proc ", version 8.1.6

      You Asked
I know that if I have a simple table with a collection (nested table) I can do
the simple select to get the columns...

in Oracle's simple example:

http://metalink.oracle.com/metalink/plsql/ml2_documents.showDocument?p_database_id=NOT&p_id=69467.1
Example 21 - Collection Unnesting using the TABLE operator
----------------------------------------------------------

SELECT p.FirstName, p.LastName, a.*
FROM People p, TABLE(p.PrevAddrs) a
WHERE p.LastName = 'Ellison';

the result gives:
p.FirstName  p.LastName  a.Street        a.City     a.State   a.Zip
------------ ----------- --------------  ---------- --------- --------
Mr.          Ellison     2 Second Street Orlando    FL        34812
Mr.          Ellison     3 Third Street  Orlando    FL        34812
Mrs.         Ellison     2 Second Street Orlando    FL        34812
Mrs.         Ellison     3 Third Street  Orlando    FL        34812
Larry        Ellison     2 Second Street Orlando    FL        34812
Larry        Ellison     3 Third Street  Orlando    FL        34812

I am doing something similar...

create or replace type step_status_rec is object (step_work_id number, step_num
number, step_stat varchar2(10), update_date DATE);

create or replace type step_status_type is table of step_status_rec;

CREATE TABLE ROLLUP_WORKFLOW (
WORK_ID NUMBER NOT NULL,
WORK_STATUS CHAR(1) NULL,
STEP_STATUS step_status_type null,
CONSTRAINT ROLLUP_WORKFLOW_PKN
PRIMARY KEY (WORK_ID)
)
NESTED table STEP_STATUS store as STEP_STATUS_TAB
/

This table stores a "work order", which has many steps (each with it's own
status). So I store them in a collection in a single row for the work order...

this query shows all steps and their statuses...

select w1.work_id, w2.step_work_id, w2.step_num, w2.step_stat
from rollup_workflow w1, table(w1.step_status) w2
where w1.work_id = w2.step_work_id
order by w1.work_id,w2.update_date asc
/

result: (note, I am storing the table PK inside the collection as well, I
thought it was needed for the join from w1 to w2)...

   WORK_ID STEP_WORK_ID   STEP_NUM STEP_STAT
---------- ------------ ---------- ----------
         1            1          0 R
         1            1          1
         1            1          2
         1            1          4
         1            1          5
         1            1          3
         2            2          0 R
         2            2          1
         2            2          2
         2            2          3
         2            2          4
         2            2          5

however, I am writing a stored procedure which takes this data into a cursor
(loop) and then does other work...

My problem is this FAILS:

declare cursor cur1 is select w1.work_id, w2.step_work_id, w2.step_num,
w2.step_stat
from rollup_workflow w1, table(w1.step_status) w2
where w1.work_id = w2.step_work_id
and w2.STEP_NUM = p_step_num  -- even without this row it fails
order by w1.work_id,w2.update_date asc;
begin
null;
end;
/

from rollup_workflow w1, table(w1.step_status) w2
                               *
ERROR at line 2:
ORA-06550: line 2, column 32:
PLS-00201: identifier 'W1.STEP_STATUS' must be declared
ORA-06550: line 1, column 24:
PL/SQL: SQL Statement ignored
...

So I tried to rewrite the second half of the "from" statement to...

declare cursor c1 is select select w1.work_id, w2.step_work_id, w2.step_stat
from rollup_workflow w1, table(select step_status from rollup_workflow) w2
where w1.work_id = w2.step_work_id
and w2.STEP_NUM = p_step_num
order by w2.update_date asc;

THIS COMPILES.. however, when I run it.. I get this:

ERROR at line 2:
ORA-02324: more than one column in the SELECT list of THE subquery

The new FROM statement is not bringing back results in an acceptable way.. also
should I be using the keyword "THE"?  how do I use it?

thanks.

      and we said...
Ok, in 9i -- both of these approaches work.  In 8i the second approach works
(and would be your current solution -- to use a ref cursor and dynamic sql,
avoiding the PLSQL sql parser which does not understand that construct.  In 9i
and up -- SQL and PLSQL share a common SQL parser so this mismatch of
functionality should not happen again)

ops$tkyte@ORA9I.WORLD> declare
  2
  3  p_step_num number;
  4
  5
  6  cursor cur1 is select w1.work_id, w2.step_work_id, w2.step_num,
  7  w2.step_stat
  8  from rollup_workflow w1, table(w1.step_status) w2
  9  where w1.work_id = w2.step_work_id
 10  and w2.STEP_NUM = p_step_num  -- even without this row it fails
 11  order by w1.work_id,w2.update_date asc;
 12  begin
 13  null;
 14  end;
 15  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> declare
  2          type rc is ref cursor;
  3
  4          l_cur1 rc;
  5          p_step_num number;
  6  begin
  7          open l_cur1 for
  8                  'select w1.work_id, w2.step_work_id,
  9                          w2.step_num, w2.step_stat
 10                     from rollup_workflow w1, table(w1.step_status) w2
 11                    where w1.work_id = w2.step_work_id
 12                      and w2.STEP_NUM = :p_step_num
 13                    order by w1.work_id,w2.update_date asc' using p_step_num;
 14
 15          close l_cur1;
 16  end;
 17  /

PL/SQL procedure successfully completed.

Review &
      Followup

      Rating: 5
Thanks, it
      worked.. I also have another solution  March 05, 2002

Reviewer:  Brian  from New York, NY USA
I made a view of the original select statement... and simply do a cursor of the
view:
CREATE OR REPLACE VIEW ROLLUP_WORKFLOW_STEP_V
(
 WORK_ID, BUSINESS_ID, FLOW_DATE, WORK_STATUS,
 ARRAY_SIZE, STEP_WORK_ID, STEP_NUM, STEP_STAT,
 UPDATE_DATE
)
AS
select
W1.WORK_ID,
 W1.BUSINESS_ID,
 W1.UPDATE_DATE as FLOW_DATE,
 W1.WORK_STATUS,
 W1.ARRAY_SIZE,
 w2.*
from rollup_workflow w1, table(w1.step_status) w2
where w1.work_id = w2.step_work_id
order by w1.work_id, w2.step_num
/

then I can use simply: select ... from (view_name)...

but I have one more question.. when these steps are done.. an inner step status
may have an error, in that case I want to update the overall status for all
those rows whose (collection) has a status or error... I get the same
single-query error as above.. I can create a cursor loop and do it that way..
but is there something that get get this to work?

 update table(select step_status from rollup_workflow where work_status = 'I')
                        set step_stat= v_done_status, update_date = sysdate
                        where step_num = p_step_num and step_stat =
v_lock_status;

I get:
ERROR at line 1:
ORA-01427: single-row subquery returns more than one row
ORA-06512: at "JOB_PROC", line 128
ORA-06512: at line 5

          Followup:

Well -- you are selecting more then on "virtual" table.  The TABLE clause is
expecting a single collection -- you are not event sending it a collection type!

You would use an update more like:

update table( select w.step_status from rollup_workflow w WHERE WORK_ID = 0 )
  set  step_stat = ...
/

That is you identifiy the COLLECTION instance you want to update with the inline
view (where work_id = 0 in my world would select at least one and at most one
row from rollup_workflow).

Then, you can treat that collection instance as if it were a table and update
the values in it -- using a where clause on it if need be.

March 05, 2002 Betty -- Thanks for the question regarding "Bit
      conversion from CHAR(1)", version 8.1.7

      You Asked
I have a table b1active
               b1st    char(1);
b1st field is used to contain 8 flags

I found your answer to bitand, which I can use on a NUMBER(5) field, whcih
contains 15 flags.
I can not use bitand with the CHAR(1) field.  I have also created the functions
to_base, to_dec, to_hex, to_bin, to_oct but I am not sure if any of these
functions would help.

So what I need to do is determine which flags are set in this CHAR(1) field and
display in report.

      and we said...
you can use

http://osi.oracle.com/~tkyte/hexdec/index.html
to get the to_base, to_dec, to_bin, etc routines.

All you need is ASCII(), sign and bitand.  Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x char(1) );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 'a' );
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 'b' );
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 'c' );

ops$tkyte@ORA817DEV.US.ORACLE.COM> column bin format a8

ops$tkyte@ORA817DEV.US.ORACLE.COM> set numformat 999

ops$tkyte@ORA817DEV.US.ORACLE.COM> select x,
  2             sign(bitand( ascii(x), 128 )) bit8,
  3             sign(bitand( ascii(x), 64 )) bit7,
  4             sign(bitand( ascii(x), 32 )) bit6,
  5             sign(bitand( ascii(x), 16 )) bit5,
  6             sign(bitand( ascii(x), 8 )) bit4,
  7             sign(bitand( ascii(x), 4 )) bit3,
  8             sign(bitand( ascii(x), 2 )) bit2,
  9             sign(bitand( ascii(x), 1 )) bit1,
 10             lpad( to_bin(ascii(x)), 8, '0' ) bin
 11   from t;

X BIT8 BIT7 BIT6 BIT5 BIT4 BIT3 BIT2 BIT1 BIN
- ---- ---- ---- ---- ---- ---- ---- ---- --------
a    0    1    1    0    0    0    0    1 01100001
b    0    1    1    0    0    0    1    0 01100010
c    0    1    1    0    0    0    1    1 01100011

Review &
      Followup

      Rating: 5
New
      Function  March 06, 2002
Reviewer:  Betty
       from Palm Harbor, FL
Thanks This is the answer.  I am using this for a field that is populated in
another database.  I knew that Tom could find the right Oracle function.

March 05, 2002 Poorna -- Thanks for the question regarding
      "Select Statement to display weekly wages for a given month and year",
      version 7.3

      You Asked
Hello Mr. Thomas

                 I came to know that you are very fast in giving the solutions
to the Oracle DB Queries. So here is my query:

Assume that I have the following table structure

EmpWages
Empno            Number                PrimaryKey
    Ename            Varchar2(20)
    WagesPerDay        Number        6    2
    WagesTakenDate        Date                This will be every day

I want a select query statement to retrieve the data from the above table in the
following format:

Empno            ename        1week    2week    3week    4week    5week
MonthlyTotal
1001            Xyz        100    100    100    100        400
1002            Yzx        200    200    200    200        800
1003            Zxy        120    100    100    100        420

Grand Total                420    400    400    400        1620

The user will input the month, based on the month the data must be breaked into
weeks and computations must be done.

Waiting for an early response to my query.

Thank s
Rao Mpv

      and we said...
It'll be something like this:

ops$ora734@ORA734.WORLD> create table t ( empno number, ename varchar2(20),
wagesPerDay number, wagesTakenDate date );

Table created.

ops$ora734@ORA734.WORLD>
ops$ora734@ORA734.WORLD>
ops$ora734@ORA734.WORLD> begin
  2      for i in 1 .. 10
  3      loop
  4          for j in 1 .. 100
  5          loop
  6              insert into t values ( i, i, i*j, sysdate+j-10 );
  7          end loop;
  8      end loop;
  9  end;
 10  /

PL/SQL procedure successfully completed.

ops$ora734@ORA734.WORLD>
ops$ora734@ORA734.WORLD> variable month varchar2(20);
ops$ora734@ORA734.WORLD>
ops$ora734@ORA734.WORLD> exec :month := 'MAR'

PL/SQL procedure successfully completed.

ops$ora734@ORA734.WORLD>
ops$ora734@ORA734.WORLD> select empno, ename,
  2             sum( decode( to_char(wagesTakenDate,'iw'),
to_char(to_date(:month,'MON'),'iw')+0, wagesPerDay, 0 ))    week1,
  3             sum( decode( to_char(wagesTakenDate,'iw'),
to_char(to_date(:month,'MON'),'iw')+1, wagesPerDay, 0 ))    week2,
  4             sum( decode( to_char(wagesTakenDate,'iw'),
to_char(to_date(:month,'MON'),'iw')+2, wagesPerDay, 0 ))    week3,
  5             sum( decode( to_char(wagesTakenDate,'iw'),
to_char(to_date(:month,'MON'),'iw')+3, wagesPerDay, 0 ))    week4,
  6             sum( decode( to_char(wagesTakenDate,'iw'),
to_char(to_date(:month,'MON'),'iw')+4, wagesPerDay, 0 ))    week5
  7   from t
  8   where wagesTakenDate between to_date(:month,'MON') and
last_day(to_date(:month,'MON'))+1-1/24/60/60
  9  group by empno, ename
 10  /

     EMPNO ENAME           WEEK1      WEEK2      WEEK3      WEEK4      WEEK5
---------- ---------- ---------- ---------- ---------- ---------- ----------
         1 1                  21         84        133        182        231
         2 2                  42        168        266        364        462
         3 3                  63        252        399        546        693
         4 4                  84        336        532        728        924
         5 5                 105        420        665        910       1155
         6 6                 126        504        798       1092       1386
         7 7                 147        588        931       1274       1617
         8 8                 168        672       1064       1456       1848
         9 9                 189        756       1197       1638       2079
        10 10                210        840       1330       1820       2310

10 rows selected.

Review &
      Followup

      Rating: 4
Please explain
      how  March 05, 2002
Reviewer:  Ravi
       from San Jose,CA USA
Tom,
Could you please explain how the decode works for every week. I executed what
ever is said but could not understand how it is working . Also what is the 'iw'
option for to_char.

          Followup:

the IW format, as all formats, is explained in the SQL Ref manual:

IW       Week of year (1-52 or 1-53) based on the ISO standard.

So, it returns a number 1..53.

What I did in the decode was to look at the week of the year the wagesTakenDate
falls in and compare that to the week of the year that the first of the MONTH of
interest falls in:

decode( to_char(wagesTakenDate,'iw'),
        to_char(to_date(:month,'MON'),'iw')+0, wagesPerDay,
        0 )

If they were equal -- I output wagesPerDay, else I output ZERO.  Later, when we
sum, we get rid of the zeroes.

I then did the same for the next 4 weeks of the year by simply adding 1, 2, 3, 4
to "to_char(to_date(:month,'MON'),'iw')"

      Rating: 5
Thank You
      Tom  March 05, 2002
Reviewer:  Ravi
       from San Jose,CA
Thanx for the explanation and a quick reply.

      Rating: 3
Without Decode
      function  March 05, 2002
Reviewer:  Poorna V
      Malla  from TX
Mr.Thomas

Thank You for your fast response.

  I want the same output without decode function, is it possible? or i need to
write a pl/sql stored procedure.

          Followup:

and what the heck is wrong with decode.

Why is decode "bad".

March 05, 2002 Tianhua -- Thanks for the question regarding
      "flash back querry", version 9i

      You Asked
Hi Tom,

I am curious about flash back query.  I understand because SMON insert or update
SMON_SCN_TIME every 5 minutes and there are only 1440 slots in it, so when we
use time, I can go back up to 5 days (5 X 1440 min).  And if I want to go back
more than 5 days, I must use SCN.  I assume there is a condition for flash back
querry:  all the old undo info will be kept in the undo tablespace, is this
correct?  Also how Oracle will handle all space requirement for undo tablespace
for a busy system will flash back query requirement?  Does it mean it will grows
infinitely?
Thank you,

T Wu

      and we said...
Well, in order to flash back 5 days you need to have enough rollback configured
to hold 5 days worth of information as well.  This is probably not a feasible
reality!

All old undo is kept in the undo tablespace or rollback segments, whichever you
choose to use.

For a busy system, we will grow the undo tablespace to be able to hold the
amount of redo generated in the undo_retention period. It will not grow
infinitely, it'll stop after it hits the size needed to hold that period of
time.

Review &
      Followup

      Rating:
      4
  March 05, 2002
Reviewer:  Tianhua
      Wu  from Atlanta, Ga
Thank you, Tom.

What will happen if the the "old undo info" have occupied all the space in the
rbs tablespace? Will the new TX will be hanging or the old one be over-written?
T Wu

          Followup:

If you use ROLLBACK SEGMENTS, you have no undo-retention period.  It'll just be
overwritten as fast as you wrap around, just like the old days.

March 05, 2002 sl
      -- Thanks for the question regarding "Buffer Hit ratio =0", version 8.0.4

      You Asked
Hi Tom,
As you know buffer hit ratio should be greater than 80%, but
I've got buffer hit ratio = 0. What does it mean? Is there anything I
should do with it?

The sql and the result are as follows:

  1  select a.value,b.value,c.value,a.value + b.value,
  2  c.value,
  3  round(100 * ((a.value+b.value)-c.value) /
  4  (a.value+b.value))
  5  "BUFFER HIT RATIO"
  6  from v$sysstat a, v$sysstat b, v$sysstat c
  7  where
  8  a.statistic# = 38
  9  and
 10  b.statistic# = 39
 11  and
 12* c.statistic# = 40
apps@PRODB> /

    VALUE     VALUE     VALUE A.VALUE+B.VALUE     VALUE BUFFER HIT RATIO
--------- --------- --------- --------------- --------- ----------------
1.845E+19 1.384E+10 1.845E+19       1.845E+19 1.845E+19                0

Thanks in advance,
mak

      and we said...
what is the output of this query:

create or replace function fix( p_number in number ) return number
as
    l_fmt1 varchar2(18) default 'fm' || rpad( '0', 15, '0' ) || 'X';
    l_fmt2 varchar2(8) default rpad( 'X', 8, 'X' );
begin
    return to_number( substr( to_char( p_number, l_fmt1), 9), l_fmt2);
end;
/

SELECT phy.value,
       fix(phy.value),
       cur.value,
       fix(cur.value),
       con.value,
       fix(con.value),
       1-(fix(phy.value)/(fix(cur.value)+fix(con.value))) "Cache hit ratio"
FROM   v$sysstat cur, v$sysstat con, v$sysstat phy
WHERE  cur.name='db block gets'
AND    con.name='consistent gets'
AND    phy.name='physical reads';

I think we have hit a known overflow problem -- I believe someone somewhere migh
t be using parallel query.   It is flooding the high bytes of the stats with FFF
FFFFF.  This gets "rid" of that.  It is fixed in a later release.

Review &
      Followup

      Rating:
      4
Reader  March 05, 2002
Reviewer:
      A reader
Tom,

Could you explain to us how the fix() function could fix
the overflow. The coversion just errors out if any
Hex Letters are detected

SQL>  select to_char('FFFFFFFF','fm000000000000000X') from dual;
 select to_char('FFFFFFFF','fm000000000000000X') from dual
                *
ERROR at line 1:
ORA-01722: invalid number

Thanks

          Followup:

arg, works in 8i and up - forgot, the xxxxxxx format was new with 8i.

Do this instead in 8.0:

o get the stuff from

http://osi.oracle.com/~tkyte/hexdec/index.html
if utlraw is not installed (errors compiling the above) have the DBA:

- cd $ORACLE_HOME/rdbms/admin
- svrmgrl
- connect SYS or INTERNAL
- @utlraw
- @prvtrawb.plb

to install it.

o use:

create or replace function fix( p_number in number ) return number
as
begin
    return to_dec( substr( lpad( to_hex( p_number ), 16, '0'), 9) );
end;
/

      Rating:
      4
Reader  March 05, 2002
Reviewer:
      A reader
I am on RDBMS 8.1.7

I trying to understand how the format 'fm000000000000000X' fixes the overflow

SQL*Plus: Release 8.1.7.0.0 - Production on Tue Mar 5 18:15:04 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected to:
Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production
With the Partitioning option
JServer Release 8.1.7.0.0 - Production

SQL> select to_number('FFFFF','xxxxxxxxx') from dual;

TO_NUMBER('FFFFF','XXXXXXXXX')
------------------------------
                       1048575

SQL> select to_char('FFFFFFFF','fm000000000000000X') from dual;
select to_char('FFFFFFFF','fm000000000000000X') from dual
               *
ERROR at line 1:
ORA-01722: invalid number

Thanksthe

          Followup:

It doesn't fix it per say -- its part of the solution.  I was just using it to
mask out the FFFFFFFF that is erroneously in the number.

See the numbers in v$sysstat are set wrong -- they have ffffffff in the high
bytes. We need to get rid of that.  A sample number might be:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select to_char( 18446744069546030898,
'fm'||rpad('0',15,'0')||'X' )
  2    from dual
  3  /

TO_CHAR(184467440
-----------------
FFFFFFFF07D5B732

So, I take that number 18446744069546030898 from v$systat, put in into hex,
substr off the low bytes (remove the FFFFFFFF) and then turn the remaining hex
back into decimal.  So, fix turns that erroneous number into:

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select fix( 18446744069546030898 )
  2    from dual
  3  /

FIX(18446744069546030898)
-------------------------
                131446578

which is the correct number.

March 05, 2002 Raj -- Thanks for the question regarding "Slow
      Query", version 8.1.6

      You Asked
We have queries in the database as follows. The reason the queries are built
this way is to obtain a result set depending on the where conditon and then sort
that result set and get certain number of rows depending on their rownum. The
application is for Message Boards and this query is used to populate web pages
depending upon the users criteria

Question is, is there a better way to do this query. would you
recommend another way of doing it (views, procedures, temp tables)

Secondly, if the queries are running slow in the database what parameters I can
play with in the database to improve response time. I had even increased the
sort_area_size to 40 Megs but still there was one sort on the disk.

here is the existing query

SELECT *
  FROM (SELECT ROWNUM row_id, message_id, main_thread_id, subject,
posted_date, no_of_times_viewed AS expert_sts, user_id, poster_name,
poster_email, new_yn, emoticon, group_id, no_of_replies,
rating_avg
  FROM (SELECT   message_thread.message_id, main_thread_id, subject,
                 posted_date, message_thread.no_of_times_viewed,
                 message_thread.user_id, poster_name, poster_email,
                 new_yn, emoticon, message_thread.group_id,
                 message_thread_stats.no_of_replies,
                 message_rating_stats.rating_avg
        FROM message_thread, message_thread_stats,
             message_rating_stats
        WHERE message_thread.message_id =
message_thread_stats.message_id(+)
        AND message_thread.message_id =
            message_rating_stats.message_id(+)
        AND subcategory_id = 48
        AND message_thread.parent_id = 0
        AND (   new_yn = 'y' OR new_yn = 'a'  )
 ORDER BY posted_date DESC))
 WHERE row_id BETWEEN 30 AND 40

I had tried the method you had suggested in one of your answers regarding the
Nth to Mth select but that was even much slower. This query gives about 22
second response on a Sun 450 box with 1 Gig memory.Is there a better way to do
queries to display certain number of rows to the users.

Thanks for your help.

Raj

      and we said...
Use:

select *
  from ( select a.*, rownum rnum
           from ( select /*+ FIRST_ROWS */   ...
                    from
                   where ..
                   order by ...
                ) a
          where rownum < 30
       )
 where rnum > 20
/

The where rownum < 30 will do a STOPKEY plan (faster, better) whereas doing
"where row_id between 30 and 40" will have to get the entire thing and then get
rows 30 through 40.

99.99999 of all tuning is done in the application -- your job will be to
eliminate sorts -- not have sorts that take 40meg.

Now, if you have a nice index on

subcategory_id, parent_id, posted_date DESC

or even better a function based index on:

subcategory_id, parent_id, decode(new_yn,'y',1,'a',1,0 ), posted_date DESC

read:

http://osi.oracle.com/~tkyte/article1/index.html
for info about them...

and use

and (decode(new_yn,'y',1,'a',1,0) = 1  in the predicate

-- we'll be able to get the rows out from message_thread sorted very fast.
We'll stop after we get 40 rows (not all).  Join them to the other two tables
and return.

No sorting....
Review & Followup

      Rating:
      5
Thanks  March 04, 2002
Reviewer:
      A reader

      Rating: 5
Response better
      but don't know how  March 05, 2002
Reviewer:
      Raj Tuli  from Los Angeles, CA USA
Thanks for the idea about the function index. It has improved the performance
tremendously. But I am very confused as to how this happened because I don't see
the function index being used. I see some other index being used. How can I
force it to use the function index and maybe improve the performance even more

I used the following script to create the index
create index idx_func_index on message_thread (subcategory_id, parent_id,
decode(new_yn,'y',1,'a',1,0 ),posted_date DESC )

But when I looked into the db, the script to recreate this
index is
CREATE INDEX IDX_FUNC_INDEX ON MESSAGE_THREAD(SUBCATEGORY_ID, PARENT_ID,
SYS_NC00037$, SYS_NC00038$ DESC)

Is this normal, not to see the function value in the index.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: FIRST_ROWS (Cost=319 Card=1
           Bytes=282)

   1    0   SORT (ORDER BY) (Cost=319 Card=1 Bytes=282)
   2    1     NESTED LOOPS (OUTER) (Cost=318 Card=1 Bytes=282)
   3    2       NESTED LOOPS (OUTER) (Cost=314 Card=1 Bytes=275)
   4    3         VIEW (Cost=313 Card=1 Bytes=268)
   5    4           COUNT
   6    5             VIEW (Cost=313 Card=1 Bytes=255)
   7    6               TABLE ACCESS (BY INDEX ROWID) OF
           'MESSAGE_THREAD' (Cost=313 Card=1 Bytes=77)

   8    7                 INDEX (FULL SCAN DESCENDING) OF
           'MESSAGE_THREAD_FK1_IDX' (UNIQUE) (Cost=312 Card=1)

   9    3         TABLE ACCESS (BY INDEX ROWID) OF
           'MESSAGE_THREAD_STATS' (Cost=1 Card=52 Bytes=364)

  10    9           INDEX (UNIQUE SCAN) OF
          'PK_MESSAGE_THREAD_STATS' (UNIQUE)

  11    2       INDEX (RANGE SCAN) OF
          'PK_MESSAGE_RATING_STATS' (UNIQUE) (Cost=4 Card=72 Bytes=504)

I have set the two Query parameters in the init.ora and the value for
cursor_sharing is EXACT. I am using the hint of /*+ FIRST_ROWS */.

Thanks a lot for your help. I really appreciate it.
Raj

          Followup:

Well -- if the query isn't using the index -- and the query is running fast --
it is not needed!  drop it.

The other suggestions on how to use rownum with inline views and such are what
worked.

the view, user_ind_expressions, shows you the functions used by the FBI.

      Rating: 4
FIRST_ROWS -
      good tip  March 05, 2002
Reviewer:  Andrew
       from ca, usa
Thanks for the first_rows tip.

I was under the impression that using DESC in the index creation had little
effect on performance but that it could affect how well balanced the index
remains over time.

Please could you comment on:
1.) the pros and cons of using DESC in the index creation in general.
2.) how the ORDER BY of the query correlates to the ASC/DESC   of the index DDL.

          Followup:

1) there are none -- the indexes in general stay balanced -- the definition of
B*Tree is "balanced tree".  The desc/asc nature won't affect that too much.

2) if you have an index (in 8i and up -- desc indexes prior to that didn't do
anything) and the index is:

  create index t_idx on t ( a, b desc, c )

and you order by a, b, c

it cannot use the index to "sort the data", if you order by a, b desc, c it may
well use the index to sort the data.

March 06, 2002 nag -- Thanks for the question regarding
      "objects", version 8.1.7

      You Asked
Tom

If I want to know what are the object_name, and there synonym name , for all the
objects to which I have access , how should I find out.

How should I find out the list of objects, there owner for all the objects I
have access to.

How should I find out, if there are any errors in any object i.e. whether they
are in a valid state of not for all the objects I own and I have access to.

when I do a desc package name , I know that a package specification exists, how
can I confirm the existence of the package body.

How can I get a list of all the objects in my local schema which are month old.

Look at following error, I ran the dbmspbp.sql as sys, but still I got the
following error.

Is there a separate script which will create the dbms_profiler package body.

Is there a document which gives the package name and the script which creates
it.

create or replace procedure no_mod1 as
    cnt number := 0;
begin
    dbms_profiler.start_profiler( 'no mod' );
    for i in 1 .. 500000
    loop
        cnt := cnt + 1;
        if ( cnt = 1000 )
        then
            commit;
            cnt := 0;
        end if;
    end loop;
    dbms_profiler.stop_profiler;
end;

SQL> execute do_mod
BEGIN do_mod; END;

*
ERROR at line 1:
ORA-04068: existing state of packages has been discarded
ORA-04067: not executed, package body "SYS.DBMS_PROFILER" does not exist
ORA-06508: PL/SQL: could not find program unit being called
ORA-06512: at "PREDDY.DO_MOD", line 4
ORA-06512: at line 1

      and we said...
To see all of the objects you have access to -- simply query "ALL_OBJECTS".
That shows you only that which you have access to.

To find a synonym name for them -- join ALL_OBJECTS to ALL_SYNONYMS....

To find the status of these objects -- query USER_OBJECTS (for your stuff) and
ALL_OBJECTS (for all stuff) and look at the STATUS column.

As for how to confirm the existence of a package body -- if you OWN it or have
CREATE|EXECUTE ANY PROCEDURE -- you can query ALL_OBJECTS.  If you do not have
these privs but you do have access to DBA_OBJECTS, you can use that view.  If
you do not have access to DBA_OBJECTS and you don't own it and you don't have
one of those two privs -- you can only try to run it to see if the body exists.

As for getting things that are one month old, use USER_OBJECTS and look at
either CREATED or LAST_DDL_TIME -- you decide which is the right column to use.

As for dbms_profiler, here is a very small cut and paste from my book (i have a
chapter on this):

...
Your DBA may have to install the profiler in your database. The procedure for
installing this package is simple:

o cd $ORACLE_HOME/rdbms/admin
o Using SVRMGRL you would connect as SYS or INTERNAL
o Run profload.sql

In order to actually use the profiler after that, you will need to have the
profiling tables installed. You can install these once per database but I
recommend that developers would have their own copy. Fortunately, the
DBMS_PROFILER package is built with invokers rights and unqualified table names
so that we can install the tables in each schema and the profiler package will
use them correctly. The reason you each want your own tables is so that you only
see the results of your profiling runs, not those of your coworkers. In order to
get a copy of the profiling tables in your schema, you would run
$ORACLE_HOME\rdbms\admin\proftab in SQLPlus. After you run proftab.sql, you'll
need to run profrep.sql as well. This script creates views and packages to
operate on the profiler tables in order to generate reports. This script is
found in $ORACLE_HOME\plsql\demo\profrep.sql. You should run this in your schema
as well after creating the tables.
.....

Review &
      Followup

      Rating: 5
..about oracle
      packages  March 06, 2002
Reviewer:  Yogeeraj
       from Mauritius
Hello,

There are numerous packages that come with each Oracle Installation. I guess
that not all these packages are installed by default..

My questions is:
Are all these packages documented?

Also, could you please give us a checklist of important packages that needs to
be added to the database just after installation? (Used for development and
production)

Btw, i was running Oracle Developper 6i Forms on my Oracle 8.1.7.0 that has a
block based on a stored procedure. At runtime (client/server), i am getting the
following error:
------------------------------------------
PLSQL_TABLE.POPULATE_BLOCK(bk_data, 'MAIN');

Built in failed.
------------------------------------------
Any idea? Is it a problem at the database level (package missing!) or on my
client?

Thank you very much for your time and guidance.

Best Regards
Yogeeraj

PS. just one compliment one of my Oracle contact made and that i wanted to
convey to u. ;)
"If you are taking Tom Kyte as your authority, you can't really go wrong. He is
one of the leading lights of the Oracle community."

          Followup:

The packages are all documented in the document named "supplied packages guide"

See

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76936/toc.htm

utlraw is the only one I frequently add.  That is the only one that comes to
mind.

As for the forms error -- insufficient data.  There should be error codes, goto
"help display last error" for details.

March 06, 2002 Yves -- Thanks for the question regarding "XML
      Type VS. Relational Schema", version 9i

      You Asked
Hi Tom I need your opinion on this subject.

My input data is XML.  Currently my requirement is to produce database reports
out of this data using Oracle Reports and Oracle Discoverer.  The XML structure
itself is pretty complex.  The database schema I have created from my XML schema
has several master detail tables (> 50).

My questions are these: would I be better off not storing this data is
relational tables, and store it instead in a few tables with XMLType columns?
How hard or easy would it be to generate reports off of this data if I had it
all in XMLType columns?

From the litterature I have read, the XMLType enables storage of XML, and
searching with xpath in pl/sql.  Can this easily be translated in a new way of
writing reports over large amounts of relational data?  What about performance?

As always, thanks for your insight.

Yves

      and we said...
I asked Sean Dillon, our local XML guy to take a look at this.  Here's what he
had to say:
----------------------------------------

Hi Yves,

To convert or not to convert, that's always the question.  There are as many
opinions on this as there are people out there willing to talk about it.  Let me
tell you what I think, and you can draw your own conclusions.

There are a variety of factors you should take into consideration when
determining how to store and access your data.  There are a few different ways
you can store your data, and each different way offers trade-offs you need to
consider.

First of all, you can store XML data as NATIVE XML in Oracle.  in 8i and below,
you'll be doing this in CLOBs or NCLOBs.  In 9i and above, XMLType.  Doing this
offers XML-purists the ability to store completely unstructured data in the
database with no prior knowledge of the format of the XML data.  This also
allows you to maintain document fidelity, so things like whitespace, processing
instructions, etc are maintained along with the original document.  Using
XMLType columns gives you the ability to perform XPath searching, XPath
extracts, and the existsnode function.  With XMLType or CLOBs, you can use
Oracle Text (Oracle interMedia) to index your XML data for high speed
XML-centric searching.  (show me all those documents in my database where the
"DESCRIPTION" tag contains the words "really groovy". In 9i, the Oracle Text
indexing capabilities have been improved significantly to offer more XPath-like
high-speed indexing.

Just because you store your data in pure XML does not mean you can't use
relational access.  In Oracle9i, you can define views on top of your XML
document storage to enable high speed relational access to your XML data using a
healthy mix of XPath expressions, pipelined functions, and function-based
indexes.  This works, and can be very fast, but has a considerable amount of
administrative overhead and is overkill if you will always be accessing your
data relationally.  The only reason I would advocate storing your data in XML
only is if you publish the data in XML the VAST MAJORITY of the time.  If you
only need to access the data relationally every once in a while, this approach
can work.  If you need to access your data relationally most of the time, read
on.

Next, you can store your data in relational tables, as you have pointed out.
You can map elements in your XML data to columns in a table (or object table),
and parse the XML document when you put it into the database into these rows and
columns.  This would be the obvious solution for Oracle Reports and Oracle
Discoverer access.  If you know the structure of your XML data, and you won't be
dealing with other XML Schemas or DTDs for the majority of your documents, this
is an efficient way to store your data.  XML is then thought of as a
data-transfer mechanism, important when importing data or exporting data, but
within the confines of the database and reporting interface, it's just DATA.
NOT XML.  There are quite a few people that like to use XML simply because it's
XML (ie, snazzy, cool, fun, exciting, etc).  In reality, you needs to use the
technology that best meets your requirements.  If the majority of your data
access is relational, store your data relationally.

People worry that because the data is stored relationally, they've lost their
XML.  This isn't a problem, however.  Oracle has a variety of ways of
constructing/generating XML data based on relational queries.  You can also
programmatically construct XML data if you need to.  There are many ways to do
this, you just need to determine the best mechanism depending on your
environment and data model.

Another approach you can take is to store BOTH.  When you put the native XML
document into the database, parse it as well.  This satisfies the relational
data access needs, as well as giving you XPath queries, Oracle Text XML
indexing, XML document fidelity, an audit trail (by storing the original XML you
loaded) if needed, etc.  Yes, there is more data storage in the database, but
again depending on your needs this is an option available depending on your
requirements.

Ultimately, it depends.  I hope that the information here helps you understand
some of the pros & cons for the different ways you can do it.  Good luck!
Review &
      Followup

      Rating: 3
thanks but
      still i do want some more tips  March 06, 2002

Reviewer:  Andrew  from ca, usa
thanks for your lightning reply. Really it is helpful to me to design further.
Could u please give me some more useful tips ?
Thanks again
hari

          Followup:

This is from tom here--

o BENCHMARK
o Test to destruction
   o test to scale
   o test beyond scale, see where it breaks
o did i mention benchmark?

by benchmark -- I do not mean like a "TPC/C" race to see whose fastest, I mean
try out various methods and come up with a quantitative measure of how good/
bad/ or indifferent each approach is.

Try out storing JUST XML.  See how easy/hard/performant reporting/storing/using
the data is in your environment (as your environment is radically different from
mine or the next guys -- your results will be different).

Try out storing JUST relational data.  See how ...... <ditto from above>
Try out storing BOTH XML and relations data. .... <ditto again>

      Rating: 5
Some light is
      finally shed   March 06, 2002
Reviewer:  Chris
       from Ireland.
As Mr. Dillon says, there are many ways of doing this. A clear , concise
article.

      Rating: 5
Finally...a
      balanced answer  March 06, 2002
Reviewer:
      Kevin  from Florida

      Rating:
      5
  March 06, 2002
Reviewer:  Eric
      Grancher  from CERN, Switzerland

      Rating:
      4
  February 26, 2003
Reviewer:
      A reader
That's great!

March 06, 2002 arunima -- Thanks for the question regarding
      "data conversion", version 8.1.7

      You Asked
Hi Tom,

I have created a table that looks like this.

@dev>create table table1(id number(1), active_date date);

Table created.

@dev>insert into table1 values (1, sysdate);

1 row created.

@dev>select id, active_date from table1;

        ID ACTIVE_DA
---------- ---------
         1 05-MAR-02

@dev>select 1 id, null active_date from dual;

        ID A
---------- -
         1

Now my question is when i try to create a view using table1 and dual I get this
error.

Create view test as
select id, active_date from table1
UNION
select 1 id, null active_date from dual;

select id, active_date from table1
           *
ERROR at line 2:
ORA-01790: expression must have same datatype as corresponding expression

Since the active_date in dual is of the datatype varchar2 rather than date. Is
there a way to resolve this problem.

Thanks a lot for your time and help.

      and we said...
you need to convert the un-typed NULL to a date:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table table1(id number(1), active_date
date);
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into table1 values (1, sysdate);
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> Create or replace view test as
  2  select id, active_date from table1
  3  UNION
  4  select 1, TO_DATE(null) from dual;

View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from test;

        ID ACTIVE_DA
---------- ---------
         1 06-MAR-02
         1

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review &
      Followup

      Rating: 5
data
      conversion  March 06, 2002
Reviewer:
      arunima
That was extremly helpful. Once again thanks for all your help.

March 06, 2002 Nitin -- Thanks for the question regarding "Bind
      Variables In SP", version 8.1.6

      You Asked
Hi Tom,

while studing the concept of bind variables and its effect on performance.I have
few qs

Is we require to use bind variable while passing parameters to stored procedure,
as i run sql provided in your book and i come accross lot of statements like
begin cpsp_ctrl_cmmprc('#','#',:R_V_STATUS=> :R_V_STATUS,:R_N_ERROR_CODE);end;

48

Does this mean i have 48 version of this SP in shared pool area?
do i had to change this to pass bind variables??? we are using Power builder at
front end so is the variables defined in PB is also treated as bind variables?

when reading ur book it emphasis that cpu usuage is lot in hard parse.

Now i want to know very basic thing
how to manage cpu usuage and where should we spend our cpu time in ?
or do we need to worry about cpu usuage when we are getting out of CPU time ? as
in our system 65% of time we spend on using cpu and i do not know whether this
is gd or bad.
Can you please explain the concept of cpu usuage

Sorry for asking very basic q .

      and we said...
Yes, it means you have 48 versions potentially.

go back to v$sql and query:

select sql_text from v$sql where sql_text like '%begin
cpsp_ctrl_cmmprc(%);%end;%';

and see if the calls differe ONLY in the values of the first two inputs.  eg: if
you see:

cpsp_ctrl_cmmprc( 'hello', '5', :R_V_STATUS=> :R_V_STATUS,:R_N_ERROR_CODE);
cpsp_ctrl_cmmprc( 'xxx', '2', :R_V_STATUS=> :R_V_STATUS,:R_N_ERROR_CODE);
cpsp_ctrl_cmmprc( 'abd', '342', :R_V_STATUS=> :R_V_STATUS,:R_N_ERROR_CODE);

You have a bind variable problem (i see this alot, for some reason people will
SKIP binding "IN" parameters and only use binds for "OUT" parameters -- when
they are FORCED to use them)

You'll have to refer to the PB documentation to see how it does what it does.  I
KNOW PB supports bind variables 100%.  If you are just building a string and
executing it -- its up to YOU to bind however.

65% is fine -- but hard parsing is not just about CPU usage -- its about using
the shared pool correctly, its about not serializing (parsing requires many
hundres of latches on the shared pool -- latches = locks = slowness).

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2444907911913
for more details.

Review &
      Followup

      Rating: 5
as always
      useful  March 07, 2002
Reviewer:
      A reader
thanks tom

March 06, 2002 A -- Thanks for the question regarding "Time
      Stamp", version 8.1.6

      You Asked
Hi Tom,

I am much pleasure to forward my questions at ur desk first time.
Can u Please answer to the following my two questions.
1.Can we decode the oracle passwords? If so what is the algorithm? will the
oracle decode the passwords always with the same algorithm?
2.Can u find timestamp of record creation in the table?

Thanks

AN Kumar

      and we said...
1) they are not encrypted.  Read:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1235636675059
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:95412348059
for more details on what they "are".

2) no, not unless you add a date column and a trigger to maintain it.  We do not
track that information.
March 06,
      2002 Peter -- Thanks for the question
      regarding "Problem with Using "in list" in PL/SQL", version 8.1.7

      You Asked
Hi Tom,

I have a situation where I need to use "in list" in a dynamic SQL.
I have taken your suggestion of using a function that converts the
items in the "in list" into a PL/SQL nested table type like the
following:

CREATE OR REPLACE type myTableType AS TBLE OF NUMBER;

CREATE OR REPLACE FUNCTION in_list(p_string IN VARCHAR2)
RETURN myTableType
AS
   l_data   myTableType := myTableType();
   l_string LONG DEFAULT p_string || ',';
   i        NUMBER;

BEGIN
   LOOP
      EXIT WHEN l_string IS NULL;
      l_data.EXTEND;
      i := INSTR(l_string, ',');
      l_data(l_data.COUNT) := SUBSTR(l_String, 1, i - 1);
      l_string := SUBSTR(l_string, i + 1);
   END LOOP;

   RETURN l_data;
END in_list;
/

Then I ran the following PL/SQL code to produce the "in list".
It seems like there is an upper limit as to how many elements can
be included this way.  The script was running fine with 1000
elements. However, when I increased the number to 1100. I get
the ORA-01460 error (see below).  Is there anyway to get around
this problem?  My "in list" could potentially be quite large.

SQL> DECLARE
  2     l_string VARCHAR2(32000) := '1';
  3
  4  BEGIN
  5     FOR i IN 2..1100 LOOP
  6        l_string := l_string || ',' || i;
  7     END LOOP;
  8
  9     FOR x IN (SELECT a.column_value
 10               FROM   THE (SELECT CAST(in_list(l_string) AS myTableType)
 11                           FROM   dual) a
 12              )
 13     LOOP
 14        dbms_output.put_line(x.column_value);
 15     END LOOP;
 16  END;
 17  /
DECLARE
*
ERROR at line 1:
ORA-01460: unimplemented or unreasonable conversion requested
ORA-06512: at line 9

Thanks

- peter

      and we said...
The problem is with binding the input string -- which exceeded 4000 (varchar2
SQL limit).  It is not a limit on the number of elements -- rather a limit on
binding varchar2 types to SQL.

So, how to work around?  Here is one way:

ops$tkyte@ORA817DEV.US.ORACLE.COM> CREATE OR REPLACE type myTableType AS TABLE
OF NUMBER;
  2  /

Type created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package state_pkg
  2  as
  3          data long;
  4  end;
  5  /

Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> CREATE OR REPLACE FUNCTION in_list
  2  RETURN myTableType
  3  AS
  4     l_data   myTableType := myTableType();
  5     l_string LONG DEFAULT state_pkg.data || ',';
  6     i        NUMBER;
  7
  8  BEGIN
  9     LOOP
 10        EXIT WHEN l_string IS NULL;
 11        l_data.EXTEND;
 12        i := INSTR(l_string, ',');
 13        l_data(l_data.COUNT) := SUBSTR(l_String, 1, i - 1);
 14        l_string := SUBSTR(l_string, i + 1);
 15     END LOOP;
 16
 17     RETURN l_data;
 18  END in_list;
 19  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> DECLARE
  2     l_string VARCHAR2(32000) := '1';
  3
  4  BEGIN
  5     FOR i IN 2..1100 LOOP
  6        l_string := l_string || ',' || i;
  7     END LOOP;
  8     state_pkg.data := l_string;
  9
 10     FOR x IN (SELECT a.column_value
 11               FROM   THE (SELECT CAST(in_list() AS myTableType)
 12                           FROM   dual) a
 13              )
 14     LOOP
 15            null;
 16        -- dbms_output.put_line(x.column_value);
 17     END LOOP;
 18  END;
 19  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

don't bind the input string -- put it in a package global where the in_string
function can "see" it.
Review & Followup

      Rating:
      5
  March 05, 2002
Reviewer:  Peter

      Rating:
      5
  March 05, 2002
Reviewer:  Peter
       from CA, USA
If instead of l_string, I already have a nested table of NUMBER:

l_tbl  myTableType

Assuming l_tbl is already populated with, say, 1000 elements.  Can I create the
in_list by selecting directly from l_tbl or do I always have to convert l_tbl
into a string and do casting.

          Followup:

You don't have to convert into a string to parse back into a table if you
already have the table type.  Just select from it straight away:

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          l_tbl myTableType := myTableType( 1,2,3,100 );
  3  begin
  4          for x in ( select * from TABLE( cast( l_tbl as myTableType ) ) )
  5          loop
  6                  dbms_output.put_line( x.column_value );
  7          end loop;
  8  end;
  9  /
1
2
3
100

PL/SQL procedure successfully completed.

      Rating: 4
Pls post link
      if on asktom to   March 06, 2002
Reviewer:
      Chris  from Ireland
your suggestion of using a function that converts the
items in the "in list" into a PL/SQL nested table type like the
following:
(as the original questioner says.... )

(is this an Ask Tom thing or from your book ? )

          Followup:

Pretty sure its in the book in one form or another but it originated here:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:146012348066
(actually the entire BOOK originated here, without this site -- I never would
have been able to write the book.  Without the questions -- I wouldn't have had
a clue as to what to write about)

March 06, 2002 Shah -- Thanks for the question regarding
      "Locking", version 8.1.17

      You Asked
Hi TOM,

One another Q in locking...

Our Apps is a multi user apps, i am sure we can figure out a way to lock the
records to insure data integerity.

our problem ia how to handle Exceptional Situation ? meaning what if the user
rebbots it when he is in edit mode or what if the machine shuts down (we r using
Windows !! )for some reason or if the use closes the apps abruptly .

how do we handle locking in it ?

is there a way in oracle ?

any kind of suggestions will be appericated..

TIA
Shah

      and we said...
enable dead client detection (DCD)

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/network.817/a76933/advcfg.htm#469509
the system will terminate their connection when the client is detected as being
"dead".

March 06, 2002 Seza -- Thanks for the question regarding "BIND",
      version 9i

      You Asked
Hi Tom,
I read all the threads about this subject, but I still can not get the following
work.

AS

type myTableType is table of number;
xtable mytabletype := mytabletype( 1115,1116,1117 );
ytable  mytabletype;

str1 varchar2(2000);

begin

str1 := 'select dim_key  from FACTS_TABLE where dim_key '||
' in ( select * from  table( cast( xtable as mytabletypex )  )) ';

execute immediate str1 bulk collect into ytable;

end;

      and we said...
well yes - xtable is NOT in scope.  Consider "execute immediate" to be a
function -- the local variable xtable is no longer in scope.

Bind it instead:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace type myTableType as table
of number;
  2  /

Type created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      xtable mytabletype := mytabletype( 1115,1116,1117 );
  3      ytable  mytabletype;
  4
  5      str1 varchar2(2000);
  6
  7      type rc is ref cursor;
  8      l_cursor rc;
  9  begin
 10      str1 := 'begin
 11                   select * bulk collect into :ytable
 12                     from table( cast( :xtable as mytabletype ) );
 13               end;';
 14
 15      execute immediate str1 USING out ytable, in xtable;
 16
 17      for i in 1 .. ytable.count
 18      loop
 19          dbms_output.put_line( ytable(i) );
 20      end loop;
 21  end;
 22  /
1115
1116
1117

PL/SQL procedure successfully completed.

March 06, 2002 Sivababu -- Thanks for the question regarding
      "Function based index", version 8i and 9i

      You Asked
Hello TOM,
       If i will create function based index. I'm seeing two entries in the
user_ind_columns and the column name values are different.

For ex
 CREATE INDEX ABC_IDX ON ABC(ABC ASC , ABC DESC);

COLUMN_NAME
===========
ABC
          2 DESC
COLUMN_NAME
===========
SYS_NC00007$

Why it is stored in SYS_NC00007$?

Thanks in advance

regards,
sivababu

      and we said...
descending indexes are implemented like function based indexes are.  The SYS_NC*
is a "dummy" column -- you'll find the expression in user_ind_expressions:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int, y int );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx on t(x,y desc);
Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select column_name from user_ind_columns
where index_name = 'T_IDX';

COLUMN_NAME
------------------------------
X
SYS_NC00003$

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from user_ind_expressions;

INDEX_NAME                     TABLE_NAME COLUMN_EXPRESSI COLUMN_POSITION
------------------------------ ---------- --------------- ---------------
T_IDX                          T          "Y"                           2

Review &
      Followup

      Rating: 3
Thank
      you  March 07, 2002
Reviewer:  sivababu
       from Germany
Thanks very much TOM.
That's it.
I don't know about this table 'user_ind_expressions'. I can manipulate with this
table for my use.

regards,
sivababu

March 06, 2002 Matt -- Thanks for the question regarding
      "density", version 8.1.6

      You Asked
What does the density column in dba_tab_columns mean?

      and we said...
Here is a support note on the topic:

Article-ID:         <Note:43041.1>
Circulation:        PUBLISHED (LIMITED)
Folder:             server.Performance.SqlTuning
Topic:              Optimizer Terms
Title:              QTUNE: What is Density?
Document-Type:      BULLETIN
Impact:             MEDIUM
Skill-Level:        NOVICE
Updated-Date:       05-APR-2000 21:20:35
References:         <NOTE:50750.1> Shared-Refs:

Content-Type:       TEXT/PLAIN
Keywords:           CBO; FEATURE;
Products:           5/RDBMS (V7.X to V8.X);
Platforms:          GENERIC;

PURPOSE

To give an explanation of 'Density' as used by the CBO.

SCOPE & APPLICATION

For Oracle Support Analysts trying to understand how density is used by the
CBO when determining the best access path.

Density is a statistic used by the Cost Based Optimizer to give selectivity
estimates for columns where better information is unavailable (i.e. from
histograms etc.).

You must gather statistics on your table to get density statistics.

You can see density statistics by looking at the following views:

USER_TAB_COLUMNS
ALL_TAB_COLUMNS
DBA_TAB_COLUMNS

Density is a column statistic and provides selectivity estimates for
equi-join predicates (e.g. and A.COL1 = B.COL1) and equality predicates
(e.g. COL1 = 5).

The density is expressed as a decimal number between 0 and 1.
Values close to 1 indicate that this column is unselective
Values close to 0 indicate that this column is highly selective

The more selective a column, the less rows are likely to be returned by a
query referencing this column in its predicate list.

The column selectivity is part of the equation used to decide on the best
path for a query to take to retrieve the data required in the most effective
manner and hence impacts the final cost value for the query.

Density is calculated as follows:

Pre 7.3
~~~~~~~

    Density = 1 / Number of distinct NON null values

The number of distinct NON-null values for a column (COL1) on table TABLE1
can be obtained as follows:

select distinct count(COL1)
from   TABLE1
where  COL1 is not null;

7.3+
~~~~

The Density calculation has been refined by the use of histograms. If
you have created histograms on your columns we can now use the histogram
information to give more accurate information. Otherwise the Density is
calculated as before. With histograms we can use information on
popular and non-popular values to determine the selectivity.

A non-popular value is one that does not span multiple bucket end points.
A popular value is one that spans multiple end points.

(Refer to <Note:50750.1> for details on histograms)

For non-popular values the density is calculated as the number of non-popular
values divided by the total number of values. Formula:

    Density =  Number of non-popular values
               ----------------------------
                  total number of values

We only use the density statistic for non-popular values.

Popular values calculate the selectivity of a particular column values by
using histograms as follows:

The Selectivity for popular values is calculated as the number of end points
spanned by that value divided by the total number of end points. Formula:

    Selectivity = Number of end points spanned by this value
                  ------------------------------------------
                         total number of end points

           [Top of Page]

Review &
      Followup

      Rating:
      5
  November 21, 2002
Reviewer:  Stephan
      Bressler  from germany
Hi,

what's the formula
Density =  Number of non-popular values
               ----------------------------
                  total number of values

In case there are no popular values, is the density = 1?

I thought there would be a density per bucket? Wouldn't that make sense?

March 06, 2002 Alessandro -- Thanks for the question regarding
      "How can i create a procedure that receives a structure of vector as a
      parameter ?", version 8.1.7

      You Asked
Hi Tom.
How are you?
I'm having a problem.
My Database is Oracle 8.1.7.
HP-UX

I want to create a procedure that receives a structure of vector as a parameter.

For example:
Array Name: Arr[Name, Address, Acts]

Arr[1] := [' John, ' Street 1 ', 25]
Arr[2] := [' Mike', ' Street 2 ', 30]
...
...
...
...
Arr[100] := [' Rick', ' Street 3 ', 25]

exec insert_array(Arr);

After that, I want to insert those data in a table.

I made an example, but it doesn't work, could you help me, please?

Thank you very much.
Alessandro Falanque.

PS: My example:

Alessandro@Camdev::SQL>desc tb1
 Name                            Null?    Type
 ------------------------------- -------- ----
 COD                                      NUMBER(2)
 NOME                                     VARCHAR2(255)

create or replace package Body Tipos_pkg is
  Procedure insere(Tipo MeuTipo) is
   i integer;
  begin

  for i in 1..3 loop
    insert into tb1 values (Tipo(i).cod, Tipo(i).nome);
    commit;
  end loop;

  end insere;
end Tipos_pkg;

Alessandro@Camdev::SQL>declare
  2   Type MeuTipo is table of tb1%ROWTYPE
  3    Index By Binary_Integer;
  4    iTipo MeuTipo;
  5  begin
  6    iTipo(1).cod := 10;
  7    iTipo(1).nome := 'Joao';
  8
  9    iTipo(2).cod := 11;
 10    iTipo(2).nome := 'Chico';
 11
 12    iTipo(3).cod := 12;
 13    iTipo(3).nome := 'Zeze';
 14
 15    Tipos_pkg.Insere(iTipo);
 16  end;
 17
 18
 19  /
declare
*
ERROR at line 1:
ORA-06550: line 15, column 3:
PLS-00306: wrong number or types of arguments in call to 'INSERE'
ORA-06550: line 15, column 3:
PL/SQL: Statement ignored

      and we said...
Alessandro@Camdev::SQL>declare
  2    -- Type MeuTipo is table of tb1%ROWTYPE
  3    -- Index By Binary_Integer;
  4    iTipo tipos_pkg.MeuTipo;
  5  begin

use the SAME type -- don't build an entirely new (conflicting) type.  Define a
type once and use it all over the place.

Review &
      Followup

      Rating: 5
Thank
      you  March 06, 2002
Reviewer:  Alessandro
      Falanque  from S?o Paulo, SP - Brazil
Thank you Tom.
It was very useful to me!

March 06, 2002 Sagi -- Thanks for the question regarding
      "Connecting using encrypted password", version 8.1.x

      You Asked
Q1.
I am writing some scripts for doing some automation. In the script it connects
to various users. So the problem is I can not hard core the password because
down the line the passwords keep changing. Therefore the easiest way is to pick
the encrypted one from DBA_USERS, which enables to get the Current Password.

So can we connect using the encrypted passwords. something like this

VAR encrypted_password

SELECT password INTO :encrypted_password FROM dba_users
WHERE username='SCOTT' ;

CONN scott/<encrypted_password>@hoststring

Q2.
Can we grant Object Privileges to Users on other Database? If Yes could you give
me an example Please.

SID-ORCL
=======
RND

SID-ORACLE
==========
SCOTT
CONN SCOTT/TIGER
GRANT SELECT ON EMP TO RND@DBLINK_TO_ORCL ;

Regards,
Krishna.

      and we said...
q1) see

http://osi.oracle.com/~tkyte/Misc/su.html

q2) No, you need to be on the SOURCE database to grant on the remote object --
you are going to be updating that remote databases data dictionary and DDL over
dblinks is not supported.

You CAN do this:

create view emp_view as select * from rnd@dblink_to_orcl;

grant select on emp_view to "whomever";

Review &
      Followup

      Rating: 5
cool
      script  March 05, 2002
Reviewer:  Tianhua Wu
       from Atlanta, Ga
I love the su.sql and modify a little bit so that it will work when connect to a
remote database.

-- export SQLPATH
SET VERIFY OFF
SET FEEDBACK OFF

whenever sqlerror exit
column password new_value pw
column instance_name new_value iname

declare
    l_passwd varchar2(45);
begin
    select password into l_passwd
      from sys.dba_users
     where username = upper('&1');
end;
/

select password
  from sys.dba_users
 where username = upper( '&1' )
/

select instance_name
  from sys.v_$instance
/

alter user &1 identified by Hello;
connect &1/hello@&iname

alter user &1 identified by values '&pw';

whenever sqlerror continue
@login
SET VERIFY ON
SET FEEDBACK ON

      Rating:
      2
  March 06, 2002
Reviewer:  sagi
       from India
Your ans is good but does not solve my problem.

Actually I am working on discoverer. I have 3 DBs (Databases). User1 and User2
are on DB1 AND DB2.

On the 3rd DB, I have:
==> DB Links to DB1 and DB2 pointing to User1 and User2
==>Three users DISC, X and Y.
==> DISC is having the EUL and Business areas say User1BusinessArea and
User2BusinessArea. Access on User1BusinessArea is given to user X and access on
User2BusinessArea  is given to user Y.

Down the Line, I move:
==>User2 from DB2 to DB1
==>DISC, X, Y from DB3 to DB1

So now we have DB1 with all the users. If I try to open the Workbooks that were
created earlier it would give me error because the DB link is hard-cored in the
EUL Tables.

So how do i solve this problem?

Would be greatful if you could give me the solution.

Regards,

          Followup:

well, that problem is a *TAD* different from the other "problem" isn't it?
rhetorical question there -- the answer is "YES, YES it is."

You'll want to contact support for assistance.  I'm not that up on discovers EUL
personally.

March 06, 2002 arunima -- Thanks for the question regarding "sql
      query", version 8.1.7

      You Asked
Hi Tom,

I have these objects in the database

@dev>desc test;
 Name                                      Null?    Type
 ----------------------------------- -------- ------------------------
 a                                                  RAW(50)
 b                                                  RAW(16)

@dev>select * from test;

a                                 b
-------------------------------- --------------------------------
24664400ECC810038914AABBCCDDEEFF 24666B10ECC810038914AABBCCDDEEFF (*)
24664400ECC810038914AABBCCDDEEFF 2466FB98ECC810038914AABBCCDDEEFF
24664400ECC810038914AABBCCDDEEFF 246741E8ECC810038914AABBCCDDEEFF
24664400ECC810038914AABBCCDDEEFF 24678450ECC810038914AABBCCDDEEFF
24664400ECC810038914AABBCCDDEEFF 246810F0ECC810038914AABBCCDDEEFF
24664400ECC810038914AABBCCDDEEFF 2468DFF8ECC810038914AABBCCDDEEFF
24664400ECC810038914AABBCCDDEEFF 99990000000000000000000000000000
24666B10ECC810038914AABBCCDDEEFF 246741E8ECC810038914AABBCCDDEEFF
24666B10ECC810038914AABBCCDDEEFF 24678450ECC810038914AABBCCDDEEFF
24666B10ECC810038914AABBCCDDEEFF 246810F0ECC810038914AABBCCDDEEFF
2466FB98ECC810038914AABBCCDDEEFF 2468DFF8ECC810038914AABBCCDDEEFF (*)
2466FB98ECC810038914AABBCCDDEEFF 99990000000000000000000000000000
2468DFF8ECC810038914AABBCCDDEEFF 99990000000000000000000000000000

13 rows selected.

@dev>select a, b from test1;

a                                  b
-------------------------------- --------------------------------
2FA5B7D0F06310038716AABBCCDDEEFF
24664400ECC810038914AABBCCDDEEFF
246AF720ECC810038914AABBCCDDEEFF
24678450ECC810038914AABBCCDDEEFF
246810F0ECC810038914AABBCCDDEEFF
2468DFF8ECC810038914AABBCCDDEEFF
246741E8ECC810038914AABBCCDDEEFF
                                 99990000000000000000000000000000

8 rows selected.

I need to write a query that gets me all the rows from test table's a and b
columns that does not exists in that does not exists in test1. I want the result
in a single column.

For example: The first is missing b and the second is missing a. I have marked
those as (*) in the result of test.

Result:

missing a and b
---------------
24666B10ECC810038914AABBCCDDEEFF
2466FB98ECC810038914AABBCCDDEEFF

I wrote this query and it not bringing the desires result.

select a
from test
where a NOT IN
        (select a
          from test1
         UNION
         select b
         from test1)
UNION
select b
from test
where b NOT IN
        (select a
         from test1
         UNION
         select b
         from test1)

Can you please help me out with this.

Thanks a lot for your time.

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> select a from t
  2  where NOT EXISTS ( select null from t2 where t2.a = t.a or t2.b = t.a )
  3  union
  4  select b from t
  5  where NOT EXISTS ( select null from t2 where t2.a = t.b or t2.b = t.b )
  6  /

A
---------------------------------------------------------------------------------
-------------------
24666B10ECC810038914AABBCCDDEEFF
2466FB98ECC810038914AABBCCDDEEFF

should do it.
Review & Followup

      Rating: 5
sql
      query  March 06, 2002
Reviewer:  arunima

Excellent Tom. It worked. But I am still not understanding why my query did not
work. Can you please explain?

BTW when is your other book 'Beginning Oracle Programming' is going to there in
market. Which three chapters have you done on that. I want to buy it right away.

Thanks a lot.

          Followup:

read

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:442029737684
to see what happens to NOT IN when NULLS come into play.

The other book is due out in March.  I wrote 3 chapters:

Chapter 6: Processing Statements in Oracle
Chapter 12: Transactions and Concurrency
Chapter 18: Tuning Tools

Chapter 6 is all new material for me, chapters 12 and 18 -- well they are sort
of "simplified versions" of what is in Expert one on one, there is alot of
overlap there.

March 06, 2002 Steve -- Thanks for the question regarding "How
      to start a job when a file is detected", version 81634

      You Asked
Hi Tom,
Do you have something in your bag of tricks for the following.

When a file is ftp'ed to the file system we want to execute a stored procedure
that will process the file. We have the stored procedure written, the problem
being
A. How does oracle know the file is there to process it.
B. How do we know when to start processing. ie. The file is just starting to be
be ftp'ed and is only 20% there, we want to wait until
the file is 100% there before we strat processing.

tks

Steve

      and we said...
Well, you cannot really initiate a job AFTER the file has been ftp'ed -- we'll
have to poll for the file.

What i've done in the past (to avoid the 20%, 30%, ... issue) is to ftp TWO
files.  say file.dat is the file we are interested in processing.  We would look
for a file "file.don" to appear -- that would tell us file.dat is there waiting.
 So, instead of looking for the file you want to process to appear, you look for
a dummy file that will appear when the file you want it ready.
Review &
      Followup

      Rating:
      4
  December 27, 2001
Reviewer:  Steve
      Montgomerie  from Halifax NS Canada
Tks Tom,
By polling the file do you mean maybe running something through dos using 'AT'
to check for existance of the file?
That is the route I am thinking and was just wondering if that was the only way
to fly.

PS Any plans for a an Ask Tom DBA book? I've worn out the cover on your latest
release!

          Followup:

Being a database guy -- I would use DBMS_JOB to schedule a recurring thing (you
only want to check when the database is available as well, no sense in looking
for the file if you won't be able to process it right?)

No plans for a DBA only book (as the current book isn't Developer only ;)  Don't
know if/when another one would be forthcoming, they are a ton of work.

      Rating: 3
Do U have a
      script available for this  March 06, 2002

Reviewer:  Pawan  from USA
Nice idea. If I have understood correctly then this is what you are saying - "
When the FTP is complete i.e. 100% of the file,file.txt,has been FTPed then
AUTOMAGICALLY another file,file.don, will appear in another or same directory.
The DBMS_JOB should keep polling this directory and once it sees the file.don
file the OTHER process should start"
What I need help in is how to use DBMS_JOB to poll this directory and then start
another Oracle Process. I tried reading but got confused. I will realy
appreciate if you could provide me with an example of say looking for the
completion of the FTP process and then loading the data in file.txt into a
table.
Thanks in advance for yr help.

          Followup:

The file "file.don" does not automagically appear -- the person doing the ftp of
file.txt would have to add a step to their script to ftp this file "file.don" to
let us know the FTP is complete.

Your job would be something like:

.....
is
begin
     begin
         file := utl_file.fopen( 'some_dir', 'file.don', 'r' );
     exception
         when utl_file.invalid_path then
            -- no data yet, just return
            RETURN;
      end;

      close file, open file.txt instead and process it
.....

You would have that job run every so often, it would "poll" the directory
looking for file.don and process file.txt when it found it.

If you don't KNOW the file name (eg: it could be abc.don or xyz.don or whatever)
you would use:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:439619916584
to look for the names of "*.don" files in a directory and process them instead.

      Rating:
      3
ftp  March 06, 2002
Reviewer:  Matt
      Anderson  from Salt Lake City, Utah USA
Wouldn't it be easier just to rename the file after it has been ftp'ed.

for instance...
put file.dat.inuse
rename file.dat.inuse file.dat

Then just look for *.dat when processing.  You know it is done uploading if it
has been renamed.

          Followup:

is is easier?  maybe, maybe not -- sort of a 6 one way, 1/2 dozen the other.

sure you can do that, variation on a theme -- if your ftp server supports
rename.

      Rating: 5
Rename vs
      ftp.done  March 06, 2002
Reviewer:  Raghu
      Srinivasan  from San Jose, CA
I have had occasion to do it both ways and I would go with the separate .done
file. The rename approach is just as good but only when you are expecting just
one file. Say you are expecting 5 files a.txt, b.txt and so on. In that case you
dont want to go about renaming all of these. Just remember to delete the .done
file at the receiving end!

      Rating: 2
Maybe using
      Oracle would be better?  March 15, 2002
Reviewer:
      Harrison Picot  from Haymarket, VA USA
Hi:

I have had to do this, and we used what, to me anyway, is infinitely better.
Assuming that this file is part of some process, you open a record when the
process starts, and then you update it when the file is transfered ( a C program
did the transfer in our case, so it was plenty robust to update the database
when the file was ready) and then whatever does the final processing can update
the record to indicate it was processed, or there was a problem.  The C
application polled the database at intervals looking for files that were
indicated as ready to process.  This can be a lot faster and handle a lot more
traffic than sending a file and creating it on both ends.  The amount of
information you can get back by logging your processes as they move files (or do
anything really) is well worth the little bit of effort required to get what
ever process you are using, to log the result. Putting the record in the
database gives you a lot of flexibility to do other things with that record,
like indicate you want email when it is done, or you want timestamps showing
when it was done, or you want a work-flow process passed on, or anything else
you care to add. In our process, we were updating softare, and all the clients
could could check to see if they had the latest version, and if not, download
it, but do it when the software was fully copied to the server (of course, it
was unix so it was easy).

Cheers,

harrison

      Rating: 2
Oh, and if you
      put the size in  March 15, 2002
Reviewer:
      Harrison Picot  from Haymarket, VA USA
Hi:

I forgot to add, you should put the size of the file in the record, just in
case, and you could (but we didn't) put in the result of sum -r (checksum, if
you are NT).  sorry about following myself but putting the size in was a life
saver a couple of times where the transfer croaked.

Cheers,

Harrison

      Rating: 4
More about
      ftp'd files  January 07, 2003
Reviewer:  David
      Scott  from Atlanta GA USA
I've done this a number of ways, and the solution depends on the ability to
control the ftp job. My current situation uses an ftp process that automatically
renames the file from *tmp to *dat when it's complete; life is simpler this way.
In addition, each of my data files includes a trailer record; if that record
does not exist, the file is invalid.

Thanks Tom (and others) for a fine thread.

March 06, 2002 aadan -- Thanks for the question regarding
      "pl/sql table manipulation ", version 8.1

      You Asked
Hi , I am trying to pass a pl/sql table wich contains 5 real number to a
function .the function sould return me the maximum number of the passed table
to a pl/sql unknow block .

How can I do this ?

************************

 declare
    type tabreals is table of real
    index by binary_integer;
    tab tabreals;
    result real;
    create or replace function returnMax( t tabreals) return real is
    i integer:=1;
     m real;
     maxi number;
     temp number;
     empty exception;
    begin
     if t.count=0 then raise empty;
     end if;
     while i<= t.count loop
       maxi:=t(i);
       temp:=t(i+1);
     if maxi < temp then
       maxi:=temp;
      else i:=i+1;
     end loop;
     return maxi;
     exception when empty then raise_application_error(-20500,'empty');
    end;
  begin
    tab(3.2,2.1,5.8,1.0);
    result:=returnMax(tab);
   dbms_output.put_line('is done '||result)
  end; end;
 /

      and we said...
this is simple logic -- nothing really complex here at all.

You do not use a create or replace INSIDE of plsql code -- you use that to
CREATE or REPLACE standalone functions.

This would be the "right" approach:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package demo_pkg
  2  as
  3          type numArray is table of number;
  4
  5          function returnMax( p_data in numArray ) return number;
  6  end;
  7  /

Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package body demo_pkg
  2  as
  3
  4  function returnMax( p_data in numArray ) return number
  5  is
  6          l_max  number;
  7          l_idx  number;
  8  begin
  9
 10          if ( p_data.count = 0 )
 11          then
 12                  return NULL;
 13          end if;
 14          l_max := p_data( p_data.first );
 15
 16          l_idx := p_data.next(p_data.first);
 17
 18          while( l_idx is not null )
 19          loop
 20                  l_max := greatest( p_data(l_idx), l_max );
 21                  l_idx := p_data.next(l_idx);
 22          end loop;
 23
 24          return l_max;
 25  end;
 26
 27  end;
 28  /

Package body created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          l_data demo_pkg.numArray := demo_pkg.numArray(
556,3.2,2.1,5.8,1.0,55 );
  3  begin
  4          dbms_output.put_line( demo_pkg.returnMax( l_data ) );
  5  end;
  6  /
556

PL/SQL procedure successfully completed.
March 07, 2002 Andy -- Thanks for the question regarding
      "Display data", version 7.3.4

      You Asked
Hi Tom,

    How can I display a table data(one row) in a more readable format?

    for example, I just enter a table name, and then it can generate the script
to prompt me to input the primary key. After that, it will display the format
like :

    column_1    : value(1)
    column_2    : value(2)
    .
    .
    .

    Thanks!

Andy

      and we said...
see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1035431863958
for a stored procedure/script that does almost just that in 8.0 and up easily
(for ANY QUERY).  In 7.3 for a single table, we can do this:

set verify off
declare
    l_theCursor     integer default dbms_sql.open_cursor;
    l_pk            varchar2(30);
    l_columnValue   varchar2(4000);

    type array is table of user_tab_columns.column_name%type index by
binary_integer;
    l_colNames      array;
    l_status        integer;

    procedure execute_immediate( p_sql in varchar2 )
    is
    BEGIN
        dbms_sql.parse(l_theCursor,p_sql,dbms_sql.native);
        l_status := dbms_sql.execute(l_theCursor);
    END;
    procedure p ( p_str in varchar2 )
    is
           l_str   long := p_str;
    begin
           loop
              exit when l_str is null;
              dbms_output.put_line( substr( l_str, 1, 250 ) );
              l_str := substr( l_str, 251 );
           end loop;
    end;
begin

     select column_name into l_pk
       from user_cons_columns
      where constraint_name = ( select constraint_name
                                  from user_constraints
                                 where constraint_type = 'P'
                                   and table_name= upper('&&tname') );

    execute_immediate( 'alter session set nls_date_format=
                        ''dd-mon-yyyy hh24:mi:ss'' ');

    dbms_sql.parse(  l_theCursor,
                     'select * from &&tname where ' || l_pk || ' = :pk',
                     dbms_sql.native );

    dbms_sql.bind_variable( l_theCursor, ':pk', '&&pk_val' );

    for x in ( select column_name , column_id
                 from user_tab_columns
                where table_name = upper('&&tname')
                order by column_id )
    loop
        l_colNames( l_colNames.count+1 ) := x.column_name;
        dbms_sql.define_column( l_theCursor, x.column_id, l_columnValue, 2000 );
    end loop;

    l_status := dbms_sql.execute(l_theCursor);

    while ( dbms_sql.fetch_rows(l_theCursor) > 0 )
    loop
        for i in 1 .. l_colNames.count loop
            dbms_sql.column_value( l_theCursor, i,
                                   l_columnValue );
            p( rpad( l_colNames(i), 30 ) || ': ' || l_columnValue );
        end loop;
        dbms_output.put_line( '-----------------' );
    end loop;
    execute_immediate( 'alter session set nls_date_format=
                           ''dd-MON-yy'' ');
exception
    when others then
        execute_immediate( 'alter session set
                         nls_date_format=''dd-MON-yy'' ');
        raise;
end;
/
set verify on
undefine tname
undefine pk_val

And then:

scott@ORA734.WORLD> @printtbl7
Enter value for tname: emp
Enter value for pk_val: 7499
EMPNO                         : 7499
ENAME                         : ALLEN
JOB                           : SALESMAN
MGR                           : 7698
HIREDATE                      : 20-feb-2081 00:00:00
SAL                           : 1600
COMM                          : 300
DEPTNO                        : 30
-----------------

PL/SQL procedure successfully completed.

scott@ORA734.WORLD> @printtbl7
Enter value for tname: dept
Enter value for pk_val: 10
DEPTNO                        : 10
DNAME                         : ACCOUNTING
LOC                           : NEW YORK
-----------------

PL/SQL procedure successfully completed.

Review &
      Followup

      Rating:
      3
  March 06, 2002
Reviewer:  Andy
       from Hong Kong
I only get this result:

SQL> set serveroutput on size 1000000

SQL>  @c:\andy\util\check_data
Enter value for tname: XXXX
Enter value for pk_val: YY

PL/SQL procedure successfully completed.

SQL>

Do I miss something?

Also can it handle composite key?

          Followup:

oh sorry -- i have

SET SERVEROUTPUT ON SIZE 1000000

in my logon.sql -- you need that for dbms_output.

As written -- no composite keys -- thats left as an exercise for the reader ;)

      Rating: 5
Just a small
      question  March 06, 2002
Reviewer:  Pratap
      Deshmukh  from India
How do you get the username and connect string in your sqlprompt?
"scott@ORA734.WORLD>"

I tried this in my glogin.sql, I got the user but how to get the connect string?

set termout off
column user new_value 1
select user from dual;
set sqlprompt '&1 >'
set termout on

SCOTT >

Thanks

Pratap

          Followup:

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:284615949318
...

March 07, 2002 sam -- Thanks for the question regarding "Sql
      Query Help", version 8.1.7

      You Asked
HiTom,
     Thanks for taking my question.I'm working on a Sql query in which
I need to join two tables .
Say I have the following tables

TABLE1
empid     ename      Sal          Comm
1         scott      4000          10%
2         peter      5000          11%

TABLE2
empid  ename   hiredate    certifications incriments  Birthplace
1      scott   10/10/1999  OCP             3000          NY
1      scott   10/10/1999  MCSE            2000          NY
2      peter   09/09/1999  SUN             1000          LA
2      peter   09/09/1999  JAVA            1500          LA

I want output like this

empid   emane    sal    comm    hiredate    birthplace
1       scott   4000     10%    10/10/1999    NY
2       peter   5000     11%    09/09/1999    LA

I know it's not a goog practice to keep hire date and birth place in
TABLE2 but it's already designed that way ad I have no choice.

Can you please let me know how to get this output ...
Thanks in advance
Sam

      and we said...
It's a TERRIBLE practice, saying it is "not a good practice" woefully
understates the problem.

sigh....

select empid, ename, sal, comm, hiredate, birthplace
  from table1, (select distinct empid, hiredate, birthplace
                  from table2 )
where table1.empid = table2.empid
/

will work -- IF the hiredate/birthplace are consistently entered.  If there are
two records in table2 with conflicting results -- you'll get two rows output for
that emp.

select empid, ename, sal, comm, hiredate, birthplace
  from table1, (select empid, max(hiredate), max(birthplace)
                  from table2 group by empid )
where table1.empid = table2.empid
/

will "solve" that dup -- but you might get a hiredate from one record and a
birthplace from another.

another method that might work faster if getting the first row is important is:

select empno, ename, sal, comm,
       (select hiredate from table2 where table2.empid = table1.empid and
rownum=1) hiredate,
       (select birthplace from table2 where table2.empid = table1.empid and
rownum=1) birthplace
  from table1;

Review
      & Followup

      Rating:
      4
Wonderful  March 07, 2002
Reviewer:
      Sam  from MA,USA
Thanks a lot Tom . The way you answer is great straight and clear.

Thanks,
Sam

March 07, 2002 Raghavendra -- Thanks for the question regarding
      "password file", version 8.05

      You Asked
hi tom
any one can take control of any data base ?
for example go to orant\database\pwdsid.ora
rename the existing pwdsid.ora file
run the orapwd80 file = d:\ orant\database\pwdsid.ora password= newpassword
using this command any one can change the internal password
security of the database is not there here

thanks in advance
rags

      and we said...
Well, if you let someone get to your password file, they can just as easily:

erase *.dbf

can't they?  sounds like you need to (ahem) secure your OPERATING SYSTEM??

You use OS specific features to prevent this from happening in the real world.
Review &
      Followup

      Rating:
      3
passwordfile  March 07, 2002
Reviewer:
      abeginner  from asia
dear Tom ,
how could os be secured regaridng oracle password file,
 why oracle doesn't provide security to password file?
thanku

          Followup:

your kidding right?  This is very very basic stuff.

The OS is the thing that provides protections on files.

How can Oracle (which is not the OS) "secure a file" -- it cannot.  the OS does
this.  Oracle might not even be running when you attempt to "hack" in.

On unix for example, I would set the permissions on the password file to:

$ ls -l prapw*

-rwSr-----   1 ora817       3584 Mar  7 08:01 orapwora817dev

and the directory to:

$ ls -ldg .
drwxr-xr-x   2 ora817   dba           512 Jan 18 11:00 .

$ mv orapwora817dev xxx
mv: cannot rename orapwora817dev: Permission denied

Now, if and only if I am ora817 (logged in) can I read or write that file and
since you have to be ora817 in order to write to the directory -- you cannot
rename it either.  No one else can.  Windows (using NTFS) has similar abilities.
 You would have to be logged in as the proper user in order to zap the password
file.

March 07, 2002 Justis -- Thanks for the question regarding
      "aggregate column", version 8.1.7

      You Asked
Hello,

Thank you for all your time and help.

I have a quick question regarding aggregating a column.
Say I have an order with multiple line items.  I'd like to determine the sum of
the order along with a split percent value. The split percent will be the value
of those records(valid) which have not been "cancelled out" by a corresponding
negative record.  Records
that are valid will all have the same split percent value, this is
part of the business logic.  Each split percent value essentially
represents the order at different points in time.  So an order may
have line items with a split percent of 100.  Then at a later date
the order changes to a split percent of 50.  So all the 100's are
"cancelled" by -100's and in come a bunch of 50's.  At this point
the split percent for the order is 50.  I have no knowledge of when which
records where entered, I only know about the records as they exist right now.  I
have no control over the data as it is entered, just read access.

I have result set returned as follows:

     PRICE SPLIT_PERCENT
---------- -------------
    2476.9           100
    2476.9           100
    3096.9           100
   18596.9           100
       310           100
   12396.9           100
    8056.9           100
   -3096.9          -100
   1548.45            50
      -310          -100
       155            50
  -18596.9          -100
  -12396.9          -100
   6198.45            50
   9298.45            50
   -2476.9          -100
   4028.45            50
   1238.45            50
   -2476.9          -100
   1238.45            50
   -8056.9          -100

In this case I would like to generate one row to be
sum(price), some_formula(split_percent)

            PRICE SPLIT_PERCENT
----------------- -------------
          23705.7            50

The 7 100s are "cancelled" by the 7 -100's and the valid records that make up
the order are the 7 50's that are left.

I would like this to be true for any combo of split percents returned.
E.G.
     PRICE SPLIT_PERCENT
---------- -------------
      1020           100
      1020           100
      1020          -100

result should be

            PRICE SPLIT_PERCENT
----------------- -------------
             1020           100

or

     PRICE SPLIT_PERCENT
---------- -------------
       250            25
       250            25
      1020           100
     -1020          -100
       750            50
      -750           -50

result should be

            PRICE SPLIT_PERCENT
----------------- -------------
              500            25

In each case every negative record will have a corresponding positive record, so
there will never be any "lone" negatives.

I guess something equivalent to

select sum(price), some_formula(split_percent)
from   table
group by columns having sum(price) > 0;

The data will never be somthing like:

     PRICE SPLIT_PERCENT
---------- -------------
       250            25
       250            25
      1020           100
       750            50
      -750           -50

where there is more than one value for split percent for "valid"
records.

Any suggestions?

Thank You,
Justis

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

     PRICE SPLIT_PERCENT
---------- -------------
    2476.9           100
    2476.9           100
    3096.9           100
   18596.9           100
       310           100
   12396.9           100
    8056.9           100
   -3096.9          -100
   1548.45            50
      -310          -100
       155            50
  -18596.9          -100
  -12396.9          -100
   6198.45            50
   9298.45            50
   -2476.9          -100
   4028.45            50
   1238.45            50
   -2476.9          -100
   1238.45            50
   -8056.9          -100

21 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select sum(price), abs(split_percent), sum(
sign(split_percent)  )
  2    from t
  3  group by abs(split_percent)
  4  having sum(sign(split_percent)) > 0
  5  /

SUM(PRICE) ABS(SPLIT_PERCENT) SUM(SIGN(SPLIT_PERCENT))
---------- ------------------ ------------------------
   23705.7                 50                        7

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

     PRICE SPLIT_PERCENT
---------- -------------
       250            25
       250            25
      1020           100
     -1020          -100
       750            50
      -750           -50

6 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select sum(price), abs(split_percent), sum(
sign(split_percent)  )
  2    from t
  3  group by abs(split_percent)
  4  having sum(sign(split_percent)) > 0
  5  /

SUM(PRICE) ABS(SPLIT_PERCENT) SUM(SIGN(SPLIT_PERCENT))
---------- ------------------ ------------------------
       500                 25                        2

should do it....

March 07, 2002 Pranav -- Thanks for the question regarding "A
      better way to a select statement on two different tables.", version 8.1.7

      You Asked
2 Tables have on common field and I am trying to do a count(*) on a group by
clause for both tables at the same time.  Ex:

Table1:

CompName       OS
Comp1          Unix
Comp2          Win
Comp3          Mac
Comp4          Unix
Comp5          Mac

Table2:

UserName       OS
User1          Win
User2          Win
User3          Unix
User4          Unix
User5          Win

I am looking for an output something like:

OS     Table1   Table2
Mac    2        0
Unix   2        2
Win    1        3

I treid a few different ways but I don't get the right result.  The problem I am
having is that I can not say anything like Table1.count(*).  Any help is greatly
appreciated.
Thanks for all your help and a great site.
Pranav

      and we said...
well, if they BOTH have the same list of OS's -- then:

select a.os, a.cnt, b.cnt
  from ( select os, count(*) cnt from table1 group by os ) a,
       ( select os, count(*) cnt from table2 group by os ) b
 where a.os = b.os/

but in your example, there is a Mac row in table1  but not table2.  If table1
has the exhaustive list, then:

select a.os, a.cnt, b.cnt
  from ( select os, count(*) cnt from table1 group by os ) a,
       ( select os, count(*) cnt from table2 group by os ) b
 where a.os = b.os (+)
/

will do.  If *neither* table1 or table2 has the exhaustive list, something like:

select os, (select count(*) from table1 where table1.os = a.os ),
           (select count(*) from table2 where table2.os = a.os )
  from ( select os from table1 union select os from table2 ) a
/

will do.
with your above data, each query returns this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.os, a.cnt, b.cnt
  2    from ( select os, count(*) cnt from table1 group by os ) a,
  3         ( select os, count(*) cnt from table2 group by os ) b
  4   where a.os = b.os
  5  /

OS           CNT        CNT
----- ---------- ----------
Unix           2          2
Win            1          3

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.os, a.cnt, b.cnt
  2    from ( select os, count(*) cnt from table1 group by os ) a,
  3         ( select os, count(*) cnt from table2 group by os ) b
  4   where a.os = b.os (+)
  5  /

OS           CNT        CNT
----- ---------- ----------
Mac            2
Unix           2          2
Win            1          3

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select os, (select count(*) from table1 where
table1.os = a.os ) table1,
  2             (select count(*) from table2 where table2.os = a.os ) table2
  3    from ( select os from table1 union select os from table2 ) a
  4  /

OS        TABLE1     TABLE2
----- ---------- ----------
Mac            2          0
Unix           2          2
Win            1          3

ops$tkyte@ORA817DEV.US.ORACLE.COM>

Review &
      Followup

      Rating:
      5
  March 04, 2002
Reviewer:
      A reader

      Rating:
      5
  March 04, 2002
Reviewer:
      A reader

      Rating: 3
Great Solution,
      but is it possible to extend it further.  March 07, 2002

Reviewer:  Pranav Shah
Thanks for a great solution.  I did think of subqueries but never in the manner
as you showed them.

I was wondering if it was possible to extend it a bit further, such that I have
the output in the manner such that I can but another group by clause on it where
I know that Table1 belongs to group A and Table2 belongs to group B and I had
anothere table, Table3 such that:

Table3:
GrpType   OS
A         Mac
A         Win
A         Unix
A         Unix
B         Mac
B         Win
B         Win
B         Unix

and I need an output in the manner:

GrpType   OS     Table1  Table2  Table3
A         Mac       2       0       1
A         Unix      2       0       2
A         Win       1       0       1
B         Mac       0       0       1
B         Unix      0       2       1
B         Win       0       3       2

The problem is while I am doing the count and group by how do I do the sort at
the same time so that the above output shows up. and neither Table1 or Table2
has any column that specifies as to which group they belong to, the only way is
that it is given/known that Table1 belongs to group A and Table2 to group B.

          Followup:

Just add an ORDER BY after the GROUP BY.

You will extend the example to three tables (take the last query and add table3
to it).

Add the extra column (instead of just OS, GRPTYPE and OS) and join by that.

Then order by grptype, os

      Rating: 5
Great
      Response  March 08, 2002
Reviewer:  Pranav

Hi,
  That definitely got what I wanted, but I ended up using the very first qurey
and using it twice once for group A and then for group B.  The reason for this
was that the UNION query was taking a lot of time.  Thanks!!!

March 08, 2002 atul -- Thanks for the question regarding
      "Unwanted tables", version 8.0.4

      You Asked
Sir,

Here is a situation which all DBA's may be facing..

Many developers creates tables for there test purpose and leaves that table or
indexes behind without dropping them,and that unwanted thing
remains there for long..

As we r in OLTP,we can't afford Auditing and also analyze tables..

How to deal with that?
Is there any view which will tell us the unwanted table ie not accessed for long
time or tables having zero rows in them?

Thanks

      and we said...
why are your developers creating tables in your production system?

If they are doing this in test -- so what?  have them create them in their own
test schemas -- something you can drop and recreate at will.  If they do this in
the "real application" schema -- these tables should never make it to production
as they are NOT part of the application and should not be part of the upgrade
script.

You CAN afford auditing.  You SHOULD use analyze.  OTLP or not.  But anyway --
neither of these things will fix your underlying problem.  If you have
developers that can log into production and do stuff like that -- you have a
serious problem.  You need to stop letting them into production!  If it is in
test/development, you don't have a problem -- its test after all.  Just let them
do it in their schema and its easy to clean up.

Sounds like you need a better configuration management environment,
documentation, something.  Perhaps a "case" tool would be called for to bring
some order into your environment.
Review &
      Followup

      Rating:
      5
  March 07, 2002
Reviewer:  Dave

Even if, for some obscure reason, you have to create these tables in the
production system, there's about twenty thousand ways of handling this. Try one
of these.

i) Dedicate a "sandbox" tablespace to the temporary tables.

ii) Force a naming convention. Initials of developer, date they won't need the
table any more etc., such as "JS_0206_WHATEVER" is a table created by John Smith
that he'll no longer need from Hune 2002 onwards.

What the developers need, as Tom implies, is a good slapping.

          Followup:

Well, I didn't mean to imply the developers need a good slapping -- people will
do what they are allowed to do.  This is an environment where it sounds like the
necessary controls (rules) where never put into place.  This is as much (if not
entirely) the fault of the mgmt here.

If someone didn't tell me "use this tablespace, use this naming convention,
etc..." -- I wouldn't.  I'd just use table names like "test", "t", "xxx", "abc"
and let them get created where ever.

If and only if -- after the introduction of some controls, rules, rigor the
developers fail to obey the rules should you consider some sort of punitive
action

      Rating: 3
Unwanted
      Tables  March 07, 2002
Reviewer:  atul
       from india
Sir,

In ur response u mentioned about devlopment/test...,
But u might get surprised to here that we don't have test/devlopment servers we
have straight production server.
This is componies policy so i can't help..

But,one schema in production is assigned to development work.ie in that schema
anyone can do there devlopment work..

As u noticed we don't have separate development server.
How much space problem i have to tackle..

Also U r correct in figuring out there r no rules about dev. tables creation ie
anyone can create tables with any name...

My concern here is users r creating test tables in there own schema if they
found little space in devlopment schema..

and these tables kept there for long..and taking unnecessary
extents and inturn space..

so is there any query to find out tables which r not accessed for a long time...

          Followup:

well, your company might be going under soon if it runs its IT that way.

Very sorry for you, might be time to tune up that resume.

(read about auditing, you can audit select/insert/update/delete on all tables,
but that in itself will consume space).

March 08, 2002 abeginner -- Thanks for the question regarding
      "backup", version 8.0.5

      You Asked
dear Tom,
is 0 level backup a full bakup.

help me uderstand the concept of 0,1,2,3,4 level of bkup.
let me appreciate that the things i try to understand in bookish language, makes
it very understandable in your plain language
thanku.

      and we said...
A level 0 will backup all blocks that have at some time contained data.  It is
equivalent to a full backup.

A level 1 backup will backup all blocks that have been modified since the last
(most recent) level 1 or level 0 backup.

A level 2 backup will backup all blocks that have been modified since the last
(most recent) level 2, level 1 or level 0 backup.

And so on.

So, if on sunday you do a level 0 -- it'll get ALL blocks from the db.
On monday you do a level 2 backup -- it'll get ALL blocks modified since sunday
On tuesday you do a level 2 backup -- it'll get ALL blocks modified since monday
On wednesday you do a level 1 backup -- it'll get ALL blocks modified since
SUNDAY

Why might you do that on wednesday?  In the even the system needs recovery --
you only need to apply the sunday backup and the wed incremental to restore --
you don't need to apply sunday/monday/tuesday/ and wednesday...

Review &
      Followup

      Rating:
      4
  March 07, 2002
Reviewer:  Navdeep
      Kumar  from Mesa, AZ, USA

      Rating: 5
...cummulative
      option  March 08, 2002
Reviewer:  Yogeeraj
       from Mauritius
hi,

Correct me if i'm wrong.. :;

A.
The "Incremental 1" is similar to a "incremental level 2 cumulative".

what will be the consequence of an "incremental 2" on thursday in each case?

B.
Also, what is the recommended backup strategy for a typical 24x7 OLTP with high
load during daytime and low load after midnight?

Is it?:
Everyday - Full Database Export, Daily archive Redologs(RMAN);
Sun: Incremental 0 (RMAN)
Mon,tue,thu,fri,sat: Incremental 2 Database (RMAN);
Wed: Incremental 1 (RMAN)

and for recovery:
(say fri)
Incremental 0(Sun)/Incremental 1(wed)/Incremental 2(Thu)

C.
Are there any set of typical scripts provided by Oracle?

thanks for your time and guidance

Best Regards
Yogeeraj

          Followup:

read

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76990/rmanconc.htm#436081

right from there:

"For example, in a cumulative level 2 backup, RMAN determines which level 1
backup occurred most recently and copies all blocks changed since that backup."

there are examples of both kinds documented right there, rather then cutting and
pasting, just read em.

b) that is up to you and your policies.  there is no such thing as a "TYPICAL"
system, everyone is special (just ask them, they'll tell you so).

If the need for speed is there, you won't be using incrementals -- they are
slower but take less space.

Full database export -- no way, might do that once a week for NOT for backup
purposes -- more to test the integrity of the data dictionary and read every
table data block.  export is not backup.

Me, on my typical system -- we do a hot backup nightly, keeping the last 5 to 10
backups available and continously get the archives off of the system as they are
generated (an archive on the machine that blows up is useless, an archive on
tape elsewhere -- very useful).

c) OEM has these. (enterprise manager)

      Rating:
      4
backup  March 08, 2002
Reviewer:  J.
      Troy Tinnes  from Tokyo, Japan
... as usual a plain english description of something people seem to take pages
and pages to write about without getting to the point. Thanks.

      Rating:
      2
backup  March 08, 2002
Reviewer:
      abeginner  from asia
dear tom,
i am not getting the difference b/w level 1,2,3 and 4
they are all the backups since the last modifications. if so why they have been
given the # from 1 to 4, on the other hand there can be 5,6,7 so on .

          Followup:

A 4 will get all blocks modified since the last level 4, or if no level 4 exists
-- the level 3, or if no level 3 exists since level 2 and so on.

So, say you do this:

Day              Level         What
--------         --------      --------------
Sun              0             All blocks that contained data
Mon              4             All blocks modified since Sun
Tue              4             All blocks modified since Mon
Wed              2             All blocks modified since Sun
Thu              4             All blocks modified since Wed
Fri              1             All blocks modified since Sun
Sat              4             All blocks modified since Fri

It just lets you set up different levels that'll back up different sets of
blocks.  You might follow the two level 4 incrementals with a level 2 like I did
so you don't have to apply so many incrementals during a restore for example.

This sort of backup stuff is very very common - people have been doing it with
file systems for many many years.

      Rating: 5
Reg. the reply
      to - abeginner from asia   May 31, 2003
Reviewer:
      A reader  from Houston
Reg. the reply to - abeginner  from asia

<quote>
Thu              4             All blocks modified since Wed
Fri              1             All blocks modified since Sun
Sat              4             All blocks modified since Fri
</quote>

The last line
Sat              4             All blocks modified since Fri

should'nt it be
Sat              4             All blocks modified since Thu

Reason: Last level 4 was done on Thu.
(This is the first time I am reading about level 0 back up etc. and I might be
wrong)

March 08, 2002 Michael -- Thanks for the question regarding
      ""Order by" in "Group by"", version 8.1.7

      You Asked
Hi Tom,

I have a sql question while I am writing for a froum page.

Let say I have a table Forum:
Name                Null?    Type
------------------- -------- ------------
ID                           NUMBER(3)
POST                         DATE

ID is the ID of a topic and POST is the date of the message regarding the topic
posted.

Here is the extract of the table:
        ID POST
---------- ------------------------------------
         1 28/02/2002 18:24:42
         2 28/02/2002 18:02:26
         3 28/02/2002 18:24:19
         4 28/02/2002 18:02:10
         0 28/02/2002 18:06:29
         1 28/02/2002 18:34:29
         2 28/02/2002 18:04:10
         3 28/02/2002 18:27:41
         4 28/02/2002 18:03:58
         0 28/02/2002 18:08:28
         1 28/02/2002 18:37:49

Now, I want to write a sql statement(not a procedure) to get a list of topics
grouped by their id and the sequence of id showing is according to the latest
post date.

For example, from the extract above, we can get the listed of ID order by theire
latest post date:
        ID POST
---------- ------------------------------------
         1 28/02/2002 18:37:49
         3 28/02/2002 18:27:41
         0 28/02/2002 18:08:28
         2 28/02/2002 18:04:10
         4 28/02/2002 18:03:58

Then, I want to get all record order by ID as the above sequence and the post
date:

        ID POST
---------- ------------------------------------
         1 28/02/2002 18:37:49
         1 28/02/2002 18:34:29
         1 28/02/2002 18:24:42
         3 28/02/2002 18:27:41
         3 28/02/2002 18:24:19
         0 28/02/2002 18:08:28
         0 28/02/2002 18:06:29
         2 28/02/2002 18:04:10
         2 28/02/2002 18:02:26
         4 28/02/2002 18:03:58
         4 28/02/2002 18:02:10

I hope I explain my case clearly.

Thank you very much.

Michael

      and we said...
Analytic functions *rule*

ops$tkyte@ORA817DEV.US.ORACLE.COM> select id, dt, last_post
  2    from ( select id, dt,
  3                  first_value(dt) over (partition by id order by dt desc)
last_post
  4             from t
  5         )
  6   order by last_post desc, id, dt desc
  7  /

        ID DT                  LAST_POST
---------- ------------------- -------------------
         1 28/02/2002 18:37:49 28/02/2002 18:37:49
         1 28/02/2002 18:34:29 28/02/2002 18:37:49
         1 28/02/2002 18:24:42 28/02/2002 18:37:49
         3 28/02/2002 18:27:41 28/02/2002 18:27:41
         3 28/02/2002 18:24:19 28/02/2002 18:27:41
         0 28/02/2002 18:08:28 28/02/2002 18:08:28
         0 28/02/2002 18:06:29 28/02/2002 18:08:28
         2 28/02/2002 18:04:10 28/02/2002 18:04:10
         2 28/02/2002 18:02:26 28/02/2002 18:04:10
         4 28/02/2002 18:03:58 28/02/2002 18:03:58
         4 28/02/2002 18:02:10 28/02/2002 18:03:58

11 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select id, dt, last_post
  2    from ( select id, dt,
  3                  (select max(dt) from t t2 where t2.id = t.id) last_post
  4             from t
  5         )
  6   order by last_post desc, id, dt desc
  7  /

        ID DT                  LAST_POST
---------- ------------------- -------------------
         1 28/02/2002 18:37:49 28/02/2002 18:37:49
         1 28/02/2002 18:34:29 28/02/2002 18:37:49
         1 28/02/2002 18:24:42 28/02/2002 18:37:49
         3 28/02/2002 18:27:41 28/02/2002 18:27:41
         3 28/02/2002 18:24:19 28/02/2002 18:27:41
         0 28/02/2002 18:08:28 28/02/2002 18:08:28
         0 28/02/2002 18:06:29 28/02/2002 18:08:28
         2 28/02/2002 18:04:10 28/02/2002 18:04:10
         2 28/02/2002 18:02:26 28/02/2002 18:04:10
         4 28/02/2002 18:03:58 28/02/2002 18:03:58
         4 28/02/2002 18:02:10 28/02/2002 18:03:58

11 rows selected.

works as well

Review &
      Followup

      Rating: 4
What is inside
      the sub query?  March 08, 2002
Reviewer:
      Michael Leung  from HK China
Tom,

Could you explain the sub query "select id, dt,      first_value(dt) over
(partition by id order by dt desc) last_post from t"?

Thanks

          Followup:

Well, if you have my book - read the chapter on analytic functions.  I spend
about 40 pages on them in detail...

select id, dt, last_post
  from ( select id, dt,
                first_value(dt) over (partition by id order by dt desc)
last_post
           from t
       )
 order by last_post desc, id, dt desc
/

That subquery takes the set of data and treats each ID as a separate group
(called a partition).  Within each partition (within each ID) we sort by DT
desc.  The first_value function retrieves the first value for DT in this
partition after sorting by dt desc and gives it to each row.

The first_value in this case, assigns to each row the MAX dt by ID.  It does
exactly what this does:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select id, dt, last_post
  2    from ( select id, dt,
  3                  (select max(dt) from t t2 where t2.id = t.id) last_post
  4             from t
  5         )
  6   order by last_post desc, id, dt desc
  7  /

March 08, 2002 naga -- Thanks for the question regarding
      "pecular problem -- dynamic sql", version 8.1.7

      You Asked
tom,

i have a pecular problem.

in a procedure i declared a cursor with a main table say 'main' and some other
tables
main table has got lot of columns in it.

decalre

   cursor c1 is
   < select stmt >...

   cursor c2(p_param1 number) is
   select
   main.a,main.b,main.c,main.d,main.e, other1.col1,.....

   from

   main,
   other1,
   other2,
   .....
   ....

   where

   main.param1 = p_param1 AND main.batch_no = 1 and other2.param1 = p_param1 and
condtions that involves other tables

begin

   for c1rec in c1
   loop

       for c2rec in c2(c1rec.param1)
       loop

         ....
         ....
         < process 1 >
end;

one of the column(other1.col1) in that cursor returns a value that has column
name of the main table

other1.col1 can have 'a','b','c','d','e'... so on.

if a or b or c or d or e(which ever returned by other1.col1) value becomes null
then i should not execute the process 1

now the problem is i have to select all the columns from the main table

the ways i can think are

1.select rowid from the cursor c2 and before process 1
  execute a dynamic sql 'select ' || other1.col1 || ' from main where rowid =
:rowid ' into <some var> using c2.rowid;

2. as in the cursor c2 i am not joining main tables and other tables( p_param1
present main and other2 tables)

   remove main from c2 and use dynamic sql for main table in second loop before
process....

   execute 'select necessary columns ' || other1.col1 || ' from main where
param1 = :param1 and ' || 'other1.col1 ' || ' is not null ' into <variables >
using c1rec.param1

with the 1st way i need to query main table twice and i need to select all the
columns from main.
with the 2nd way i will have to parse dynamic sql

or do you suggest me any other way..

i know this is such a weird thing, b'cos i am modifying somebody else's code.

      and we said...
I would add a column:

  decode( other1.col1, 'a', nvl2( a, 0, 1 ),
                       'b', nvl2( b, 0, 1 ),
                       'c', nvl2( c, 0, 1 ),
                        ....
                       'z', nvl2( z, 0, 1 ) )  dont_run_process1

if that column comes back as 1 -- then DONT run process 1, if it comes back as
0, run process 1.

Review &
      Followup

      Rating: 5
new column can
      be added to main table  March 08, 2002
Reviewer:
      naga
tom,

that is cool solution, i didn't think of that. thanks for that.

but i suspect a new column can be added to the main table say 'f', then again i
need to modify that decode stmt to get that column value as well.so, in that
case can you tell me what shall we do?

thanks

          Followup:

If you add a column to the table (thats a pretty serious change right?) -- just
update the view that hides this decode (yes, I would use a view to hide the ugly
decode from the plsql, it just makes the code harder to read).  The owner of the
table would own that view and be responsible for setting it up.

To protect yourself you could write a function:

ops$tkyte@ORA8I.WORLD> drop table t;

Table dropped.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> create table t ( a int, b int, c int, col varchar2(1) );

Table created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> create or replace function f return number
  2  as
  3  begin
  4     raise_application_error( -20001, 'Uknown Column Detected!!!' );
  5  end;
  6  /

Function created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> create or replace view v
  2  as
  3  select a, b, c, decode( col, 'a', nvl2(a,0,1),
  4                                                           'b', nvl2(b,0,1),
  5                                                           'c', nvl2(c,0,1),
  6                                                            f() )
dont_run_process
  7    from t
  8  /

View created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> set arraysize 1
ops$tkyte@ORA8I.WORLD> insert into t values ( 1, 1, 1, 'a' );

1 row created.

ops$tkyte@ORA8I.WORLD> insert into t values ( 1, 1, 1, 'b' );

1 row created.

ops$tkyte@ORA8I.WORLD> insert into t values ( 1, 1, 1, 'c' );

1 row created.

ops$tkyte@ORA8I.WORLD> insert into t values ( 1, 1, 1, 'd' );

1 row created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> select * from v;

         A          B          C DONT_RUN_PROCESS
---------- ---------- ---------- ----------------
         1          1          1                0
         1          1          1                0
         1          1          1                0
ERROR:
ORA-20001: Uknown Column Detected!!!
ORA-06512: at "OPS$TKYTE.F", line 4
ORA-06512: at line 1

ops$tkyte@ORA8I.WORLD>

so that if a bogus value gets put in there (cause they didn't update the view)
you'll find out about it straight away...

March 08, 2002 Ravi -- Thanks for the question regarding "Type
      Creation Error", version 8.1.5

      You Asked
Hi Tom,
when I run following command, I am getting the warning like:

SQL> create or replace TYPE array_test AS TABLE OF NUMBER index by
binary_integer
SQL>/

Warning: Type created with compilation errors.

What could be the problem ?
Thanks
Ravi

      and we said...
You cannot create SQL TYPES that are index by tables -- they are 100% specific
to PLSQL.

They can only be created in PLSQL.
March 08, 2002 Richard -- Thanks for the question regarding
      "implications of authid current_user", version 9i

      You Asked
Hi Tom,

I work on a project where there are several quasi mirrored schemas,
i.e. same table structure different contents.

I would like to implement PL/SQL packages in one schema and
use them for other schemas with:

alter session set current_schema = schema
+
authid current_user in the package definition.

It seems to work so far I could test it.

Q1. Is there a performance penalty when using invoker rights
    procedures as compared to definer ones?

Q2. Is there a way to create my packages in a "super"-schema which
    is none of the schemas where the package will be utilized
    without the need of duplicating the standard schema?

Cheer,
Richard

      and we said...
First -- if you want the entire story -- I will recommend my book.  I go into
this in depth (invokers/definers rights)

q1) yes, yes it will.  shared sql is definitely impacted.  The shared sql cache
is somewhat defeated in this environment.  The code is also "less stable" due to
potential schema difference.

q2) You might want to look into VPD (fine grained access control).  See

http://osi.oracle.com/~tkyte/article2/index.html
or my book for details.  Using this you can use one schema for many people
simultaneously -- each thinking they have their OWN schema.
March 08, 2002 Scott -- Thanks for the question regarding
      "Populating CLOBS via JAVA streams and Database triggers", version 8.1.7

      You Asked
Given an JAVA application that is populating Clobs in the streaming techniques
you've referenced in some of your responses. I'm not a JAVA person but it is
something like

Insert into (clob_column)
value (empty_clob());

Then
the JAVA statement that streams the data into the locator that was just
inserted.

MY Question:
What database triggers fire during this streaming process? We're contemplating
parsing the clob into another table on an After Update trigger and wanted to
know if JAVA streaming fires the After Update trigger?

      and we said...
No database triggers fire on the "write" to the lob.  The only fire when the
structured data changes.

So, an update of a CLOB to an empty_clob() will fire the trigger, but the
WRITING of data to the empty_clob will not (which is good -- else the trigger
would fire for each CHUNK you wrote).  You need to call a procedure after
streaming the data to process it further.
Review &
      Followup

      Rating: 4
That's what I
      wanted to knoe  March 11, 2002
Reviewer:
      Scott  from Orlando, Florida
Thanx

March 08, 2002 Esther -- Thanks for the question regarding
      "Snapshot fed by many masters", version 8.0.5

      You Asked
Is it possible to have a snapshot fed by many master tables, geografically
distributed?
I would like to do that to gather records from about 500 separated offices, and
have a central database. All the master tables and the central have the same
structure.
Possibly, at the refresh moment, some of distributed offices databases will not
available.
The remote offices use Personal 7.3.4.0.0 and the central one 8.0.5.2.1.
Thank you very much,

      and we said...
No, but you can have a single MASTER have many updatable snapshots (reverse the
roles).

Each updatable snapshot will be some horizontal slice of the single MASTER.
Each can send their updates whenever they want, independent of the others.

I can say that neither 7.3 or 8.0.5 is even remotely current software, the
advances in the replication software over time make this even worse.  I would
recommend upgrading the 7.3 instances at the very least.
March 10, 2002 Betty -- Thanks for the question regarding
      "Conversion Of Datatype RAW", version 8.1.7

      You Asked
I have a tables converted from another database as datatype RAW (8), which
contains 64 flags.  I needed to display the flags as 'Y' or 'N'.  Is there a
function that will select the field and display 'Y', or 'N'?
When I select the RAW field -  (select o1class from mic_o1rg
                                                            where aa_id = 57;)
the following is displayed:     0001000000000000

Can I make a view?

Thanks

      and we said...
you can use UTL_RAW.BIT_AND

ops$tkyte@ORA8I.WORLD> select
  2          x,
  3     decode( utl_raw.bit_and( x, hextoraw( '0001000000000000' ) ),
'0001000000000000', 'Y', 'N' )
  4  from t;

X                D
---------------- -
0001000000000000 Y
0002000000000000 N

ops$tkyte@ORA8I.WORLD>

If utl_raw is not installed on your system, simply:

cd $ORACLE_HOME/rdbms/admin
run utlraw.sql prvtrawb.plb using SVRMGRL when connected as SYS or as sysdba
Review &
      Followup

      Rating:
      4
Reader  March 08, 2002
Reviewer:
      A reader
What is the difference between utl_raw.bit_and and
plain bitand.

SQL> select bitand(5,5)+0 from dual;

BITAND(5,5)+0
-------------
            5

Therefore plain bitand is applied to Decimals and
utl_raw.bit_and is applied to raw digits. Is this right

Thanks

          Followup:

bitand works on numbers.  It'll not work on RAWS or STRINGS

ops$tkyte@ORA817DEV.US.ORACLE.COM> select bitand( hextoraw( '0A' ), 1 ) + 0 from
dual;
select bitand( hextoraw( '0A' ), 1 ) + 0 from dual
               *
ERROR at line 1:
ORA-00932: inconsistent datatypes

ops$tkyte@ORA817DEV.US.ORACLE.COM> select bitand( '0A', 1 ) + 0 from dual;
select bitand( '0A', 1 ) + 0 from dual
               *
ERROR at line 1:
ORA-01722: invalid number

utl_raw.bitand will work on RAWS.

March 10, 2002 Ravi -- Thanks for the question regarding
      "Retrieve Dates Between Two Dates ", version 8.1.5

      You Asked
Hello Tom ,
        I am trying to retrieve the dates between two given dates , though this
is possible by writing a pl/sql Block , I would like to achieve the same in a
Single Sql Statement , if possible .
Doing it this way ...
  declare
  ed number;
  bd date;
  begin
  select sysdate-30,sysdate-(sysdate-30) into bd,ed from dual;
  for i in 0..ed loop
  dbms_output.put_line(bd+i);
  end loop;
  end;
/
-------------------------------------------------------------
Also I would like to know if u are in the process or have already written a Book
like Expert One-on-One for Oracle 9i . I would like to buy the latest edition ,
so the question .

Thanx and Regards
R.Venkatesh

      and we said...
select sysdate-30+(rownum-1)
  from all_objects
 where rownum <= 30
/

Expert One on One is the current book.  I have no current plans to do a 9i
release of that (frankly, 90% or more of the material in there applies to 9i or
even 7.3 as well).

I've been toying with the idea of a performance book but haven't signed on the
dotted lines yet.
Review & Followup

      Rating: 3
Retrieve Dates
      Between Two Dates   March 06, 2002
Reviewer:
      Bharath  from NYC
How about this...

SELECT TO_DATE('01/01/2001','MM/DD/YYYY') + (ROWNUM - 1) DATEGEN
 FROM ALL_OBJECTS
WHERE TO_DATE('01/01/2001','MM/DD/YYYY') + (ROWNUM - 1) <=
TO_DATE('01/31/2002','MM/DD/YYYY')
;

          Followup:

if you factor that predicate so that ROWNUM is on the LHS by itself -- it'll be
OK

rownum <=
TO_DATE('01/31/2002','MM/DD/YYYY')-TO_DATE('01/01/2001','MM/DD/YYYY')+1

otherwise, it'll evaluate that for every row in all_objects -- not just the 31
you want.

SELECT TO_DATE('01/01/2001','MM/DD/YYYY') + (ROWNUM - 1) DATEGEN
 FROM ALL_OBJECTS
WHERE TO_DATE('01/01/2001','MM/DD/YYYY') + (ROWNUM - 1) <=
TO_DATE('01/31/2002','MM/DD/YYYY')

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.02       0.03          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch       28      0.74       0.72          0      14080          4         396
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       30      0.76       0.75          0      14080          4         396

SELECT TO_DATE('01/01/2001','MM/DD/YYYY') + (ROWNUM - 1) DATEGEN
 FROM ALL_OBJECTS
WHERE
rownum <=
TO_DATE('01/31/2002','MM/DD/YYYY') - TO_DATE('01/01/2001','MM/DD/YYYY') + 1

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.02       0.02          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch       28      0.10       0.08          0       1636          4         396
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       30      0.12       0.10          0       1636          4         396

      Rating: 3
Dates b/w two
      given dates ....  March 07, 2002
Reviewer:
      Priya  from Singapore
Tom,
  This is fine as long as the number of rows in DBA_objects or for that matter
any other table or view may have sufficient rows as as to return the dates . In
the case where the dates b/w 2 given dates may exceed the number of rows in for
eg : DBA_OBJECTS , we have a problem . Do we have a fix for this ?

          Followup:

if you need more then 2 to 3 years of dates generated -- (given that all objects
in 73 is around 1000 rows and 20-30,000 for a java install in 8i), I question
what you are doing.  What would you do with 1,000 days in a list?

The solution of course would be for YOUR application to install and populate a
table that is big enough for you.  Part of your install would be:

create table rows ( x int );
begin
   for i in 1 .. 1000000 -- or whatever
   loop
       insert into rows values ( i );
   end loop;
   commit;
end;
/

In 9i, we can use a pipelined PLSQL function to generate as many rows as you
like on the fly.  See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2270447621346
for an example of that.

      Rating: 4
Securtiy
      Implications  March 07, 2002
Reviewer:
      A reader
Yesterday Eveing had a Long Argument with my friend when i told him this
solution he was saying that a DBA in a Production environment will not give
Rights to all_objects.

I Felt it was a Bit Crazy because AFAIK i know we wont have any security
Implication on Giving access to all_objects.

What are your thoughts on this.

Thanks.

Best Regards,
Ganesh R

          Followup:

A DBA in production doesn't have to give the rights -- select on all_objects is
always granted to PUBLIC upon install.  Many Many products will not work without
it.  It is called "the data dictionary".

This is in catalog.sql:

grant select on ALL_OBJECTS to PUBLIC with grant option
/

all of the ALL_* and USER_* views are selectable by PUBLIC (anyone with CREATE
SESSION can see them)

Hopefully your friend is not a DBA for a production environment!

      Rating: 5
Generating
      Dates...  March 09, 2002
Reviewer:
      A reader
Hi Tom,

Thanks for the earlier Response.  I have another one for you. [This actuall was
the Main thread of the previous discussion that i had posted]

I Usually use this Generate Dates for a varitey of purposes.

Select '01-jan-2001' + rownum from all_objects where rownum < 100;

The Above Query Generates 100 Dates..

Now i heard from people that instead of using the all_objects view u should
create a Table that has 4000 Odd rows that we can use so that if all_objects
Privs have been revoked then also u'r function will work.

IMHO ... I feel there is no penality in using all_objects for this purpose.

Your thoughts on these...

Regards,
Ganesh R

          Followup:

Read the above comments about all_objects and security.

If you every have a database where all_objects access has been revoked, you are
in a database that is

o not supported by Oracle support in all likelyhood.
o won't support most any 3rd party application

ALL_OBJECTS is *always* granted select on to PUBLIC.  If it isn't, the database
is broken and needs to be fixed.

The very idea that accessing ALL_OBJECTS is a security "risk" is utter nonsense.
 I would be very interested in hearing from others who think otherwise AND WHY
-- so I can correct their thinking.

March 10, 2002 Rui -- Thanks for the question regarding "See
      info writed in a LONG column", version 8.1.6

      You Asked
Hi Tom

In USER_VIEWS, for example, the view code is write in a LONG field.
If I made a SELECT, I only see part of this code.
How can I see this entire field ?
I need do it in PL/SQL ?

Thanks
Rui Brito

      and we said...
use the sqlplus set long command to set the amount of LONG data fetched by
sqlplus:

SQL> set long 50000

for example.
Review & Followup

      Rating: 5
See info writed
      in a LONG column  November 20, 2002
Reviewer:
      Chris Barr  from Boston, MA
Thanks - I needed the "set long 5000" command in sqlplus.

March 10, 2002 Shivaswamy -- Thanks for the question regarding
      "How is this possible..?", version 817

      You Asked
Hi, Tom.

I ran into a strange situation. I could not figure out how is this happening.
Could you explain this please..?(I ran this as SYS. I have LMT)

TIA,
Shivaswamy
10:06:00 SQL> desc v$temp_extent_map;
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 TABLESPACE_NAME                                    VARCHAR2(30)
 FILE_ID                                            NUMBER
 BLOCK_ID                                           NUMBER
 BYTES                                              NUMBER
 BLOCKS                                             NUMBER
 OWNER                                              NUMBER
 RELATIVE_FNO                                       NUMBER
10:06:00 SQL> select tablespace_name from v$temp_extent_map where rownum < 3;

TABLESPACE_NAME
------------------------------
TEMP
TEMP

10:06:00 SQL>
10:06:00 SQL> desc dba_temp_files;
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 FILE_NAME                                          VARCHAR2(513)
 FILE_ID                                            NUMBER
 TABLESPACE_NAME                           NOT NULL VARCHAR2(30)
 BYTES                                              NUMBER
 BLOCKS                                             NUMBER
 STATUS                                             CHAR(9)
 RELATIVE_FNO                                       NUMBER
 AUTOEXTENSIBLE                                     VARCHAR2(3)
 MAXBYTES                                           NUMBER
 MAXBLOCKS                                          NUMBER
 INCREMENT_BY                                       NUMBER
 USER_BYTES                                         NUMBER
 USER_BLOCKS                                        NUMBER

10:06:00 SQL> select tablespace_name from dba_temp_files;

TABLESPACE_NAME
------------------------------
TEMP
TEMP
TEMP

10:06:00 SQL>
10:06:00 SQL> select a.tablespace_name,  b.file_name
10:06:00   2  from v$temp_extent_map a, dba_temp_files b
10:06:00   3  where a.tablespace_name = b.tablespace_name;

no rows selected
10:06:00 SQL> spool off

      and we said...
Ok, this is a strange one -- all queries against V$ tables are a bit tricky as
they are in memory structures and the currently executing query can cause
strange things to happen to them.

As per the Oracle Ref Manual:

"You can query the dynamic performance views to extract information from them.
However, only simple queries are supported. If sorts, joins, GROUP BY clauses
and the like are needed, you should copy the information from each V$ view
into a table (for example, using a CREATE TABLE ... AS SELECT statement), and
then query from those tables."

You can work around this particular issue by doing:

select 'A', a.tablespace_name,  b.file_name
from (select rownum, a.* from v$temp_extent_map a ) a, dba_temp_files b
 where a.tablespace_name = b.tablespace_name
/

the inline view using rownum forced us to build this "A" table off to the side
in a temp segment itself and then join this "real" table with dba_temp_files.

This is unusual but "ok" (expected in some cases)...

Review &
      Followup

      Rating:
      4
  March 10, 2002
Reviewer:
      Shivaswamy
Thanks, TOM.

March 10, 2002 Srinivasacharyulu -- Thanks for the question
      regarding "Page 276 Index", version 9.0.1

      You Asked
I am not able to understand this screen shot:

INDEX (RANGE SCAN DESCENDING)

=================================================
these are my experiences:

  1* create index desc_t_indx on t(owner desc,object_type desc,object_name desc)
Scott@Know9i: SQL> /

Index created.

Scott@Know9i: SQL> select owner,object_type,object_name from t where owner
between 'T' and 'Z' and
  2  object_type is not null order by owner desc ,object_type desc
  3  /

no rows selected

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (ORDER BY)
   2    1     TABLE ACCESS (FULL) OF 'T'

Statistics
----------------------------------------------------------
         29  recursive calls
          0  rows processed

Scott@Know9i: SQL>

===================================

Scott@Know9i: SQL> create index t_indx on t(owner desc,object_type asc);

Index created.

Scott@Know9i: SQL> select owner,object_type,object_name
  2  from t
  3   where owner between 'T' and 'Z'
  4  and
  5  object_type is not null
  6  order by owner desc ,object_type desc
  7  /

no rows selected

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (ORDER BY)
   2    1     TABLE ACCESS (FULL) OF 'T'

Statistics
----------------------------------------------------------
         29  recursive calls
          0  rows processed

Scott@Know9i: SQL>
=====================================================

Scott@Know9i: SQL> drop index t_indx;

Index dropped.

Scott@Know9i: SQL> create index t_idx on t(owner,object_type);

Index created.

Scott@Know9i: SQL> select owner,object_type,object_name
  2  from t
  3   where owner between 'T' and 'Z'
  4  and
  5  object_type is not null
  6  order by owner desc ,object_type desc
  7  /

no rows selected

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (ORDER BY)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'T'
   3    2       INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE)

Statistics
----------------------------------------------------------
         29  recursive calls
          0  rows processed

Scott@Know9i: SQL>

      and we said...
As with most of the examples in the book (i'll show you how to tell), the CBO
was involved -- statistics had been taken.  Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t as select * from all_objects;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index desc_t_indx on t(owner
desc,object_type desc,object_name desc)
  2  /

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table t compute statistics
  2  for table
  3  for all indexes
  4  for all indexed columns
  5  /

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> select owner,object_type,object_name from t
where owner
  2  between 'T' and 'Z' and
  3  object_type is not null order by owner desc ,object_type desc
  4  /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=1 Bytes=45)
   1    0   INDEX (RANGE SCAN) OF 'DESC_T_INDX' (NON-UNIQUE) (Cost=2 Card=1
Bytes=45)

So, how can I tell the CBO was used?  The autotrace tells me -- when you see the
Cost/Card/Bytes --> CBO.  When you dont --> RBO.

So many new features are available only with the CBO now, you really need to use
that for everything.
March 10,
      2002 Munzer -- Thanks for the question
      regarding "Array initialization", version 8.1.7

      You Asked
Tom:

I have a code:

create or replace procedure test
as

x shipm_request%rowtype
type prefixArray is table of varchar2(8) index by binary_integer;
prefix  prefixArray;

begin

  SELECT * into x FROM shipm_request WHERE  request_id=i_request_id;
   IF x.prefix is NULL THEN
       prefix(1):='SELECTED';
    elsif x.prefix='Mr.' THEN
       prefix(2):='SELECTED';
    elsif x.prefix='Mrs.' THEN
      prefix(3):='SELECTED';
    END IF;
    htp.p('<TR>');
    htp.p('<TH align=right>    Prefix:&nbsp</TH>');
    htp.p('<TD>');
    htp.p('    <SELECT name="i_prefix" >');
    htp.p(' <OPTION VALUE=""'||prefix(1)||'>Pick One --->  ');
    htp.p('    <OPTION VALUE="Mr." '||prefix(2)||'>Mr.' );
    htp.p('    <OPTION VALUE="Mrs." '||prefix(3)||'>Mrs.');
        htp.p('    </SELECT>  ');
    htp.p('</TD>');
    htp.p('</TR>');

end;

It seems I get "NO DATA FOUND" for some problem I think due to not initializing
the array.

When i declare 3 variables instead it works.  do you know what is happening.

2.  Do you know why i can not do :

htp.p(i_record_found);

if l_record_found is boolean variable.

Thank you,

      and we said...
1) yes, you are always REFERENCING prefix(1), prefix(2) and prefix(3) but only
setting prefix(1 or 2 or 3).  In a plsql index by table -- if you do not set a
value, you do not have an entry - hence NO DATA FOUND.

suggest you investigate owa_util.listPrint, I can do this in a query:

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      p_request_id number default 55;
  3      i_prefix     varchar2(20) default 'field_name';
  4      l_prefix     varchar2(20);
  5  begin
  6      select prefix into l_prefix
  7        from shipm_request
  8       where request_id = p_request_id;
  9      owa_util.listPrint(
 10         owa_util.bind_variables(
 11              'select ''Mr.'' a, ''Mr.'' b, decode( :prefix, ''Mr.'', ''Y'',
NULL ) c
 12                 from dual
 13                union all
 14               select ''Mrs.'', ''Mrs.'', decode( :prefix, ''Mrs.'', ''Y'',
NULL )
 15                 from dual
 16                union all
 17               select ''Pick One --->'', ''Pick One --->'', decode( :prefix,
NULL, ''Y'', NULL )
 18                 from dual' ,
 19              ':prefix', l_prefix ),
 20              i_prefix,
 21              5 );
 22  end;
 23  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec owa_util.showpage
<SELECT NAME="field_name" SIZE="5">
<OPTION value="Mr.">Mr.
<OPTION SELECTED value="Mrs.">Mrs.
<OPTION value="Pick One --->">Pick One --->
</SELECT>

PL/SQL procedure successfully completed.

2) booleans don't "print", there is no "string" function for them.  you'll need
to write a small function:

create function boolStr( x in boolean ) return varchar2
as
begin
   if x then return 'TRUE';
   elsif NOT x then return 'FALSE';
   else return 'UNKNOWN';
   end if;
end;

and then htp.p( boolStr( i_record_found ) );

Review &
      Followup

      Rating: 5
Initializing
      array variable  March 08, 2002
Reviewer:
      Munz  from Reson, USA
Tom:

OK I see your answer using owa_util.listprint.  To create all those values I
have to do several "UNION" select statemnts.

But since I implemented the first solution already, how to i initializt he
prefix variable of type array.

do i have to do:

prefix(1):=null;
prefix(2):=null;
prefix(3):=null;
and  so on.

if this is correct, then what is really the use of using an a array variable.  I
could declare 3 variables instead as:

prefix_1  varchar2(8) default null;
prefix_2  varchar2(8) default null;
prefix_3  varchar2(8) default null;

Thank you,

          Followup:

you could just code:

create or replace procedure test
as

x shipm_request%rowtype
type prefixArray is table of varchar2(8); -- DELETE index by binary_integer;
prefix  prefixArray := prefixArray( null, null, null );

begin

  SELEC.....

if you like having lots of procedural code...

      Rating:
      5
Arrays  March 09, 2002
Reviewer:
      Munz  from Reston, VA
Tom:

Great.  But the initiaization

prefix  prefixArray:=(null,null,null) ;

is based in using three variables.  let us say you do not know how many variabes
program will use.  How ill you do that.

Also, why you deleted INDEX BY BINARY_INTEGER and when do you use that.

thank you

          Followup:

If you delete the "index by binary_integer", you can initialize the way I did
(in the declare block).  Else you have to write yet more procedural code.

Tell me -- if you don't know how many elements you'll use, how can you be
writing code like this:

 IF x.prefix is NULL THEN
       prefix(1):='SELECTED';
    elsif x.prefix='Mr.' THEN
       prefix(2):='SELECTED';
    elsif x.prefix='Mrs.' THEN
      prefix(3):='SELECTED';
    END IF;

???  You know exactly how many.  If you don't know how many -- you'll be looping
some number of times setting to null I suppose although its hard to tell you how
to do that -- since I don't have an example of how you would approach a
situation with an unknown number of elements.

      Rating:
      5
Arrays  March 10, 2002
Reviewer:
      Munz  from Reston, USA
Tom:

My example would be let us say you have a shopping cart variable of type array.
Let us say you have 3000 books a user  can buy, and I need to store every
selection in a variable to insert it eventually into a table.

so I would declare :

type bookArray is table of varchar2(20);
books   bookArray:=bookArray(null,null,null);

This allows me to have books(1),books(2),books(3) for user selecting 3 books.

But what about if he selected 100 books.

How do you initialize books(4)..books(100).

Thanks

          Followup:

If he selected 100 books -- he would be passing you a plsql table already filled
in with 100 books.  You would have nothing to initialize.  They would send you a
table with 100 books.  You also have a table with 3000 books in it.  You want to
generate a list that has 3000 books in it (bad idea by the way, way too large
for HTML, but anyway....) and have the 100 selected books "selected".  I would
do this:

procedure show( p_my_list in owa_util.ident_arr )
is
    l_tmp        varchar2(4000) default '/';
begin
    for i in 1 .. p_my_list.count loop
        l_tmp := l_tmp || p_my_list(i) || '/';
    end loop;
--
    htp.formOpen( 'whatever' );

    owa_sql.listPrint(
        owa_sql.init(
        'select book_name, book_name,
            decode( sign(instr( :x, ''/''||book_name||''/'' )),1,1,null)
           from books', ':x', l_tmp ),
        'p_my_list', 5, TRUE );

    htp.formSubmit;
    htp.formClose;
end;

there you go -- just build a string that has all of your selections in it and
use the builtin function....  You don't need tons of procedural code to do this.

March 10, 2002 Munzer -- Thanks for the question regarding
      "oracle date format", version 8.1.7

      You Asked
Tom:

1. I created a date query and never got the results expected.

I have a table that has a column "Creation_Date" and I inserted several records
into the table with a date of '01-MAR-2002'.

Then afterwards I tested 3 queries:

SELECT * from table where creation_date='01-MAR-2002' or
SELECT * from table where creation_date=sysdate or
SELECT * from table where creation_date=max(creation_Date)

and did not receive any rows.  I assume that when you specify a date it is
searching all records that have a creation_date of 01-mar-2002 00:00:00 and
23:59:59.

When I changed the date to 01-FEB-2002 and did the query for 01-FEB-2002 it
retrieved the records.

What do you think is happening.

2.  WHEN you do "SELECT SUM(quantity) from table where state='VA' and there are
no records for VA does it raise "NO-DATA-FOUND" exception with a group function.

3.  I have a created a dropdown list for a text field using owa_util.listprint.
WHen I inclide this dropdown in a loop to repeat it 200 times, the page starts
looking funky.

You can probably see it in your browser too.  Is it caused by memory limitation
or HTML.

There is nothing wrong with the code becuase it works fine for like a 50
dropdowns.

Thanks,

      and we said...
1) your assumption is wrong.  You need to query:

select *
  from table
  where creation_date between TO_DATE( '01-mar-2001', 'dd-mon-yyyy' )
                and TO_DATE( '01-mar-2001 23:59:59', 'dd-mon-yyyy hh24:mi:ss' )

If you are storing DATES with times (eg: you insert sysdate), then the date will
have the date AND time.  If you just want the date, you must insert
TRUNC(sysdate), that'll strip off the time and the date will be stored as
'01-mar-2002 00:00:00'.

Words of advice:

o compare strings to strings, dates to dates, numbers to numbers.  DO NOT
compare strings to dates, numbers to strings, etc.  ALWAYS be explicit and
convert the datatypes to the proper datatypes to avoid confusion and ambiguities

o ALWAYS use a date format in "real" code.  do not rely on the DEFAULT date
format -- you never know when someone will change that default on you!

2) An aggregate with no GROUP BY will ALWAYS return a single row.

select count(*) from t where state = 'VA';

returns exactly one record.

select count(*) from t where state = 'VA' group by state;

will return 0 or 1 records -- if VA doesn't exist, no rows.  If VA exists -- one
row.

3) it would be a browser bug.  200 drop down lists is a tad excessive.  An end
user would NEVER be able to use such a form in real life.
Review &
      Followup

      Rating: 5
oracle date
      format  March 02, 2002
Reviewer:  Munz
       from Reston, VA
Tom:

Thanks.  I will always use 'trunc(sysdate)' when I insert into creation date.

so when you query 'where cretion_date='01-MAR-2002' you are really querying a
string nt a date.
it should always be preceded by to_Date('01-mar-2002,'dd-mon-yyyy');

2. in the application i am creating a total number of iems in inventory by
adding total +(total received - totalshipped)

I have 3 statemenets:

select sum(qty) into total from table1 where item_number='aaa';

I initialized the variables.  However if the first select did not find records,
I see that it is not continuing on with the next two statements and it is not
giving me a result.

select sum(qty_received) into total from table2 where item_number='aaa';

select sum(qty_shipped) into total from table3 wher item)number='aaa';

net_total=total + (total_received - total_shipped);

retund net_total;

3. The form is an update form for a list of transactions a user created within a
time frame.  so you can do a search and get 200 or more records and user can
update a spreadsheet like form and then save it.  But HTML is nto wroking well
with many dropdowns.

          Followup:

no, when you query:

where creation_date = '01-mar-2002'

it will be the same as:

where creation_date = to_date( '01-mar-2002' )

but here you are relying on the DEFAULT date format to be in place and as we all
know, defaults are just defaults and may be changed -- hence your app that works
today might not work tomorrow when and if someone changes the default. Also, by
explicitly using TO_DATE, you are being very clear as to what exactly you are
doing (comparing a date to a date).

So, yes, you should use  to_Date('01-mar-2002,'dd-mon-yyyy') to ensure
correctness.

2) It IS continueing on -- the first SELECT does always find records -- it'll
just return a single record with NULL in it if there are no matches.  If it
didn't find any records -- it would FLING an exception.  That query however is
assured to return at least one and at most one record each and every time --
even if table1 is empty.

Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( qty_received int,
qty_shipped int, qty int );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_qty int;
  3      l_qty_received int;
  4      l_qty_shipped int;
  5      l_net_total int;
  6  begin
  7      select sum(qty) into l_qty from t where 1=0;
  8      select sum(qty_received) into l_qty_received from t where 1=0;
  9      select sum(qty_shipped) into l_qty_shipped from t where 1=0;
 10
 11      l_net_total := l_qty + ( l_qty_received - l_qty_shipped );
 12
 13      if ( l_net_total IS NULL )
 14      then
 15          dbms_output.put_line( 'net_total is NULL' );
 16      else
 17          dbms_output.put_line( 'net_total is ' || l_net_total );
 18      end if;
 19  end;
 20  /
net_total is NULL

PL/SQL procedure successfully completed.

so you see -- one of qty, qty_received, qty_shipped is NULL (in this case, all
three are.

So, one "fix" is as thus:

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_qty int;
  3      l_qty_received int;
  4      l_qty_shipped int;
  5      l_net_total int;
  6  begin
  7      select sum(qty) into l_qty from t where 1=0;
  8      select sum(qty_received) into l_qty_received from t where 1=0;
  9      select sum(qty_shipped) into l_qty_shipped from t where 1=0;
 10
 11      l_net_total := nvl(l_qty,0) + ( nvl(l_qty_received,0) -
nvl(l_qty_shipped,0) );
 12
 13      if ( l_net_total IS NULL )
 14      then
 15          dbms_output.put_line( 'net_total is NULL' );
 16      else
 17          dbms_output.put_line( 'net_total is ' || l_net_total );
 18      end if;
 19  end;
 20  /
net_total is 0

PL/SQL procedure successfully completed.

it seems you want to treat NULL in this case as ZERO, nvl will do that. A
different, less code intensive way would be simply:

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_net_total int;
  3  begin
  4      select nvl(qty,0) + ( nvl(qty_received,0) - nvl(qty_shipped,0) )
  5        into l_net_total
  6        from ( select sum(qty) qty from t where 1=0 ),
  7             ( select sum(qty_received) qty_received from t where 1=0 ),
  8             ( select sum(qty_shipped) qty_shipped from t where 1=0 );
  9
 10      if ( l_net_total IS NULL )
 11      then
 12          dbms_output.put_line( 'net_total is NULL' );
 13      else
 14          dbms_output.put_line( 'net_total is ' || l_net_total );
 15      end if;
 16  end;
 17  /
net_total is 0

PL/SQL procedure successfully completed.

let SQL do the work for you.

3) come up with a different design.  I cannot imagine have 200 drop downs and
being able to do anything with it.  I cannot imagine how BIG (in bytes) that
page must be.  Much much better to change your design to something more "web
like".  A spreadsheet is great if you install excell.  A browser is NOT a
spreadsheet.

      Rating: 5
date
      format  March 10, 2002
Reviewer:  Munz
       from Reston, USA
Tom:

Is this correct.

I need to store the time with the date in the "Creation Date" field.

SO, the only way for me to search through this column for records created on a
specific day is by:

1.  select * from table where trunc(creation_Date)="03-03-2002";

or
2. select * from table where creation_date is between
TO_DATE( '03-mar-2002', 'dd-mon-yyyy' )
                and TO_DATE( '03-mar-2001 23:59:59', 'dd-mon-yyyy hh24:mi:ss')

Am I correct.  I guess the first one is easier and will always get the results.

2.  You always refer to online documentation for roacle that has about 24
chapters.  IS this also published in a book that I can buy.

Thank you,

          Followup:

1)
(2) is the proper way.  (1) is the bad way.

Use (2), not (1).  (1) will in general preclude the use of an index on the
creation_date field.

I prefer to use:  where DT between trunc(:some_bind_variable) and
                                   trunc(:some_bind_variable)+1-1/24/60/60;

2) if you mean the server concepts guide -- you can download it from technet
right now.  Just goto
http://otn.oracle.com/
-> documentation.

March 11, 2002 atul -- Thanks for the question regarding
      "Tablespace Fragmentation", version 8.0.4

      You Asked
Hi....,

Regarding tablespace fragmentation..
You can coalsece tablespace if u have free adjucent extents..

I use following script to check for adjacent free extent to coalesce..

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

create table Col_space (
 TABLESPACE_NAME        CHAR(30),
 CONTIGUOUS_BYTES       NUMBER)
/

declare
  cursor query is select *
          from dba_free_space  where tablespace_name='TEST_IND_L'
                  order by tablespace_name, block_id;
  this_row        query%rowtype;
  previous_row    query%rowtype;
total           number;

begin
  open query;
  fetch query into this_row;
  previous_row := this_row;
  total := previous_row.bytes;
  loop
 fetch query into this_row;
     exit when query%notfound;
     if this_row.block_id = previous_row.block_id + previous_row.blocks then
        total := total + this_row.bytes;
        insert into col_space (tablespace_name)
                  values (previous_row.tablespace_name);
     else
        insert into col_space values (previous_row.tablespace_name,
               total);
        total := this_row.bytes;
     end if;
previous_row := this_row;
  end loop;
  insert into col_space values (previous_row.tablespace_name,
                           total);
end;
/

set pagesize 60
set newpage 0
set echo off
ttitle center 'Contiguous Extents Report'  skip 3
break on "TABLESPACE NAME" skip page duplicate
spool contig_free_space.lis
rem
column "CONTIGUOUS BYTES"       format 999,999,999
column "COUNT"                  format 999
column "TOTAL BYTES"            format 999,999,999
column "TODAY"   noprint new_value new_today format a1
rem
select TABLESPACE_NAME  "TABLESPACE NAME",
       CONTIGUOUS_BYTES "CONTIGUOUS BYTES"
from SPACE_TEMP
where CONTIGUOUS_BYTES is not null
order by TABLESPACE_NAME, CONTIGUOUS_BYTES desc;

select tablespace_name, count(*) "# OF EXTENTS",
         sum(contiguous_bytes) "TOTAL BYTES"
from col_space
group by tablespace_name;

spool off

drop table col_space

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

Is that correct...

Output of this is,

TABLESPACE NAME                CONTIGUOUS BYTES
------------------------------ ----------------
TEST_IND_L                             1,032,192
TEST_IND_L                             1,032,192
TEST_IND_L                             1,032,192
TEST_IND_L                             1,032,192
TEST_IND_L                             1,032,192
TEST_IND_L                             1,015,808
TEST_IND_L                             1,015,808
TEST_IND_L                             1,015,808
TEST_IND_L                               999,424
TEST_IND_L                               999,424
TEST_IND_L                                65,536
TEST_IND_L                                65,536
TEST_IND_L                                49,152
TEST_IND_L                                32,768
TEST_IND_L                                32,768
TEST_IND_L                                32,768
TEST_IND_L                                32,768
TEST_IND_L                                32,768

262 rows selected.

                            Contiguous Extents Report

TABLESPACE_NAME                # OF EXTENTS  TOTAL BYTES
------------------------------ ------------ ------------
TEST_IND_L                               282  272,683,008

Initial extent of the tablespace 1M...
and u can see many small extents < 1M..

So by this Can i be benefial by using..

"ALTER TABLESPACE TEST_IND_S COALESCE"

2)if u want to resize tablespace down ie from 200m to 100m
Should we have to calculate HWM?

tHANKS.
ATUL

      and we said...
1) you know what -- I would just issue "alter tablespace test_ind_s coalesce".
Less filling, tastes great.  Rather then over analyze the problem with lots of
code, just coalesce it and let the database do the work.

If there was nothing to do, nothing will be done.  If there were some to be
coalesced -- it would.

2) search this site for maxshrink.  I have a script that shows you the smallest
you can make any given datafile (datafile, NOT tablespace, you resize files, not
tablespaces)
Review & Followup

      Rating:
      4
Resize  March 11, 2002
Reviewer:
      Atul  from india
Thanks for your quick response..

I read your maxshrink script..
It's really great..

Some of my douts are..

1)If from ur scripts i get HWM=20M for file "test.dbf"
so that, by keeping some space for future use i can resize that upto say 25M,but
will that give me UNIX space..
Because in VMA i have noticed that it will not.(i don't know is this a bug or i
am missing something)

2)Again i have read that resize down may cause CORRUPTION
How much truth in that?

3)If my operation failed during resize command what would be that state of that
datafile?

          Followup:

1) no idea what "vma" is.

2) i've not seen it myself.  if you are concerned, make sure you have good
backups in place (that should be a GIVEN in all environments)

3) not sure what you mean -- but all DDL statements are recoverable.  The
database is designed to be resiliant that way.

March 11, 2002 Hamid -- Thanks for the question regarding
      "Unicode", version 8.1.6

      You Asked
Hi Tom,

Thanks you for your previous answer. My next question is:

We are planning to set up a database which can be accessed by
people throughout the world in many different languages. I want
to know if  Oracle supports Unicode and what is the best source
to go about this as far as the database is concerned.

Many thanks.

Hamid

      and we said...
Yes, we support UNICODE.  You would probably be creating a UTF8
database though.  See

http://technet.oracle.com/doc/oracle8i_816/server.816/a76966/toc.htm
for details in this area.
Review & Followup

      Rating:
      3
  March 15, 2001
Reviewer:  Hamid
       from London (UK)

      Rating:
      3
Unicode  March 15, 2001
Reviewer:
      Hamid  from London (UK)
I want to have a database in Unicode, within this database I want to create say
a table with data (Text)stored in it also in Unicode. Now can people throught
the world see the text in thier own languages and make sense of it. if not how
can something like this be implemented.

Many thanks Tom.

      Rating:
      2
Unicode  March 11, 2002
Reviewer:
      Donald Holliday  from Colorado Springs, CO USA
Sure would be nice if the reply included a link to a white paper with
compilable, working code rather than the typical link to an Oracle manual table
of contents.

          Followup:

Warning <rant = on>

well donald, if more people read the docs, I wouldn't get so many questions.
Then I wouldn't have to post links to the docs.

If you browse around on this site, I think you'll find I try to answer things
that aren't spelt out clearly in the docs with liberal examples.  On the whole,
I post links to docs when the docs clearly and properly answer the question.

Everybody races to condem the docs -- wonder how many people have actually READ
them?  The concepts guide for example -- totally excellent, wonderful book.
People pay $40/$60 USD for books by others that aren't 1/2 as good yet never
bother to read the concepts guide (a document I do tend to link to frequently
here).

The NLS stuff -- most people don't even bother to read the list of documents we
ship -- don't bother to find we have a document that covers this NLS stuff front
to back.  I'm not going to reproduce the wheel here, I'll be glad to point out
where the wheel exists when proper.

<rant = off>

      Rating: 4
Docs
      good  March 11, 2002
Reviewer:  Jim Kennedy
       from Willsonville, OR USA
Love it that you can carry the docs around on a CD.  I have found that if you
take some time getting used to what is in what "book" and get familiar with the
TOC (table of contents) then you can find information quite easily.  In fact,
often better than having a search engine look for you.  Carrying a CD around or
having a copy on your hard disk is so much easier than lugging the number of
books around!


Subject | Article | Last Updated (East coast time)
Date format when a user logs in using an application from a client computer | article only | Mar 12, 2002 07:53 am
Data Type | article only | Mar 12, 2002 08:02 am
Procedures calling each other ! | article only | Mar 12, 2002 04:37 pm
query with like '...._....' where _ is not a wildcard | article only | Mar 12, 2002 04:58 pm
Hiding Table Object Typing | article only | Mar 12, 2002 06:06 pm
index fragmentation | article only | Mar 13, 2002 06:57 am
Materialized Views & DBA Account ! | article only | Mar 13, 2002 07:09 am
Sorting a PL/SQL Nested Table | article only | Mar 13, 2002 07:16 am
Negative Hit Ratio! | article only | Mar 14, 2002 08:23 am
DB Link Working | article only | Mar 14, 2002 09:14 am
explain,sql_trace and tkprof | article only | Mar 14, 2002 10:12 am
Dynamic Query -- using sqlplus to run various reports driven by a table | article only | Mar 14, 2002 10:49 am
Disabling subquery evaluation | article only | Mar 14, 2002 12:54 pm
SQL Question with NVL function | article only | Mar 14, 2002 12:58 pm
Count using intermedia | article only | Mar 14, 2002 01:11 pm
dba_tab_modifications | article only | Mar 14, 2002 04:13 pm
Looping | article only | Mar 14, 2002 04:40 pm
Time Span Estimation | article only | Mar 15, 2002 01:17 pm
Using a variable in the sql statement in the from clause | article only | Mar 15, 2002 01:22 pm
Web Log Connection Pooling Log | article only | Mar 15, 2002 01:26 pm
displaying the results of procedures in SQLPlus | article only | Mar 15, 2002 01:29 pm
TIME FUNCTIONS | article only | Mar 15, 2002 03:15 pm
is this a bug or I am wrong? | article only | Mar 16, 2002 08:44 am
Convert blob to varchar2 | article only | Mar 17, 2002 10:29 am
Table Access | article only | Mar 17, 2002 11:28 am
Dimensions | article only | Mar 17, 2002 11:36 am
data replication | article only | Mar 17, 2002 11:49 am
Selecting first n records after using order by | article only | Mar 17, 2002 11:54 am
Oracle Forms- selecting everything through a block, and number of records are displayed set to 10, are the records selected everytime the down arrow key is presses. | article only | Mar 17, 2002 08:51 pm
SQL Query | article only | Mar 18, 2002 07:47 am
table partition | article only | Mar 18, 2002 08:23 am
Analyze Command | article only | Mar 18, 2002 08:30 am
Automating Data Loading | article only | Mar 18, 2002 08:51 am
Index & Free.sql | article only | Mar 18, 2002 08:56 am
Index | article only | Mar 18, 2002 08:59 am
Reallocate Cluster | article only | Mar 18, 2002 09:23 am
Wrap and dynamic SQL | article only | Mar 18, 2002 02:11 pm
Create New Index for a query degrades Other queries | article only | Mar 18, 2002 02:31 pm
SQL Server 7.0 conversion | article only | Mar 18, 2002 09:13 pm
row(s) 1 - 39 of 39
March 12, 2002 Francisco -- Thanks for the question regarding "Date format when a user logs in using an application from a client computer", version 8.0.6
You Asked
Hi Tom:

I have my initORCL.ora with nls_date_format set, and is being ignored. I checked
your FAQ
and found the following on your Knowledge database:

CREATE OR REPLACE TRIGGER DATA_LOGON_TRIGGER
AFTER LOGON
ON DATABASE
BEGIN
    EXECUTE IMMEDIATE
        'ALTER SESSION SET NLS_DATE_FORMAT = ''yyyymmdd'' ';
END;
/

This will be perfect but it doesn't work on Oracle 8.0.6 on NT. Is there
something similar that I can implement in this oracle version every time a user
logs in
using an application from a client computer?

Thanks,

Francisco
and we said...
No there is not -- you'll need to either

o have the client set the NLS_DATE_FORMAT in their registry (windows) or
environment.

o have the application issue "alter session ...." right after connecting.

Logon events are new with Oracle8i release 1 and up and are not available prior
to that.
Review & Followup
Rating: 4
Date format when a user logs on Oracle 8.0.6 March 12, 2002
Reviewer: Francisco Perez from Wantage, New Jersey, USA

March 12, 2002 Aamer -- Thanks for the question regarding "Data Type ", version 8.0.5
You Asked
I want to save paragraphs of data in database.
I am unable to find the approperiate datatype for inserting large volume of data
in a column.
and we said...
CLOB is the datatype you are looking for.  do NOT use LONG.

This is a pointer to the 8i docs, but 90% of what is written in here applies to
8.0 as well:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76940/toc.htm
...
March 12, 2002 arjun -- Thanks for the question regarding "Procedures calling each other !", version 8.1.5
You Asked
Hi Tom,
       To tell u the truth, this is not a problem that i have faced in my work
coz the requirement is absurd but was asked this time and again at interviews
and couldnot find a answer to it my self..

1)Can i create a procedure 'a' which in turn would be calling
  procedure 'b' which has to call 'a'..???

2) If say the procedures are already there and for some reason,any one of them
gets invalid status..can i run them successfully?? since it is calling each
other, the procedures dont get compiled and the session hangs when i try to
compile one of them which is invalid !

Hope this hasnt irritated you..Sorry if it has !
Thanx for your time and knowledge !!.
Bye,
Arjun
and we said...
1) yes you can (but why).

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package demo_pkg
  2  as
  3          procedure a;
  4          procedure b;
  5  end;
  6  /

Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package body demo_pkg
  2  as
  3
  4  g_cnt number := 0;
  5
  6  procedure a
  7  is
  8  begin
  9          g_cnt := g_cnt+1;
 10          b;
 11  end;
 12
 13  procedure b
 14  is
 15  begin
 16          if ( g_cnt >= 5 )
 17          then
 18                  g_cnt := 0;
 19                  return;
 20          end if;
 21          a;
 22  end;
 23
 24
 25  end;
 26  /

Package body created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec demo_pkg.a

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec demo_pkg.b

PL/SQL procedure successfully completed.

you can create them as standalone procedures but that would be even dumber...

2) Ahh, they didn't want to use packages (guess I would lose on this interview,
they want you to use the wrong answer from the beginning).

Well, I'd still question the question.  suppose I answered (instead of using the
package approach above) with:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure b(g_cnt in
number)
  2  as
  3  begin
  4          if ( g_cnt >= 5 ) then
  5                  return;
  6          end if;
  7          execute immediate 'begin a(:x); end;' using g_cnt+1;
  8  end;
  9  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure a(g_cnt in
number)
  2  as
  3  begin
  4          execute immediate 'begin b(:x); end;' using g_cnt+1;
  5  end;
  6  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec a(0);

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec b(0);

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> @invalid
ops$tkyte@ORA817DEV.US.ORACLE.COM> break on object_type skip 1
ops$tkyte@ORA817DEV.US.ORACLE.COM> column status format a10
ops$tkyte@ORA817DEV.US.ORACLE.COM> select object_type, object_name, status
  2  from user_objects
  3  where status = 'INVALID'
  4  order by object_type, object_name
  5  /

no rows selected

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter procedure a compile;

Procedure altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> @invalid
ops$tkyte@ORA817DEV.US.ORACLE.COM> break on object_type skip 1
ops$tkyte@ORA817DEV.US.ORACLE.COM> column status format a10
ops$tkyte@ORA817DEV.US.ORACLE.COM> select object_type, object_name, status
  2  from user_objects
  3  where status = 'INVALID'
  4  order by object_type, object_name
  5  /

no rows selected

Here, A and B are not dependent on eachother still -- so they lose, what they
are trying to ask is still not answered in the way they were looking for (I win
the interview ??)

If you do create procedures A and B dependent on eachother as standalone
procedures, they will NEVER be valid -- they will NEVER run -- they will ALWAYS
be invalid -- forever.

You haven't irritated me -- nonsense interview questions, thats a different
story!
March 12, 2002 Lim -- Thanks for the question regarding "query with like '...._....' where _ is not a wildcard", version
You Asked
I wish to query the object name start with S_ in
DBA_OBJECTS, so I issue the SELECT statement as below,
select object_name
from dba_objects
where object_name like 'S_%';

But what I get is all the objects start with S.

Can you please tell me why this happen, and to issue the SQL
statement to get what I want.

Tks.
and we said...
% matches 0, one or more of ANY character.
_ matches any ONE character (eg: where X like 'A_B' is the same
as where X like 'A%B' and length(x) = 3)

you can code:

select * from dba_objects where object_name like 'S\_%' escape
'\';

you get to pick the escape character and by placing it in front
of _ or %, you can make them be 'normal' characters.
Review & Followup
Rating: 5
Quick follow up March 12, 2002
Reviewer: Doug from CT, USA
Is there anyway to specifiy alphanumeric ranges? Like [a-d]
or [a-z] to indicate an alphanumeric character?

Followup:
Not with like, see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2200894550208
for an approach to this

March 12, 2002 Steve -- Thanks for the question regarding "Hiding Table Object Typing", version 8.1.6
You Asked
Tom, thanks for your help.  I want to create a general trigger to audit certain
table changes in OLTP Production because I have heard that turning on Oracle
auditing adds alot of overhead.  Specifically 5 or 6 tables need auditing out of
5000 in the database.  And these are configuration tables not transaction
tables.  Anyway I'd like to create an audit table that uses a ref object for the
table data row
Create table cv_audit(
audit_timestamp date not null default sysdate,
table_name varchar2(30) not null,
dml_action varchar2(15) not null,
row_values REF table_row )
and
Create type table_row as OBJECT (
table_name varchar2(30),
table_column REF table_columns)
and
then something like tablename%ROWTYPE to define the columns.  I can't quite see
how to do this next step.  Can you give me a push?
Thanks in Advance, Steve
and we said...
ARG -- turning on Oracle auditing doesn't add lots of overhead, it adds ENOUGH
overhead in order to audit the data.

You want to see overhead?  Add tons of triggers to do what could be done in
optimized, compiled C code (native auditing that is)....

Rather then relying on mythology handed down by generation after generation --
benchmark it.  Get a test system, run up a load, measure how you are doing.
Now, turn on auditing and do it again...

Now, you may discover that auditing doesn't meet your needs but I cannot allow
the myth to propagate (i have YET to meet someone who says that that has
actually  TRIED it!)

You cannot use %ROWTYPE for the columns -- its a chicken and egg.  You are
asking to define a type on a table that is defined on a type.  In any case,
types cannot be defined on existing tables.  Tables can use types, not the other
way around.

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:59412348055
for an idea that might help you
Review & Followup
Rating: 5
Thanks for the "ARG"! March 12, 2002
Reviewer: Steve Meller from Atlanta, GA USA
Tom,
I agree with your comment on benchmarking.  Like many people, I often accept
anecdotal evidence without challenge.  We only need to audit configuration table
changes to be able to generate reports for government audits.  I thought it
would be lower maintenance to use a generic audit as opposed to many
table-specific ones.  These triggers will fire rarely, order of once a day for
each table.
Thanks for all your work on this website, I consult it regularly.

March 13, 2002 kathy -- Thanks for the question regarding "index fragmentation", version 8.1.6
You Asked
Hi Tom,

i have a question about the index fragmentation, on the famous white paper "How
to stop defragmenting and start living: the definitive world of fragmentation"
by Bhaskar Himatsingka and Juan Loaiza, on section 4.2, they provide a script to
tell if the index can reduce a blevel, the procedure is this:
1.) analyze index index_name validate structure
2.) select name  NAME,
    (br_rows_len*100)/(br_blk_len*br_blks) BRANCH_UTILIZATION,
    ((lf_rows_len - del_lf_rows_len)*100)/ (lf_blk_len*lf_blks) LEAF_UTILIZATI,
    decode (sign(ceil(log(br_blk_len/(br_rows_len/br_rows),
            lf_blk_len/((lf_rows_len - del_lf_rows_len)/(lf_rows - del_lf_rows)
           +1 - height), -1,'YES','NO') CAN_REDUCE_LEVEL
  from index_stats;

i have a couple of indexes, after run the script, "CAN_REDUCE_LEVEL" shows yes,
after i did a "alter index xxx rebuild ...", the flag still shows yes, why the
"height" didn't reduce after "rebuild" ?
and we said...
I think the operative word here is "CAN_REDUCE_LEVEL" -- it is CAN, not
"will".

This query is (as they say) based on 'the formula existent in most database
textbooks on how to calculate expected optimal height of the
tree based on the statistics'

EXPECTED optimal height
^^^^^^^^

It is a query to help you locate candidates, it does not assure you "you will
lower the height".

My opinion -- 99.9% of all reorgs, rebuilds, etc are a total and utter waste of
time and energy.  We spend way way way too much time losing sleep over this
non-event.

If you are going to spend time on this exercise -- make sure you come up with a
way to MEASURE what you've just done in some quanitative fashion you can report
to your mgmt (eg: these rebuilds I spend X hours a week doing save us from doing
X IO's every day, or let us do Y more transactions then otherwise possible, or
.....)   No one, but no one, seems to do that (keep metrics).  They just feel
"it must be better".  Who knows -- you may actually be DECREASING performance!!
(you'll never know until you measure)
Review & Followup
Rating: 4
Excellent March 12, 2002
Reviewer: pawan from USA
I think Tom has gone over this atleast 10 times and I totally agree with him. I
used to awake at 2 AM reorganizing Indexes and never kept any metrices so am not
sure whether it helped. My question though is if there exists with Tom a
script/method to gather statistics on  a regular basis ( before and after).

Thanks

Followup:
things like statspack (IO's and waits), application logs (showing transactions
processed, response times), tkprof reports are what you need.

Rating: 5
LMT March 13, 2002
Reviewer: Dusan Valasek from Slovakia
"How
to stop defragmenting and start living: the definitive world of fragmentation" -
there is the only answer: Start living in Locally Managed Tablespaces.

March 13, 2002 OP -- Thanks for the question regarding "Materialized Views & DBA Account !", version 8.1.6
You Asked
Hi Tom,

Hope you are Enjoying Life !

My Question is

Is it possible to do DML operations on a Materialized
View  ?

Through Enterprise Manager We can check the GRANT
WITH ADMIN option for a User.What is the equivalent
command line for this option.

I have created a account OP Then I wrote

Alter user OP
Grant default role DBA;

After this command when I try to give some priveleges
to some users on certain objects,I am not able to do
it ?

What is the solution to this ? Kindly Help!

Regards
OP
and we said...
It is possible but then we would consider it an UPDATEABLE
snapshot.  You would be doing replication at that point.  You
would want to read the distributed systems and replication guide
for implementation details.  It would not be a materialized view
so much as it would be an updateable snapshot.

grant <system priv> to <user> with admin option;

would be the equivalent command line option.

For the alter user, when you granted DBA, you would need to
supply me an example (otherwise I have no idea truely what you
are doing).   It sounds like you have a user OP and some other
user "X".  "X" owns a table T and OP is trying to

  grant select on X.T to Y;

and it fails.  In order for OP to be able to grant on X.T, OP
would have had to been granted select on X.T WITH GRANT OPTION;
There is no system privelege that lets a user grant another
users table without having been granted the grant option on that
table as well.
Review & Followup
Rating: 3
what previleges are needed ? March 12, 2002
Reviewer: reader
Hi tom,

  to create a Materialized view what exeplicit previleges are need ?

  I am trying to create a simple Materialized in a "test" schma I have given
sysdba previleges to test
the table on which I am creating a Materialized view is also
in the test schma than also it is gives some error
(not sufficient previlages) from enterprice manager

 can you plese explicitley tell me what are the previlages needed to create
marterialized veiw

  also can you tell me if I want to create a materialized view based on db link
do i need any previleges on the production site ?

Thanks...

Followup:
arg -- DON'T use SYSDBA to "test" with.  SYSDBA is special, don't use it except
to manage the database!!!

to find what privs you need, please see the SQL ref manual under CREATE
MATERIALIZE VIEW.  There is a prereqs section (as most commands have) that list
these.

As for the dblink -- you need the privs to access the base table on the other
site and you may need read/write access on the mv log if you are using fast
refreshes.

March 13, 2002 Peter -- Thanks for the question regarding "Sorting a PL/SQL Nested Table", version 8.1.7
You Asked
Hi Tom,

I understand that a nested table can be sorted in the following manner:

CREATE OR REPLACE type myTableType AS TABLE OF NUMBER;
/

DECLARE
   x_partner_tbl myTableType := myTableType(8, 9, 23, 4, 5);

BEGIN
   FOR x IN (
      SELECT * FROM TABLE (CAST(x_partner_tbl AS myTableType))
      ORDER  BY column_value DESC)
   LOOP
      dbms_output.put_line(x.column_value);
   END LOOP;
END;
/

I have two questions relatting this:

(1). Is there anyway that I can display the index of the table in the
     same SQL?

(2). Is it possible to do the sorting for a nested table of records?

Thanks

- peter
and we said...
1) rownum will do that.

ops$tkyte@ORA817DEV.US.ORACLE.COM> DECLARE
  2     x_partner_tbl myTableType := myTableType(8, 9, 23, 4, 5);
  3
  4  BEGIN
  5     FOR x IN (
  6        select *
  7          from (select rownum idx, column_value
  8                                  from (SELECT column_value
  9                          FROM TABLE (CAST(x_partner_tbl AS myTableType)))
 10                           )
 11          ORDER  BY column_value DESC)
 12     LOOP
 13        dbms_output.put_line(x.idx || ' ) ' || x.column_value);
 14     END LOOP;
 15  END;
 16  /
3 ) 23
2 ) 9
1 ) 8
5 ) 5
4 ) 4

PL/SQL procedure successfully completed.

2) sure, just declare an object type (that is your record) and a nested table of
that type.  Fill it up and do exactly what you did above.
see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:246014735810
...
Review & Followup
Rating: 5
March 10, 2002
Reviewer: Peter
Incredibly useful as always!

Rating: 3
Wrong order ? March 11, 2002
Reviewer: Sasa from Belgrade, Yugoslavia
Hi Tom,
I run your example(1) and got next result:

2 ) 9
1 ) 8
5 ) 5
4 ) 4
3 ) 23

So what's the reason  for difference?

Followup:
Looks to me like you created your MyTableTypa as a table of varchar2's -- not
numbers.  That sorted "stringwise" from big to small.

ops$tkyte@ORA817DEV.US.ORACLE.COM> CREATE OR REPLACE type myTableType AS TABLE
OF varchar2(20);
  2  /

Type created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> DECLARE
  2     x_partner_tbl myTableType := myTableType(8, 9, 23, 4, 5);
  3
  4  BEGIN
  5     FOR x IN (
  6        SELECT * FROM TABLE (CAST(x_partner_tbl AS myTableType))
  7        ORDER  BY column_value DESC)
  8     LOOP
  9        dbms_output.put_line(x.column_value);
 10     END LOOP;
 11  END;
 12  /
9
8
5
4
23

PL/SQL procedure successfully completed

Rating: 4
Wrong orde - Sorry, my fault March 11, 2002
Reviewer: Sasa from Belgrade, Yugoslavia
Hi Tom,

I said in a last review that I run your example and got "wrong order".Actually
it was my fault because I created myTabletype as a table of varchar.
After I created table type as a table of number everything worked perfect.
Sorry again

Rating: 4
ROWNUM not strictly the SUBSCRIPT March 11, 2002
Reviewer: Adrian from UK
Tom

I responded in exactly the same way (use ROWNUM, TABLE and CAST) to a similar
question on determining the subscript of a PL/SQL table on the PL/SQL Pipeline
and it was promptly followed up by the SYSOPS that ROWNUM is NOT the same as the
subscript when we have sparse index-by tables. Which I have to agree with,
though on NTs and VARRAYs I was, like you, 100% correct in the workaround :)

Regards

Adrian

Followup:
My response to the SYSOP -- so what?

You cannot select from plsql index by tables so it is a NON ISSUE.  Show me
an example with ROWNUM and a PLSQL index by table (they cannot, it cannot be
done).

You can only select from SQL Collection Types, so -- it is a 100% solution for
all cases.

Rating: 5
March 11, 2002
Reviewer: Peter
Hi Tom,

I tried the solution you suggested for question #2. It worked pretty well.
However, is there anyway this can be done without using an object type? If not,
can an object type be declared within a PL/SQL package much the same way a
PL/SQL table is declared?

Thanks

Followup:
No, in order to use a type in SQL, SQL must be aware of the type.  PLSQL is a
layer on top of SQL, not the other way around.  Hence the type must be defined
as a SQL type using the CREATE OR REPLACE TYPE command.

Otherwise, SQL quite simply cannot "see" the type.

Rating: 4
What about PLSQL TYPES in 9i ? March 12, 2002
Reviewer: Adrian Billington from UK
Tom

Can expressions such as CAST work on PL/SQL types in 9i where as I understand
it, the SQL and PLSQL parsers are one and the same?

Thanks

Adrian

Followup:
Nope.  Cast is a SQL function, as such it takes only SQL types.

And... even if you *could* you would have to cast it to a SQL type, hence you
would need the "create or replace type" in the end (which is what I assume you
were trying to avoid)

Rating: 3
SYSOP replies March 13, 2002
Reviewer: A reader
Well I'm afraid Adrian misquotes the post he refers to - as far as I am aware
there is no mention of PL/SQL (index-by) table in the thread at all. The
solution given was equivalent to yours, but some discussion followed over
whether this solution would work in all cases.

I would say ROWNUM is not 100% solution for returning collection index value.
Consider following situations where SELECT TABLE () is based on sparse
collection, or where it includes WHERE clause...

CREATE OR REPLACE TYPE myTableType AS TABLE OF NUMBER;

/* Sparse collection */
DECLARE
  x_partner_tbl myTableType := myTableType (8, 9, 23, 4, 5);
BEGIN
  x_partner_tbl.DELETE (2);
  FOR x IN (
     SELECT *
       FROM (SELECT ROWNUM idx, column_value
             FROM  (SELECT column_value
             FROM   TABLE (CAST (x_partner_tbl AS myTableType))))
       ORDER  BY column_value DESC)
  LOOP
    DBMS_OUTPUT.PUT_LINE (x.idx || ' ) ' || x.column_value);
  END LOOP;
END;

2 ) 23
1 ) 8
4 ) 5
3 ) 4

If we look at first result it indicates that element with index value 2 contains
value 23 (or vice versa). This is incorrect, element 2 does not exist. As you
would expect index values reported for all elements after non-existent element
are incorrect.

/* Select with where clause */
DECLARE
  x_partner_tbl myTableType := myTableType (8, 9, 23, 4, 5);
BEGIN
  FOR x IN (
     SELECT *
       FROM (SELECT ROWNUM idx, column_value
             FROM  (SELECT column_value
             FROM   TABLE (CAST (x_partner_tbl AS myTableType))
             WHERE  column_value >= 5))
       ORDER  BY column_value DESC)
  LOOP
    DBMS_OUTPUT.PUT_LINE (x.idx || ' ) ' || x.column_value);
  END LOOP;
END;

3 ) 23
2 ) 9
1 ) 8
4 ) 5

In this case, result implies that value 5 is at index position 4 in collection.
It is not.

Padders
SYSOP, Quest Software / RevealNet Labs PL/SQL Pipeline

Followup:
Very good -- the rownum solution is returning the postition in the collection,
not the "index" necessarily.

thanks for the followup.

Rating: 5
Apologies to SYSOP March 13, 2002
Reviewer: Adrian Billington from UK
My apologies to Padders (PLSQL Pipeline SYSOP) for the mis-quote. As for his
example, well we can see why he is the SYSOP and I'm not :)

Adrian

March 14, 2002 Tony -- Thanks for the question regarding "Negative Hit Ratio!", version 8.1.6
You Asked
Hi Tom,
This is regarding data warehouse query. While looking at the performance of one
of the query (processed 41 rows in 31 secs), I found that hit ratio is negative
(db block gets=> 120, consistent gets => 6712, and physical reads => 25955).
Does it mean that I have to increase sort_area_size and/or hash_area_size Or
there are other areas I need to consider also?
Thanks
1. Here is the Query and plan:
SELECT
  REPORTING_WEEK.REP_WEEK_END_DATE,
  SUM (SALE_TRANSACTION.PREMIUM_AMOUNT),
  count(SALE_TRANSACTION.REF_NUMBER || SALE_TRANSACTION.RISK_TYPE_CODE ||
        SALE_TRANSACTION.SALE_TRANSACTION_TYPE_CODE),
  LINE_OF_BUSINESS.LINE_OF_BUSINESS_DESC,
  RISK_TYPE.RISK_TYPE_DESC,
  SALE_TRANS_TYPE.SALE_TRANS_TYPE_DESC
FROM
  REPORTING_WEEK,
  SALE_TRANSACTION,
  LINE_OF_BUSINESS,
  RISK_TYPE,
  SALE_TRANS_TYPE,
  STATE  STATE_GEN,
  BUSINESS_CHANEL  BUS_CHANEL_GEN,
  PRODUCT,
  PRODUCT_RISK,
  REGION  REGION_GEN,
  SITE  SITE_GEN
WHERE
  (
SALE_TRANS_TYPE.SALE_TRANS_TYPE_CODE(+)=SALE_TRANSACTION.SALE_TRANSACTION_TYPE_CO
DE  )
  AND  ( PRODUCT.LINE_OF_BUSINESS_ID=LINE_OF_BUSINESS.LINE_OF_BUSINESS_ID(+)  )
  AND  ( PRODUCT_RISK.RISK_TYPE_CODE=RISK_TYPE.RISK_TYPE_CODE(+)  )
  AND  ( PRODUCT.SOURCE_PRODUCT_CODE(+)=PRODUCT_RISK.PRODUCT_CODE  )
  AND  ( SALE_TRANSACTION.GENERATING_BRANCH_CODE=SITE_GEN.SITE_CODE(+)  )
  AND  ( SITE_GEN.BUSINESS_CHANEL_CODE=BUS_CHANEL_GEN.BUSINESS_CHANEL_CODE(+)  )
  AND  ( SITE_GEN.REGION_CODE=REGION_GEN.REGION_CODE(+)  )
  AND  ( REGION_GEN.STATE_CODE=STATE_GEN.STATE_CODE(+)  )
  AND  ( SALE_TRANSACTION.RISK_TYPE_CODE=PRODUCT_RISK.RISK_TYPE_CODE(+)  )
  AND  ( SALE_TRANSACTION.EXTRACTION_DATE between
REPORTING_WEEK.REP_WEEK_START_DATE and REPORTING_WEEK.REP_WEEK_END_DATE  )
  AND  (
  BUS_CHANEL_GEN.BUSINESS_CHANEL_DESCRIPTION  =  'BRANCH'
       )
  AND  (STATE_GEN.DIST_VERSION = '200110')
  AND  REPORTING_WEEK.REP_WEEK_END_DATE  =
       '09-DEC-2001'
GROUP BY
  REPORTING_WEEK.REP_WEEK_END_DATE,
  LINE_OF_BUSINESS.LINE_OF_BUSINESS_DESC,
  RISK_TYPE.RISK_TYPE_DESC,
  SALE_TRANS_TYPE.SALE_TRANS_TYPE_DESC
  ;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=7923 Card=311 Bytes=
          80549)

   1    0   SORT (GROUP BY) (Cost=7923 Card=311 Bytes=80549)
   2    1     HASH JOIN (Cost=7915 Card=311 Bytes=80549)
   3    2       TABLE ACCESS (FULL) OF 'STATE' (Cost=1 Card=2
          Bytes=16)

   4    2       HASH JOIN (OUTER) (Cost=7857 Card=15544 Bytes=3901544)
   5    4         HASH JOIN (Cost=7755 Card=9478 Bytes=2312632)
   6    5           TABLE ACCESS (FULL) OF 'BUSINESS_CHANEL' (
          Cost=1 Card=2 Bytes=70)

   7    5           HASH JOIN (OUTER) (Cost=6323 Card=473895 Bytes=990
          44055)

   8    7             HASH JOIN (OUTER) (Cost=4551 Card=184844 Bytes=3
          6599112)

   9    8               HASH JOIN (OUTER) (Cost=4147 Card=56355 Bytes=
          9129510)

  10    9                 HASH JOIN (OUTER) (Cost=3963 Card=34363 Byte
          s=4020471)

  11   10                   HASH JOIN (OUTER) (Cost=3881 Card=20953 By
          tes=1697193)

  12   11                     HASH JOIN (OUTER) (Cost=3838 Card=12776
          Bytes=830440)

  13   12                       NESTED LOOPS (Cost=3813 Card=7790 Byte
          s=451820)

  14   13                         TABLE ACCESS (FULL) OF 'REPO
          RTING_WEEK' (Cost=1 Card=4 Bytes=72)

  15   13                         TABLE ACCESS (BY GLOBAL INDEX ROWID)
           OF 'SALE_TRANSACTION' (Cost=953 Card=779022 Bytes=3
          1160880)

  16   15                           INDEX (RANGE SCAN) OF 'IN_MARS_SAL
          E_20' (NON-UNIQUE) (Cost=64 Card=779022)

  17   12                       TABLE ACCESS (FULL) OF 'PRODUC
          T_RISK' (Cost=1 Card=164 Bytes=1148)

  18   11                     TABLE ACCESS (FULL) OF 'PRODUCT'
           (Cost=1 Card=164 Bytes=2624)

  19   10                   TABLE ACCESS (FULL) OF 'RISK_TYPE'
           (Cost=1 Card=164 Bytes=5904)

  20    9                 TABLE ACCESS (FULL) OF 'LINE_OF_BUSI
          NESS' (Cost=1 Card=164 Bytes=7380)

  21    8               TABLE ACCESS (FULL) OF 'SALE_TRANS_TYP
          E' (Cost=1 Card=328 Bytes=11808)

  22    7             TABLE ACCESS (FULL) OF 'SITE' (Cost=2 Ca
          rd=1146 Bytes=12606)

  23    4         TABLE ACCESS (FULL) OF 'REGION' (Cost=1 Card
          =164 Bytes=1148)

Statistics
----------------------------------------------------------
        108  recursive calls
        120  db block gets
       6712  consistent gets
      25955  physical reads
          0  redo size
       3615  bytes sent via SQL*Net to client
        519  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
         41  rows processed

2. The number of records in various tables are as below:
select count(*) from reporting_week;
  COUNT(*)
----------
      1000
select count(*) from sale_transaction;
COUNT(*)
----------
    779022
select count(*) from sale_trans_type;
 COUNT(*)
----------
       304
select count(*) from site;
  COUNT(*)
----------
      2639

All other tables have < 100 records.
3. None of the table is created with parallel option

Regards,
Tony
and we said...
Oh ok, i see now.  the physical reads include disk reads done by us to and from
the temp hash areas. They do not however get counted as consistent gets (since
they are not)

Consider this small example:

create table t as select * from all_objects;  (about 20k rows in my system)

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace on
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set hash_area_size = 10485760;
Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select /*+ use_hash( t1 t2 ) */ count(*)
  2  from t t1, t t2
  3  where t1.owner = t2.owner and t1.object_name = t2.object_name and
t1.object_id = t2.object_id;

  COUNT(*)
----------
     17158

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=26 Card=1 Bytes=94)
   1    0   SORT (AGGREGATE)
   2    1     HASH JOIN (Cost=26 Card=365 Bytes=34310)
   3    2       TABLE ACCESS (FULL) OF 'T' (Cost=7 Card=19114 Bytes=898358)
   4    2       TABLE ACCESS (FULL) OF 'T' (Cost=7 Card=19114 Bytes=898358)

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

so with a 10meg hash area size, no IO, now lets do that same query again:

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set hash_area_size = 32765;
Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select /*+ use_hash( t1 t2 ) */ count(*)
  2  from t t1, t t2
  3  where t1.owner = t2.owner and t1.object_name = t2.object_name and
t1.object_id = t2.object_id;

  COUNT(*)
----------
     17158

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=3060 Card=1 Bytes=94)
   1    0   SORT (AGGREGATE)
   2    1     HASH JOIN (Cost=3060 Card=365 Bytes=34310)
   3    2       TABLE ACCESS (FULL) OF 'T' (Cost=7 Card=19114 Bytes=898358)
   4    2       TABLE ACCESS (FULL) OF 'T' (Cost=7 Card=19114 Bytes=898358)

Statistics
----------------------------------------------------------
          0  recursive calls
         24  db block gets
        476  consistent gets
       4324  physical reads
          0  redo size
        369  bytes sent via SQL*Net to client
        430  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

and there you go -- 4,324 physical reads - but they were all done for the
hashing, not for block gets.

Play with your hash_area_size -- make it larger for this query.
Review & Followup
Rating: 5
Negative Hit Ratio! January 01, 2002
Reviewer: Tony from Australia
Hi Tom,
Excellent clarification!
However, as this query is generated from OLAP tool, can you suggest me what
should I keep in mind (like other parameters: hash_multiblock_io_count/memory
etc.) for changing hash_area_size?, and where should I draw line about its
optimal value without effecting other queries? Even suggested reading will be
helpful...
Regards,
Tony

Followup:
As with everything -- it depends.  do you have a tiny box, smaller values.  Do
you have a big box with gobs of ram but thousands of users -- smaller values.
Big box, gobs of ram, few OLAP users -- huge values.

The hash area size is the upper bound on a chunk of memory we'll use during a
query.  You have to look at how many users, how much ram, etc etc to determine
the optimal setting on YOUR system.

Rating: 5
From Radha, India January 02, 2002
Reviewer: Radhakrishnan from India
Hi Tom,
It is very useful information.

Best regards,
Radha

Rating: 4
January 02, 2002
Reviewer: Andy from Hong Kong
when I query the v$parameter, I found the following parameter:

hash_area_size 0
hash_multiblock_io_count 1

however, I found some information about the hash_area_size:

it is 2x the sort_area_size if it is not set and show 0.

is it true?

thanks!

Followup:
It is 2x the init.ora setting of sort_area_size when set to zero.

Rating: 4
Good example, but January 15, 2002
Reviewer: Marc Terzer from Switzerland
we have queries in a datawarehouse running fine with hash join. If the optimizer
change to use nested loop the runtime will increase from minutes to more than 24
hours. Yes of course a hint use_join will help, but we have 100s of queries. It
could not be the solution to set the hint use_hash on so many different queries.
Is there a method or init.ora parameter to force hash joins instead of nested
loops.

Followup:
anayze the tables -- if a hash join is called for -- it'll be used.

I used the hint cause if I didn't -- a hash join wouldn't be used with a piddly
little 32k hash area size.

You don't (and should not use) need a hint.  Just analyze the tables and run
your queries.

Rating: 2
This is not working January 17, 2002
Reviewer: Marc from Switzerland
I have analyzed all my tables. The hint /*+ ordered */ will do the same as /*+
use_hint(xxx) */. How can I force this effect, that more queries are using hash
join. By the way: the difference between hash join and nested loop is:
- Hash Join took 82 sec.
- Nested Loop took more than 5 hours for the same query !!!!
So the impact of using hash join here is verfy huge.
Many thanks for feedback.
Marc

Followup:
up your hash_area_size.

Rating: 5
January 17, 2002
Reviewer: A reader
Note from metlink

"Oracle does not recommend using the HASH_AREA_SIZE parameter unless the
instance is configured with the shared server option. Oracle recommends that you
enable automatic sizing of SQL working areas by setting PGA_AGGREGATE_TARGET
instead. HASH_AREA_SIZE is retained for backward compatibility. "

Can you explain?

Followup:
That is valid advice for Oracle9i release 9.0.1 and up (as pga_aggregate_target
is new with that release).

In 816, as they have, use hash_area_size.

Rating: 5
Negative Hit Ratio January 18, 2002
Reviewer: Judith from US from US
Excellent answer. A quick question to Tom:
How did you get the statistics data? What is the command to get the info you
displayed? Thanks

Followup:
set autotrace on

see
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a82950/ch8.htm#1001974
for details.

Rating: 4
Allocation of hash_area_size bytes March 13, 2002
Reviewer: Kapil Bidawatka from Mumbai, India
Hi Tom,
That was a good insight into hash joins.
But just a question regarding the parameter hash_area_size.
Does the server process allocate hash_area_size bytes on its start up or does it
do that only in case it encounters that a query needs a hash join?
Similarly for sort_area_size.

Kapil

Followup:
It is the same for both.  It is an upper limit, it will be allocated a bit at a
time UPTO that limit as needed.  It is not allocated at startup, its allocated
(and freed) as needed.

March 14, 2002 Nitin -- Thanks for the question regarding "DB Link Working", version 8.1.6
You Asked
Hi Tom,

Because of ur suggestion earlier my system performance has improved a lot.
thanks for that.

We are working on Distributed Database where 70% application is one server and
30% on other unix server ( Both are connected through WAN)
We are using db link and replication. Database is 30GB in size and hybrid syste
more towards OLTP

We have few tables ( which relates to accounts balances) require updation(and
select) only on the real balance and that update can be at anytime from any user
server so we can not use replication or snapshots. So we have to rely on db link
only. But performance has bad impact if we work on DB LINK. We can not upgrade
network. I have discused with this with senior DBAs till now i cant got any
help.Can u suggest something on this?

Can u explain how the db link work with network in other words if i have 9sqls
on server A and 9sqls on server B they all have to use db link how will they
proceed. Will they go for mechanism of time slicing or serialization or
something else. I have tried to find this in oracle documentation but i cannot
get enough help i want to know better the concept behind it.

Becoz i have seen queries fired at server A are in active status but dont have
active status in server B for long time.

Thanks in Advance

Regards,
Nitin
and we said...
They will not serialize, they will be processed in the same manner as any other
query.  If you see them "inactive" on the remote site, then some work is being
done on the local site.

recommendations:

o use the CBO, the CBO is much much better over a dblink then RBO.
o read the tuning/performance guide.  It explains how this all works and gives
tuning hints.
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76992/optimops.htm#5885
...
March 14, 2002 wallacel -- Thanks for the question regarding "explain,sql_trace and tkprof", version 8.1.6
You Asked
Tom:

I am doing some testing,
i created table so(request int,error_flag int,
interface int,order_source int,original int)
and insert 100,000 rows into it,
the values is (1,1,1,1,1) to
(100000,100000,100000,100000,100000);

then i created another table so_headers(original
int,order_source int,request_id int);
and then insert 10,000 record to it
from (1,1,1) to (10000,10000,10000);

then i set autotrace on;
select /*+ordered*/ so.* from so,so_headers where
so.request=so_headers.request_id;
Elapsed 00:00:07.75
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=595 Card=10000
Bytes
          =230000)

   1    0   HASH JOIN (Cost=595 Card=10000 Bytes=230000)
   2    1     TABLE ACCESS (FULL) OF 'SO' (Cost=63 Card=100000
Bytes=2
          000000)

   3    1     TABLE ACCESS (FULL) OF 'SO_HEADERS' (Cost=4
Card=10000 B
          ytes=30000)

select /*+ordered*/ so.* from so_headers,so where
so.request=so_headers.request_id;
Elapsed: 00:00:05.19

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=779 Card=10000
Bytes
          =230000)

   1    0   HASH JOIN (Cost=779 Card=10000 Bytes=230000)
   2    1     TABLE ACCESS (FULL) OF 'SO_HEADERS' (Cost=4
              Card=10000 Bytes=30000)

   3    1     TABLE ACCESS (FULL) OF 'SO' (Cost=63
               Card=100000  Bytes=2000000)

as you can see, the first one take longer time but cost lower,
the second one take shorter but cost higher.
I tried several times and got the similar result.
To me, it doesn't make sense, can you explain it to me?
BTW, I am the only user of the database and I analyze table
compute statistics before the query.

then i run sql_trace and tkprof:

select /*+ordered*/ so.*
from
 so,so_headers where so.request=so_headers.request_id

call     count       cpu elapsed   disk   query  current   rows
------- ------  -------- ------- ------- ------- -------  -----
Parse        1      0.00   0.00     0       0     0
Execute      2      0.00   0.00     0       0     0           0
Fetch      668      0.00   0.00   452     434     8       10000
------- ------  -------- ------ -------- ------- -------
-------
total      671      0.00   0.00   452     434     8       10000

select /*+ordered*/ so.* from
so_headers,so where so.request=so_headers.request_id

call   count    cpu  elapsed     disk    query  current   rows
----- -----  ------ --------- ------- -------- --------
----------
Parse     1   0.00      0.00        0        0       0      0
Execute   2   0.00      0.00        0        0       0      0
Fetch   668   0.00      0.00      304      434       8  10000
----- -----  ------ --------- ------- -------- -------
---------
total   671   0.00      0.00      304      434       8  10000

does that mean COSE in explain plan is not reliable?
and we said...
the cost in the explain is extremely reliable -- but only to the
CBO.

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:953429842098
it is basically the same question and the answer applies 100%.
Hints artificially inflate and or deflate costs associated with
various operations in an attempt to make them more desirable to
the optimizer.  It picks the plan with the lowest cost.  anytime
you introduce a hint - the cost is influenced by it.

you can NEVER compare costs across two different queries --
never.  I wish they did not expose that number, it only leads to
confusion.
Review & Followup
Rating: 4
Reader March 14, 2002
Reviewer: A reader
Tom,

If the output from the explain plan (with set autotrace on)
wraps around, it is difficult to read the IDs, not lined
up.

Tried linesize 200 from SQL>, no effect. Is there a way to
set the linesize for explain plan output

Thanks

Followup:
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> column plan_plus_exp format a20
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from dual;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Opt
          imizer=CHOOSE

   1    0   TABLE ACCESS (FULL
          ) OF 'DUAL'

ops$tkyte@ORA817DEV.US.ORACLE.COM> column plan_plus_exp format a100
ops$tkyte@ORA817DEV.US.ORACLE.COM> /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'DUAL'

ops$tkyte@ORA817DEV.US.ORACLE.COM>

Rating: 5
Reader March 14, 2002
Reviewer: A reader
Thanks Very Much

Rating: 5
Man I find almost everyting I need to know at this site October 08, 2003
Reviewer: Tom Best

March 14, 2002 Wendy -- Thanks for the question regarding "Dynamic Query -- using sqlplus to run various reports driven by a table", version 8i
You Asked
Hi, Tom:

I don't know dynamic SQL very well, but I thought this could be done in some
way.

The UNIX box will kick off a job on a daily basis. The job will call a script
file. In SQLPLUS, the script file (or from the script file to call a procedure)
will be able to spool out # of reports on the UNIX box for the users. I have a
table to store the reports request from users (table will be updated by user):

REPORT ID DIVISION  PLANT  BUILDING FLOOR ROOM
----------------------------------------------
REPORT 1  RC        SF      DAL     nd
REPORT 2  XY        XP      ORD
REPORT 3  RC        DD      LKF     01    RENTAL
REPORT 4  AD        *
REPORT 5  AB
...

The rows in this table could be 0 to any. 'REPORT ID' and 'DIVISION' can not be
null. (Division/plant will match the records from table_A, and
Building/Floor/Room will match the records from table_B, where none of these is
PK in the table.)

I would need to generate (spool out) number of daily reports depend on the
number of rows in the table, which column 2-6 will store the criteria from users
(which will run/build query) for that row of report. There should generate 5
different reports for the above table. The report's name would be build by the
actual value in the columns (for example, for report 2 in above table, the file
name would be: REPORT2_XY_XP_ORD)

The criteria stored in each row could be random. But there are some rules:
1) if a column is null, then the columns under/after that column must be null
(for instance, if building is null, then floor and room must be null).
2) a " * " means match/select all the records in the database/table for that
criteria (which I think this is the same as when it is null).

The difficult parts for me are:
- I don't know the exact query conditions/numbers when I build the WHERE clause.

- I don't know the exact report names and the number of reports that will
create. (I think spool can only hard code the file name)

All these info will be changed everyday from users. For me this table is a
unknown_number_row X unknown_number_column array (which up to 6 column).

Could you suggest a way out? Hope I explain it clear.

Thanks in advance.

Regards,

Wendy
and we said...
Here is one approach.

I would start with a table T like this:

create table t
( report_id  varchar2(10) not null,
  division   varchar2(2) not null,
  plant      varchar2(2),
  building   varchar2(3),
  floor      varchar2(2),
  room       varchar2(6)
)
/

create or replace trigger t_aifer
after insert on t for each row
begin
    if (( :new.plant is NULL  and
          :new.building || :new.floor || :new.room is NOT NULL )  OR
        ( :new.building is NULL  and
          :new.floor || :new.room is NOT NULL )  OR
        ( :new.floor is NULL  and
          :new.room is NOT NULL ))
    then
        raise_application_error( -20001, 'not null value where null expected' );
    end if;
end;
/

That satifies your "report id and division" are not null constraint and the
constraint that as soon as you hit a column with a NULL, the remaining columns
must be NULL as well....

Then, I would suggest you write a SCRIPT per report (you have 5 so its not too
many).  These scripts will take a ROWID as input and might look like this (this
is the script that takes three inputs of PLANT, BUILDING and FLOOR):

---------------- run_rep_3.sql ----------------------------------

prompt running report with 3 inputs

-- this is needed to use BIND variables which are VERY important.
alter session set cursor_sharing=force;

-- this next block gets the PARAMETER values into SQLPlus substitution
-- variables for us easily.  If the parmeter is NOT '*', we get the quoted
-- value.  Else, in this case, I am just getting the column name itself

column division new_value DIVISION
column plant    new_value PLANT
column building new_value BUILDING
column floor    new_value FLOOR

select decode(division,'*','division',''''||division||'''') division,
       decode(plant,'*','plant',''''||plant||'''') plant,
       decode(building,'*','building',''''||building||'''') building,
       decode(floor,'*','floor',''''||floor||'''') floor
  from t
 where rowid = '&1'
/

-- your query goes here --
-- this is just a sample, it selects out the row we are processing

select *
  from t
 where division = &DIVISION
   and plant = &PLANT
   and building = &BUILDING
   and floor = &FLOOR
/

-- after the query is done, get rid of the request

delete from t where rowid = '&1';
commit;
-------------------------- eof -------------------------------------

Then, the master script to run the reports might look like this.  It is quite
simply a script to write and then run a script:

----------------------------- report.sql ----------------------------
set feedback off
set serveroutput on size 1000000
set echo off
spool tmp.sql
begin
    for x in ( select rowid rid,
                 replace( report_id, ' ', '_' ) || '_' || division ||
        decode( plant, null, null, '_'||replace(plant,'*','star') ) ||
        decode( building, null, null, '_'||replace(building,'*','star') ) ||
        decode( floor, null, null, '_'||replace(floor,'*','star') ) ||
        decode( room, null, null, '_'||replace(room,'*','star') ) filename,
                 decode(plant,null,0,1)+
                 decode(building,null,0,1)+
                 decode(floor,null,0,1)+
                 decode(room,null,0,1) report_to_run
                 from t )
    loop
        dbms_output.put_line( 'set serveroutput on size 1000000' );
        dbms_output.put_line( 'spool ' || x.filename );
        dbms_output.put_line( '@run_rep_' || x.report_to_run || ' ' || x.rid );
        dbms_output.put_line( 'spool off' );
    end loop;
end;
/
spool off
set feedback on

@tmp
------------------------------ eof ----------------------------------
Review & Followup
Rating: 4
June 04, 2001
Reviewer: Harrison from Haymarket, VA USA

Rating: 4
why the following error March 13, 2002
Reviewer: nagarjuna
i see you using

column division new_value DIVISION
column plant    new_value PLANT
column building new_value BUILDING
column floor    new_value FLOOR

select decode(division,'*','division',''''||division||'''') division,
       decode(plant,'*','plant',''''||plant||'''') plant,
       decode(building,'*','building',''''||building||'''') building,
       decode(floor,'*','floor',''''||floor||'''') floor
  from t
 where rowid = '&1'
/

-- your query goes here --
-- this is just a sample, it selects out the row we are processing

select *
  from t
 where division = &DIVISION
   and plant = &PLANT
   and building = &BUILDING
   and floor = &FLOOR

i don't know why the following is happening:

SQL> set define off
SQL> column grpid new_value GRPID
SQL> select 2 grpid from dual;

     GRPID
----------
         2

SQL> select &GRPID from dual;
SP2-0552: Bind variable "GRPID" not declared.
SQL>

Followup:
umm, whats up with the "set define off" -- it turns off that substitution
feature in sqlplus.  don't do that, or set define ON before trying to use &GRPID

Rating: 3
why the following error March 14, 2002
Reviewer: nagarjuna from us
if i make set define on

its prompting me Enter value for GRPID

do i have to set something off.

thanks

Followup:
give me the cut and paste of the example, it does not for me

ops$tkyte@ORA817DEV.US.ORACLE.COM> set define on
ops$tkyte@ORA817DEV.US.ORACLE.COM> column grpid new_value GRPID
ops$tkyte@ORA817DEV.US.ORACLE.COM> select 2 grpid from dual;

     GRPID
----------
         2

ops$tkyte@ORA817DEV.US.ORACLE.COM> select &GRPID from dual;
old   1: select &GRPID from dual
new   1: select          2 from dual

         2
----------
         2

ops$tkyte@ORA817DEV.US.ORACLE.COM>

March 14, 2002 Edgars -- Thanks for the question regarding "Disabling subquery evaluation", version 9i
You Asked
Hello Tom!

I have an C/C++ programming background and there we can disable checking of
other statements by using something like:
bool c = true;
if( ( c == true ) or ( this check is never done ) )
Can this technique be used in oracle selects, for example something like:

select count(Id) as count
from otis.schema s
where s.object_id = :obj_id and (1=:fall or s.schematype_id in (:wrm,:sdl,:elk))

and when I don't need to check for schematype_id I simply set :fall to 1? is
this a correct technique, or should I better use two different sql queries?

Thank you!
and we said...
The order of evaluation of SQL statement predicates is 100% indeterminate.

see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1299201885045
so while we do short circut's in the predicate (evaluating only what we need to
and then stopping) -- you have no idea if we are doing left to right, top to
bottom, right to left, bottom up, from the middle or what.

I would personally use 2 queries myself for the best overall plans possible.
March 14, 2002 Peggy -- Thanks for the question regarding "SQL Question with NVL function", version 8.1.7
You Asked
I have an update statement to update one column from another table.  I would
like to have the update statement put in a zero 0 when the join condition is not
true and the column will be set to null.

Original SQL:
SQL> update testc c
     set fk_test_wh =
              (select ddd_wh from test t
                      where t.ddd_wh = c.claim_line_all_wh);

19 rows updated.

I tried to add a NVL function so that fk_test_wh will not get set to null when
there is no join or match between test table and testc table.  I have tried the
following:
SQL> r
  1  update testc c
  2  set NVL(fk_test_wh =
  3            (select ddd_wh from test t
  4*                   where t.ddd_wh = c.claim_line_all_wh),0)
set NVL(fk_test_wh =
       *
ERROR at line 2:
ORA-00927: missing equal sign

I have tried changing things around but no luck.
Is this possible or is there a different way to go about doing this?

Thanks in advance for any insight.
and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t1 ( x int, y int );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t2 ( x int, y int );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t1 values ( 1, 1 );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t1 values ( 2, 2 );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t2 values ( 1, 100 );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t1;

         X          Y
---------- ----------
         1          1
         2          2

ops$tkyte@ORA817DEV.US.ORACLE.COM> update t1
  2     set y = ( select nvl(max(y),0) from t2 where t1.x = t2.x );

2 rows updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t1;

         X          Y
---------- ----------
         1        100
         2          0

ops$tkyte@ORA817DEV.US.ORACLE.COM>

or better yet, if t2 has a primary key:

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t2 add constraint t2_pk primary
key(x);

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t1;

         X          Y
---------- ----------
         1          1
         2          2

ops$tkyte@ORA817DEV.US.ORACLE.COM> update ( select t1.y t1_y, t2.y t2_y
  2                     from t1, t2
  3                    where t1.x = t2.x (+) )
  4     set t1_y = nvl(t2_y,0)
  5  /

2 rows updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t1;

         X          Y
---------- ----------
         1        100
         2          0

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review & Followup
Rating: 4
nvl review March 14, 2002
Reviewer: Peggy from Okemos, MI USA
This looks like what I was looking for.  This should work great. thanks.

March 14, 2002 Jose Luis -- Thanks for the question regarding "Count using intermedia", version 8.1.6
You Asked
Hello,

I have a table that contain a CLOB column with large text.
Table t_test ( id_column      number,
               id_seq_column  number,
               tx_column      clob )

PK_t_test (id_column, id_seq_column)

I need to know how many words there are in the tx_column to pk_t_test.  I can
have the search word in the tx_column any times and I want to know how many.

I used the ctx_query.count_hits but the total its returned is full, the table, I
need the total by PK.

Wich the best way to resolve this problem.

Thanks regards

Jose Luis
Brazil
and we said...
Well, the score includes that (the scoring is a function of the number of words
in the text, the number of times the term you searched for has that word and so
on).

We don't have a function to tell you exactly how many times the term appeared in
the text (thats what score is used for, to rank the documents).
March 14, 2002 Ravi -- Thanks for the question regarding "dba_tab_modifications", version 9.0.1.1.1
You Asked
Hi Tom,
I was trying to use dbms_stats.gather_schema_stats package.procedure.
I enabled monitoring for a table, say, emp.
alter table emp monitoring.
I understand that any modifications (including partitions) to the table are
maintained in SGA and input into dba_tab_modifications table periodically (3
hours) and after shutdown.
But, I don't see any rows after I inserted a row in the table, commit;
shutdown in dba_tab_modifications table even after startup.
Am I doing something wrong?? Should I have to wait for those 3 hours for the
SMON to put the information in DataDictionary from SGA?? Please Help. Thanks.
This is for us to gather statistics on stale objects in the tab_mod...
Ravi.
and we said...
The table is only considered modified after 10% of the rows have changed.
Inserting a row will not cause it to be marked "stale" (it is still fresh).

You didn't change enough data to trip the threshold.
Review & Followup
Rating: 3
what is "stale" March 14, 2002
Reviewer: A reader
Hi

What are stale objects?
Can we automatize statistics gathering? Will it hit performance hard though?

Followup:
"stale" objects are objects in need of being analyzed, more then "10%" of the
object has changed.

We use "alter table T monitoring" to enable the capture of "stale" tables to the
data dictionary.

You can then use dbms_stats to gather stats automagically on just the "stale"
objects.

Analyzing does work on the system -- but consider the impact on performance you
have with OUT OF DATE stats!

Search this site for

dbms_stats monitoring

for other discussions.

March 14, 2002 Betty -- Thanks for the question regarding "Looping", version 8.1.7
You Asked
I need to update a table that has 1,836,361
I have used all of the space allowed for rollback segments and must split the
update into smaller number of rows.  How do I loop if my update is

set transaction use rollback segment RBS_TMS;
update problem
set closedate = '13-MAR-2002';
commit;

Thanks
and we said...
well, if it were me, i'd just allocate sufficient rbs to do my job.  (but thats
me, I always look for the easy way out).

One approach (and I DESPISE the use rollback segment thing, won't do it, just
won't -- except to demonstrate how to easily achieve ora-1555's anyway).

  loop
    update problem set closedate = TO_DATE( '13-mar-2002', 'dd-mon-yyyy' )
     where closedate <> TO_DATE( '13-mar-2002', 'dd-mon-yyyy' )
       and rownum <= 10000;

    exit when sql%rowcount = 0;
    commit;
  end loop;
  commit;

That'll update 10k rows at a time.  Has the side effect that each update will
read all of the previously read data (i'm assuming there is NO WAY closedate is
indexed, since they all have the same values)...  It'll full scan -- read the
first 10k rows and update them.  Then it'll full scan, read the first 10k rows
-- ignore them then read the next 10k rows and update them.  This it'll full
scan, read the first 20k rows, ignore them and read the next 10k and so on...

If you have a numeric primary key on this table, you might be able to do
something nicer.  eg: select MIN(key), MAX(key).  divvy up the ranges in a loop
update by primary key range.  eg:

   i := min_key;
   loop
       update problem set closedate = TO_DATE( '13-mar-2002', 'dd-mon-yyyy' )
         where key between i and i+10000;
       commit;
       i := i+10000+1;
       exit when i > max_key;
   end loop;

here you will do (max_key-min_key)/10000 + 1 index range scans for upto 10k rows
each....

me, i'd just add more rollback, disk is cheap -- our TIME is not....  It'll be
faster to do as a single update regardless.
March 15, 2002 Yogesh -- Thanks for the question regarding "Time Span Estimation", version 8.1.7
You Asked
I want to know if there is any system table which stores the information about
the expected process execution time which is executing currently.

Lets say if I'm executing some procedure (ex. p1) I don't know how much more
time it is going to take. I want to know how much more time it will take.

In short I want to know how instance manager calculates this ? I want to give
same interface to user who will execute procedure p1. I want to provied user
with info like time left ...... plz let me know if you need any more info.

Regards
Yogesh
and we said...
Well, if the procedure is:

begin
    dbms_lock.sleep( 1 );
    ... do something
    dbms_lock.sleep( 1000 );
    ... do something
    dbms_lock.sleep( 100 );
    .... do something
    and so on
end;

WE have no way to tell you how long this'll take.  Only YOU can estimate that.
the database cannot estimate how long an arbitrary procedure will take to
execute.

Many commands have hooked themselves into the v$session_longops feature. (search
this site for v$session_longops and read some of the articles on it).  So, a
long sort, an index rebuild, an index create, etc -- they all estimate
themselves as to how long they'll take and report this info in the
v$session_longops tables via the dbms_application_info package.

(thats how instance manager does this, it just reads that view)

You can do this yourself.  If your procedure has 100 steps, you can call
dbms_application_info to report that you are on step N of 100 and the database
will use that to estimate how long it'll take you to run to completion.  This
information will then appear in the v$session_longops table.

If you have my book -- I have a complete example in there describing how to use
this feature.
Review & Followup
Rating: 5
Very useful March 16, 2002
Reviewer: Yogesh from PUNE, India
Thanx a lot, solved my problem. I will definately get a copy of your book today.
Thanx once again :)

March 15, 2002 Saby -- Thanks for the question regarding "Using a variable in the sql statement in the from clause", version 8.1.6
You Asked
Dear Sir,

I would like to know whether I can use a variable in a SQL statement for a table
name. I want to use this in a PL/SQL procedure block.

the PL/SQL block is as shown :

SET SERVEROUTPUT ON
DECLARE
    v_key t_rtqm_test_header.testdata_ref_key%TYPE;
    v_psn t_rtqm_test_header.production_number%TYPE;
    v_total NUMBER(9) := 0;
    v_table VARCHAR2(20);
    v_table_num NUMBER(2);

    CURSOR label_cursor IS
    SELECT production_number, testdata_ref_key
    FROM t_rtqm_test_header
    WHERE module_code = '0201142'
    AND testplan_id = 20
    AND pts_testplan_rev = 8
    AND phase_id = 455
    AND no_of_teststeps = 43
    AND result_table_number = v_table_num;

    CURSOR finu_cursor IS
    SELECT production_number, testdata_ref_key
    FROM t_rtqm_test_header
    WHERE module_code = '0201142'
    AND testplan_id = 23
    AND pts_testplan_rev = 4
    AND phase_id = 440
    AND no_of_teststeps = 70
    AND result_table_number = v_table_num;
BEGIN
    SELECT table_name, table_number
    INTO v_table, v_table_num
    FROM t_rtqm_switch_admin
    WHERE status = 1;

    DBMS_OUTPUT.PUT_LINE (v_table);

    OPEN label_cursor;
    LOOP
    FETCH label_cursor INTO v_psn, v_key;
    EXIT WHEN label_cursor%NOTFOUND;

    DELETE FROM t_rtqm_test_result1
    WHERE testdata_ref_key = v_key
    AND test_step_id = 4999;

    UPDATE t_rtqm_test_header
    SET no_of_teststeps = 42
    WHERE production_number = v_psn
    AND testdata_ref_key = v_key;

    commit;

    v_total := v_total + 1;

    END LOOP;
    CLOSE label_cursor;
    DBMS_OUTPUT.PUT_LINE (TO_CHAR(v_total) || ' LABE results corrected in
'||v_table);

    v_total := 0;

    OPEN finu_cursor;
    LOOP
    FETCH finu_cursor INTO v_psn, v_key;
    EXIT WHEN finu_cursor%NOTFOUND;

-------------------------------------------------------------------
--The following 2 sql statements use variable "v_table" in the place --of
--table name. When I run it. it shows error
-------------------------------------------------------------------

    DELETE FROM v_table
    WHERE testdata_ref_key = v_key
    AND test_step_id IN (2009, 2013,2017);
    commit;

    INSERT INTO v_table(testdata_ref_key, test_step_id, test_status,
test_result, test_time)
    VALUES(v_key, 2009, 1, 1, 1);
    commit;

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

    INSERT INTO v_table(testdata_ref_key, test_step_id, test_status,
test_result, test_time)
    VALUES(v_key, 2013, 1, 1, 1);
    commit;

    INSERT INTO v_table(testdata_ref_key, test_step_id, test_status,
test_result, test_time)
    VALUES(v_key, 2017, 1, 1, 1);
    commit;

    UPDATE t_rtqm_test_header
    SET no_of_teststeps = 66
    WHERE production_number = v_psn
    AND testdata_ref_key = v_key;
    commit;

    v_total := v_total + 1;

    END LOOP;
    CLOSE finu_cursor;
    DBMS_OUTPUT.PUT_LINE (TO_CHAR(v_total) || ' FINU results corrected in
'||v_table);

END;
/
and we said...
use dynamic sql

execute immediate
'DELETE FROM ' || v_table || '
  WHERE testdata_ref_key = :v_key
    AND test_step_id IN (2009, 2013,2017)' USING v_key;
commit;

execute immediate
'INSERT INTO ' || v_table || '
 (testdata_ref_key, test_step_id, test_status, test_result, test_time)
 VALUES(:v_key, 2009, 1, 1, 1)' using v_key;
commit;

question for you -- why do you commit after each statement??  what happens when
the delete works but the insert fails.... You should only commit when ALL
related work is done.  committing frequently not only impacts performance but it
is usually a bug in the code.  Your transactions should be as small as they can
big, as big as they have to be.
Review & Followup
Rating: 1
only in 8.1.6? March 14, 2002
Reviewer: Peggy Morgan from Los Angeles, CA
This solution is what I need. However when compiling, I get this error at the
line in the code that states 'execute immediate':
PLS-00103: Encountered the symbol "IMMEDIATE" when expecting one of the
following:
:= . ( @ % ;

Was this solution only for 8.1.6?

Followup:
execute immediate is new with 8i (815) and up.

Prior to that, you would use DBMS_SQL.  Search this site for DBMS_SQL for lots
of examples.

March 15, 2002 Eduardo -- Thanks for the question regarding "Web Log Connection Pooling Log ", version 8.1.7
You Asked
Hello,
I´m Working with WebLogic,and I can´t Log who is updating , deleting and
inserting in a table.

Do you have any ideia to resolve this problem????

Thanks.
and we said...
You would have to have your application set the "application user" in a context.

Do this:

Have all apps call

dbms_application_info.set_client_info( 'The Username goes here' )

RIGHT AFTER getting a connection and set the application username.  Now a simple
select userenv( 'client_info' ) from dual will tell you the app user.  Also,
it'll appear in v$session -- the client_info column, making it so you can see
from sqlplus whos "logged on"
Review & Followup
Rating: 3
"Web Log Connection Pooling Log ", version 8.1.7 March 14, 2002
Reviewer: Eduardo Guedes from Rio de Janeiro / Brazil
will the aplication have to execute dbms_application_info.set_client_info( 'The
Username goes here' ) in before every database access ???
If No , how It will save this information after request database and give back
the session to WebLogic connection Pooling ?

Followup:
Not before every ACCESS, after every "grab" from the connection pool.

Your apps gets a connection from the connection pool

You, in the database, want to know who the application user is.

Hence your app (which at this point is the ONLY thing in the world that knows
this bit of information) needs to tell the database.

You need to do this once upon grabbing the connection from the pool.

Rating: 5
Great! March 15, 2002
Reviewer: Yogeeraj from Mauritius
Hello,

I am using 9iAS and web forms. Unfortunately when i query v$session for users
running Web forms, i don't get the program name:

                                dbname Database
                              UNIX/Oracle Sessions
PID       SID   SER#  BOX    USERNAME   OS_USER  PROGRAM
_________ _____ _____ ______ __________ ________ ______________________________
...
26756     207   19271 C159   LTKKNITF6  jll      OraPgm
26728     65    14087 L1000  LYAQ       ias
26728     241   13175 L1000  MAL        ias
26728     79    12607 L1000  MASA       ias
26760     17    14782 C437   OPS$AANL   pat      OraPgm
26728     174   11984 L1000  OPS$AB01   ias
26732     289   13691 L1000  OPS$AB01   ias
26744     175   17510 cmtk25 OPS$ADJ    adj      f45run@cmtk250 (TNS interface)
26730     144   17366 cmtk25 OPS$AG     ag       f45run@cmtk250 (TNS interface)
26740     263   14011 cmtk25 OPS$AG     ag       f45run@cmtk250 (TNS interface)
26738     276   15432 cmtk25 OPS$AG     ag       f45run@cmtk250 (TNS interface)
...

==============================
Do you think it will be a good idea to include the following line of code in all
our web forms (pre_form trigger)?:

dbms_application_info.set_client_info( 'Form Name');

(I can then query the client_info column to get everything i want...)

Will that incur any overhead?

thank you in advance for your time and guidance

Best Regards
Yogeeraj

Followup:
I would DEFINITELY (and I do this) sprinkle calls to dbms_application_info all
over my code setting not only the CLIENT_INFO but the MODULE and ACTION as well.
 Then, I can easily look in v$session and see what overall Form (client_info),
fmx (module) and screen (action) they are looking at.

The overhead is trivial compared to the payback you get.

March 15, 2002 Angira -- Thanks for the question regarding "displaying the results of procedures in SQLPlus", version
You Asked
 How can I display table datatype output( OUT parameters)
from my procedure when I try ro execute it from sqlplus
I am using Oracle 7.3
and we said...
2 ways

1) use bind variables
2) use an anonymous block + dbms_output.

for example:

ops$tkyte@8i> create or replace procedure foo( x out date )
  2  as
  3  begin
  4          x:=sysdate;
  5  end;
  6  /

Procedure created.

ops$tkyte@8i>
ops$tkyte@8i>
ops$tkyte@8i> variable x varchar2(25)
ops$tkyte@8i>
ops$tkyte@8i> exec foo( :x )

PL/SQL procedure successfully completed.

ops$tkyte@8i> print x

X
--------------------------------
18-MAR-00

ops$tkyte@8i>
ops$tkyte@8i>
ops$tkyte@8i> set serveroutput on
ops$tkyte@8i> declare
  2          l_x date;
  3  begin
  4          foo( l_x );
  5          dbms_output.put_line( to_char( l_x, 'dd-mon-yyyy
hh24:mi:ss' ) );
  6  end;
  7  /
18-mar-2000 08:25:30

PL/SQL procedure successfully completed.

\
Review & Followup
Rating: 4
There is not Date type in sqlplus March 15, 2002
Reviewer: Igor from France
Hi

 I can't say

  variable v1 date (no date type)

   select * from
   where sysdate > :v1;

 using bind variables.
 Any hint?

                                  Igor

Followup:
correct, you have to:

varaible v1 varchar2(25);

exec :v1 := '15-mar-2002'

select * from t where sysdate > to_date(:v1, 'dd-mon-yyyy')

Rating: 4
Oops I forgot to put email March 15, 2002
Reviewer: Igor from France

March 15, 2002 Venkateswara -- Thanks for the question regarding "TIME FUNCTIONS", version 8.1.5
You Asked
Hi Tom

Today i want your help to sort my problem related time.

We are involved developing application software.For that
We need to caliculate TIME AND OT Hours.

Suppose in my application time and rate are defined.

If I give for 1 hours, hourely rate is 30.00Rs. fine

I want generally 1.30MTS I would like to caliculate the rate of 1.30hrs.
Similarly for 1.30.50Sec's How can I caliculate
is this way correct

If I enter like this 1:30:50 * 30 what I would like to caliculate for One hour
thirty minutes and fifty secs how much amount.

Thanks in advance.

Reddy
and we said...
Well, if asked to multiply 1:30:50 by 30, I might:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select to_date( to_char(sysdate,'yyyy/mm/dd
')||'01:30:50', 'yyyy/mm/dd hh24:mi:ss' ) - trunc(sysdate) portion_of_day
  2    from dual;

PORTION_OF_DAY
--------------
    .063078704

ops$tkyte@ORA817DEV.US.ORACLE.COM> select (to_date( to_char(sysdate,'yyyy/mm/dd
')||'01:30:50', 'yyyy/mm/dd hh24:mi:ss' ) - trunc(sysdate))*24
portion_of_day_in_hours
  2    from dual;

PORTION_OF_DAY_IN_HOURS
-----------------------
             1.51388889

ops$tkyte@ORA817DEV.US.ORACLE.COM> select ((to_date( to_char(sysdate,'yyyy/mm/dd
')||'01:30:50', 'yyyy/mm/dd hh24:mi:ss' ) - trunc(sysdate))*24) * 30 the_answser
  2    from dual;

THE_ANSWSER
-----------
 45.4166667

ops$tkyte@ORA817DEV.US.ORACLE.COM>

The last one shows you your answer -- put the hours/minutes/seconds (use a BIND
VARIABLE please) you want in place of 01:30:50 and you are good to go.

(i'm sure there are dozens of other ways -- watch below, someone will be along
to trump my answer with something "better" ;)
Review & Followup
Rating: 4
Time Functions March 15, 2002
Reviewer: R.V.Reddy from Bangalore, India
Thank your Mr. Tom

It is very use full to. For this can I have function to accept the time like
1:30:50 and output should my answer.

Reddy

Followup:
Yes, your function is:

((to_date( to_char(sysdate,'yyyy/mm/dd')||:bv, 'yyyy/mm/dd hh24:mi:ss' ) -
trunc(sysdate))*24) * 30

QED -- i gave you the function already!  just bind in your value and away you
go.

Rating: 4
Y not!!! March 15, 2002
Reviewer: Ravi Kumar from Chicago, IL
CREATE OR REPLACE function getrate ( t in varchar2, rate in number) return
number is
   portion number;
begin
   select to_date(to_char(sysdate,'yyyy/mm/dd') || t, 'yyyy/mm/dd hh24:mi:ss' )
-
          trunc(sysdate)
     into portion
     from dual;
   return round(portion * 24 * rate, 2);
end getrate;
/

and use it as below :

scott ON Test> select getrate('01:30', 30) from dual;

GETRATE('01:30',30)
-------------------
                 45

1 row selected.

make sure to pass hours as 01 not just 1

-Ravi.

Followup:
well, it'll be slower and would be more efficient as simply:

ops$tkyte@ORA817DEV.US.ORACLE.COM> CREATE OR REPLACE function getrate ( t in
varchar2, rate in number) return number
  2  is
  3  begin
  4          return round( (to_date(to_char(sysdate,'yyyy/mm/dd') || t,
'yyyy/mm/dd hh24:mi:ss' ) - trunc(sysdate))*24*rate, 2);
  5  end getrate;
  6  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select getrate('01:30', 30) from dual;

GETRATE('01:30',30)
-------------------
                 45

ops$tkyte@ORA817DEV.US.ORACLE.COM>

I would prefer to inline that code if you are going to use it in a SQL query.
Compare:

ops$tkyte@ORA817DEV.US.ORACLE.COM> set timing on
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from all_objects where getrate(
'01:30', object_id ) < 0;

no rows selected

Elapsed: 00:00:07.81
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from all_objects where
  2  round( (to_date(to_char(sysdate,'yyyy/mm/dd') || '01:30', 'yyyy/mm/dd
hh24:mi:ss' ) - trunc(sysdate))*24*object_id, 2  )<0;

no rows selected

Elapsed: 00:00:01.29
ops$tkyte@ORA817DEV.US.ORACLE.COM>

which would you rather run??

(besides, I sort of thought that given the query -- you developing a function on
your own was pretty trivial)

March 16, 2002 -- Thanks for the question regarding "is this a bug or I am wrong?", version 8.1.6, 8.1.7, 9.0.1
You Asked
Hi Tom

I have following procedure which compiles and works for Tru64 but when I tried
it on Windows(8.1.7.2.6, 9.0.1) and Linux (8.1.6.1) both fails with -20000
exception, insufficient privilege. The code is as follows

CREATE OR REPLACE PROCEDURE ANALIZAR_BBDD
AS
  type array is table of varchar2(30);
  l_users array := array('ARPA_OWN', 'BSCS_OWN', 'CM','CMGED', 'CMSAPDWH',
'CMSAPGED',
                         'CMSAPMDW',  'LSC', 'METADATOS_OWN', 'METRICAS',
'OPSC','PERFSTAT', 'RECHAZOS');
  l_junk varchar2(30);
  errmsg ctl_error_proceso.des_error%type;
BEGIN
  /* asignamos calendario espanola a la sesion */
  execute immediate 'alter session set nls_territory=''SPAIN''';
  IF to_char(sysdate, 'D') in (2, 4, 6)
  THEN
    /* se asigna un sort_area_size grande para acelerar el proceso */
    execute immediate 'alter session set sort_area_retained_size=65536';
    execute immediate 'alter session set sort_area_size=10485760';
    /* bucle para procesar los usuarios */
    FOR i in 1 .. l_users.count
    LOOP
        BEGIN
          select username into l_junk
          from dba_users
          where username = l_users(i);
              /* se borra las estadisticas antes de analizar por bug de Oracle
8.1.7 */
          dbms_stats.delete_schema_stats(OWNNAME => l_users(i));
              /* se analizan las esquemas, se generar histogramas de 10 buckets
*/
          dbms_stats.gather_schema_stats(OWNNAME => l_users(i),
                                         ESTIMATE_PERCENT => 20,
                                         METHOD_OPT => 'FOR ALL COLUMNS SIZE
10',
                                         DEGREE => 1,
                                         CASCADE => TRUE);
        EXCEPTION
          WHEN NO_DATA_FOUND THEN
            errmsg := 'Error durante analyze: no existe el usuario
'||l_users(i);
            insert into ctl_error_proceso values
            (seq_cod_error_proceso.nextval, sysdate, errmsg);
            commit;
          WHEN OTHERS THEN
            raise;
        END;
      END LOOP;
    END IF;
EXCEPTION
  WHEN OTHERS THEN
    errmsg := 'Error durante analyze: '||sqlcode||', '||sqlerrm;
    insert into ctl_error_proceso values
    (seq_cod_error_proceso.nextval, sysdate, errmsg);
    commit;
END;
/

and it works if I do DECLARE instead of create or replace. I guess it's bug but
I cant find anything about it on Metalink, am I missing something?

cheers
and we said...
It is not a bug.  read
http://osi.oracle.com/~tkyte/Misc/RolesAndProcedures.html
to prove its not a bug, issue:

set role none
<do the block with a DECLARE> -- it'll not work.

you have the privelege to do what you are trying to do granted DIRECTLY to you
on the Tru64 instance.  You have the privilege to do what you are trying to do
granted to you via a ROLE on the others.

You need to direct grant or you can use "authid current_user" (invokers rights -
if you have my book i have a huge section on this topic).
Review & Followup
Rating: 3
I dont think that is my problem March 15, 2002
Reviewer: A reader
hi tom

In tru64 I can run this procedure with DBA role (and direct grant on dba_users),
in windows and linux I have explicitly grant execute dbms_stats to the user
running it with no luck, I even created the procedure as SYS and ran as SYS but
still got error

Followup:
sigh, did you try

set role none
declare
  .......

??? (answer = no, cause if you did, you would not have written this
followup....)

It is the roles, you do not have the permission to analyze the tables. You have
permissions to use dbms_stats, just not on the tables you are trying to.

Rating: 3
What about ANALYZE TABLE privilege? March 15, 2002
Reviewer: Adrian Billington from UK
Tom

Could this be anything to do with not having an explicit analyze privilege
granted to allow DBMS_STATS to execute?

Adrian

Rating: 4
ok I didnt grant analyze any to user March 16, 2002
Reviewer: A reader
I granted this to the user and it works now
thnaks tom and adrian

March 17, 2002 david -- Thanks for the question regarding "Convert blob to varchar2", version 8.0.4.3
You Asked
I have a table with a blob field.  This field contains only
character data.  I would like to convert the field to varchar2.
Is there an easy way to do this?

The only way I can think to do this conversion is via what
essentially is a byte by byte conversion.

Any ideas would be appreciated.
and we said...
There is a package utl_raw.

You can use

  some_vc_data := UTL_RAW.CAST_TO_VARCHAR2( some_raw_data );

All that function effectively does is change the datatype marker
of the raw into varchar2 and just leaves the data as is.  On the
way into a RAW, you can use utl_raw.cast_to_raw to conver a
varchar2 into a raw type (no conversion, just a cast)

UTL_RAW may not be installed on your system.  If not (there
would be a grant execute to public and a public syonym if it is
installed), simply:

o cd $ORACLE_HOME/rdbms/admin
o using SVRMGRL
o connect as INTERNAL or SYS (and only these 2 users, none
other)
o @utlraw
o @prvrawb.plb

And thats it, it'll be there now.
Review & Followup
Rating: 3
how to convert blob to raw March 14, 2002
Reviewer: Nishant from PA USA
Hi
In this reply u said everything about raw data type. but basic question is how
to convert BLOB to RAW/VARCHAR2 data type. beacuse right now i am passing from
powerbuilder application blob variable and when i am using this
UTL_RAW.CAST_TO_VARCHAR2( some_raw_data )it requires to be raw data to convert
but i have BLOB data..so how can i convert  my BLOB data to RAW data.

i am bit confused in BLOB and RAW. and as powerbuilder is not supporting RAW
data type i am using BLOB data type.

yr any help will be appreciated.

Thanks
Nishant

Followup:
just use:

utl_raw.cast_to_varchar2( dbms_lob.substr( BLOB_LOCATOR, 32000, 1 ) );

...  the biggest varchar2 variable is 32k so you will use dbms_lob.substr to get
32k chunks of it at a time.

Rating: 3
how can i insert my blob value from sql ? March 16, 2002
Reviewer: Nishant Shah from PA USA
Table  : pdf_test (pdf_tk number(8), pdf_blob blob)

create or replace procedure write_pdf(tk in number,content in blob)
 as
 b_lob BLOB;
 len number;
 begin
 len:=5000;
 insert into pdf_test values(tk,empty_blob()) return pdf_blob into
b_lob;
DBMS_LOB.WRITE(b_lob,len,1,utl_raw.cast_to_varchar2(dbms_lob.substr(b_lob,32000,1
)));
 commit;
 end;

i am passing my blob variable from powerbuilder while calling this procedure.
and it fails and returns me -1 and saying procedure could  not executed. now how
can i check from SQL prompt that my data has been entered in this table. i tried
following ways but it fails.

SQL> execute write_pdf(1,'this is test');
BEGIN write_pdf(1,'this is test'); END;

      *
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of arguments in call to 'WRITE_PDF'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored

SQL> execute write_pdf(1,empty_blob());
BEGIN write_pdf(1,empty_blob()); END;

*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error
ORA-06512: at "SYS.DBMS_LOB", line 700
ORA-06512: at "SHAHPRA.WRITE_PDF", line 9
ORA-06512: at line 1

it seems to me is my procedure do not understand the supplied parameter blob
variable. actually i want to save my pdf document in database. i saw examples of
that. but due to security/access reasons i can't use that create directory
functions of oracle.

how can i test from SQL prompt that my blob variable has inserted ?.

any help will be appreciated ?

Followup:
You won't be writing a PDF file from SQLPlus without using
dbms_lob.loadfromfile.

SQPLUS is a rather rudimentary scripting environment.

SQLPlus don't have a "blob".

Your interface should be RAW, not blob using the technique above.  Try:

create or replace procedure write_pdf(tk in number,content in varchar2 )
as
 b_lob BLOB;
begin
   insert into pdf_test values(tk,empty_blob())
   return pdf_blob into b_lob;

   DBMS_LOB.WRITE( b_blob, length(content), utl_raw.cast_to_raw( content ) );
end;

if you want to pass varchar2's and put them into blobs.

BTW: just advice here -- never commit in a procedure.  Let the client, the
caller control the transaction.  committing in a procedure is a really bad idea.
 Sure, today your transaction is that insert and a lob write.  tomorrow though,
you'll want to take that procedure and some other procedure -- call them both
and have that be the transaction.  IMPOSSIBLE if you commit in the procedure.
It is just a bad practice.  Transactions should be controlled by the caller, not
the callee!

Rating: 3
how can i insert my blob value from sql ? March 16, 2002
Reviewer: Nishant Shah from PA USA
let me correct here that i used this way also

create or replace procedure write_pdf(tk in number,content in blob)
 as
 b_lob BLOB;
 len number;
 begin
 len:=5000;
 insert into pdf_test values(tk,empty_blob()) return pdf_blob into
b_lob;
DBMS_LOB.WRITE(b_lob,len,1,utl_raw.cast_to_varchar2(dbms_lob.substr(content,32000
,1)));
 commit;
 end;
/

using CONTENT VARIABLE  as my lob loator which i m passing. but it gives me
following error.

SQL> execute write_pdf(1,empty_blob());
BEGIN write_pdf(1,empty_blob()); END;

*
ERROR at line 1:
ORA-22275: invalid LOB locator specified
ORA-06512: at "SYS.DBMS_LOB", line 664
ORA-06512: at "SHAHPRA.WRITE_PDF", line 9
ORA-06512: at line 1

thanks
nishant

March 17, 2002 Shetty -- Thanks for the question regarding "Table Access", version 7.3.6
You Asked
Tom,
i have problem i have two tables say tab1 (col1, col2) and tab2( x1,x2) .Now
i  create a view which is Union of both the tables..

create view test_View as
select * from tab1
union
select * from tab2;

Now if i do..

select * from Test_view where tab1.col1='xxx' it does a full table scan for
both tab1 and tab2. What i want is when i specify tab1='xxx' it should just
scan tab1 rather than both the tables..

I have 10 such table whose union have to create..only way i can acess those
tables is through Views..(We r using an outdated tool which is forcing us to
use Views )

We r using Oracle 7.30 (Optimizer_mode='RULE').

Example...

select * from tab1;
        ID NAME
---------- ------------------------------
         1 ronny
         2 kelly
         3 kurt
         4 ron
         1 ronny
         2 kelly
         3 kurt
         4 ron

select * from tab2;

        ID NAME
---------- ------------------------------
         1 ronny
         2 kelly
         3 kurt
         4 ron
         1 ronny
         2 kelly
         3 kurt
         4 ron

Now i create a view
create view view_test as
select '100' id,name from tab1
 union all
select '200' id,name from tab2;

SQL> select * from view_test where id=200;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE
   1    0   VIEW OF 'VIEW_TEST'
   2    1     UNION-ALL
   3    2       TABLE ACCESS (FULL) OF 'tab1'
   4    2       TABLE ACCESS (FULL) OF 'tab2'

what i want when i specify id=200 its should just access tab1 and not both
the  tables....

Thanks In Advance..
and we said...
The first is possible -- when the column is in the table.  We can use
constraints to perform what is known as "partition views".   The second one
however, since we don't have  a constraint to work with, isn't viable -- you
would nee dto have ID be a column in the table itself with a check constraint.

You'll want to read:

Oracle7 Server Tuning
Release 7.3
Part No. A32537?1
June, 1996

Appendix 'C', Managing Partition Views.

In 8.0 and up, partitioning (physical table partitioning) makes this operation
*trivial* of course.
March 17, 2002 -- Thanks for the question regarding "Dimensions", version 8.1.7
You Asked
Hello Tom,

Can you please explain me how to create a Dimension with a simple example? ( The
terms Level, HIERARCHY , Join key, Attribute).
and we said...
Have you read:
http://technet.oracle.com/doc/oracle8i_816/server.816/a76994/dimensio.htm#11221
Also, if you have my book -- I have a complete example in there as well.
Dimensions, materialized views, all of the concepts -- are not "simple".  I
would consider them very advanced.  Hence, a simple trivial example doesn't cut
it.  The examples in that chapter above are pretty good.
Review & Followup
Rating: 5
March 18, 2002
Reviewer: A reader

March 17, 2002 jyothi -- Thanks for the question regarding "data replication", version 8.1.7
You Asked
we need a database (which should be a copy of the primary database
and constantly updated with the changes taking place in primary)
 which we plan to use for reporting purposes .
 Prob. is our reports needs to write some log data. And if it were read only
then I know standby would serve very good purpose, but unfortunately not. Is
there any way we could acheive this
configuration. One of the team members are wondering if oracle 9i RAC would
serve this purpose, which I don't think so.I appreciate any input in this.
Thanks.
and we said...
Why does it need be a copy?  If you have appropriate hardware, a single box can
(should) do both.

Actually, Oracle8i OPS would satisfy this.  We would have a single database,
mounted and opened by more then one computer system (shared disk system).  9i
RAC would do this as well (single database, more then one computer able to
read/write the system).

Replication would do this as well but there is a price (performance) to be paid
for such a thing.  I would not recommend replication just to have a reporting
instance.

9i Release 2 (coming out late spring/early summer) will support the concept of a
logical standby -- like the current PHYSICAL standby, but the standby may be
opened READ/WRITE while the copy of the data is taking place.
Review & Followup
Rating: 4
March 18, 2002
Reviewer: jyothi bikkumanla from FL USA
Eventhough we have already considered all the options you gave us below, but
your answer clarified it even more and let us know which is the place for each
one of them, so keeping all the things you have told us we will talk to the
application group about what kind of reports they will be generating and do they
even need one of the solutions below......etc., So, thank you very much for the
quick response to my question.

March 17, 2002 Ivica -- Thanks for the question regarding "Selecting first n records after using order by", version 8.0.4
You Asked
Hi!

I found answer in your archive how to select first n records after using order
by clause for Oracle 8i, but didn't found it for Oracle 8.
So, I have two tables on Oracle 8.0.4:

Table1:
id varchar2(10),
name varchar2(30)

Table2:
id varchar2(10),
value1 number(15,2),
value2 number(15,2),
...

Query like this:

SELECT DECODE(SIGN(s.sum), -1, 'P', 'D') AS dp,
       s.sum12,
       p.id, p.name
  FROM table1 p,
       (SELECT id,
               SUM(value1 - value2) AS sum12,
          FROM table2
          WHERE ...
          GROUP BY id) s
  WHERE p.id = s.id
  ORDER BY dp, ABS(s.sum12) DESC

returns:

DP      SUM12         ID       NAME
------------------------------------
D      92121,11      323
D       2222,23       43
D         33,10     9876
D          1,99     3445
...
P     -43567,34    98843
P      -5544,33       91
P        -90,22       12
P         -5,99       90
....

This is OK, I get SUM12 values, first pozitive then negative, ordered by size.
But, how to get only first n records (two for example) having DP='D' and only
first n records having DP='P'?
I want result like this for n=2:

DP        SUM12         ID       NAME
--------------------------------------
D      92121,11        323
D       2222,23         43
P     -43567,34      98843
P      -5544,33         91

Thanks.
and we said...
You did not find an answer because no reliable answer exists for 8.0 and before.
 Prior to having an order by in a subquery -- there is no reliable method to
do this in SQL.

There are tricks you can play with having clauses and such, however you will
find them painfully slow.  In 8i with analytic functions -- your query to get
the top-n within a group is easily done:

select * from (
select a.*, row_number() over (partition by id order by dp, sum12 desc ) rn
from (
SELECT DECODE(SIGN(s.sum), -1, 'P', 'D') AS dp,
       s.sum12,
       p.id, p.name, abs(s.sum12) sum12
  FROM table1 p,
       (SELECT id,
               SUM(value1 - value2) AS sum12,
          FROM table2
          WHERE ...
          GROUP BY id) s
  WHERE p.id = s.id
) a
)
where rn <= 2;

Before the addition of these extensions to SQL, you'll be using procedural code
to achieve this result -- over fetching data you don't want and ignoring it.

The addition of analytic functions to SQL makes the upgrade worth it alone
(forget about the hundreds of other features for a moment, just this new
functionality is worth it)
March 17, 2002 Nag -- Thanks for the question regarding "Oracle Forms- selecting everything through a block, and number of records are displayed set to 10, are the records selected everytime the down arrow key is presses.", version 8.1.7
You Asked
Tom

1.Lets say there is a oracle block based on a 100,000 record table. Then I query
without giving any criteria. This will in effect issue a select * from
100000_rows_table.

Now my records displayed block property is set to 10. Where are the rest of the
records when the first 10 records are displayed in the block. Are the records
requeried everytime we press the down arrow key?

2.Can we write to a excel file from Oracle Forms and Oracle reports, if so how
can we do it. Can we write to the excel file using the text_io package.

Thank you
and we said...
1) see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:839027059026
the data is stored in the table and will be fetched from the table as needed..
once the first 10 are fetched, they won't be refetched, but the next 10 will be
fetched later.

2) from reports you can use CVS file output.  From forms, you can query this
site for OWA_SYLK.  It can produce a "nicer" file.
Review & Followup
Rating: 5
? March 17, 2002
Reviewer: Nag
"the data is stored in the table and will be fetched from the table as needed..
once the first 10 are fetched, they won't be refetched, but the next 10 will be
fetched later."

1.Tom, what do you mean by ' the next 10 will be fetched later'. Are you saying
that the first 10 are displayed as soon as they are gotten, and then when you
press the down arrow key you can see the next 10 if they have already been
retrieved, or else we need to wait.

Basically are you saying that, the query is executed only once, and the rows are
returned to the client as they are retrieved from the table in the database.

2.Further, when you say from reports we can write in cvs output, what do you
mean. Is it that it is a format compatible with Oracle reports. So you are
indirectly saying that 'NO we cannot open an excel file and write to it either
from reports or forms'

Kindly clarify.

Followup:
1) the query is executed ONCE.

The rows are fetched from the server as needed.

2) I'm not "indirectly" saying that, I'll directly say that.  Forms and Reports
do not create a binary XLS file.

Reports can easily generate a CSV file (comma separated values) which Excel can
easily read.

Forms can use text_io to do the same or, by using the standard, documented,
non-proprietary SYLK format, write a file that can contain things like
formula's, fonts, bolding, and other markup.

March 18, 2002 Prasad -- Thanks for the question regarding "SQL Query", version 8.1.7
You Asked
hi tom
i have a table as follows,
table error(id number,desc varchar2);
values: id       desc
                1        fatal
                2      critical
                3       medium
                 4    cosmetic
table2 transactions(m_id,i_severity)
values  ;
    2                   4
    3                   4
    2                   4
    3                   1
    4                   2
table screens:MODULE_ID             MODULE_NAME
        1                        Salary
        2                  Leave
        3                        Invoicing
        4                         Payments
 when i writ a query select count(desc) ,desc from
error a,transactions bscreens c
where b.module_id=cmodule_id
and b.i_severity=a.id
and b.module_id=3
group by  a.desc
the output is :
COUNT(DESC) ,DESC
1                                     Fatal
1                                       cosmetic
i want a query on execution gives like below

Fatal           cosmetic critical         medium
 1                   1     0                0

Thanx in advance
and we said...
select sum( decode( desc, 'Fatal', cnt, 0 ) ) Fatal,
       sum( decode( desc, 'cosmetic', cnt, 0 ) ) cosmetic,
       ..
       sum( decode( desc, 'medium', cnt, 0 ) ) medium
from (
select count(desc) cnt ,desc
  from error a,transactions b,screens c
 where b.module_id=cmodule_id
   and b.i_severity=a.id
   and b.module_id=3
 group by  a.desc
     )
/

will do that.
Review & Followup
Rating: 3
SQL Query March 17, 2002
Reviewer: Prasad rao from Pune,Maharastra,India.
hi!
Thanks for yor response.
I do not want use Decode b'coz it is something like hard coding.
my values of  Fatal ,cosmetic,critical,medium are not static. tomarrow user can
add a new value say ,Trival.
I want use this query in reports.
Pl. advise me.
Thanks..

Prasad.

Followup:
Well think about it.  A SELECT statment has a fixed number of columns -- it
either has 5 columns or 50 columns or whatever.

It never has an "indeterminate number" of columns.

Your report tool, if its any good, will have a transpose or pivot function.
Most everyone I've seen does.  You'll be using that if you don't know how many
"columns" you'll be getting until AFTER you get them.

Another option is to use a view and when your user wants to add a new value,
they do so through some procedure you write.  This procedure will create a view
to incorporate this new value, replacing the old view.

March 18, 2002 zen -- Thanks for the question regarding "table partition", version 8.1.5
You Asked
Hi Tom,
Is it possible to partition an existing table in 8.1.5,How?
Thanks
Zen
and we said...
You will have to rebuild the table -- since all the of the data
must be moved.

The steps could be:

ops$tkyte@ORA8I.WORLD> create table t ( x int, y int );

Table created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> create table t2  (x,y) UNRECOVERABLE
  2  partition by range(x)
  3  ( partition part_1 values less than(5),
  4    partition part_2 values less than(10)
  5  )
  6  as
  7  select * from t
  8  /

Table created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> drop table t;

Table dropped.

ops$tkyte@ORA8I.WORLD> rename t2 to t;

Table renamed.

remember to add any constraints and or indexes on T2 as well as
grants.  One method to get all of the definitions out would be
to use EXP ... tables=t and then IMP ... indexfile=tmp.sql
full=y.  The resulting tmp.sql file will have the entire
structure of T in it that you can modify and use to build the
new T2 with (indexes, constraints, etc)....
Review & Followup
Rating: 4
foreign keys March 18, 2002
Reviewer: Brigitte Hendrischk from Leipzig, Germany
Hi Tom

we have many foreign keys with on delete cascade.
So I can't delete the table. Can I use the  alter table ... move tablespace ...
partition ...  to keep the constraints?
We use Oracle 8.1.7.

Thanks

Brigitte

Followup:
You'll need to drop the constraints, rebuild the table, add the constraints back
again.
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:137212348065
has a script to generate the alter tables to add the constraints back again --
you'll need to modify slighty to get the "on delete cascade" part.

For example:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table p ( x int primary key );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table c1 ( x references p on delete
cascade );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table c2 ( x references p on delete
cascade );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into p select rownum from all_objects;
17288 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into c1 select * from p;
17288 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into c1 select * from p;
17288 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into c2 select * from p;
17288 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into c2 select * from p;
17288 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;
Commit complete.

so, there is your sample schema, now lets partition p, during this period --
you must make it so P, C1, C2 cannot be modified (eg: add a before
insert/update/delete trigger to raise PROGRAM_ERROR on P)

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table new_p
  2  (
  3    x int ,
  4    constraint p_pk PRIMARY KEY(x )
  5    using index local
  6  )
  7  PARTITION BY RANGE (X)
  8  (
  9    PARTITION part1 VALUES LESS THAN (5000) ,
 10    PARTITION part2 VALUES LESS THAN (10000) ,
 11    PARTITION part3 VALUES LESS THAN (20000)
 12  )
 13  /

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert /*+ append */ into new_p select * from
p
  2  /

17288 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit
  2  /

Commit complete.

now that P has been copied, drop the constraints on C1/C2

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table c1 drop constraint SYS_C005806;
Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table c2 drop constraint SYS_C005807;
Table altered.

and drop P

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table p;
Table dropped.

rename new_p to p

ops$tkyte@ORA817DEV.US.ORACLE.COM> rename new_p to p;
Table renamed.

and add the constraints back.  There is a "slow way" and a "fast way"

ops$tkyte@ORA817DEV.US.ORACLE.COM> set timing on

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table "C1"
  2  add constraint "SYS_C005806"
  3  foreign key ( "X" )
  4  references "P" ( "X") on delete cascade;

Table altered.

Elapsed: 00:00:01.51

that was the slow way -- since we KNOW the data is OK in C1, C2 and P (we
prevented mods to it) we can enable the constraint without checking it

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table "C2"
  2  add constraint "SYS_C005807"
  3  foreign key ( "X" )
  4  references "P" ( "X") on delete cascade
  5  enable novalidate
  6  /

Table altered.

Elapsed: 00:00:00.05

March 18, 2002 Shah -- Thanks for the question regarding "Analyze Command", version 7.3.3.6
You Asked
Hi,

I'm not really understand what is the usage of analyze command
i.e.
compute statistic, estimate statistic and validate structure;
and when is the best time to use/run it.

I'd like to know what is the impact if I choose validate
structure ?

Thanks
and we said...
You would use the analyze command to compute or estimate
statistics when you are using the cost based optimizer (CBO).
If you are not using the CBO but rely on the RBO -- don't
analyze est|compute stats at all -- it would not be necessary.

So, unless you are currently doing it -- don't just "do it".  Do
it in a test instance and fully test your application to see the
impact.  Your query plans will change -- sometimes for the
better and sometimes not.  You spent some amount of time tuning
your queries in your existing system and by using the CBO after
an analyze -- it will change the plans and necessitate some
amount of re-tuning.

As for the validate structure, it is typically used only if
there is a suspicion that there is a problem.  I would say most
people do not use it on a regular basis -- only when they
suspect an issue.  The side effects of using it are:

o it will fail with

ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified

if there are active transactions

o it will block inserts/updates/deletes to the table while it is
processing.
Review & Followup
Rating: 3
Meaning of Suspicion/Problem March 17, 2002
Reviewer: Tony from Australia
Hi Tom,

Could you please elaborate the meaning of suspicion/problem. The reason I want
to know is:
I'm planning to convert a huge non-partitioned table to a partitioned table, and
in order to have a fall back arrangement, I'm going to take the export of the
existing table. What precautions should I take (apart from space)? so in case I
may end up in import, I should not have any problem. Is it good to run validate
structure before export???
Regards,

Followup:
Well, #1 and first and foremost is

DO THIS ON A TEST SYSTEM.  It would be a really truly bbbbaaaaaddd idea to walk
into your production system and take a non-partitioned table and partition it
and "see what happens" (i'll tell you what will happen, you'll mess everything
up and be really mad at us cause it got messed up).

As with any "major" change, if you don't test it on a test system, under load,
with the real application, to see how it behaves -- it'll almost certainly
behave differently then you expect.

Ok, so lets say you have this test system (cause if you do it on production
without testing, you are on your own).  Then you need not back it up via
export/import -- you'll have the data in the database.  You will

0) disable your application
1) create a new table "partitioned" as select * from old_table 2) index new
table
3) add constraints to new table
4) add triggers, whatever remains to new table
5) drop old table
6) rename new table to old table
7) enable your application
8) test it

(note in 9i, steps 0 and 7 may be skipped with online redefs, none of the others
can be!)

If you don't like what you have, do steps 0-8 again but create a non-partitioned
table instead of partitioned.

Now, when you goto production, which is RUNNING IN ARCHIVELOG MODE (else its
just not production right), you might want to take a backup (hot) sometime
shortly before you do this operation, just as a precaution but thats it.  If you
need to recover, you have your backups.

Rating: 3
Thanks March 18, 2002
Reviewer: Tony from Australia
Hi Tom,
Thanks for your prompt reply.
The reason I asked for validate structure or any problem may come in Production
not because of skipping testing of the whole partitioning process but just in
case any thing may be specific to Production which can not be tested in Test
environment like, block corruption (You can through light here). The idea is to
take maximum possible precautions before even starting the process.
The export was considered because it is a data warehouse system, load is huge
but once a month, and DB running is not running in archivelog mode.
Regarding 2, as the indexes are also huge, I am thinking of creating indexes on
new table after dropping the old table as in this way I can reclaim the index
space from old table and use in new table (index space). Of course, the index
creation script will be ready before dropping the old table.

Followup:
Well, instead of taking an EXPORT (if the data is huge, it is really big -- an
export import would take a very very very long time).

Transport the tablespaces containing the table and indexes off to tape.  In the
event you want to go back, you can snap these tablespaces back on the database
just like restoring a datafile.  No reload necessary.

Rating: 4
Analyze table March 18, 2002
Reviewer: Irfan from Kuwait
Hi Tom,
We use Analyze table t compute statistics, occassionally to check and calculate
the actual size of table. Does this has any side effects?

Followup:
Sure, any query that accesses that table will use the CBO (cost based
optimizer).  So, if you do not do this analyze frequently enough and the stats
get stale -- bad plans will result.  If you do not do this analyze to all tables
you query, bad plans will result.

Rating: 2
Analyze stats not being imported April 22, 2003
Reviewer: Reader from USA
When I use TTS everything works wonderfully, except, that some tables have the
analyze stats imported and some do not last_analyzed column in dba_tables is
null. I think all the parameters are correct, and are as follows :
For Export:
transport_tablespace=y
tablespaces=x,y
triggers=y
constraints=y
grants=y
statistics=compute
file=expdat.dmp

For Import:
TRANSPORT_TABLESPACE=Y
DATAFILES= ...
TABLESPACES=x,y
GRANTS=y
fromuser=noname
touser=noname
constraints=n
analyze=y
FILE=expdat.dmp

Please help,
thank you.

March 18, 2002 Sriram -- Thanks for the question regarding "Automating Data Loading", version 8.1.6
You Asked
The main master oracle database at site A is updated with the data from other
oracle databases from site B , site C . The data comes daily by e mail in the
form of export .dmp file . This file is imported in to the master database. we
do not have any direct network connection with the other databases. We want to
eliminate human intervention in this process( Receive the e mail , save the file
and run the import utility. ).

Please let us know how to automate this whole process.

Regards
and we said...
Well, you can do this:

o load the java mail API into the database
o create a java stored procedure that uses this java mail API to periodically
look for "new mail".
o when it finds new mail, it'll read it, get the attachment, save the attachment
(dmp file)
o when it saves a dmp file, it'll use the java api's to run a host command --
running imp.

To see how to load the java mail API, see:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:255615160805
To see how to run a host command:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:952229840241
The java mail example focuses on SENDING mail, not reading it.  You'll need to
build your own java code to read email (it is not difficult if you have a java
programmer).

You can use DBMS_JOB to schedule this stored procedure to run in the database on
a periodic basis.
March 18, 2002 Ak -- Thanks for the question regarding " Index & Free.sql", version 8.1.5
You Asked
Hi Tom,
   1)How do I get a Nice formatted Index Report with the column name
and the order(in case of composite index).With the Index Report
I mean ,all the index along with column name,table name and the order
available in the Datbase for the perticular owner.
I spooled the contains of  Dba_indexes table,but it wasn't good,meaning output
was Not proper.It was wrapped in the next line etc.

2)If i get a file which has output of a query of all the indexes in
a database for a perticular owner,so that in case of index file
corruption or things like that,i can rebuild it ,it will be of lot
of Help.

3)I would like Your free.sql to give me the report for all the database,sitting
in one place.Yes..i can connect to all the database
from One place,i can do a/a@siteA ,b/b@siteB.My tns.ora contains
all this.I did following..
      I hardcoded the username/password@site name in your free.sql and spooled
the output to one txt file.But I have problem here...

A)I have to hardcode usename/password which can be seen by anybody
by opening that sql file.Can You suggest me something Here
To overcome this.
           I am thinking of writting a stored proc ,hardcode
the Username/password@site and then wrap this procedure.
I will have to Use UTL package to send the output of this to a file
at OS lavel.Is this the better way to do this ?? Your comment please.

Thanks,
 Regards
    AK
and we said...
1) here is a query I use to get index information for a given table (&1).  You
can modify this query as you need to get the results you want.

prompt
prompt
variable owner varchar2(30)
variable tname varchar2(30)
begin
  :owner := USER;
  :tname := upper('&1');
end;
/
Prompt Indexes on &1
column index_name heading "Index|Name"
column Uniqueness heading "Is|Unique" format a6
column columns format a32 word_wrapped

select substr(a.index_name,1,30) index_name,
       decode(a.uniqueness,'UNIQUE','Yes','No') uniqueness,
max(decode( b.column_position,  1, substr(b.column_name,1,30),
NULL )) ||
max(decode( b.column_position,  2, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position,  3, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position,  4, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position,  5, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position,  6, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position,  7, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position,  8, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position,  9, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position, 10, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position, 11, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position, 12, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position, 13, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position, 14, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position, 15, ', '||
substr(b.column_name,1,30), NULL )) ||
max(decode( b.column_position, 16, ', '||
substr(b.column_name,1,30), NULL )) columns
from all_indexes a, all_ind_columns b
where a.owner = :owner
and a.table_name = :tname
and b.table_name = a.table_name
and b.table_owner = a.owner
and a.index_name = b.index_name
group by substr(a.index_name,1,30), a.uniqueness
/

2) exp userid=system/manager rows=n owner=THAT_USER file=THAT_USER.dmp

That DMP file will have everything you need.  If you want their indexes:

imp userid=system/manager file=THAT_USER.dmp indexfile=index.sql full=y

then, index.sql will have all of the create indexes (as well as create tables,
etc).

3) look up DATABASE LINKS.  Then, from one database, you could run:

@free 1 some_other_database

and free.sql would be:

...
from ( select sum(bytes)/1024 Kbytes_free,
              max(bytes)/1024 largest,
              tablespace_name
       from  sys.dba_free_space@&&2
       group by tablespace_name ) a,
     ( select sum(bytes)/1024 Kbytes_alloc,
              sum(maxbytes)/1024 Kbytes_max,
              tablespace_name
       from sys.dba_data_files&&2
       group by tablespace_name
       union all
      select sum(bytes)/1024 Kbytes_alloc,
              sum(maxbytes)/1024 Kbytes_max,
              tablespace_name
       from sys.dba_temp_files&&2
       group by tablespace_name )b
....
March 18, 2002 Ak -- Thanks for the question regarding "Index", version 8.1.5
You Asked
Hi tom,
   I have a query like this..

1)select * from x where stat <> 'F' and stat is having btree index.
But plan shows ,index is Not being Used.

2)Then i put select * from x where stat='F'..and Here index was
Used.
Table has 100,000 records..Can You tell me ..what's Happening Here,why
index is Not used when i USE NOT operator ???

Thanks,
  Regards
    Ak
and we said...
read
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3069705433534
My question to the world -- why do people believe that

a) indexes are always faster
b) if you do not use an index and an index exists, you have done something wrong
c) a NOT EQUALS should use an index (that just seems counter intuitive to me
from the very beginning)

This just seems to come up soooo often.  I'm curious.
Review & Followup
Rating: 3
Index myth March 18, 2002
Reviewer: Trevor
Tom,
I have found that people have the beliefs you mention
because they do not understand how indexes work.

People have either been told or read that INDEXES make
your SQL go faster.

From the 8.1.7 Doco
SQL Reference
"An index is a schema object that contains an entry for each value that appears
in the indexed column(s) of the table or cluster and provides direct, fast
access to rows. Oracle supports several types of index: "

Concepts Manual
"You can create indexes on one or more columns of a table to speed SQL statement
execution on that table. Just as the index in this manual helps you locate
information faster than if there were no index, an Oracle index provides a
faster access path to table data. Indexes are the primary means of reducing disk
I/O when properly used. "

The above statements are probably a bit misleasing.
I am sure there used to be an excellent description
about indexes in the Oracle (earlier versions) doco that said indexes CAN speed
up certain operations.
I can't find this in the 8.1.7 doco but it was a much better
explanation of index benefits and drawbacks.

March 18, 2002 Rosidi -- Thanks for the question regarding "Reallocate Cluster", version 8.1.6
You Asked
Hi,
I want to shrink my SYSTEM Tablespace, but I got this cluster c_file#_block# at
the end of IT. So Oracle gives me an error
ORA-03297.
The question is can I reallocate the cluster to other tablespace ?
and we said...
Nope, you'll not really ever be shrinking system (unless you put stuff at the
"end", and immediately drop it before we extend out -- very very rare to
actually be able to shrink system).

You cannot move SYS objects.  Even if you "can", you should NEVER move SYS
objects without support saying "its ok".  In this case, I can tell you -- you
won't be moving that object.  That object tracks space usage AND you are wanting
to change space usage -- chicken and egg.
Review & Followup
Rating: 3
March 18, 2002
Reviewer: Roidi Mohd from Malaysia
Thanks, Tom

March 18, 2002 frederic -- Thanks for the question regarding "Wrap and dynamic SQL", version 8.1.7
You Asked
Hello Tom,

We want to encrypt our pl/sql sources using wrap utility but there is
a little problem. We must sometimes use dynamic sql and these queries
appears in clear in the result of the wrap utility.

Is there a way to hide the strings used for dynamic sql ?

Thank a lot.
and we said...
it'll be ugly, but you can obscure them yourself and store them in HEX.

Suppose you have the query "select * from emp" and want to hide it.  Well:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select utl_raw.cast_to_raw( 'select * from
emp' ) from dual;

UTL_RAW.CAST_TO_RAW('SELECT*FROMEMP')
---------------------------------------------------------------------------------
--------------------------------------------------
73656C656374202A2066726F6D20656D70

will obscure it.  Now, you use that string in your PLSQL instead of 'select *
from emp'.  In the code to get it back, you will:

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_output.put_line(
utl_raw.cast_to_varchar2( '73656C656374202A2066726F6D20656D70' ) );
select * from emp

PL/SQL procedure successfully completed.

But -- at this point -- you have to ask yourself "is it really worth it" --
especially since ALL WILL BE SEEN in v$sql at the end of the day anyway (and
there is NO WAY to hide that).

You can wrap your logic, you cannot wrap dynamically executed stuff -- that'll
be in v$sql no matter what you do.
March 18, 2002 Anindya -- Thanks for the question regarding "Create New Index for a query degrades Other queries", version 8.1.6
You Asked
Hi Tom,

   I have two parts to my question. One, I had created an index on a table for a
particular index, but the optimizer started using the index for other queries
too and this caused huge performace degradation in some cases (1 report went
from 15 mins refresh time to 3.5 hours). Now, would doing an analyze of the
table right after creating the index have enabled the CBO to make a more
informed choice and not use that index. I am unable to put hints to prevent the
use of the index as there could be hundreds of reports running on that table.

The second part to my question is , is there an easier way of finding out which
queries are using a table rather than going through individual reports/screens
accessing the database?

Regards,
Anindya
and we said...
The RBO tends to take the last index created that makes sense.  I don't see why
a suitable index would do this -- you, might want to analyze this further to see
what the issue is.

see
http://asktom.oracle.com/~tkyte/tkprof.html
for how to trace this -- you should run the query with a hint to use various
indexes and see what the differences are.

Now, as for doing an analyze -- I would say that in 8i you should be using the
CBO for pretty much everything.  All objects should be analyzed.  However -- if
you did not build that way, you might find that some things run slower out of
the box (some will run faster, some might just stay the same).  You need to test
this -- just like you should have tested the addition of a new index before
putting it out into production (slight jab there, the only way to keep
production running is to test things and then put them out there.  Everything,
no matter how small you think it is, can have a ripple effect)

As for the second part -- if you used REF CURSORS and put the SQL in the
database in a stored procedure -- it would be trivial (and tuning would be oh so
much easier as well).  We would set up a dependency between the procedure and
the base tables/views.  This level of information is not kept about cursors
(transient objects that come and go).  You can use v$sql and try to parse out
the tables...
March 18, 2002 Michael -- Thanks for the question regarding "SQL Server 7.0 conversion", version 8.1.7
You Asked
Hi Tom,
I am currently attending th DBA 8i tract. I have completed the PL/SQL
intro., and has accepted a position in an Oracle shop. My first task
is to convert several SQL Server scripts to Oracle. My situation is that
I have not worked on either products before..
Could you advise on how I can convert the following script to an
Oracle stored procedure..I am contemplating using context, corsors,
packages, and procedures.
I would appreciate very much any help..a conversion with comments of the
following would get me started.
 I thank you in advance.

> CREATE procedure prLIVE_SALE_SOLD_RPT
>   @StartDate datetime,
>   @EndDate   datetime,
>   @Site      varchar(4),
>   @Make      varchar(20),
>   @Model     varchar(20),
>   @Sponsor   varchar(30)
>
> as
>
>   set nocount on
>
>   declare
>     @Year varchar(4),
>     @Make1 varchar(50),
>     @Model1 varchar(50),
>     @vin varchar(17),
>     @Sku varchar(50),
>     @SellingPrice decimal(11,2),
>     @ReservePrice decimal(11, 2),
>     @DealerId int,
>     @DealerName varchar(50),
>     @City varchar(40),
>     @Province varchar(2),
>     @NumberOfBids int,
>     @NumberOfUniqueBidders int,
>     @NumberOfVehiclesOffered int,
>     @TotalNumberOfUniqueBidders int,
>     @IsArchived char(1),
>     @sponsorId int,
>     @sellerName varchar(50),
>     @eventId int
>
>
>
>
>     create table #result(
>       event_id int null,
>       site_code varchar(4) null,
>       [Year] varchar(4) null,
>       Make varchar(50) null,
>       Model varchar(50) null,
>       vin varchar(17) null,
>       Sku varchar(50) null,
>       StartPrice decimal(11,2) null,
>       SellingPrice decimal(11,2) null,
>       ReservePrice decimal(11, 2) null,
>       StartTime datetime null,
>       EndTime datetime null,
>       DealerId int null,
>       DealerName varchar(50) null,
>       City varchar(40) null,
>       Province varchar(2) null,
>       NumberOfBids int null,
>       NumberOfUniqueBidders int null,
>       NumberOfVehiclesOffered int null,
>       TotalNumberOfUniqueBidders int null,
>       sellerName varchar(50) null,
>       IsArchived char(1) null
>     )
>
>   create table #tmp (
>     business_id int null
>   )
>
>    select @EndDate = DATEADD(day, 1, @EndDate)
>    select @Site = upper(rtrim(@Site))
>
>    select @SponsorId = 0
>    select @SellerName = 'ALL'
>
>    if upper(@Sponsor) != 'ALL'
>    begin
> select @SponsorId = sponsor_id from sponsor where name like @Sponsor
> + '%'
>         select @SellerName = name from sponsor where sponsor_id =
@sponsorId
>    end
>
>
>    insert into #result(
>       Sku,
>       SellingPrice,
>       ReservePrice,
>       event_id,
>       site_code,
>       StartPrice,
>       StartTime,
>       EndTime,
>       DealerId,
>       DealerName,
>       City,
>       Province,
>       IsArchived
>    )
>     select
>       x1.sku, x1.current_price, x1.reserved_price, x7.auction_event_id,
> substring(x1.sku,1,4),
>       x1.start_price, x7.start_timestamp, x7.end_timestamp,
x3.business_id,
> x3.[name], x5.city, x6.prov_code, 'N'
>     from
>       auction_item as x1 join
>       ice_user as x2 on (x1.high_bid_acct_id = x2.[user_id]) join
>       business as x3 on (x2.business_id = x3.business_id) join
>       business_address as x4 on (x3.business_id = x4.business_id) join
>       address as x5 on (x4.address_id = x5.address_id) join
>       province as x6 on (x5.province_id = x6.province_id),
>       auction_event as x7
>     where
>       x1.current_price >= x1.reserved_price and
>       x1.start_date >= @StartDate and
>       x1.start_date <= @EndDate
>       and ((@Site = 'ALL') or (substring(x1.sku, 1, 4)= @Site))
>       and x1.auction_event_id = x7.auction_event_id
>       and ((@SponsorId = 0) or (x7.sponsor_id = @SponsorId))
>
>
>
>     declare result_cursor cursor for
>     select sku from #result
>
>     open result_cursor
>     fetch next from result_cursor
>     into @sku
>     while @@FETCH_STATUS = 0
>     begin
>         begin
>         SELECT @Year = VALUE
>         FROM PRODUCT_SKU_DICTIONARY t1
>         WHERE (KEY_NAME = 'year') and SKU = @sku
>
>         SELECT @Make1 = t2.VALUE
>         FROM PRODUCT_SKU t1 INNER JOIN
>           PRODUCT_LOOKUP t3 ON (t1.PRODUCT_ID = t3.PRODUCT_ID) INNER JOIN
>           LOOKUP_DETAIL t2 ON (t3.LOOKUP_DETAILS_ID = t2.LOOKUP_DETAIL_ID)
>         WHERE (t2.KEY_NAME = 'make') and t1.SKU = @sku
>
>         SELECT @Model1 = t2.VALUE
>         FROM PRODUCT_SKU t1 INNER JOIN
>           PRODUCT_LOOKUP t3 ON (t1.PRODUCT_ID = t3.PRODUCT_ID) INNER JOIN
>           LOOKUP_DETAIL t2 ON (t3.LOOKUP_DETAILS_ID = t2.LOOKUP_DETAIL_ID)
>         WHERE (t2.KEY_NAME = 'model') and t1.SKU = @sku
>
>         SELECT @Vin = VALUE
>         FROM PRODUCT_SKU_DICTIONARY t1
>         WHERE (KEY_NAME = 'vin') and SKU = @sku
>
>         select @NumberOfBids = count(*) from
>           bid as x1 join
>           auction_item as x2 on (x1.auction_item_id = x2.auction_item_id)
>         where
>           x2.sku = @sku
>
>         select @NumberOfUniqueBidders = count(distinct x3.business_id)
from
>           bid as x1 join
>           auction_item as x2 on (x1.auction_item_id = x2.auction_item_id)
> join
>           ice_user as x3 on (x1.user_id = x3.user_id)
>        where
>           x2.sku = @sku
>
>       end
>
>         update #result
>       set
>         [year] = @Year,
>         make   = @Make1,
>         model  = @Model1,
>         vin    = @Vin,
>         NumberOfBids          = @NumberOfBids,
>         NumberOfUniqueBidders = @NumberOfUniqueBidders,
>         sellerName            = @SellerName
>       where sku = @sku
>
>       fetch next from result_cursor
>       into @sku, @isarchived
>     end
>
>     close result_cursor
>     deallocate result_cursor
>
>
>     declare resultCursor cursor for
>     select distinct(event_id)
>     from #result
>
>     open resultCursor
>     fetch next from resultCursor
>     into @eventId
>
>     while @@FETCH_STATUS = 0
>     begin
>
>
>   insert into #tmp
>   select x3.business_id
>   from auction_item as x1 join
>         bid as x2 on x1.auction_item_id = x2.auction_item_id join
>         ice_user as x3 on x2.user_id = x3.user_id
>   where
>        x1.start_date >= @StartDate and
>        x1.start_date <= @EndDate and
>    x1.auction_event_id = @eventId
>
> --    ((@Site = 'All') or (substring(x1.sku, 1, 4) = @Site))
>
>          select @TotalNumberOfUniqueBidders = 0
>        select @NumberOfVehiclesOffered = 0
>
>    select @TotalNumberOfUniqueBidders = count(*) from #tmp
>    delete from #tmp
>
>     select @NumberOfVehiclesOffered = count(sku) from
>       auction_item
>     where
>       start_date >= @StartDate and
>       start_date <= @EndDate and
>       auction_event_id = @eventId
>
> --     ((@Site = 'ALL') or (substring(sku, 1, 4)= @Site))
>
>     select @NumberOfVehiclesOffered = @NumberOfVehiclesOffered +
count(sku)
> from
>       adesa_arc..auction_item
>     where
>       start_date >= @StartDate and
>       start_date <= @EndDate and
>       auction_event_id = @eventId
>
> --     ((@Site = 'ALL') or (substring(sku, 1, 4)= @Site))
>
>
>     update #result
>     set
>       TotalNumberOfUniqueBidders = @TotalNumberOfUniqueBidders,
>       NumberOfVehiclesOffered = @NumberOfVehiclesOffered
>       where event_id = @eventId
>
>       fetch next from resultCursor
>       into @eventId
>     end
>
>     close resultCursor
>     deallocate resultCursor
>
>
>
>
>
>     select * from #result
>     where
>      ((Upper(RTrim(@Make)) = 'ALL') or (make = @Make)) and
>      ((Upper(RTrim(@Model)) = 'ALL') or (model = @Model))
>     order by event_id
>
>
>
>
> /*
and we said...
oh, ouch -- that hurts my head.  Classic "SQLServer programming" tho -- use as
many temp tables as you can.  Yuck.

I think that 6 pages of "code" boils down to a single query.  Pretty sure I have
it right -- you might have to tweak it.  As I don't have the tables or anything
-- I couldn't really test it.

(we could do a simple "port" using temp tables and everything but that would
just be a waste of resources.  there is nothing here we couldn't do verbaitim,
its just not a good idea)

I turned all of their updates into inlined selects.  No need to
insert/update/delete/ and then finally query when all you really want to do is
-- QUERY.

create or replace package your_pkg
as
    type rc is ref cursor;

    procedure prLive_Sale_Sold_Rpt( p_startDate in date,
                                    p_endDate in date,
                                    p_site in varchar2,
                                    p_Make in varchar2,
                                    p_Model in varchar2,
                                    p_sponsor in varchar2,
                                    p_cursor in out rc );
end;
/

create or replace package body your_pkg
as
procedure prLive_Sale_Sold_Rpt( p_startDate in date,
                                p_endDate in date,
                                p_site in varchar2,
                                p_Make in varchar2,
                                p_Model in varchar2,
                                p_sponsor in varchar2,
                                p_cursor in out rc )
as
begin
    open p_cursor for
     'select x1.sku, x1.current_price SellingPrice,
             x1.reserved_price ReservePrice,
             x7.auction_event_id event_id,
             substring(x1.sku,1,4) site_code,
             x1.start_price, x7.start_timestamp StartTime,
             x7.end_timestamp endTime,
             x3.business_id dealerId,
             x3.name DealerName, x5.city, x6.prov_code Province,
             ''N'' isArchived,
             (select value
                from product_sku_dictionary t1
               where (key_name=''year'') and sku = x1.sku ) year,
             (select t2.value
                from product_sku t1, product_lookup t3, lookup_detail t2
               where t1.product_id = t3.product_id
                 and t3.lookup_details_id = t2.lookup_detail_id
                 and t2.key_name = ''make''
                 and t1.sku = x1.sku ) make,
             ( select t2.VALUE
                 from product_sku t1, product_lookup t3, lookup_detail t2
                where t3.LOOKUP_DETAILS_ID = t2.LOOKUP_DETAIL_ID
                  and t1.PRODUCT_ID = t3.PRODUCT_ID
                  and t2.KEY_NAME = ''model''
                  and t1.SKU = x1.sku ) model,
             ( select value
                 from product_sku_dictionary t1
                where (key_name=''vin'') and sku = x1.sku ) vin,
             ( select count(*) from bid t1, auction_item t2
                where t1.auction_item_id = t2.auction_item_id
                  and t2.sku = x1.sku ) numberOfBids,
             ( select count(distinct t3.business_id)
                 from bid t1, auction_item t2, ice_user t3
                where t1.auction_item_id = t2.auction_item_id
                  and t1.user_id = t3.user_id
                  and t2.sku = x1.sku ) numberOfUniqueBidders,
             ( select nvl( max(name), ''ALL'' )
                 from sponsor
                where name like :p_sponsor || ''%'') sellerName,
             ( select count(*)
                 from auction_item t1, bid t2, ice_user t3
                where t1.auction_item_id = t2.auction_item_id
                  and t2.user_id = t3.user_id ) TotalNumberOfUniqueBidders,
             ( select count(sku)
                 from auction_item
                where start_date between :p_startDate and :p_endDate
                  and auction_event_id = x7.auction_event_id )
NumberOfVehiclesOffered
        from auction_item as x1,
             ice_user        x2,
             business        x3,
             business_address x4,
             address          x5,
             province         x6,
             auction_event    x7
       where x1.high_bid_acct_id = x2.user_id
         and x2.business_id = x3.business_id
         and x3.business_id = x4.business_id
         and x4.address_id = x5.address_id
         and x5.province_id = x6.province_id
         and x1.current_price >= x1.reserved_price and
         and x1.start_date between :p_startDate and :p_endDate
         and and ((upper(rtrim(:p_Site)) = ''ALL'') or (substring(x1.sku, 1, 4)=
upper(rtrim(:p_Site))))
         and x1.auction_event_id = x7.auction_event_id
         and ((:p_Sponsor = ''ALL'') or (x7.sponsor_id = (select sponsor_id from
sponsor where name like :p_sponsor || ''%'') ))
         and (upper(rtrim(:p_make)) = ''ALL'' or make = :p_make)
         and (upper(rtrim(:p_model))= ''ALL'' or model = :p_model)
       order by x7.auction_event_id'
       using p_sponsor , p_startDate , p_endDate, p_startDate , p_endDate,
                p_Site, p_Site, p_Sponsor, p_sponsor , p_make, p_make,
                p_model, p_model;

end;

end;
/

and point your developers to:
http://osi.oracle.com/~tkyte/ResultSets/index.html
for examples using ref cursors in various 3/4gl languages.
Review & Followup
Rating: 5
Michael Bacchus March 19, 2002
Reviewer: A reader
Thank you Tom. This along with help from the Oracle forum
will be most helpful in getting me started. I will provide
better feedback later.

Rating: 5
Michael Bacchus March 19, 2002
Reviewer: Michael Bacchus from Canada
Thank you Tom. This along with help from the Oracle forum
will be most helpful in getting me started. I will provide
better feedback later.


Subject | Article | Last Updated (East coast time)
forms prompting me do u want to save changes | article only | Mar 19, 2002 09:10 am
How could I perform an equijoin on a LONG column? | article only | Mar 19, 2002 09:28 am
JDBC-ODBC from inside the database | article only | Mar 19, 2002 09:32 am
Generation of CSV files | article only | Mar 19, 2002 09:39 am
Foreign Key question | article only | Mar 19, 2002 10:22 am
oerr ora 6512 | article only | Mar 19, 2002 10:30 am
What is the meaning of ROWID? | article only | Mar 19, 2002 04:34 pm
Nested query and optimization | article only | Mar 19, 2002 07:30 pm
Dynamic SQL | article only | Mar 19, 2002 08:18 pm
Deletion | article only | Mar 20, 2002 03:11 pm
Updation Time Span | article only | Mar 20, 2002 04:50 pm
Create users in Oracle Portal | article only | Mar 20, 2002 07:22 pm
In list question with bind variables. | article only | Mar 21, 2002 04:05 pm
Radio botton/ check box, updating multiple rows using OWA | article only | Mar 21, 2002 07:09 pm
selecting from the same cursor twice | article only | Mar 22, 2002 08:12 am
OCIPlogon and SID | article only | Mar 22, 2002 08:24 am
I wonder about redo log tuning.... | article only | Mar 22, 2002 08:44 am
Alter Table Privilege | article only | Mar 22, 2002 09:26 am
Where is my table? | article only | Mar 22, 2002 10:14 am
Simulate On_Commit Trigger Behaviour From PL/SQL | article only | Mar 22, 2002 01:55 pm
Bind Variables | article only | Mar 22, 2002 03:58 pm
Consequences of not Separating Redo and Datafiles | article only | Mar 22, 2002 04:17 pm
Creating object type but is there a limit to what you can define in the type? | article only | Mar 22, 2002 04:34 pm
inline views | article only | Mar 23, 2002 08:18 pm
Optimizing query for response time | article only | Mar 24, 2002 03:44 pm
How can I avoid a table scan? | article only | Mar 24, 2002 04:25 pm
Cross Tab Report | article only | Mar 24, 2002 04:36 pm
Delete current row in after insert trigger | article only | Mar 25, 2002 08:16 am
row(s) 1 - 28 of 28
March 19, 2002 Ugandhar -- Thanks for the question regarding "forms prompting me do u want to save changes", version forms6i
You Asked
i have a post-query trigger in my block whcih is used to populate the detail
records when i execute query.I am able to populate the records the records but
it's prompting me for "do you want to save the changes".I am only querying the
records and am not performing any DML Statements on it.Please help me in this
regard.bye
and we said...
use the SET_RECORD_PROPERTY builtin after making the changes to set the status
of the record to QUERY_STATUS.

It is because you are modifying the record, forms believes it has changed.
Review & Followup
Rating: 2
How to suppess Do you want to save changes March 19, 2002
Reviewer: Abdul Samed from Chennai, Tamil Nadu, India
Hi,
This solutions is excellent for setting the record status in Post Query.

I would like to know, is there any way to suppress 'Do you want to save
changes?' message in Master - Detail record while navigating to the next master
record after entering the details of the first record.

I do not want to use POST built-in.

Your help is highly appreciated.

Thanks & Regards
Abdul.

Followup:
Well, if you navigate to the next master record -- the detail block is cleared.
If you do NOT POST the changes, you will LOSE the changes.  Is that what you
want?

Why not POST the changes??? I mean, the records are already locked in the
database, no one else is going to lock them (so it cannot be that you don't want
to lock them as they are already locked).  No one else will see the POST'ed
changes except for your session (until you commit).

POSTing the data is the only technically correct answer.  It is what you need
do.

March 19, 2002 Robin -- Thanks for the question regarding "How could I perform an equijoin on a LONG column?", version 8.1.7
You Asked
Hi Tom,

Many thanks for your site and your book! Let me give you some background on my
question. (Sorry if I'm being rather long-winded.)

I have two tables which are range partitioned on a date field, one partition per
month; there's a "sliding window" where the oldest partition is dropped once a
month and a new one added. The partition names are re-used. The partitions in
both tables are named MONTH01, MONTH02 and so on.  Each partition of A takes
about 1GB (data and index).

Table B is a summary table containing a roll-up of the data in Table A; the
roll-up involves a join with a third table C.  The data in table C changes from
time to time, invalidating the roll-up.  Therefore on a periodic basis I
truncate B and re-roll A into B.
Note that partition MONTH01 in table A might not be for the same month as
MONTH01 in table B.

I have been asked not to do the roll-up in one (huge) transaction.
What I would like to do is roll one partition at a time, if I could.

I would like to use CTAS (Create Table As Select) to create a temporary table
rollup of table A partition (month01), and then Exchange the temporary table
with one of the partitions of the partitioned table.  But I need to find out the
partition name in table B which has the same high value as partition(month01) in
table A.

I can't do it by joining dba_tab_partitions to itself because the high_value
column is a LONG.  Do you have any suggestions?

(I'll be using Pro*C.)

Thank you very much!
and we said...
why not do it in one big transaction?  Use UNRECOVERABLE and parallel -- it'll
go fast, not generate redo/rollback and it will be the easiest approach.

Anyway -- if you must do it the hard way (which will involve more resources on
the server then the easy way), you can:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function high_value_str(
p_table_owner in varchar2,
  2
       p_table_name  in varchar2,
  3
       p_partition_name in varchar2 )
  4  return varchar2
  5  is
  6          l_text long;
  7  begin
  8          select high_value into l_text
  9            from dba_tab_partitions
 10           where table_owner = p_table_owner
 11             and table_name = p_table_name
 12             and partition_name = p_partition_name;
 13
 14          return substr( l_text, 1, 4000 );
 15  end;
 16  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from
  2  (
  3  select table_owner, table_name, partition_name,
  4  high_value_str(table_owner, table_name, partition_name ) high_value,
  5  rownum r
  6  from dba_tab_partitions
  7  )
  8  where high_value like '%0'
  9  /

TABLE_OWNE TABLE_NA PARTITIO HIGH_VALUE          R
---------- -------- -------- ---------- ----------
OPS$TKYTE  P        PART2    10000               1
OPS$TKYTE  P        PART3    20000               2
OPS$TKYTE  P        PART1    5000                3

ops$tkyte@ORA817DEV.US.ORACLE.COM>

The rownum inclusion is intentional.  Try running the query with/without it to
see why.  I do not want the predicate on the function to be merged into the
view, it'll take far too long.  Subqueries with rownum won't be "merged" into.
March 19, 2002 -- Thanks for the question regarding "JDBC-ODBC from inside the database", version 9.0.1
You Asked
Hi Tom,

I need to access SQL-Server from inside Oracle Database. We don't want to use
GateWays. Can I run JDBC-ODBC bridge code in JAVA to be run under Oracle JVM?

Thanks.

Sudhakar.
and we said...
You can load their THIN (type4) jdbc driver into the database (pure java).  You
cannot load a JDBC-ODBC bridge as they almost always include native code and we
don't let you do native code in the database (paranoid about memory corruption,
it could kill us big time).
Review & Followup
Rating: 5
March 19, 2002
Reviewer: Sudhakar from USA
Thanks,

It worked.

March 19, 2002 Sitarama -- Thanks for the question regarding "Generation of CSV files", version 9.0.1
You Asked
Hi Tom,
I have been to trying to submit a question to you...finally i got the chance. I
am working in an environment where a lot of reports are generated in CSV
formats. These reports will be downloaded by members so that they can upload the
same at their back offices. We are writing a different procedure for each
report. My question is, is there a way to have a single procedure which
generates CSV files for all the reports just by passing some input parameters.
Thing is, the source of information for each of these report can be obtained
directly from a table or through a join between multiple tables. One idea which
was thought about was storing the query text itself in a table and pass the text
as a parameter to the common procedure. Ofcourse, the procedure should generate
separate files for each member. Common procedure will be as follows.

CREATE OR REPLACE PROCEDURE Generate_Csv (vc2_report_id IN varchar2,dt_stlmnt
DATE) as
   TYPE ViewCurTyp IS REF CURSOR;
   Cur_1 ViewCurTyp;
   v_col_list     VARCHAR2(4000);
   p_filedir      VARCHAR2(200) :='/ccil/reports/dev1';
   p_filename   VARCHAR2(200);
   v_filehandle SYS.UTL_FILE.FILE_TYPE;
BEGIN
  SELECT text
    INTO sql_stmt
    FROM report_mst
   WHERE report_id = vc2_report_id;
  p_filename := 'test.csv';

--Sample sql_stmt is as follows....
--SELECT DISTINCT    MBM_MBR_ID, MBM_NDS_MBR_ID, MBM_MBR_NAME,
--       SAT_TXN_DATE,SAT_TXN_ID, SAT_VCHR_NO, SAT_INM_ISIN,
--     SAT_DR_CR_IND, SAT_QNTY,SAT_BSNS_SGMNT, SAT_BSNS_SUB_SGMNT,
--       INM_DSCRPTN
--  FROM MBR_MST , INSTRMNT_MST, SGL_ACNT_TXN
-- WHERE MBR_MST.MBM_MBR_ID = SGL_ACNT_TXN.SAT_MBM_MBR_ID
--   AND INSTRMNT_MST.INM_ISIN = SGL_ACNT_TXN.SAT_INM_ISIN
--   AND INSTRMNT_MST.INM_GNRC_SPCFCTN =
--                       SGL_ACNT_TXN.SAT_INM_GNRC_SPCFCTN
--   AND SAT_TXN_DATE = dt_sett_date **(dt_sett_date is passed during
--                                      the runtime as I/p parameter
--                                      to the common procedure)
-- ORDER BY SAT_TXN_DATE ASC,SAT_TXN_ID ASC

  v_filehandle:=UTL_FILE.FOPEN(p_filedir,p_filename,'W');

  OPEN Cur_1 FOR sql_stmt;
  LOOP
  FETCH Cur_1 INTO v_col_list;
  EXIT WHEN cur_1%NOTFOUND;
  UTL_FILE.PUT_LINE(v_filehandle,v_col_list);
  END LOOP;
  CLOSE Cur_1;
  UTL_FILE.FFLUSH(v_filehandle);
  UTL_FILE.FCLOSE(v_filehandle);
END;

We faced some problems in implementing this as we are not able to assign
"dt_sett_date" (variable in the query text) to "dt_stlmnt" (parameter passed to
the procedure).

Can you please help us out in resolving this issue. I shall be glad if you can
let me know if a better way exists to resolve the same. Hope I explained my
problem clearly. Please let me know in case of any clarification.

Thank you very much in advance.

Have a great day Tom....
and we said...
see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:235814350980
for my approach to this problem.  See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1288401763279
and
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2742950295172
for two approaches to using bind variables (important) with this approach.
Review & Followup
Rating: 4
Generate CSV files March 21, 2002
Reviewer: Sitarama Peruvel from India
Tom,
The links whch you have provided has helped me in arriving at a desirable
solution to my problem. Really thank you much.

March 19, 2002 chaitanya -- Thanks for the question regarding "Foreign Key question", version 8.1.6
You Asked
hi Tom,

I have parent child relationship in four tables.
Table tabp is parent table which has tabc as child, and table tabc1 is child of
tabc and so fourth. Now my data inserting pattern is like this. Each table has 5
columns with varchar2(50).

Each time rows inserted in tabp : 2 to 10
          rows inserted in tabc : 4 to 20
          rows inserted in tabc1 : 16 to 80
          rows inserted in tabc2 : 400 to 4000

I don't have frequent updates and deletes on these tables. What would be my
strategy of implementing the foreign key relationship ? Please come back asap.

Thanks in advance

Chaitanya
and we said...
You would use declaritive integrity.

You would read:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:292016138754
to figure out if you needed to INDEX the foreign keys in the child tables or
not.
March 19, 2002 Alvin -- Thanks for the question regarding "oerr ora 6512", version 8.1.6
You Asked
Hi,
I got a big trouble when I shutdown database
here bellow is the trace file:

Oracle8i Enterprise Edition Release 8.1.6.0.0 - 64bit Production
With the Partitioning option
JServer Release 8.1.6.0.0 - Production
ORACLE_HOME = /opt/oracle/product/8.1.6
System name:    SunOS
Node name:    gis-db
Release:    5.7
Version:    Generic_106541-12
Machine:    sun4u
Instance name: gis
Redo thread mounted by this instance: 1
Oracle process number: 140
Unix process pid: 7729, image: oracle@gis-db

*** 2002-02-24 02:00:13.286
*** SESSION ID:(146.4766) 2002-02-24 02:00:13.283
Error in executing triggers on instance shutdown
*** 2002-02-24 02:00:13.286
ksedmp: internal or fatal error
ORA-00604: error occurred at recursive SQL level 1
ORA-29516: Aurora assertion failure: Assertion failure at eox.c:187
Uncaught exception Root of all Java exceptions:
ORA-06512: at "SYS.DBMS_JAVA", line 0
ORA-06512: at line 2
SHUTDOWN: waiting for active calls to complete.
*** 2002-02-24 02:10:34.401
SHUTDOWN: waiting for active calls to complete.
*** 2002-02-24 02:15:45.502
SHUTDOWN: waiting for active calls to complete.
*** 2002-02-24 02:20:56.564
SHUTDOWN: waiting for active calls to complete.

Regards,

Alvin

*****

I use shutdown immediate....
I did cold backup at 2:00 am last Sunday(cron), but I found that the DB still
shutting down in the Monday morning(I did not dump full trace file), so, I used
shutdown abort and then startup the db.

I can't believe, two days for shutdown?? there must be something wrong.
and we said...
(looks like 6 hours, not 2 days but anyway....)

Well, here is my opinion on unattended shutdowns.

Shutdowns should NEVER be done in a script -- never.  Shutdowns should never be
done unattended.  When do things break?  During shutdown and startup -- thats
when most things "go wrong".  Suppose you updated your init.ora during the week
and added a parameter.  Unbeknownest to you -- you made a typo.  Now when you
shutdown and startup -- the startup fails.  When your users come in monday
morning -- you are "hosed", the system is down, they have to wait for you to
arrive and fix it.

There is NEVER a need to shutdown the database for something as simple as a
backup.  NEVER.  Hot backups are not significantly more difficult then cold.  If
you don't shutdown/startup -- you'll not only have significantly more uptime,
you won't experience any nasty surprises.  The only time my database instances
have been down unexpectedly was due to unattended startups/shutdowns and
something going wrong.

Every notice how lightbulbs always seem to burnout when you turn them on?

So my advice -- don't shutdown unless you are sitting there.  I remember the
last time I rebooted by server remotely.  It was about 7 years ago now.  I had
to drive 45 miles on a sunday morning just to hit "ctl-d" cause the disk failed
an fschk and was waiting for someone to hit ctl-d to continue.  So -- i don't
reboot my servers unless I'm sitting there and I don't shutdown my databases
unless I'm sitting there.
Review & Followup
Rating: 1
help with ORA-29516 March 19, 2002
Reviewer: Jim Hill from Birmingham, AL
I was looking for help with ORA-29516.

Followup:
The course of action is clear then:

$ oerr ora 29516
29516, 00000, "Aurora assertion failure: %s"
// *Cause: An internal error occurred in the Aurora module.
// *Action: Contact Oracle Worldwide Support.

March 19, 2002 Omar -- Thanks for the question regarding "What is the meaning of ROWID?", version 8.1.7
You Asked
Hi Tom,
I know it is a selly question but I want to know !!
What is the meaning of ROWID ? How does it works? Does it generated by ORACLE
Engine during installation? How could I use it during development?
Please explain in details,
Thanks Tom,
Regards,
Omar.
and we said...
A rowid is a physical address of a row on disk -- it includes the FILE/ BLOCK
and slot on the block of the row.

It is assigned to a row when you insert it based on where the row physically
resides.

You do not in general use it. It is of very limited use. See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:927629362932
for an example of where you do use.
Review & Followup
Rating: 4
Good explanation March 20, 2002
Reviewer: Omar Foda. from Egypt
The explanation was very good and I really know now the meaning of ROWID,
Thanks Tom.

March 19, 2002 -- Thanks for the question regarding "Nested query and optimization", version 8.1.7
You Asked
Dear Tom,

I want to select customers with their most actual card.

I have two tables. CUSTOMERS is one to many CARDS.

CUSTOMERS
-----
CUSTOMER_ID INT,
CUSTOMER_ID_MASTER INT,
NAME VARCHAR2,
SURNAME VARCHAR2

CARDS
-----
CARD_ID,
CUSTOMER_ID,
STATUS

CUSTOMER_ID_MASTER points to CUSTOMER_ID. It's up to *one*
level deep tree.

I think this query would be quite optimal if it would
accept C.CUSTOMER_ID and C.CUSTOMER_ID_MASTER in the FROM statement.

SELECT C.CUSTOMER_ID, C.NAME, C.SURNAME,
   ((SELECT CARD_ID
     FROM (SELECT CARD_ID, DECODE (STATUS, 'U', 1,
                                           'N', 2,
                                           'P', 3,
                                           'V', 4,
                                           'X', 5) AS SORTING_ORDER
                   FROM CARDS
                   WHERE CUSTOMER_ID=C.CUSTOMER_ID
                   ORDER BY SORTING_ORDER)
     WHERE ROWNUM=1)) AS CARD_ID,
   ((SELECT CARD_ID
     FROM (SELECT CARD_ID, DECODE (STATUS, 'U', 1,
                                           'N', 2,
                                           'P', 3,
                                           'V', 4,
                                           'X', 5) AS SORTING_ORDER
                   FROM CARDS
                   WHERE CUSTOMER_ID=C.CUSTOMER_ID_MASTER
                   ORDER BY SORTING_ORDER)
     WHERE ROWNUM=1)) AS CARD_ID_MASTER
FROM CUSTOMERS C
WHERE UPPER(C.SURNAME) LIKE 'something%'

When I run this query I get:
                WHERE CUSTOMER_ID=C.CUSTOMER_ID
                                  *
ERROR at line 9:
ORA-00904: invalid column name

I get same error if I move those two selects statements
into WHERE clause I get same error.

It seems that I cannot use values from superqueries in
the FROM <query> statements.

I would like to avoid temporary tables. Is there any other way
to make this query optimal? I did find another way to do it,
but it was very very slow.

Result should be like this:

CUSTOMER_ID  NAME            SURNAME       CARD    CARD_MASTER
-----------  --------------- ---------- ---------- ----------
       551   NAME_1          SURNAME_1     1000363    1000363
       552   NAME_2          SURNAME_1     2000190    1000363
       634   NAME_3          SURNAME_1     1000413    1000413
       685   NAME_4          SURNAME_1     1000450    1000450
       686   NAME_5          SURNAME_1     2000238    1000450
      1183   NAME_6          SURNAME_1     1000787    1000787
      2537   NAME_7          SURNAME_1     2001328    2001326

Thanks, Damjan
and we said...
given that i have this data:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select c.customer_id, c.name, c.surname,
  2         cards.card_id, cards.customer_id, cards.status
  3    from customers c, cards
  4   where cards.customer_id = c.customer_id_master
  5   order by decode(status,'U',1,'N',2,'P',3,'V',4,'X',5)
  6  /

CUSTOMER_ID NAME       SURNAME       CARD_ID CUSTOMER_ID STATUS
----------- ---------- ---------- ---------- ----------- ----------
          1 tom        kyte       1914578998           1 U
          2 mary       smith       221726209           2 U
          3 george     jones      1470613633           3 U
          1 tom        kyte        685194559           1 N
          2 mary       smith      2134443361           2 N
          3 george     jones      1956568948           3 N
          1 tom        kyte        128287500           1 P
          2 mary       smith       586121870           2 P
          3 george     jones       271948309           3 P
          1 tom        kyte        931369049           1 V
          2 mary       smith       714790523           2 V
          1 tom        kyte       1283286789           1 X

12 rows selected.

and that I believe you want the output of the three bolded rows, you can try
the following queries

ops$tkyte@ORA817DEV.US.ORACLE.COM> select customer_id, name, surname,
  2         to_number( substr( data, 3, 11 ) ) card_card_id,
  3         to_number( substr( data, 15, 11 ) ) card_customer_id
  4   from (
  5  select c.customer_id, c.name, c.surname,
  6         (select MIN( decode(status,'U',1,'N',2,'P',3,'V',4,'X',5) || '/' ||
  7                      to_char( card_id, 'fm00000000009' ) || '/' ||
  8                      to_char(customer_id,'fm00000000009' ) )
  9            from cards
 10           where customer_id = c.customer_id_master ) data
 11    from customers c
 12    )
 13  /

CUSTOMER_ID NAME       SURNAME    CARD_CARD_ID CARD_CUSTOMER_ID
----------- ---------- ---------- ------------ ----------------
          1 tom        kyte         1914578998                1
          2 mary       smith         221726209                2
          3 george     jones        1470613633                3

ops$tkyte@ORA817DEV.US.ORACLE.COM>

that works by using a MIN trick -- glue all of the fields of interest together
-- putting the "min fields" (or max) first.  Make sure they are sortable as
strings and you'll get all of your data (without having to use 2 subqueries) in
a string that you can substring the data back out

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select customer_id, name, surname,
  2         to_number( substr( data, 3, 11 ) ) card_card_id,
  3         to_number( substr( data, 15, 11 ) ) card_customer_id
  4   from (
  5  select c.customer_id, c.name, c.surname,
  6         MIN( decode(cards.status,'U',1,'N',2,'P',3,'V',4,'X',5) || '/' ||
  7              to_char( cards.card_id, 'fm00000000009' ) || '/' ||
  8              to_char(cards.customer_id,'fm00000000009' ) ) data
  9    from cards, customers c
 10   where cards.customer_id = c.customer_id_master
 11   group by c.customer_id, c.name, c.surname )
 12  /

CUSTOMER_ID NAME       SURNAME    CARD_CARD_ID CARD_CUSTOMER_ID
----------- ---------- ---------- ------------ ----------------
          1 tom        kyte         1914578998                1
          2 mary       smith         221726209                2
          3 george     jones        1470613633                3

this is simply a variation on the above -- instead of running the subquery for
each row -- just do an aggregate

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select distinct c.customer_id, c.name,
c.surname,
  2         first_value(cards.card_id) over
  3              ( partition by c.customer_id
  4                order by decode(cards.status,'U',1,'N',2,'P',3,'V',4,'X',5) )
  5                card_card_id,
  6         first_value(cards.customer_id) over
  7              ( partition by c.customer_id
  8                order by decode(cards.status,'U',1,'N',2,'P',3,'V',4,'X',5) )
  9                card_customer_id
 10    from cards, customers c
 11   where cards.customer_id = c.customer_id_master
 12  /

CUSTOMER_ID NAME       SURNAME    CARD_CARD_ID CARD_CUSTOMER_ID
----------- ---------- ---------- ------------ ----------------
          1 tom        kyte         1914578998                1
          2 mary       smith         221726209                2
          3 george     jones        1470613633                3

ops$tkyte@ORA817DEV.US.ORACLE.COM>

And this used analytic functions to do much the same.
Review & Followup
Rating: 5
first_value applied on ( function_call()) March 19, 2002
Reviewer: steve mckee from Montreal
Hi Tom,

what would happen if we added another column to the select list that was a
function call. For example:

select ...
   first_value(some_function(cards.card_id, cards.status))
          over ( partition by c.customer_id
                 order by decode(cards.status,'U',1,'N',2,'P',3,'V',4,'X',5) )
            some_column_name

If a particular partition has 5 rows, will the function be called only once or 5
times (I'm assuming once but I may be way off here).

I tried to write a dummy finction that inserted a row in a table to see how many
times it is called but that didn't work (ORA-14551).

I'm assuming this is what happens:

   o apply the decode
   o order the result
   o choose the values from the first row
   o call the function with the values from this row.

Followup:
It is actually called possibly many many times.  For example:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function f return number
  2  as
  3  begin
  4          dbms_output.put_line( 'I was called' );
  5          return 1;
  6  end;
  7  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t as select 1 x, rownum y from
all_objects where rownum <= 5;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select x, first_value(f()) over ( partition
by x order by decode( y, 1, 5, 2, 4, 3, 3, 4, 3, 5, 1 ) )
  2    from t
  3  /

         X
FIRST_VALUE(F())OVER(PARTITIONBYXORDERBYDECODE(Y,1,5,2,4,3,3,4,3,5,1))
----------
----------------------------------------------------------------------
         1
1
         1
1
         1
1
         1
1
         1
1

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec null
I was called
I was called
I was called
I was called
I was called
I was called

PL/SQL procedure successfully completed.

It was called 6 times -- once for each row, and once again to retrieve the
first_value i suppose.

It'll build the result set (calling the function), partition the data, order it
and then find the first_value typically.

Rating: 5
more info on first_value(function_call)) March 19, 2002
Reviewer: steve mckee from Montreal
I did manage to write a dummy funtion that did a select sequence.nextval.
Although my partition only had 4 rows (when I removed the "distinct"), the
function was called 8 times (or at least the sequence.nextval increased by 8).

Now I'm totally confused....

March 19, 2002 Satish -- Thanks for the question regarding "Dynamic SQL", version 9i
You Asked
Hi Tom,
I am begining to understand the power of dynamic sql and with help from your
book, I was able to avoid building 28 seperate JSP pages, beans etc., just by
using the pl/sql module of 9ias and using dbms_sql.
I currently want to write a generic procedure wherein the number of inputs to
the procedure are not the same. The inputs would vary depending on the tablename
which would be one of the parameter passed to it.
Could you give me a hint or point me to a a link wherein you have already
answered this question or if you could help me out with it.

Thanks Tom

Satish
and we said...
Don't get carried away.  Like anything, too much of dynamic sql is not a good
thing.

If you are trying to make a generic "inserter" routine, capable of inserting
into any table -- I think that would be very bad.  I mean - what benefit would
you get out of it?  Nothing as far as I can see.  The end user (client) still
has to do exactly the same amount of work as they would otherwise.  The database
on the other hand has to do a ton more work.

The other problem is that all of the inputs to this generic routine would have
to be homogenous types (well, I suppose you could use the sys.anydata type but
now we are getting REALLY obscure).  You would have to pass everything as a
string -- not good.

You can use a collection type to pass varying numbers of elements into a
procedure easily.

create type varargs as table of varchar2(4000);
/

create procedure bar( x in number, y in number, z in varargs )
as
....

and then call:

exec bar( 1, 2, varargs( 'a', 'b', 'c' ) );
exec bar( 3, 4, varargs( 'd', 'e', 'f', 'g', 'h' ) );

but -- I don't think you are going down the right path.
Review & Followup
Rating: 3
March 19, 2002
Reviewer: Satish from Seattle,WA
Thanks for your reply Tom, I understand the impact of having a generic stt like
that.
Well I think I spelt it wrong. Its not for any table in the schema. I 've got a
bunch of validation tables..(lookup tables) which changes probably once a year.
I felt its better to have a generic script just for this module so that I dont
have to  to have a seperate JSP page and associated stored procedures built for
each lookup table maintenance.
Need to try it out I guess..how much effort I save.

March 20, 2002 Ak -- Thanks for the question regarding "Deletion ", version 8.1.5
You Asked
Hi tom,
   1)I have deleted 400,000 rows from a tables,Here Aim is to have
More space in database.Ofcourse dba_free_space doesn't show anything
Because high water mark will Not Get reset,But i would like to know
In the allocated extent how much Got free after deletion.

2)As I understand, Now for Newly 400,000 record ,database will Not
allocate NEW space,please correct me if i am wrong.

3)After deletion i have Noticed Chained row ratio has Gone to 34 %,earlier it
was 1%..what could have happened.

4)My another question is ...I have a PRODUCTION DATABASE which uses
RBO.It has lot of query where it uses function in WHERE CLAUSE..
MEANING....where to_char(date) > 'some condition '.So Obvisouly
index is Not Getting Used on date field.
Now I am thinking to analyze ONLY certain table and create a function
index,but Here i am little worried since i am analyzing those table
it will use CBO for all the query for those table(the one which i analyzed).With
this it is possible that it MIGHT become slow beacuse
of plan change,What i want to ensure is Once i feel it has become
slow i want to revert back to previous One i:e RBO.
can I achive this by means of DELETE STATISTICS COMMAND.Any side
effect of this ??

Thanks,
  Regards
     Ak
and we said...
See
http://www.oracle.com/oramag/oracle/02-mar/index.html?o22asktom.html
this is exactly one of the topics I cover this month in Oracle Magazine....

As for the chained row ratio -- I assume you are computing the number of chained
rows vs non-chained rows.  What happened here is that the 400,000 rows you
removed -- the majority of them were not chained, leaving behind a higher ratio
of chained rows to unchained rows.

4) this is a good opportunity for a HINT.  This is one of the times when I find
a hint useful.

Create the FBI.  Hint just that query (that query will then use the CBO).  Hint
that query to use that FBI.  That way you do not need to worry about
destabalizing your production instance, just tuning this one query.
March 20, 2002 Yogesh -- Thanks for the question regarding "Updation Time Span", version 8.1.7
You Asked
I have written following code, to update table

CREATE OR REPLACE PROCEDURE Update_Lastname
(
    p_OutputStatus        OUT     NUMBER,
    p_OutputMessage     OUT     VARCHAR2
)
AUTHID Current_User
AS
Type Ref_Cursor_Type        IS REF CURSOR;
Cust_Ref_Cur                Ref_Cursor_Type;
v_Customercode                NUMBER;
v_CommitVar                    NUMBER:=0;
BEGIN
OPEN Cust_Ref_Cur FOR SELECT Customercode FROM Customer_Master;
LOOP
    EXIT WHEN Cust_Ref_Cur%NOTFOUND;
    FETCH Cust_Ref_Cur INTO v_Customercode;
    UPDATE Customer_master set LASTNAME = SUBSTR(LASTNAME,1,2)
    WHERE Customercode = v_Customercode;
    IF v_CommitVar = 25 THEN
        COMMIT;
        v_CommitVar :=0;
    END IF;
END LOOP;
EXCEPTION
    WHEN OTHERS THEN
        p_OutputStatus     := -1;
        p_OutputMessage     :=     'Failed In ' ||Substr(SQLERRM,1,100) ||
                                ' Customercode ' || v_Customercode;
END;

Which took nearly 14-15 hrs.  No of records in Customer Master 620000 appx.
Initally I tried the same by simply using the Update command but in that case
RBS got full and I could not continue the operation. So to avoid that I wrote
above scipt.

I want to reduce time span took for the exection. Here No of records were very
less...  What modification we can make to make it faster ?  Here Customercode is
PK.  Plz Advice
and we said...
Well, you took the wrong approach to the problem.  Instead of trying to fix this
with procedural code (which requires your time, your effort, debugging, lots of
wasted cycles) you should have increased the rollback segments.

This should quite simply be:

update customer_master set lastname = substr( lastname, 1, 2 );

And thats all -- that'll be

o the fastest

o generate the least amount of redo and rollback in total

o cost the very very least -- even if you have to buy 100gig of disk to do it
(consider how much money has been wasted trying to fix this and how much more
will be wasted tuning it???)

Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> desc t
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 OWNER                               NOT NULL VARCHAR2(30)
 OBJECT_NAME                         NOT NULL VARCHAR2(30)
 SUBOBJECT_NAME                               VARCHAR2(30)
 OBJECT_ID                           NOT NULL NUMBER
 DATA_OBJECT_ID                               NUMBER
 OBJECT_TYPE                                  VARCHAR2(18)
 CREATED                             NOT NULL DATE
 LAST_DDL_TIME                       NOT NULL DATE
 TIMESTAMP                                    VARCHAR2(19)
 STATUS                                       VARCHAR2(7)
 TEMPORARY                                    VARCHAR2(1)
 GENERATED                                    VARCHAR2(1)
 SECONDARY                                    VARCHAR2(1)

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from t;

  COUNT(*)
----------
    553696

And now (after rewriting your code to actually COMMIT -- you never committed in
your code -- also you exit when should be below your FETCH, else you process the
last row twice.  Additionally -- i optimized your code to use rowid instead of
primary key).

I cannot IMAGINE this taking 14-15 hrs, couple of minutes maybe.  You need to
look at your system.  the very fact that it WORKS means you have sufficient
rollback to do this in a single statement.

Here is the example comparing the two approaches:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table run_stats ( runid varchar2(15),
name varchar2(80), value int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> grant select any table to ops$tkyte;

Grant succeeded.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view stats
  2  as select 'STAT...' || a.name name, b.value
  3        from v$statname a, v$mystat b
  4       where a.statistic# = b.statistic#
  5      union all
  6      select 'LATCH.' || name,  gets
  7        from v$latch;

View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> column name format a40
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_start number;
  3      l_data  varchar2(255);
  4  begin
  5      insert into run_stats select 'before', stats.* from stats;
  6
  7      l_start := dbms_utility.get_time;
  8      update t set object_name = substr(object_name,1,2);
  9      for x in ( select used_ublk from v$transaction )
 10      loop
 11          dbms_output.put_line( 'Rollback used ' || x.used_ublk );
 12      end loop;
 13      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 14      commit;
 15      insert into run_stats select 'after 1', stats.* from stats;
 16
 17      l_start := dbms_utility.get_time;
 18
 19      declare
 20          Type Ref_Cursor_Type        IS REF CURSOR;
 21          Cust_Ref_Cur                Ref_Cursor_Type;
 22          v_Customercode              rowid;
 23          v_CommitVar                    NUMBER:=0;
 24      BEGIN
 25          OPEN Cust_Ref_Cur FOR SELECT rowid FROM t;
 26          LOOP
 27              FETCH Cust_Ref_Cur INTO v_Customercode;
 28              EXIT WHEN Cust_Ref_Cur%NOTFOUND;
 29
 30              UPDATE t set object_name = SUBSTR(object_name,1,2)
 31               WHERE rowid = v_Customercode;
 32              IF v_CommitVar = 25 THEN
 33                  COMMIT;
 34                  v_CommitVar :=0;
 35              else
 36                   v_commitVar := v_commitVar + 1;
 37              END IF;
 38          END LOOP;
 39          commit;
 40      END;
 41
 42      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 43
 44      insert into run_stats select 'after 2', stats.* from stats;
 45  end;
 46  /
Rollback used 6439
25100 hsecs
25396 hsecs

PL/SQL procedure successfully completed.

So, what we've seen is that this won't necessarily generate GOBS of rollback
(it'll be a function of the original size of the column you are shortening of
course).  Now, looking at the stats (did this on a single user system)

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value-a.value run1,
c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11   order by abs( (c.value-b.value)-(b.value-a.value))
 12  /

NAME                                           RUN1       RUN2       DIFF
---------------------------------------- ---------- ---------- ----------
...
STAT...data blocks consistent reads - un          0     116456     116456
do records applied

STAT...consistent changes                         0     116457     116457
STAT...no work - consistent read gets          7537     531104     523567
STAT...buffer is not pinned count              7550     553712     546162
STAT...table scan blocks gotten                7534     553696     546162
STAT...execute count                              7     553702     553695
STAT...calls to get snapshot scn: kcmgss         24     596309     596285
STAT...consistent gets                         7565     677714     670149
STAT...session logical reads                 574222    1305196     730974
LATCH.library cache                            1425    1108810    1107385
STAT...recursive calls                           36    1128723    1128687
LATCH.cache buffers chains                  2853026    4510895    1657869
STAT...redo size                          135996524  143585372    7588848

75 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

Look at the extra work performed by the procedural approach!.  7.5meg more redo.
 Lots more blocks read.  Tons more latching (locks), etc.

Do the single update.
Review & Followup
Rating: 3
Update taking time March 19, 2002
Reviewer: Yogesh from PUNE, India
Well I have executed first without using PROC, I have done the updation of
FirstName using query only. Which also took appx 15 hrs. ..... what can be the
reason ??  Is it because of type of DB ? While installation I installed it with
DSS option.

Followup:
Well, you need to apply some diagnosing to this problem....

Do you have a trigger on the table that fires for update?

Do you have really really tiny logs?

Do you see messages like "checkpoint not complete, cannot allocate new log" in
your alert log?

Do you see messages like "archival required, cannot allocate new log"

Are there other processes that are accessing this table, preventing you from
updating a row (are you getting blocked).

Did you run statspack on this system during the update to see what the major
wait events are?

What have you done to diagnose this yourself?

Rating: 5
March 20, 2002
Reviewer: A reader
Hello Tom,

In our application most of the business logic are applied at the database level
using triggers. Most of the tables have one or more database triggers that does
lot of inserts and update to other tables.

Is this a good approach or would you advice me to write stored procedures that
can be called from the application level to do the same?

Thank you

Followup:
If you can do your logic in triggers and the triggers are understandable -- go
for it.

I personally find it almost impossible to do all of the "business logic" in a
trigger in practice.  A trigger is fired due to an insert/update or delete.  A
BUSINESS transaction typical consists of multiple insert/updates/deletes.  I
find a trigger many times has insufficient information to do the business level
logic and a procedure is necessary (and sometimes what happens due to an insert
into a table should be different depending on what process initiates it).

Rating: 2
Diagnose March 20, 2002
Reviewer: Yogesh from Pune, India
Well there is no trigger on this table !! Db is not in Archive Mode. When this
process was in execution no other process was accessing this DB. what is
statspack ?

Yes one index is present on Customercode, Firstname, Lastname Could it be a
reason ? If this is a reason what could be a solution ? " Drop Index / Update /
Recreate Index " ?

Followup:
if you have my book, read chapter 10 -- it covers statspack.

Else read
http://www.oracle.com/oramag/oracle/00-Mar/index.html?o20tun.html
for a quick intro.

Yes, an index could affect that, definitely, HOWEVER - 15 hours seems too long.

I have a small e450 and tried this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t2_idx on t2(object_id, owner,
object_name) parallel 8 unrecoverable;
ops$tkyte@ORA817DEV.US.ORACLE.COM> @desc t2
Datatypes for Table t2

                               Data                 Data
Column Name                    Type                 Length      Nullable
------------------------------ -------------------- ----------- --------
OWNER                          VARCHAR2             30          not null
OBJECT_NAME                    VARCHAR2             30          not null
SUBOBJECT_NAME                 VARCHAR2             30          null
OBJECT_ID                      NUMBER                           not null
DATA_OBJECT_ID                 NUMBER                           null
OBJECT_TYPE                    VARCHAR2             18          null
CREATED                        DATE                 7           not null
LAST_DDL_TIME                  DATE                 7           not null
TIMESTAMP                      VARCHAR2             19          null
STATUS                         VARCHAR2             7           null
TEMPORARY                      VARCHAR2             1           null
GENERATED                      VARCHAR2             1           null
SECONDARY                      VARCHAR2             1           null

Indexes on t2

Index                          Is
Name                           Unique COLUMNS
------------------------------ ------ --------------------------------
T2_IDX                         No     OBJECT_ID, OWNER, OBJECT_NAME

Triggers on t2
ops$tkyte@ORA817DEV.US.ORACLE.COM> set timing on
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from t2;

  COUNT(*)
----------
    553824
Elapsed: 00:00:01.07
ops$tkyte@ORA817DEV.US.ORACLE.COM> update t2 set object_name =
substr(object_name,1,2);
Elapsed: 01:18:07.32
ops$tkyte@ORA817DEV.US.ORACLE.COM>

So, 1 hour, 18 minutes indexed, versus 4 minutes not.  Big difference but not 15
hours.

Yes, I would recommend dropping the index, do the update, create the index
(unrecoverable and parallel).  Overall that'll take MUCH less time and your
index will be in much better shape afterwards.

March 20, 2002 Mauriel -- Thanks for the question regarding "Create users in Oracle Portal", version 1.0.2.2.1
You Asked
Hi tom my question :
I have an aplication to manage 65,000 students in the university but we going to
use Oracle Portal to register students for internet if we already have an
student, profesors, administrative personal tables in university aplication
we have to create 65,000 Portal Users using API or exist some procedure to use
existing users to register via login server

Thanks tom..
and we said...
Sorry so long -- I had to ask around for this (don't program portal personally
myself).

Matt Piermarini responded with:

API docs on all of the portal security functions (add user, modify user, add
group, add user to group, etc):
http://portalstudio.oracle.com/pls/ops/docs/FOLDER/COMMUNITY/PDK/plsql/doc/sdk6pkg.htm

To add a user, wwsec_api.add_portal_user is the function to call.  Specific
docs are at:
http://portalstudio.oracle.com/pls/ops/docs/FOLDER/COMMUNITY/PDK/plsql/doc/sdk6apo.htm

So, to create a bunch of users,  you will have to create a procedure which
selects from the existing table and calls the wwsec_api.add_portal_user for
each user.
March 21, 2002 Mark -- Thanks for the question regarding "In list question with bind variables.", version
You Asked
Hi Tom,
I am using Pro*C and I need to send an array of strings
in a "where <value> in :<value>" is this possible?
And if not what should I do?
and we said...
In Oracle7, this is rather difficult.  You cannot bind in a
SINGLE value and expect it to be treated as MANY values (think
about how hard it would be to find something in an IN LIST that
had a "comma" in it for example).

In Oracle8.0 and up, this is easy with object types and nested
tables.  Just to see what that will look like, I'll show that -
then I'll show the v7 implementation.

ops$tkyte@8i> create or replace type myTableType
              as table of number;
  2  /
Type created.

ops$tkyte@8i> create or replace function in_list
                     ( p_string in varchar2 ) return myTableType
  2  as
  3      l_data             myTableType := myTableType();
  4      l_string           long default p_string || ',';
  5      l_n                number;
  6  begin
  7
  8    loop
  9      exit when l_string is null;
 10      l_data.extend;
 11      l_n := instr( l_string, ',' );
 12      l_data( l_data.count ) := substr( l_string, 1, l_n-1 );
 13      l_string := substr( l_string, l_n+1 );
 14    end loop;
 15    return l_data;
 16  end;
 17  /

Function created.

So, in Oracle8 and up we can "select * from PLSQL_FUNCTION" when
the function returns a SQL Table type as follows:

ops$tkyte@8i>
ops$tkyte@8i> select *
  2    from THE ( select cast( in_list('1,2,3,5,12')
                        as mytableType ) from dual ) a
  3  /

COLUMN_VALUE
------------
           1
           2
           3
           5
          12

Which means we can do that in a subquery:

ops$tkyte@8i> select *
  2    from all_users
  3   where user_id in
  4      ( select * from THE (
               select cast( in_list('1,2,3,5,12')
                      as mytableType ) from dual ) a )
  5  /

USERNAME                          USER_ID CREATED
------------------------------ ---------- ---------
SYSTEM                                  5 20-APR-99

In v7, we can do something very similar (works in 8.0 and up as
well) with plsql tables.  Its a little more work but not too
much.  It would look like this:

ops$tkyte@8i> create or replace package my_pkg
  2  as
  3          function in_list( p_string in varchar2,
                            p_idx in number ) return varchar2;
  4          pragma restrict_references( in_list,
                        wnds, rnds, wnps, rnps );
  5
  6          function get_list_count(p_string in varchar2)
                      return number;
  7          pragma restrict_references( get_list_count,
                            wnds, rnds, wnps, rnps );
  8
  9          pragma restrict_references( my_pkg,
                            wnds, rnds, wnps, rnps );
 10  end;
 11  /

Package created.

ops$tkyte@8i>
ops$tkyte@8i> create or replace package body my_pkg
  2  as
  3  function in_list( p_string in varchar2,
                       p_idx in number ) return varchar2
  4  is
  5          l_start number;
  6          l_stop  number;
  7  begin
  8    if ( p_idx = 1 ) then
  9         l_start := 1;
 10    else
 11         l_start := instr( p_string, ',', 1, p_idx-1 )+1;
 12    end if;
 13
 14    l_stop := instr( p_string ||',', ',', 1, p_idx );
 15
 16    return ltrim( rtrim( substr( p_string, l_start,
                                       l_stop-l_start ) ) );
 17  end;
 18
 19  function get_list_count( p_string in varchar2 )
             return number
 20  is
 21          l_cnt number default 0;
 22  begin
 23     for i in 1 .. 1000 loop
 24        exit when nvl( instr( p_string, ',', 1, i ), 0 ) = 0;
 25        l_cnt := i+1;
 26     end loop;
 27
 28     return l_cnt;
 29  end;
 30
 31  end;
 32  /

Package body created.

So, now I can code a query like:

ops$tkyte@8i> variable bindVar varchar2(255)
ops$tkyte@8i>
ops$tkyte@8i> exec :bindVar := '1, 3, 44, 1000'

PL/SQL procedure successfully completed.

ops$tkyte@8i>
ops$tkyte@8i> select my_pkg.in_list( :bindVar, rownum )
  2    from all_objects
  3   where rownum <=
         ( select my_pkg.get_list_count( :bindVar ) from dual )
  4  /

MY_PKG.IN_LIST(:BINDVAR,ROWNUM)
---------------------------------------
1
3
44
1000

All we need for that to work is a table with MORE rows then we
have IN LIST items -- all_objects is generally a good candidate.
 It works by using rownum as an "index" into the plsql table.

So, now we can simply:

ops$tkyte@8i> select *
  2    from all_users
  3   where user_id in (select my_pkg.in_list(:bindVar,rownum)
  4                       from all_objects
  5                      where rownum <=
          ( select my_pkg.get_list_count( :bindVar ) from dual )
  6                                    )
  7  /

USERNAME                          USER_ID CREATED
------------------------------ ---------- ---------
WEB$AZEILMAN                         1000 24-JUN-99
Review & Followup
Rating: 4
Followup March 19, 2002
Reviewer: Tim Glasgow from Belfast, Northern Ireland
Tom, thanks for re-directing me to this section. Is it correct to pass the
attribute name (as apposed to the value)to the in_list function and is the
function visible to the query at run time?

This is the relevant section from my code:
-----------------------------------------------------
dbms_session.set_context( 'MY_CTX', 'estateagentid', pc_estate_agent_id_in);

IF li_estateagentid_count = 0 THEN

sql_stmt := sql_stmt || ' and ea.estateagentid =  sys_context(
''MY_CTX'',''estateagentid'')';

ELSE

dbms_output.put_line('hi1');

sql_stmt := sql_stmt || ' and ea.estateagentid IN ( select * from THE ( select
cast( in_list(''estateagentid'') as mytableType ) from dual ) a )';

END IF;
---------------------------------------------------
The line of code after the ELSE is giving me the following error:

SQL> create or replace type myTableType as table of number;
  2  /

Type created.

SQL> @unit_tests/properties/test_all_nulls;
Input truncated to 1 characters
hi1
BEGIN
*
ERROR at line 1:
ORA-00904: invalid column name
ORA-06512: at "PROP.AGENTPROPERTYREPORTS", line 362
ORA-06512: at line 3

I've copied the in_list function like for like and inserted it into my package
body as a private function.

It's obviously something in the sql_stmt string at run time but I'm not sure
what. Any help would be greatly appreciated.
Thanks,
Tim

Followup:
in_list must be VISIBLE outside of the package and qualified.  Otherwise SQL
cannot "see it".

Rating: 5
Followup March 20, 2002
Reviewer: Tim Glasgow from Belfast
Tom, I have made the in_list function public and qualified it in the dynamic sql
as follows:
----------------------------------
sql_stmt := sql_stmt || ' and ea.estateagentid IN ( select * from THE ( select
cast( AgentPropertyReports.in_list(''estateagentid'') as mytableType ) from dual
) a )';
----------------------------------
Unfortunately something seems to be wrong with this as my sql*plus session is
not responding when I pass my procedure two estate agentid's in the format
'139,44'

Please help, I really need to find a way of making this work. Would it be
possible to send you an attachment of the code?
Thanks,
Tim

Followup:
just put the code here, in a followup.  A cut and paste from sqlplus would be
great.

Rating: 5
Followup March 21, 2002
Reviewer: Tim Glasgow from Belfast
Tom,thanks for your message.. here's the code. I couldn't send sql*plus outputs
as sql is locking up when I pass multiple estate agents. So I've pasted in the
entire package and the 2 relevant procedures. Not that it makes much difference,
but I'm passing the following inputs:

Thanks very much for your help. Tim

AgentPropertyReports.PropertyReport
(pc_start_date_in       => '01-OCT-2001'
,pc_end_date_in         => '31-OCT-2001'
,pc_period_type_in      => 'MONTH'
,pc_estate_agent_id_in  => '139,44'
,pi_property_type_id_in => null
,pc_type_in             => null
,outRS                  => rout);

/*----------------------*/
CREATE OR REPLACE PACKAGE AgentPropertyReports IS

TYPE RecordSet_cursor IS REF CURSOR;

PROCEDURE PropertyReport
(pc_start_date_in       IN VARCHAR
,pc_end_date_in         IN VARCHAR
,pc_period_type_in      IN VARCHAR DEFAULT 'MONTH'
,pc_estate_agent_id_in  IN VARCHAR2
,pi_property_type_id_in IN property.propertytypeid%TYPE
,pc_type_in             IN property.type%TYPE
,outRS                  OUT RecordSet_cursor);

FUNCTION in_list ( p_string IN VARCHAR2 ) RETURN myTableType;

CREATE OR REPLACE PACKAGE BODY AgentPropertyReports IS

FUNCTION in_list ( p_string IN VARCHAR2 )

RETURN myTableType AS

l_data             myTableType := myTableType();
l_string           LONG DEFAULT p_string || ',';
l_n                NUMBER;

BEGIN
LOOP
 EXIT WHEN l_string IS NULL;
 l_data.extend;
 l_n := instr( l_string, ',' );
 l_data( l_data.count ) := substr( l_string, 1, l_n-1 );
 l_string := substr( l_string, l_n+1 );
END LOOP;

RETURN l_data;

END in_list;

PROCEDURE PropertyReport
(pc_start_date_in       IN VARCHAR
,pc_end_date_in         IN VARCHAR
,pc_period_type_in      IN VARCHAR DEFAULT 'MONTH'
,pc_estate_agent_id_in  IN VARCHAR2
,pi_property_type_id_in IN property.propertytypeid%TYPE
,pc_type_in             IN property.type%TYPE
,outRS                  OUT RecordSet_cursor)AS

/*=====================================================
 * declare a variable to hold a dynamic sql statement
 *====================================================*/

sql_stmt        VARCHAR2(3000);

/*=======================================================
 * declare variables to hold the new start and end dates
 *======================================================*/

ld_new_start_date property.dateadded%TYPE := null;
ld_new_end_date   property.dateadded%TYPE := null;
li_estateagentid_count NUMBER(1) := 0;

BEGIN
--clear out the property_report table
EXECUTE IMMEDIATE 'truncate table property_report';

/*======================================================
 * call function set_date to get the correct start_date
 *=====================================================*/
if pc_start_date_in IS NULL
then
ld_new_start_date := set_date(pd_sd_date_in    => NULL
                             ,pc_range_type_in => gc_start);
else
ld_new_start_date := set_date(pd_sd_date_in    =>
to_date(pc_start_date_in,'DD-MON-YYYY')
                             ,pc_range_type_in => gc_start);
end if;

/*====================================================
 * call function set_date to get the correct end_date
 *===================================================*/
if pc_end_date_in is NULL
then
ld_new_end_date := set_date(pd_sd_date_in    => NULL
                           ,pc_range_type_in => gc_end);    else
ld_new_end_date := set_date(pd_sd_date_in    =>
to_date(pc_end_date_in,'DD-MON-YYYY')
                           ,pc_range_type_in => gc_end);
end if;

/*======================================================= * --has more than one
estate_agent_id has been passed in
*========================================================*/
IF pc_estate_agent_id_in IS NOT NULL
THEN
select instr(pc_estate_agent_id_in,',',1,1)
into   li_estateagentid_count
from   dual;

if li_estateagentid_count >0
then
li_estateagentid_count := 1;
else
li_estateagentid_count := 0;
end if;
END IF;

/*==============================================
* create a namespace for the context MY_CTX
* and associate the namespace with the package
*=============================================*/
EXECUTE IMMEDIATE 'create or replace context MY_CTX using
prop.AgentPropertyReports';

/*========================================================
* build up a dynamic sql statement depending on the *parameters
*========================================================*/
sql_stmt := 'INSERT INTO PROPERTY_REPORT ';
--monthly or weekly
IF pc_period_type_in = gc_period --'MONTH'
THEN
sql_stmt := sql_stmt || '  select
''01-''||to_char(p.dateadded,''MON-YYYY''),count(p.propertyid),round(avg(decode(p
.askingprice,0,null,p.askingprice))),0,0,0,0,0,0,0 ';
ELSE
sql_stmt := sql_stmt || '  select
TRUNC(p.dateadded,''DAY''),count(p.propertyid),round(avg(decode(p.askingprice,0,n
ull,p.askingprice))),0,0,0,0,0,0,0 ';
END IF;

--estate_agent_id
IF pc_estate_agent_id_in IS NOT NULL
THEN
  --set the context
  dbms_session.set_context( 'MY_CTX', 'estateagentid',
pc_estate_agent_id_in);

  sql_stmt := sql_stmt || '  from property p ';
  sql_stmt := sql_stmt || ' , estateagentlink eal ';
  sql_stmt := sql_stmt || ' , estateagent     ea ';
  sql_stmt := sql_stmt || ' where eal.propertyid =  p.propertyid ';
  sql_stmt := sql_stmt || ' and eal.estateagentid = ea.estateagentid ';
  sql_stmt := sql_stmt || ' and p.dateadded between :start_date and :end_date ';

--check if there is more than one estate agent id
IF li_estateagentid_count = 0
THEN
  sql_stmt := sql_stmt || ' and ea.estateagentid = sys_context(
''MY_CTX'',''estateagentid'')';
ELSE
  --I'VE PASSED IN MORE THAN ONE ESTATE_AGENT_ID
  sql_stmt := sql_stmt || ' and ea.estateagentid IN (  select * from THE (
select cast( AgentPropertyReports.in_list(''estateagentid'') as mytableType )
from dual ) a )';
END IF;
ELSE
sql_stmt := sql_stmt || ' from property p ';
sql_stmt := sql_stmt || ' where p.dateadded between :start_date and :end_date ';
END IF;

--property_id
IF pi_property_type_id_in IS NOT NULL
THEN
dbms_session.set_context( 'MY_CTX', 'propertytypeid', pi_property_type_id_in);
sql_stmt := sql_stmt || ' and p.propertytypeid = sys_context(
''MY_CTX'',''propertytypeid'')';
END IF;

--sale or rental
IF pc_type_in IS NOT NULL
THEN
dbms_session.set_context( 'MY_CTX', 'type', upper(pc_type_in));
sql_stmt := sql_stmt || ' and upper(p.type) = sys_context(
''MY_CTX'',''type'')';
END IF;

--monthly or weekly
IF pc_period_type_in = gc_period  --'MONTH'
THEN
sql_stmt := sql_stmt || ' group by to_char(p.dateadded,''MON-YYYY'')';
ELSE
--'WEEK'
sql_stmt := sql_stmt || ' group by TRUNC(p.dateadded,''DAY'')';
END IF;

 /*=====================================================
  * execute the sql statement depending on the input *parameters
 *========================================================*/
EXECUTE IMMEDIATE sql_stmt using ld_new_start_date,ld_new_end_date;
  commit;

Followup:
#1 -- get that execute immediate 'create or replace context' out of there.  You
want to avoid doing DDL, not do it as often as possible.  do the create or
replace as part of your install, not runtime.

#2 -- hopefully you are using temporary tables, you don't need to truncate them,
you could use a transaction level temporary table and just commit when you are
done.

do this to debug.

Instead of RUNNING the query, print the query out using dbms_output:

create or replace procedure p ( p_str in varchar2 )
is
   l_str   long := p_str;
begin
   loop
      exit when l_str is null;
      dbms_output.put_line( substr( l_str, 1, 250 ) );
      l_str := substr( l_str, 251 );
   end loop;
end;
/

so, collect a query without the IN and with the IN and use

set autotrace traceonly explain

to generate the query plans.  Lets see what the difference between them is.

Also, search this site for showsql and run that script sometime when this
"hangs" -- see if it is running the SQL and the sql is taking really long or if
its "hung" elsewhere.

Rating: 5
Followup March 22, 2002
Reviewer: Tim Glasgow from Belfast
Tom, I got it working ..no prizes for spotting the difference between these two:

---------------------------------------------------
sql_stmt := sql_stmt || ' and ea.estateagentid IN ( select * from THE ( select
cast( AgentPropertyReports.in_list(sys_context( ''NEW_CTX'',''estateagentid''))
as mytableType ) from dual ) a )';

sql_stmt := sql_stmt || ' and ea.estateagentid IN (  select * from THE (
select cast( AgentPropertyReports.in_list(''estateagentid'') as mytableType )
from dual ) a )';
---------------------------------------------------

Your reply put me on the right track and particularly your debug proc which was
extremely useful, thanks for posting it as I had been trying for ages to get my
sql_stmt output.

Also, you'll be pleased to hear I have removed my DDL!!

Another happy customer!
Thanks again,
Tim

March 21, 2002 Shruti -- Thanks for the question regarding "Radio botton/ check box, updating multiple rows using OWA", version 4.0.8.1
You Asked
Tom,

This new site is very user friendly.

I have a question regarding radio button/ checkbox.

I want to write a code in PL/SQl/owa.

I want to  select some rows based on some criteria and then I
want checkbox or radio button for each row and if I check and
submit the multiple rows I want to update those records for each
rows.

How can I do this?

Thanks,
Shruti
and we said...
See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:384818739348
for an example that should get you going.
Review & Followup
Rating: 3
radio buttons not addressed in answer. March 20, 2002
Reviewer: Job Miller from Dayton, OH
I thank you for this service.  It is truly invaluable to me.

Your solution addresses elements that can be named the same in a form.  sets of
radio buttons can only be named the same for that one set, unlike text
fields/checkboxes/etc..

Imagine you want a yes/maybe/no radio button set for every row returned.  How do
you name them so that your procedure can handle them without knowing ahead of
time how_many there are going to be?

Any thoughts or suggestions would be tremendously appreciated.  I am hoping that
since the original question asked specifically for you to address radio buttons,
you can re-address the original question or point to another resource that does.

The solution presented also relies on the browser to submit the values in the
form in the order in which they appear in the form.  Last I heard most browsers
handle it this way, although you cannot be guaranteed of this by the
specification.  (I have never seen the spec, just read the Perl CGI docs which
make this claim as well)

Thanks Tom!

Followup:
You cannot use radio buttons like that for the very reasons you point out -- the
browser won't POST any data for a radio button that is not on.  The arrays get
"messed" up.

I use a drop down list with the words YES/MAYBE/NO in it.  The user toggles this
list.  The list always has value -- hence the indexes into the array correspond
to the rows themselves.

I can only address that which can be done.  Since radio buttons explicitly
CANNOT be passed when set null (see below), you cannot use them in this generic
fashion.

The browsers post the data in that order, if they did not -- virtually 100% of
web based apps would break.

See
http://www.w3.org/MarkUp/html-spec/html-spec_8.html#SEC8.2
...
The fields are listed in the order they appear in the document with the name
separated from the value by `=' and the pairs separated from each other by `&'.
Fields with null values may be omitted. In particular, unselected radio
buttons and checkboxes should not appear in the encoded data, but hidden fields
with VALUE attributes present should. (26)
...

that is from

Hypertext Markup Language - 2.0
September 22, 1995
T. Berners-Lee (1)
D. Connolly (1)
(1) MIT/W3C

Rating: 5
Thanks again March 21, 2002
Reviewer: Job Miller from Dayton, OH
Initially I wasn't referring to the fact that NULLs screw up the order.  Even if
the radios are all set, you still can not use radio buttons.

Each set would have to have a unique form element name, otherwise they would
function as one set of radios.  In most other web languages(perl/php) this would
be ok because you could name each set something different dynamically, but not
in pl/sql where the parameters to the procedure have to be defined ahead of
time.  An array won't work, because the form will only give you one value for
300 radio elements all named the same thing (100 DB rows with 3 radio choices
each)

I overlooked the obvious.  Thank you for pointing it out.

Job

Followup:
Well, you might be interested in:
http://technet.oracle.com/docs/products/ias/doc_library/1022doc_otn/apps.102/a90099/feature.htm#1006742
then.  Instead of passing formal named parameters, you can pass an array of
Names and an array of values to the procedure, sort of like getting a
QUERY_STRING but already parsed.

Rating: 5
Great! March 22, 2002
Reviewer: Job Miller from Dayton, OH
Thank you for your additional response.  That was exactly what I was looking
for.

March 22, 2002 Vanitha -- Thanks for the question regarding "selecting from the same cursor twice", version 8i
You Asked
I have a situation where I have to get the meaning for a lookup value in an API
but I will have to call the cursor twice to get the meaning for the Lookup since
I want meaning for two different status codes. My query would ideally be like
this

   select MEANING
   from as_statuses_vl
   where enabled_flag = 'Y'
   and opp_flag = 'Y'
   and status_code = pc_status_code;

But I have to execute this query twice to get meaning for status codes 'WON' and
'ACTIVE'. Another problem is I have to select them into two different PL/SQL
variables. i.e meaning of WON into an variable and meaning of ACTIVE into an
another variable. Is it possible to do in a single tranaction efficiently
and we said...
select max( decode( status_code, 'WON', meaning, null ) )
       max( decode( status_code, 'ACTIVE', meaning, null ) )
  into l_var1, l_var2
  from t
 where enabled_flag = 'Y'
   and opp_flag = 'Y'
   and status_code in ( 'WON', 'ACTIVE' )
/

Here is a quick (parameterized -- so WON ACTIVE are not "hard coded" example
showing how to use it and how it performs against individual selects.  A 'nice'
side effect (depends on how you look at it actually) is that the max will ALWAYS
return at least one row and at most one row -- so watch out for that.  (eg: if
you have more then one meaning for a give status_code, enabled_flag, opp_flag --
you'll get the MAX meaning.  If you have NO observation for a given tuple,
you'll get a NULL meaning.  we need to code exception blocks for that using the
singleton selects)

ops$tkyte@ORA817DEV.US.ORACLE.COM> @runstats
ops$tkyte@ORA817DEV.US.ORACLE.COM> /*
DOC>
DOC>drop table t;
DOC>
DOC>create table t
DOC>( status_code varchar2(10), enabled_flag varchar2(1), opp_flag varchar2(1),
meaning varchar2(80),
DOC>  primary key (status_code,enabled_flag,opp_flag) )
DOC>/
DOC>
DOC>exec gen_data( 'T', 500 );
DOC>insert into t values ( 'WON', 'Y', 'Y', 'This is the meaning won' );
DOC>insert into t values ( 'ACTIVE', 'Y', 'Y', 'This is the meaning active' );
DOC>commit;
DOC>
DOC>*/
ops$tkyte@ORA817DEV.US.ORACLE.COM>  select * from t where status_code like 'A%';

STATUS_COD E O MEANING
---------- - - --------------------------------------------------------
ACTIVE     Y Y This is the meaning active
ACdqSo]hoe E W tCRrdpO^^ZSclJM[dlDNiiWbngYGor[GlLlplhcPYmqmMpXBfaBcZ[DnijD_
AEl[EFTcV[ [ ] ^pp^UXgEPAG\gZ]ORHH`XUqC_SNUTHGlMGsC]\LjOXNg_UjVM[OnoW`IOJri
AJRBmbpdot M B GtNmeCCVjCNOXffASEU]SrWaeCgTQqdqMHtSCREPKYfXoCBKgQLMrcckBALB
AMmt_[TRNY s H YB_IgYqLQ]RcNMBWc`srAKg]gcYIaDohCBAFNmfsEQSNYnVNGRPNceeKdCQT
AVVjXBgUBH c f mTUaboINUHYDlVAH[piarTVH[VNpsjREEB[\\pHm[iKlkWrrq\Bs\EnSib\`
AZ__JGaOON ` j Yc]TjrnUdIQEFBlgHOi\gci_T`LZSKBPZ_c^FpL[TdcG\WRQVUjBpTTmNinH

7 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select max( decode( status_code, 'WON',
meaning, null ) ),
  2         max( decode( status_code, 'ACTIVE', meaning, null ) )
  3    from t
  4   where enabled_flag = 'Y'
  5     and opp_flag = 'Y'
  6     and status_code in ( 'WON', 'ACTIVE' )
  7  /

MAX(DECODE(STATUS_CODE,'WON',MEANING,NULL))
--------------------------------------------------------------------------------
MAX(DECODE(STATUS_CODE,'ACTIVE',MEANING,NULL))
--------------------------------------------------------------------------------
This is the meaning won
This is the meaning active

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table run_stats;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table run_stats ( runid varchar2(15),
name varchar2(80), value int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> grant select any table to ops$tkyte;

Grant succeeded.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view stats
  2  as select 'STAT...' || a.name name, b.value
  3        from v$statname a, v$mystat b
  4       where a.statistic# = b.statistic#
  5      union all
  6      select 'LATCH.' || name,  gets
  7        from v$latch;

View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> column name format a40
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_start number;
  3      l_data  varchar2(255);
  4      l_var1  t.meaning%type;
  5      l_var2  t.meaning%type;
  6      l_code1 varchar2(20) default 'WON';
  7      l_code2 varchar2(20) default 'ACTIVE';
  8  begin
  9      insert into run_stats select 'before', stats.* from stats;
 10
 11      l_start := dbms_utility.get_time;
 12
 13      for i in 1 .. 1000
 14      loop
 15          select max( decode( status_code, l_code1, meaning, null ) ),
 16                 max( decode( status_code, l_code2, meaning, null ) )
 17            into l_var1, l_var2
 18            from t
 19           where enabled_flag = 'Y'
 20             and opp_flag = 'Y'
 21             and status_code in ( l_code1, l_code2 );
 22      end loop;
 23      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 24      commit;
 25      insert into run_stats select 'after 1', stats.* from stats;
 26
 27      l_start := dbms_utility.get_time;
 28
 29      for i in 1 .. 1000
 30      loop
 31          begin
 32              select meaning into l_var1
 33                from t
 34               where enabled_flag = 'Y'
 35                 and opp_flag = 'Y'
 36                 and status_code = l_code1;
 37          exception
 38              when no_data_found then l_var1 := NULL;
 39          end;
 40          begin
 41              select meaning into l_var2
 42                from t
 43               where enabled_flag = 'Y'
 44                 and opp_flag = 'Y'
 45                 and status_code = l_code2;
 46          exception
 47              when no_data_found then l_var2 := NULL;
 48          end;
 49      end loop;
 50      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 51
 52      insert into run_stats select 'after 2', stats.* from stats;
 53  end;
 54  /
27 hsecs
33 hsecs

PL/SQL procedure successfully completed.
so, that shows the decode trick can be "faster", looking at stats and latches
now:

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value-a.value run1,
c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11   order by abs( (c.value-b.value)-(b.value-a.value))
 12  /

NAME                                           RUN1       RUN2       DIFF
---------------------------------------- ---------- ---------- ----------
...
STAT...calls to get snapshot scn: kcmgss       1007       2006        999
STAT...execute count                           1005       2005       1000
STAT...recursive calls                         1032       2032       1000
LATCH.library cache                            2271       4263       1992

42 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

It does less latching which is always a good thing (more scalable)...
Review & Followup
Rating: 5
March 22, 2002
Reviewer: Vanitha from Redwood Shores, CA USA
Thanks tom. This is very helpful for me

March 22, 2002 Prince -- Thanks for the question regarding "OCIPlogon and SID", version 8.1.7
You Asked
Hi Tom,

Thank you for the response to my prev questions. I have a question on OCIPlogon
(persistent connection).

Suppose my program is conneting to the DB using OCIPlogon and sets some session
level variable. (say dbms_application.set_client_info). If anothet instance of
the program  executes, it may be possible to reuse the connection. Will the new
session be treated as a different session?  (I noticed the same sid for many
requests) If not, will it be able to read the session level value set by another
instance of the program (say a .html program).

Thanks,
and we said...
I am not familar with an Oracle API called OCIPLogon.  It is not one of ours.

A search on google tells me it is a PHP specific function, one they wrote.

It would appear to just be a regular old fashion connection.  Each program
instance will have its OWN session.  Variables will not be shared as far as I
know but - you would really have to ask the makers of PHP what the intention of
this function is
Review & Followup
Rating: 3
I tried some tests March 22, 2002
Reviewer: Prince.
Thanks Tom,

As u found, its a php function. I was under the impression, they wrap some
equivalent OCI calls inside the function.

Anyway, I did some testing (say set some session values using
DBMS_APPLICATION_INFO.SET_CLIENT_INFO) and found that another session (if gets
the same SID, the possibilty is so high with persistent conn) after the current
session goes away, can read the value.

March 22, 2002 Ji Sook -- Thanks for the question regarding "I wonder about redo log tuning....", version 8.1.5
You Asked
Hi!

Please share your knowledge

1. Log switches occur frquently, and subsequently, checkpoints occur

Mon Feb  4 11:06:03 2002
Thread 1 advanced to log sequence 210820
  Current log# 4 seq# 210820 mem# 0: /data/oradata/SMS_DB/redo41.log
  Current log# 4 seq# 210820 mem# 1: /data1/oradata/redo42.log
Mon Feb  4 11:09:29 2002
Thread 1 advanced to log sequence 210821
  Current log# 5 seq# 210821 mem# 0: /data1/oradata/redo51.log
  Current log# 5 seq# 210821 mem# 1: /user/oracle/oradata/SMS_DB/redo52.log
Mon Feb  4 11:13:41 2002
Thread 1 advanced to log sequence 210822
  Current log# 6 seq# 210822 mem# 0: /data1/oradata/redo61.log
  Current log# 6 seq# 210822 mem# 1: /user/oracle/oradata/SMS_DB/redo62.log

Log file has 6 group, each 2 members and size is 3 mega

init_.ora parameter

    log_checkpoint_interval = 10000
    log_checkpoint_timeout = 1800

    db_block_buffer = 16384

It doesn't happen to cannot allocate new log, checkpoint not complete.

What can i do....?
Do I resize of the redolog files?

2. I don't know what is wrong....

Would you please tell me what is wrong...

set echo off

drop table sqltemp;

create table sqltemp
(addr varchar2(16),
 sql_text varchar2(2000),
 disk_reads number,
executions number,
parse_calls number);

set echo on

CREATE or REPLACE PROCEDURE do_explain (
  addr varchar2,
  sqltext varchar2) as
  dummy varchar2(3000);
  mycursor integer;
  ret integer;
  mysqlerm varchar2(85);

BEGIN
   dummy :='explain plan set statement_id=';
   dummy := dummy ||''''||addr||''''||'for '||sqltext;

   mycursor := dbms_sql.open_cursor;
   dbms_sql.parse (mycursor, dummy, dbms_sql.v7);
   ret := dbms_sql.execute (mycursor);
   dbms_sql.close_cursor (mycursor);
   commit;

EXCEPTION
  WHEN OTHERS THEN
  mysqlerm :=substr(sqlerrm,1,80);
  insert into plan_table(statement_id,remarks) values(addr, mysqlerm);
  commit;

END;
/

declare
    cursor c1 is select address, sql_text, DISK_READS, EXECUTIONS, PARSE_CALLS
    from v$sqlarea
    where command_type in (2,3,6,7)
    and parsing_schema_id != 0;
    cursor c2 is select addr, sql_text from sqltemp;
    v_addr2 varchar(16);
    v_sqltext v$sqlarea.sql_text%type;
    v_dreads v$sqlarea.disk_reads%type;
    v_execs v$sqlarea.executions%type;
    v_pcalls v$sqlarea.parse_calls%type;

begin
    open c1;
    loop
    fetch c1 into v_addr2,v_sqltext,v_dreads,v_execs,v_pcalls;
    insert into sqltemp values(v_addr2,v_sqltext,v_dreads,v_execs,v_pcalls);
    commit;
    exit when c1%notfound;
    end loop;

    close c1;

    open c2;
    loop
    fetch c2 into v_addr2, v_sqltext;
    do_explain(v_addr2,v_sqltext);
    commit;
    exit when c2%notfound;
    end loop;
    close c2;
end;
/

=> result is

ORA-01000: maximum open cursors exceeded
ORA-06512: at "GWSMS.DO_EXPLAIN", line 22
ORA-00942: table or view does not exist
ORA-06512: at line 28

Thank you....
and we said...
1) well, why do you believe this to be a problem?  are you experiencing some
measurable, identifiable performance issue elsewhere?  If not, switching logs
every 4 minutes is not outrageous, not at all.

I would say however that if you are switching logs every 4 minutes, you are an
OLTP system -- OLTP systems benefit from small blocksizes -- not big one (eg: we
run our tpc-c's with a 2k block).  a 16k block wouldn't be my first choice for
such a database....

If you want the logs to switch less frequently for whatever reason (but I'm not
a fan of doing things "just because it makes me feel better", I'd want to know
exactly what I could expect to gain from such an exercise, some metric, some
benchmark, some quantifiable benefit), you would make the logs bigger.

2) You are trying to generate explain plans for every query in the shared pool.
You are doing this as a single user (say "BOB" is the owner of the procedure).
BOB is not able to parse the VAST MAJORITY of these statements.  BOB is getting
the error:

ERROR at line 1:
ORA-00942: table or view does not exist
ORA-06512: at "SYS.DBMS_SYS_SQL", line 824
ORA-06512: at "SYS.DBMS_SQL", line 32
ORA-06512: at "BOB.DO_EXPLAIN", line 14
ORA-06512: at line 28

hundreds or thousands of times.  You are logging this error into a log table but
every time you do that -- you leak a DBMS_SQL cursor (you opened the cursor but
you NEVER close it).

IMHO -- a when others that is not followed by a RAISE is almost always a BUG.
Your solutions:

1) just protect the statement you expect to fail with the exception block:

   BEGIN
   dummy :='explain plan set statement_id=';
   dummy := dummy ||''''||addr||''''||'for '||sqltext;

   mycursor := dbms_sql.open_cursor;
   begin
      dbms_sql.parse (mycursor, dummy, dbms_sql.v7);
      ret := dbms_sql.execute (mycursor);
    exception
      when others
      then
        null;
    end;
   dbms_sql.close_cursor (mycursor);
   commit;

-- EXCEPTION
  -- WHEN OTHERS THEN
  -- mysqlerm :=substr(sqlerrm,1,80);
  -- insert into plan_table(statement_id,remarks) values(addr, mysqlerm);
  -- commit;

END;

2) use the easier "execute immediate"

CREATE or REPLACE PROCEDURE do_explain (
  addr varchar2,
  sqltext varchar2)
as
  mysqlerm varchar2(85);

BEGIN
    execute immediate 'explain plan set statement_id=' ||''''||addr||''''||'for
'||sqltext;
EXCEPTION
  WHEN OTHERS THEN
  mysqlerm :=substr(sqlerrm,1,80);
  insert into plan_table(statement_id,remarks) values(addr, mysqlerm);
  commit;

END;
/

among others.

I personally find your above approach to be a TOTAL performance drain.  I would
generate the PLAN on the fly -- when I needed it, rather then generate the plan
for every single query in the worl -- flooding my shared pool with unique
explain plan statements, killing performance, causing tons of hard parses, and
so on....
Review & Followup
Rating: 5
It's very useful March 24, 2002
Reviewer: Ji Sook
Thank you very much Tom

March 22, 2002 Venkateswara Reddy -- Thanks for the question regarding "Alter Table Privilege", version 8.1.5
You Asked
Hi Tom

Thanks for your help for my previous questions. Here I am facing some problem.

Right now I am able to controle for creating a table via restricting the
CREATING PRIVILEGE for some users.But I am not able to restrict them to ALTER
TABLE . For Ex ALTER TABLE T ADD SOMECOL VARCHAR2(20); Some thing like this. I
would like to prevent them to alter a table for set of tables say starting with
"BUDG%" . How can I prevent them to alter a tables directly or indirectly.

Thanks in advance

Reddy
and we said...
You can use ddl event triggers, look at the object name and fail the ALTER via
raise_application_error.

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:267415465220
for an example of a DDL trigger.
Review & Followup
Rating: 3
Alter table privelege March 20, 2002
Reviewer: R.v.Reddy from Bangalore, India
Thanks Mr. Tom it is very usefull for me , Here it is createing a log, but I
want no one should be able to alter table how can I do with this example.

Thanks

Reddy

Followup:
as i said:

You can use ddl event triggers, look at the object name and fail the ALTER via
raise_application_error.

instead of logging, call raise application error and fail it.

Rating: 2
Alter Table Privelege March 22, 2002
Reviewer: Reddy from Bangalore, India
Mr. Tom

I have executed the what you gave the code(following link). But my database is
8.1.5 Here under V$OPEN_CURSOR there is no ORA_SYSEVENT column. How can I do in
8.1.5

Followup:
sorry about that.  ora_sysevent (the function) was added in 816 (Oracle 8i
release 2).

So, you don't have ora_sysevent.  What you'll need to do is create three
triggers, one for DROP, one for CREATE, one for ALTER -- then the ora_sysevent
is "implied".

March 22, 2002 Venu -- Thanks for the question regarding "Where is my table?", version 8i
You Asked
Hi Tom,
    This is very Simple one but I am not getting the reason.
    I am in a process of transferring data from excel
sheets(Given by the client)to Oracle Database(8i). I have no
Idea whether Oracle can read data from excel sheet directly,
therefore I started transferring data from excel to MS Access
and then to Oracle using ODBC. I am quite happy with process as
it is very simple but now  I have 2 questions to ask.
    1. If Access table has a col
"Released_in_the_middle_of_the_day"
having text datatype of size 1 why does the ODBC call fails. The
error message is Identifier is too long. After I altered the
column with a short name like R_Day it exported successfully.
    2. See the following case. The data is imported from Access
database. For any commands it says table or view does not exists
but lists the table when I query the Tab.
SQL>  select * from tab where tname like 'O%';

TNAME                          TABTYPE  CLUSTERID
------------------------------ ------- ----------
OFFSHORE_ADDRESS               TABLE
OFFSHORE_ASSOCIATE             TABLE
OFFSHORE_REPORTING             TABLE
ONSITE_ADDRESS                 TABLE
ONSITE_ASSOCIATE               TABLE
ONSITE_COORDINATOR_RATE        TABLE
ONSITE_REPORTING               TABLE
ONSITE_TIME_SHEET              TABLE
OffRep                         TABLE

9 rows selected.

SQL> select * from OffRep;
select * from OffRep
              *
ERROR at line 1:
ORA-00942: table or view does not exist
and we said...
1)
scott@DEV816> select length('Released_in_the_middle_of_the_day'
) from dual;

LENGTH('RELEASED_IN_THE_MIDDLE_OF_THE_DAY')
-------------------------------------------
                                         33

the max column length in Oracle is 30, you will have to alias
that column in Access.

2)
Access is using "quoted identifiers".  It is preserving the case
of the object name in Oracle.  You would have to:

select * from "OffRep"

not

select * from OffRep

Suggest you make ALL access tablenames be in UPPER case to avoid
this issue (and column names as well).
Review & Followup
Rating: 5
Thank you. March 12, 2001
Reviewer: C.D.Venu Vijay from SECUNDERABAD, INDIA
Thanx a lot Tom.
    I found the answer for first question immediately after posting my question.
I remembered that Max length of a column in a table is 30. It is basic.
    Your answer for the second question is really thrilling.
                                      Thank you.
                                              Vijay

Rating: 3
Is it possible from oracle March 22, 2002
Reviewer: kisho from usa
Hi Tom ,
Is it possible to read the data from excell spread sheet in to oracle table
I have information like this in the spread sheet
user id    username   lastname

i have to transfer this data in to oracle table which has the same fields ..if
so ,how pl help
kisho

Followup:
You can save it in CSV format and load it in.

If there is a way -- you would be asking microsoft how you can program excel to
connect to Oracle and insert the data.  Using oledb, odbc, etc -- it is most
likely 100% possible.

Rating: 5
excel to oracle tool March 22, 2002
Reviewer: Rob Balter from Pittsburgh, PA
See the following link for an add on that makes it easy
to move data directly between excel and oracle.
http://www.oraxcel.com/projects/sqlxl/index.htm

      Rating: 5
Really
      Useful  November 13, 2002
Reviewer:  Abhijit
       from Mumbai
It was really useful to know how to get those tables across from SQLServer to
Oracle.

Cheers!
Abhijit

March 22, 2002 Jagtar -- Thanks for the question regarding
      "Simulate On_Commit Trigger Behaviour From PL/SQL", version 8.1.7

      You Asked
Hi Tom,

I have a group of applications developed in 4 GL langauge which uses Oracle as
the back end. Some of these applications insert/update the Header row first and
Child rows later and in some others the order is reversed.

I need to add some new functionality ( without altering the original
applications) which performs calculations using data from both Header and Child
tables. The results are stored in a new grand child table.

I want to implement this functionality through database
triggers/package/procedure.

As data from both tables is required for calculations so I am forced to write to
write triggers on both the tables, which impacts performance.

However, in case we had a trigger which fired On_Commit then I could use that to
call the package which I have created.

Could you kindly suggest a method by which I could create/simulate an On_Commit
trigger.

Thanks,

Cheema(jcheema@glovia.com)

      and we said...
We do not have an on-commit trigger.  The closest thing we have is an "on commit
refresh" materialized view.

You can read about them and see if they can help you out.  There is a chance,
you may be able to use a materialized view to realize your "new grand child
table".
Review
      & Followup

      Rating:
      3
  March 08, 2002
Reviewer:
      A reader

      Rating: 5
"Simulate
      On_Commit Trigger Behaviour From PL/SQL", version
      8.1.7  March 08, 2002
Reviewer:  Cheema
       from Los Angeles
Thanks a lot.
I think that is a very good idea and just what I needed.

      Rating: 3
"Simulate
      On_Commit Trigger Behaviour From PL/SQL", version
      8.1.7  March 20, 2002
Reviewer:  Cheema
       from Los Angeles
Hi Tom,
I have investigated the idea of materialized view(MV).
In order to make it work I need to create a trigger on MV,
which I have been able to create. There are no compilation
errors, however, the trigger doesn't seem to fire.

I tried creating an INSTEAD OF Triggers, but this doesn't compile. Probably,
beacue MV is like a Table than a view.
Any advise?
Cheema

          Followup:

The triggers fire, upon commit, they do fire.

However, I'm confused here - why would you need a trigger - the MV is there to
do the work for you.

      Rating: 5
"Simulate
      On_Commit Trigger Behaviour From PL/SQL", version
      8.1.7  March 20, 2002
Reviewer:  Cheema
       from Los Angeles
Hi Tom,

Sorry about my earler e-mail. It was premature.
Trigger is firing, and I should be able to make it work.

Regards,

      Rating: 5
"Simulate
      On_Commit Trigger Behaviour From PL/SQL", version
      8.1.7  March 21, 2002
Reviewer:  Cheema
       from Los Angeles
Hi Tom,
>>However, I'm confused here -
That is because the logic to populate RC(new) tables is complex and requires
procedural code. So the trigger on MV calls a package which pouplates RC tables
which are real tables.
In fact MV is only a mechanism to invoke the package after a commit in any
transaction in which Master table of MV has been modified. That is exactly what
I wanted.

Cheema

          Followup:

And this shows they fire upon commit:

ops$tkyte@ORA817DEV.US.ORACLE.COM> @test
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop materialized view t_mv;

Materialized view dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int primary key,  y int
);

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create materialized view t_mv
  2  refresh on commit
  3  as
  4  select y, count(*) cnt from t
  5  group by y;

Materialized view created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger t_mv_trigger
  2  after insert or update or delete
  3  on t_mv
  4  begin
  5          if ( inserting )
  6          then
  7                  dbms_output.put_line( 'Hey, I fired!! for insert' );
  8          end if;
  9          if ( deleting )
 10          then
 11                  dbms_output.put_line( 'Hey, I fired!! for deleting' );
 12          end if;
 13          if ( updating )
 14          then
 15                  dbms_output.put_line( 'Hey, I fired!! for updating' );
 16          end if;
 17  end;
 18  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1, 1 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec null;

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec null;
Hey, I fired!! for deleting
Hey, I fired!! for insert

PL/SQL procedure successfully completed.

      Rating: 4
oops!
        March 22, 2002
Reviewer:  Yogeeraj
       from Mauritius
hi,
sorry, i missed something..
What is this "exec null" trick?

thanks and
Best Regards
Yogeeraj

          Followup:

sqlplus calls dbms_output.get_lines after every insert/update/delete and EXEC
call.  sqlplus then prints out whatever it gets.

It does not do this after commit for example.

In order to show WHEN (which statement) caused the trigger to fire, I used exec
null; to have sqlplus attempt to dump the dbms_output buffer.  If I did not have
it after the commit, we would not see it (the messages).  I put it after the
insert just to make sure that there was nothing up my sleeves.

      Rating:
      4
  March 22, 2002
Reviewer:  Stanley
       from Slovakia
SQL*Plus receives contents of DBMS_OUTPUT buffer only after executing PLSQL
block, not after a commit. Hence, to get messages from trigger that fires when
you issue commit, you need to execute some PLSQL block.

      Rating: 5
"Simulate
      On_Commit Trigger Behaviour From PL/SQL", version
      8.1.7  March 22, 2002
Reviewer:  Cheema
       from Los Angeles
>>Hey, I fired!! for deleting
>>Hey, I fired!! for insert
I have found that regardless of the operation(insert/update/delete) on t , the
trigger on t_mv is always being fired for delete and insert in that order.
Any explanation?

Regards,

Cheema

          Followup:

Out with the old, in with the new.

The refresh isn't done as an update, its done as a delete of stale and insert of
fresh.

      Rating: 5
Trigger on
      mview - very good idea!  August 29, 2003
Reviewer:
      Piotr Jarmuz  from Braunschweig, Germany
I had a similar problem and solved it in a more convoluted way.

I used:

create some_function(p_dummy number) return number
deterministic
is
begin
   if sys_context('some_app_ctx','signal')='true' then
      dbms_alert.signal('some_signal','some_message');

      /* this one is associated with the app context */
      reset_signal_flag_in_app_ctx;
   end if;
   return p_dummy;
end;

create materialized view dummy_mview
build immediate
on commit refresh
as
select count(*) cnt from some_table where 1=0

create index dummy_mview_idx
on dummy_mview(some_function(cnt))

So a function-based index on materialized view.

mview and index had just one dummy row(entry).

And whenever someone modified some_table (via stored procedure or instead of...
triggers at the sam time saving a flag to send a signal to the listener session
later on) and commited then some_function has been called sending signal on a
common signal NAME thus avoiding blocking problem with dbms_alert

Sending signal and commiting immediately after reduces the blocking time
window...

But your idea is even better!
No need of an index. Simple and pure.

I don't know why I didn't get this idea before?!?!

I seems like it takes some time to learn thinking in your way.

Best regards.

P.S. Hope Oracle 10g will have before commit triggers on schema and/or on
database. Seems like Oracle uses internally on commit hooks already (mview
refresh, redo log flush, locks release) so it would be nice to expose that
functionality to developers (just like you did with dbms_lock, snapshots, jobs,
etc). And you could forbid commiting and/or rolling back in such a trigger to
avoid infinite recursion...

March 22, 2002 Shivaswamy -- Thanks for the question regarding
      "Bind Variables", version 817

      You Asked
Hi, TOM.

What is the effecient way of identifying how well the application is using the
bind variables in an existing database?
What is the most effective way of finding the queries, which will have maximum
return, if used bind varibales properly?

TIA,
Shivaswamy

      and we said...
This script:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1163635055580
will ID them and the ones with the highest counts are the ones you want to work
on first.
Review & Followup

      Rating:
      4
  March 22, 2002
Reviewer:
      Shivaswamy
Thanks, Tom.

March 22, 2002 Van -- Thanks for the question regarding
      "Consequences of not Separating Redo and Datafiles", version 8.1.7

      You Asked
   The hardware folks are setting up some new servers with an EMC disk array.
Essentially, the DBAs don't get to control which physical disks hold what
information, datafiles, online redo, etc.
   The question is if we lose all the online redo logs is the case any worse if
we also lose datafiles?  That is, is the case of total redo log loss AND datfile
not lost just as bad as total redo file loss AND datafile not lost.

Thanks,

Van

      and we said...
Assuming you are in archivelog mode, it doesn't matter, you just goto backups
and point in time recover upto the point of failure.  You

If you have concerns that you will be losing your redo, I would be trying to
make it so I didn't have those concerns.  EMC is pretty good stuff, very
"robust".  It should be easy to make it so that this isn't a concern.  Losing
redo is the same as "I lost data" -- you will lose some committed transactions
-- that would be bad.

Review &
      Followup

      Rating: 4
Hardware not
      under our control  March 22, 2002
Reviewer:  Van
      Messner  from Groton, CT
Thanks for the quick response
We certainly don't want to lose any redo but we've all seen cases where the
"impossible" happened.  One example: I worked at a site where the servers all
had backup power supplies that kicked if power failed.  Then the sysadmins were
automatically paged.  One night the building lost power, the backups kicked in,
the sysadmins were not in fact paged, and the backup power supplies all ran out.
 Every server/disk array died.
So we're really looking at the worst case and what we'd do if it happened.
Van

March 22, 2002 -- Thanks for the question regarding "Creating
      object type but is there a limit to what you can define in the type?",
      version 8.1.7

      You Asked
I tried the following:
create type restrictions_ty as object
   (sale    char(1) constraint ck_sale(sale in('N','Y')),
    use        char(1) constraint ck_use(use in('N','Y')),
    description varchar2(100));/

and received the following errors:
SQL> show err;
Errors for TYPE RESTRICTIONS_TY:

LINE/COL ERROR
-------- -----------------------------------------------------------------
2/18     PLS-00103: Encountered the symbol "CONSTRAINT" when expecting one
         of the following:
         := ) , not null default character
         The symbol "," was substituted for "CONSTRAINT" to continue.

3/18     PLS-00103: Encountered the symbol "CONSTRAINT" when expecting one
         of the following:
         := ) , not null default character

After much frustration of searching otn on a different subject from this I
believe my mental capacities are shot in trying to find limitations of building
object types.

Can I please be directed to the location on otn that contains the limitation for
building object types?  If there is not one, then is constraint one of the
limits?  Or did I simply do something wrong with building the object type.

      and we said...
I would have started with the SQL Reference manual myself:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a85397/statem7d.htm#2064465

The wire diagram shows that you can have:

attribute name
datatype

and that is is.  You do not put constraints inside of types. Constraints go with
COLUMNS in tables only.
March 23,
      2002 gaurang -- Thanks for the question
      regarding "inline views", version 8.1.6

      You Asked
hi tom
can u explain in detail about inline views,there is no good documentation about
this on technet.

      and we said...
search for

inline view

on my site for more info.  Inline views are not heavily documented for the
simple reason that they are simply a select in a select -- nothing more, nothing
less.

select *
  from ( select deptno, count(*) cnt
           from emp ) emp,
        dept
  where dept.deptno = emp.depto
/

the select in the from list is an inline view.  It is nothing "special" -- hence
no documentation for it.
Review & Followup

      Rating:
      3
  March 21, 2001
Reviewer:  David
       from usa

      Rating:
      3
  March 21, 2001
Reviewer:  David
       from usa

      Rating:
      4
  March 22, 2001
Reviewer:  Helena
      Markova  from Bratislava, Slovakia

      Rating:
      4
  June 15, 2001
Reviewer:  Ivan
       from USA

      Rating: 4
Explanation 5,
      code 3  March 23, 2002
Reviewer:  Harrison
      Picot  from Haymarket, VA USA "Arkansas without the glitter"

Hi Tom:

I am not seeing any comments from other readers (names but no comments) so maybe
this was covered but even after I fix the column names, the code seems a bit
frisky;

scott@SARAH> l
  1  select *
  2  from  ( select deptno, count(*) cnt
  3  from emp) emp,
  4  dept
  5* where dept.deptno = emp.deptno
scott@SARAH> /
from  ( select deptno, count(*) cnt
               *
ERROR at line 2:
ORA-00937: not a single-group group function

As you usually fix the code when it has an error, I have the feeling that nobody
else complained.

Cheers,

Harrison

          Followup:

Nope, no other complaints -- you are correct, I'm missing a group by deptno in
there.

March 24, 2002 Tamás -- Thanks for the question regarding
      "Optimizing query for response time", version Oracle 7.3.4, 8.0.5, 8.1.6

      You Asked
Hi Tom,

I was wondering wether Oracle makes it possible for you to
optimize your query not for execution time - as it normally does
-, but for response time. Can it be donne from SQL*Plus? And
from a C application?

Thank you for your help in advance.

Tama Szecsy
Geometria GIS Systems House Ltd.

      and we said...
There are many ways to have a query optimized --

o RULE based optimization

o ALL_ROWS optimization (try to get the plan that gets me access
to the LAST row as fast as possible -- at the expense of getting
to the first row)

o FIRST_ROWS optimization (try to get the plan that gives me the
first row of data as soon as possible).

o CHOOSE which will use RULE if no tables are analyzed, and the
Cost based optimizer if any are (PLSQL will either use RULE or
ALL_ROWS in all cases unless you use a hint).

You can set these at the database level via the init.ora
parameter optmizer_goal.

You can set these at the session level via the alter session
command.

You can set this at the query level via a HINT:

select /*+ RULE */ * from t;
select /*+ ALL_ROWS */ * from t;
select /*+ FIRST_ROWS */ * from t;

Review &
      Followup

      Rating: 4
Changing From
      Rule to Choose  March 23, 2002
Reviewer:
      Lakshmi narasimhan R  from Dubai, UAE
HI tom

happens to see this thread when readng through information.
I have a database which is running On RBO.  All my queries is being tuned and
structured for RBO.  if i want to use CBO in half-way,  making the optimizer in
init.ora as CHOOSE and running the analyse only for the tables created from that
day onwards will make both queries work in their respective optmizer types.
(Since ther is no analysis for old tables that will run in RBO and since there
is analysis available for newly created table it will run in CBO). Is it a worth
doing ?

          Followup:

as long as you don't query both analyzed and un-analyzed tables simultaneously.
CBO is used if at least ONE table in a query is analyzed.

      Rating: 4
If Force for
      RULE with HINT  March 24, 2002
Reviewer:
      Lakshmi Narasimhan R  from Dubai , UAE
Tom

Thanks for the followup.

In case it happens that i have to use two tables
as joins one analyzed and one un analyzed, and to avoid
optimizer using CBO, if we give RULE hint will it give
the same performance effect or its not advisable.

          Followup:

it'll be the same as if both tables were not analyzed.

Me -- I'd just go for it and analyze the whole lot (in TEST), tweak what I
needed to and use the CBO all around.  Be a whole lot easier then walking on
eggshells like this...

      Rating: 4
Optimizing
      query for response time  March 24, 2002
Reviewer:
      Reader
As a developer if I want to find out which tables of the query are analyzed and
which are not how do I find out. I do not have dba privileges.

Thanks for your time.

          Followup:

select table_name, num_rows from user_tables;

anything with a non-null num_rows is anlayzed.

March 24, 2002 R
      -- Thanks for the question regarding "How can I avoid a table scan?",
      version 8.1.6

      You Asked
Tom,

I am having a problem with a query doing a full table scan.  I am using Oracle
8.1.6 with the optimizer_mode = “Choose”.  My database statistics are estimated
and current.  The following query works as expected:

SELECT   /*+ INDEX(a INDEX_N5) */
DISTINCT name
FROM  table_a a, table_b b
 where name like 'b'||'%'
and  b.id = a.id
 order by name

table_a has 250,00 rows.  Table_b has 4,000,000 rows.  The id columns on both
tables are indexed, as well as the name column.  The ‘b’||’%’ represents a
wildcard search beginning with the letter ‘b’.  I choose this because it returns
a lot of data.

Here is my problem.  I also need to restrict the querry using a non-indexed
column on table_b.  Here is the version that doesn’t work:
SELECT   /*+ INDEX(a INDEX_N5) */
DISTINCT name
FROM  table_a a, table_b b
 where name like 'b'||'%'
and  b.id = a.id
and b.column_2 != ‘X’
 order by name

When I add the “and b.column_2 != ‘X’” clause, the query does a full table scan
on table_b (the larger table).  I tried specifying the table_b index on
table_b.id in the optimizer hint.  That did not help.

Any suggestions?

Thanks.

      and we said...
before anything else, I would try:

analyze table b compute statistics
for table
for all indexes
for all indexed columns;

analyze table a compute statistics
for table
for all indexes
for all indexed columns;

and let the query run "as is" - with no hints.  4million rows isn't too much
data to analyze like that and 250k rows is nothing.  Give the optimizer the most
data to work with in this case and see what happens.

Short of that, sounds like you want to use a nested loops join with the tables
in a specific order.  If so, use the ordered and use_nl hints.  consider:

scott@TKYTE816> set autotrace traceonly explain
scott@TKYTE816>
scott@TKYTE816> select /*+ first_rows ordered use_nl(emp) */ *
  2  from  dept, emp where emp.deptno = dept.deptno
  3  /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: FIRST_ROWS (Cost=165 Card=67
Bytes=7839)
   1    0   NESTED LOOPS (Cost=165 Card=67 Bytes=7839)
   2    1     TABLE ACCESS (FULL) OF 'DEPT' (Cost=1 Card=82 Bytes=2460)
   3    1     TABLE ACCESS (BY INDEX ROWID) OF 'EMP' (Cost=2 Card=82 Bytes=7134)
   4    3       INDEX (RANGE SCAN) OF 'EMP_DEPTNO' (NON-UNIQUE) (Cost=1 Card=82)

That used the tables in order -- making DEPT be the driving table in the
query.  We used a nested loops join to EMP.

Now to reverse that

scott@TKYTE816> select /*+ first_rows ordered use_nl(dept) */ *
  2  from  emp, dept where emp.deptno = dept.deptno
  3  /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: FIRST_ROWS (Cost=83 Card=67
Bytes=7839)
   1    0   NESTED LOOPS (Cost=83 Card=67 Bytes=7839)
   2    1     TABLE ACCESS (FULL) OF 'EMP' (Cost=1 Card=82 Bytes=7134)
   3    1     TABLE ACCESS (BY INDEX ROWID) OF 'DEPT' (Cost=1 Card=82
Bytes=2460)
   4    3       INDEX (UNIQUE SCAN) OF 'DEPT_PK' (UNIQUE)

scott@TKYTE816>
scott@TKYTE816> set autotrace off

followup to comment one below

In my example above -- having NO predicates other then a join condition -- the
full scan is not something "to be avoided".  It is desirable.  I want every row
from EMP (or dept).

Using an index to access the driving table in these examples would take longer
then full scanning the table.

followup to comment two below

Prasad -- a negative operator most certainly does NOT preclude an index!
Espeicially if the negation is being done to the UNINDEXED column as the
original questioner states.  Consider:

scott@TKYTE816> create index emp_idx on emp(ename);
scott@TKYTE816> set autotrace traceonly;
scott@TKYTE816> select ename, sal from emp where ename like 'K%' and sal != 1;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'EMP'
   2    1     INDEX (RANGE SCAN) OF 'EMP_IDX' (NON-UNIQUE)

  1  select ename, sal, dname
  2  from emp, dept
  3  where ename like 'K%'
  4  and sal != 1
  5* and emp.deptno = dept.deptno
scott@TKYTE816> /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   NESTED LOOPS
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'EMP'
   3    2       INDEX (RANGE SCAN) OF 'EMP_IDX' (NON-UNIQUE)
   4    1     TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'
   5    4       INDEX (UNIQUE SCAN) OF 'DEPT_PK' (UNIQUE)

Clearly, the negation does not preclude the use of the index on anything.  We
might not use an index to answer a negation, but the negation does not preclude
the use of indexes.

The main reason in this case is lack of full statistics (and especially stats on
the INDEXED column).

Review &
      Followup

      Rating:
      4
FTS  June 18, 2001
Reviewer:
      A reader
In both the cases ther's a full table scan of (non-driving) table, eventhough
the column was indexed (like, FTS of dept in case 1 and FTS of emp in case 2).
How can we avoid that?

      Rating: 2
How can I avoid
      a table scan?  June 21, 2001
Reviewer:
      Prasad  from Newton, MA USA
Index will not be used in case of != operator. A negative operator forces a full
table scan. I guess that is the main reason for full table scan in this
situation.

      Rating: 4
ORDERED
      hint  March 24, 2002
Reviewer:  Brigitte
      Hendrischk  from Germany
Why Oracle does a full table scan on the driving table when there is an index?

Oracle used the largest table testarchiv as driving table. I try to change it
with ORDERED to reduce the consistent gets.
Why Oracle does then a full table scan on testgruppe? Nutzer is the first column
of the primary key
pk_testgruppe of testgruppe.

Na so was> SELECT DISTINCT a.fe_id FROM Recherche r, TESTArchiv a
2  WHERE r.gruppe IN (SELECT gruppe FROM TESTGruppe
3  WHERE nutzer = 'emil mueller') AND a.art=r.art2
4  AND a.ugr=r.ugr2  AND Betreff LIKE '%test%';

15 rows selected.

Elapsed: 00:00:00.21

Execution Plan
----------------------------------------------------------
       0      SELECT STATEMENT Optimizer=CHOOSE (Cost=8 Card=28 Bytes=3640)
       1    0   SORT (UNIQUE) (Cost=8 Card=28 Bytes=3640)
       2    1     NESTED LOOPS (Cost=6 Card=36 Bytes=4680)
       3    2       HASH JOIN (Cost=6 Card=36 Bytes=3312)
       4    3         TABLE ACCESS (FULL) OF 'TESTARCHIV' (Cost=4 Card=29
Bytes=1479)
       5    3         INDEX (FULL SCAN) OF 'PK_RECH' (UNIQUE) (Cost=1 Card=100
Bytes=4100)
       6    2       INDEX (UNIQUE SCAN) OF 'PK_TESTGRUPPE' (UNIQUE)

Statistics
----------------------------------------------------------
         0  recursive calls
         4  db block gets
        89  consistent gets
         0  physical reads
         0  redo size
       510  bytes sent via SQL*Net to client
       311  bytes received via SQL*Net from client
         4  SQL*Net roundtrips to/from client
         4  sorts (memory)
         0  sorts (disk)
        15  rows processed

Na so was> r
  1  SELECT /*+ ORDERED */
  2  DISTINCT a.fe_id FROM Recherche r, TESTArchiv a
  3  WHERE r.gruppe IN (SELECT gruppe FROM TESTGruppe
  4  WHERE nutzer = 'emil mueller') AND a.art=r.art2
  5* AND a.ugr=r.ugr2  AND Betreff LIKE '%test%'

15 rows selected.

Elapsed: 00:00:00.81

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=11 Card=4 Bytes=520)
   1    0   SORT (UNIQUE) (Cost=11 Card=4 Bytes=520)
   2    1     HASH JOIN (Cost=9 Card=4 Bytes=520)
   3    2       NESTED LOOPS (Cost=4 Card=11 Bytes=869)
   4    3         TABLE ACCESS (FULL) OF 'TESTGRUPPE' (Cost=1 Card=3 Bytes=114)
   5    3         INDEX (RANGE SCAN) OF 'PK_RECH' (UNIQUE) (Cost=1 Card=100
Bytes=4100)
   6    2       TABLE ACCESS (FULL) OF 'TESTARCHIV' (Cost=4 Card=29 Bytes=1479)

Statistics
----------------------------------------------------------
          0  recursive calls
          8  db block gets
         31  consistent gets
          0  physical reads
          0  redo size
        523  bytes sent via SQL*Net to client
        311  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          4  sorts (memory)
          0  sorts (disk)
15    rows processed

          Followup:

Oracle won't always use a full scan on the driving tables -- totally depends on
the query.

There is so little data here as to not be meaningful to test with.

Taking this query:

Na so was> SELECT DISTINCT a.fe_id FROM Recherche r, TESTArchiv a
2  WHERE r.gruppe IN (SELECT gruppe FROM TESTGruppe
3  WHERE nutzer = 'emil mueller') AND a.art=r.art2
4  AND a.ugr=r.ugr2  AND Betreff LIKE '%test%';

 0      SELECT STATEMENT Optimizer=CHOOSE (Cost=8 Card=28 Bytes=3640)
 1    0   SORT (UNIQUE) (Cost=8 Card=28 Bytes=3640)
 2    1     NESTED LOOPS (Cost=6 Card=36 Bytes=4680)
 3    2       HASH JOIN (Cost=6 Card=36 Bytes=3312)
 4    3         TABLE ACCESS (FULL) OF 'TESTARCHIV' (Cost=4 Card=29
 5    3         INDEX (FULL SCAN) OF 'PK_RECH' (UNIQUE) (Cost=1 Card=100
 6    2       INDEX (UNIQUE SCAN) OF 'PK_TESTGRUPPE' (UNIQUE)

It basically FULL SCANNED testarchiv (there are NO PREDICATES on testarchive
that would lend themselves to a index to start) and full scanned the index on
recherche and hash joined them together to produce a subrequest (here, an INDEX
would have been really much slower -- you have NO data to speak of!  INDEXES do
not IMPLY "faster", "better", "cheaper" by any means!!!!!)

That subresult then did an index probe into testgruppe to check for existence
and output any row that found a hit in testgruppe.

Using an index would have been inefficient in putting testarchiv and recherche
together.  Just repeat over and over -- Indexes are not the fastest way to the
data, indexes are NOT the fastest way to the data (just to counterbalance the
thought that is currently in mind -- that indexes ARE the fastest way).

Even if you have millions of rows in there, we would most likely blow off the
index -- an index is generally used when we are going to get less then 5%/20% of
the table (depending on the size of the table, the width, the clustering factor
and other things).  If we are going to get virtually every row from the table
(as the queries above would -- no predicates that can use an index, just join
conditions) we would tend to NOT use an index unless you used "first rows" to
get the first row as soon as possible (queries are generally optimized for total
throughput -- get the last row asap)

      Rating:
      3
ORDERED  March 25, 2002
Reviewer:
      A reader
Thanks for your quick reply

Brigitte

March 24, 2002 Mohammed -- Thanks for the question regarding
      "Cross Tab Report ", version 8i

      You Asked
Hi Tom,

I have asked to do Report Cross tab Report .
I  am monthly generate report for bills amount  on Subscriber Level
And I have these Tables
Bills  fields  (Status, id,end_date, …. ) and other fields
bill_details   ( bd_value, bill_id, subscriber_id , value_type, item_type ,…… )

My SQL only get the amount bill for each SQL I run , and depends on  bill
end_date
And the sql statement is as follow

select  d.subscriber_id,b.invoice_number,sum (d.bd_value) summa,
       decode(,'A','PAid',
                       '3','Printed Out',
                       '9','Sent'),b.End_date
from bills b,
     bill_details d,
where b.id = d.bill_id
      and b.bill_type = '1'
      and d.value_type in ('2','6')
      and b.status in ('A','3','9')
      and d.item_type in ('10','20','30','40','28')
      and b.end_date between to_date('31.01.2002 00:00:00','dd.mm.yyyy
hh24:mi:ss')
      and  to_date('31.01.2002 23:59:59','dd.mm.yyyy hh24:mi:ss')
group by
             d.subscriber_id,b.invoice_number, b.status
order by
             d.subscriber_id

My Guestion is how to find  a report like the following

SUBSCRIBER_ID /    MARCH    APRIL      MAY     JONE     JULY     ……..
---------------------------------------------------------------------------------
----------------
     Xxxxxx                       'Paid          Sent          ..            …
         ….       …….
     Yyyyyyy                       sent          paid          …           …
      ….         ……
      Zzzzzzzz                      paid        sent             ….         ….
        ….          ……
        ……                           …..          …..            …..        ….
        …..         …..

and so on for about 30 thousand Subscribers .
What do you thing , IS it possible to  create it as the above by using SQL
To Know that each bill have END_DATE which is the date of defining the bill for
each month for each subscriber .

*********************************************************************
Yes there will be for each subscriber_id + End_date only one status or
IF I can Put the summa( Bill_Amount) insted of putting the status ,so for each
subscriber_id+ End_date will be also one summ ( bill_Amount)

I hope that i explaned to you as you wanted.

with my best regards .

By By Tom :)

*******************************************************************

      and we said...
This query will do the "transpose" for you however, your where clause seems to
pick off a SINGLE date for end date (jan, 31'st 2002) so it won't be too
interesting

select subscriber_id,
       max( decode( to_char(end_date,'MON'), 'JAN', status, null ) ) jan,
       max( decode( to_char(end_date,'MON'), 'FEB', status, null ) ) feb,
       ...
       max( decode( to_char(end_date,'MON'), 'DEC', status, null ) ) DEC
  from (
select distinct
       d.subscriber_id,
       decode(,'A','PAid',
               '3','Printed Out',
               '9','Sent') status,
       b.End_date
  from bills b,
       bill_details d,
 where b.id = d.bill_id
   and b.bill_type = '1'
   and d.value_type in ('2','6')
   and b.status in ('A','3','9')
   and d.item_type in ('10','20','30','40','28')
   and b.end_date between to_date('31.01.2002 00:00:00','dd.mm.yyyy hh24:mi:ss')
   and  to_date('31.01.2002 23:59:59','dd.mm.yyyy hh24:mi:ss')
)
order by subscriber_id

Review &
      Followup

      Rating: 5
What you want
      you can find it with TOM  March 18, 2002
Reviewer:
      Mohammed Al-moayed  from YEMEN
It was realy helpful ,and It works Ok I Just add group by to subscriber_id ,and
from this report I start to learn how to create Cross tab Report(in SQL/PLus)
.thank you Tom :)

      Rating: 5
What you want
      you can find it with TOM  March 18, 2002
Reviewer:
      Mohammed Al-moayed  from YEMEN
It was really helpful, and It works Ok I Just add group by to subscriber_id ,and
from this report I start to learn how to create Cross tab Report(in SQL/PLus)
.thank you Tom :)

      Rating:
      4
Confuse  March 19, 2002
Reviewer:
      A reader
Hi, TOm,

Which is greater: status or null?

if max(decode ...), you compare status and null, right?

Please clarify,

Thanks

          Followup:

NULL is discarded from aggregates but not scalar functions.  Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int, y int );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1, null );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( null, 1 );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

         X          Y
---------- ----------
         1
                    1

ops$tkyte@ORA817DEV.US.ORACLE.COM>

so think of that table like a matrix  It would seem to make sense that if we
sum ACROSS (add x and y) we should get the same as if we sum DOWN (add x to x
and y to y). But we don't:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select x+y from t;

       X+Y
----------

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select sum(x), sum(y) from t;

    SUM(X)     SUM(Y)
---------- ----------
         1          1

You see -- x+y is NULL since one of X or Y is null -- the results of that are
undefined -- not known

However, the SUM(x) is 1 -- the NULL value is ignored.  Same with sum(y).  Now,
MAX (similar to greatest) exhibits the same behaviour:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select greatest( x, y ) from t;

GREATEST(X,Y)
-------------

ops$tkyte@ORA817DEV.US.ORACLE.COM> select max(x), max(y) from t;

    MAX(X)     MAX(Y)
---------- ----------
         1          1

ops$tkyte@ORA817DEV.US.ORACLE.COM>

The greatest of 1, NULL is NULL - not known.  But the MAX of 1 and NULL is 1
since NULL is ignored.

Hence, in the max(decode...) the NULL never comes into play, it doesn't
exist...

      Rating:
      5
Gottcha  March 20, 2002
Reviewer:
      A reader
Hi, Tom,

Thank you so much.

      Rating: 4
if doesn't know
      how many columns will be generated in advance?  March 23,
      2002
Reviewer:  Charlie
Hi Tom,

       max( decode( to_char(end_date,'MON'), 'JAN', status, null ) ) jan,
       max( decode( to_char(end_date,'MON'), 'FEB', status, null ) ) feb,
       ...
       ...
       ...
       max( decode( to_char(end_date,'MON'), 'DEC', status, null ) ) DEC

Using above technologies we can achieve the cross tab report if we know how many
columns will be generated, say, we know here it has 12 months a year.

The question is if we don't know how many columns will be generated, for
example, if we don't know how many months a year, it may have 12 months a year,
18 months a year, 23 months a year.  How can we dynamically generate this cross
tab report by using max(decode...) 'cause I am not sure how many max(decode...)
I have to write per line in advance?

Thanks,

          Followup:

You will have to (as you said) dynamically generate it.  You would have to
figure out somehow that there are N months, then write the query.  If you have
my book -- I have an example of doing just that in the chapter on analytic
functions. It is a stored procedure that returns a ref cursor that will pivot
most any result set dynamically for you.

      Rating: 5
How to find the
      sumation of all monthes as new field  March 24, 2002

Reviewer:  Mohammed Al-moayed  from Yemen
Hi Tom,
       I did little changes in the query to get the bill amount instead of
finding bill status ,but I  want to find the total amount for all each row is it
possible ,and this is the updated SQL :

select phone_number,
        max( decode( to_char(end_date,'MON'), 'MAR',status, null ) ) MARCH,
        max( decode( to_char(end_date,'MON'), 'APR',status, null ) ) APRIL,
        max( decode( to_char(end_date,'MON'), 'MAY',status, null ) ) MAY,
        max( decode( to_char(end_date,'MON'), 'JUN',status, null ) ) JUNE,
        max( decode( to_char(end_date,'MON'), 'JUL',status, null ) ) JULY,
        max( decode( to_char(end_date,'MON'), 'AUG',status, null ) ) AUGUST,
        max( decode( to_char(end_date,'MON'), 'SEP',status, null ) ) SEPTEMBER,
        max( decode( to_char(end_date,'MON'), 'OCT',status, null ) ) OCTOBER,
        max( decode( to_char(end_date,'MON'), 'NOV',status, null ) ) NOVEMBER,
        max( decode( to_char(end_date,'MON'), 'DEC',status, null ) ) DECEMBER,
        max( decode( to_char(end_date,'MON'), 'JAN',status, null ) ) JANUARY,
        max( decode( to_char(end_date,'MON'), 'FEB',status, null ) ) FEBRUARY
    from (select distinct
                 sc.phone_number,sum(d.Bd_Value) bill_amount,
                 b.End_date
         from flagship.bills b,
              flagship.bill_details d,
              flagship.subscriber_channels sc
         where b.id = d.bill_id
               and sc.subscriber_id = d.subscriber_id
               and b.bill_type = '1'
               and d.value_type in ('2','6')
               and b.status in ('A','3','9')
               and d.item_type in ('10','20','30','40','28')
               --and b.end_date between to_date('01.03.2001
00:00:00','dd.mm.yyyy hh24:mi:ss')
               --and  to_date('31.01.2002 23:59:59','dd.mm.yyyy hh24:mi:ss')
               and  TRUNC(b.end_date) in ( last_day( to_date('&1','mon-yy') ),
last_day(to_date('&1','mon-yy') ),
                                    last_day( to_date('&1','mon-yy') ),
last_day(to_date('&1','mon-yy') ),
                                    last_day( to_date('&1','mon-yy') ),
last_day(to_date('&1','mon-yy') ),
                                    last_day( to_date('&1','mon-yy') ),
last_day(to_date('&1','mon-yy') ),
                                    last_day( to_date('&1','mon-yy') ),
last_day(to_date('&1','mon-yy') ),
                                    last_day( to_date('&1','mon-yy') ),
                                    last_day( to_date('&1','mon-yy') ) )
         group by sc.phone_number,b.End_date
          )
 group by  phone_number
 --order by subscriber_id

          Followup:

if you mean the sums of the months for each row -- sure, thats easy:

select a.*,
       march+april+may+....+february
  from (
select phone_number,
        max( decode( to_char(end_date,'MON'), 'MAR',status, null ) ) MARCH,
        max( decode( to_char(end_date,'MON'), 'APR',status, null ) ) APRIL,
        max( decode( to_char(end_date,'MON'), 'MAY',status, null ) ) MAY,
        max( decode( to_char(end_date,'MON'), 'JUN',status, null ) ) JUNE,
        max( decode( to_char(end_date,'MON'), 'JUL',status, null ) ) JULY,
        max( decode( to_char(end_date,'MON'), 'AUG',status, null ) ) AUGUST,
        max( decode( to_char(end_date,'MON'), 'SEP',status, null ) ) SEPTEMBER,
        max( decode( to_char(end_date,'MON'), 'OCT',status, null ) ) OCTOBER,
        max( decode( to_char(end_date,'MON'), 'NOV',status, null ) ) NOVEMBER,
        max( decode( to_char(end_date,'MON'), 'DEC',status, null ) ) DECEMBER,
        max( decode( to_char(end_date,'MON'), 'JAN',status, null ) ) JANUARY,
        max( decode( to_char(end_date,'MON'), 'FEB',status, null ) ) FEBRUARY
    from (select distinct
       .......
  group by phone_number
) a

      Rating: 3
Pitfall with
      NLS settings  March 24, 2002
Reviewer:  Tanel
      Hiir  from Estonia
Small NLS related remark.

> max( decode( to_char(end_date,'MON'), 'JAN', status, null ) ) jan

.. may not work as expected under other than AMERICAN language settings.

For example take today (24th March):

SQL> select to_char( sysdate, 'MON' ) from dual;

TO_
---
MAR

SQL> alter session set NLS_DATE_LANGUAGE='GERMAN';

Session altered.

SQL> select to_char( sysdate, 'MON' ) from dual;

TO_
---
MÄR

.. and row for March would end up NULL.

One could use:
to_char( sysdate, 'MON', 'NLS_DATE_LANGUAGE=''AMERICAN''' )
or
to_char( sysdate, 'MM' )
instead.

          Followup:

Yes, I would expect that people would recognize that easily (themselves) but...
sure.  It only works in "english" since that's how we spell the months.

An NLS "friendly" way is:

to_char(to_date('01','mm'),'MON') to get the spelling of january, instead of
using the constant 'JAN'

to_char(to_date('02','mm'),'MON') to get the spelling of feburary, instead of
using the constant 'FEB'

and so on.

or use MM instead of MON.

(but then again, looking at your supplied remarks....

ops$tkyte@ORA817DEV.US.ORACLE.COM> select
to_char(sysdate,'MON'),to_char(sysdate,'MONTH' ) from dual;

TO_ TO_CHAR(S
--- ---------
MAR MARZ

you picked a bad example -- MAR would work -- no nulls for me there, MAR is the
same in germany as the US.

but ... i really did expect that anyone -- really anyone -- would be able to use
this technique without being told "in germany, use the german months"....)

      Rating: 3
Not bad example
      but wrong charset  March 25, 2002
Reviewer:
      Tanel Hiir  from Estonia
Tom,

> you picked a bad example -- MAR would work
> -- no nulls for me there, MAR is the
> same in germany as the US.

did not :-)

to_char(sysdate,'MON') will return MAR in GERMAN when you use US7ASCII charset.
Try setting client charset EE8ISO8859P2 on *P4 or *P15 and you will get MÄR.

> but ... i really did expect that anyone
> -- really anyone -- would be able to use
> this technique without being told
> "in germany, use the german months"....)

My point is that code should not use German or US months but rather work
everywhere. World is small...

      Rating:
      5
Brilliant  August 21, 2002
Reviewer:
      Jim  from Oz
Just read pages 577 - 582 of your book Tom,
very very clever. I'm not easily impressed but
you seem to have a knack of constantly surprising me
what you (and therefore all of us) can do with the database

Cheers
Jim

March 25, 2002 Kristina -- Thanks for the question regarding
      "Delete current row in after insert trigger", version 8i 8.1

      You Asked
Hello,
  As data is entered into a 'staging' table, I want to push it out to other
tables and delete that row when it is done.  I have put this logic into an After
Insert For each Row trigger (except for the delete) and it works great.  Now
that I put the delete in at the end of the trigger: (Delete from test where id =
:new.ID), I get ORA-00600: internal error code..
Can I get this to work in the After Insert trigger?  I might also try to put it
into a before insert trigger, but how do I abort the actual insert to the
staging table after all other processing is done without raising an error or
rolling back the insert into the 'real' table?
Thanks,
Kristina

      and we said...
You don't want that logic in the AFTER INSERT FOR EACH ROW trigger, you want
that in an AFTER INSERT trigger....

Put your logic in the AFTER INSERT FOR EACH ROW, and code simply an AFTER
INSERT:

AFTER INSERT ON T
begin
   delete from t;
end;

and that'll clear it out.  If we do not make it to the AFTER INSERT trigger
(some error) the statement will be backed out.  If we do make it (statement
succeeded as did all of you logic) we'll delete every row inserted by that
single insert.
Review & Followup

      Rating: 4
what does this
      ora 600 internal error meant for  March 25, 2002

Reviewer:  lakshmi narasimha r  from dubai, uae

hi tom

we have a problem in our Client's Db with internal error(ora600).  its suddenly
happens.
The sequence is

WE have updated the Package on friday and there is no problem on friday , sat
and sun.  monday morning when they
shutdown and up the server after that they started getting
Internal error ora 600
what are all the possible reasons.  i hope it is undocumented error.

          Followup:

as with all ora-600's (and what the heck does this have to do with deleting
current row in after insert trigger???) -- contact support.

      Rating: 3
What about
      INSTEAD OF trigger?  March 25, 2002
Reviewer:
      Tanel Hiir  from Estonia
View + INSTEAD OF trigger could be alternative solution.

      Rating: 3
Used Bind
      Variables, but not supplied  September 03, 2002

Reviewer:  Janardhana  from Bangalore - India

Hi,

I also got the same error ora-00600. And thought i need to contact support. Then
when debugging I found the error
My code was :

execute immediate 'delete from user1.table1 where col_id = :b1 and period_id
in(select period_id from temp_chart)' ;

So the bind variable was not supplied.. The error disappered when I changed the
code

execute immediate 'delete from user1.table1 where col_id = :b1 and period_id
in(select period_id from temp_chart)' using col_id;

Regards,
Janna


Subject | Article | Last Updated (East coast time)
A couple of questions | article only | Mar 26, 2002 07:34 am
Airline Website Research | article only | Mar 26, 2002 07:43 am
table access(full) | article only | Mar 27, 2002 08:10 am
Top 'N' Analysis | article only | Mar 28, 2002 05:32 pm
sql query | article only | Mar 28, 2002 09:12 pm
convert column data to a row | article only | Mar 29, 2002 10:55 am
Tune-PL/SQL Block | article only | Mar 29, 2002 11:24 am
How to find out from which procedure the error is emanating.... | article only | Mar 29, 2002 12:09 pm
Truncate and Coalesce | article only | Mar 29, 2002 01:03 pm
sqlplus working path | article only | Mar 29, 2002 01:15 pm
Log_miner | article only | Mar 29, 2002 01:17 pm
Statspack: DB File Scattered Read and DB File Sequential Read | article only | Mar 29, 2002 01:22 pm
Batch processing -- send an email every half hour | article only | Mar 29, 2002 02:59 pm
PL/SQL Tables vs Global Temporary Tables | article only | Mar 29, 2002 04:34 pm
Index and V$SESSION_LONGOPS | article only | Mar 30, 2002 10:18 am
Loop execution control | article only | Mar 30, 2002 10:20 am
drop tables in standby db | article only | Mar 30, 2002 10:30 am
Back end Process of SQL | article only | Mar 30, 2002 10:42 am
V$Recovery_file | article only | Mar 30, 2002 02:16 pm
Java debugging in the server (how?) | article only | Mar 30, 2002 04:40 pm
Compress=y | article only | Mar 31, 2002 09:03 am
further search through the records which are NOT included in the previous resultset | article only | Mar 31, 2002 09:13 am
A SQL Query | article only | Mar 31, 2002 09:36 am
the best way to revieve active sql statements on the fly | article only | Mar 31, 2002 09:40 am
NOT IN vs != | article only | Mar 31, 2002 09:45 am
Change file attributes read-only | article only | Mar 31, 2002 01:27 pm
Emulating 10,000 concurrent users | article only | Mar 31, 2002 06:08 pm
Create Table as Select Order by | article only | Mar 31, 2002 07:10 pm
Open Cursors | article only | Apr 01, 2002 01:04 pm
in case there are no rows fetched by cursor !! | article only | Apr 01, 2002 08:34 pm
row(s) 1 - 30 of 30
March 26, 2002 Leon -- Thanks for the question regarding "A couple of questions", version 7.3
You Asked
Tom,

I read your answer to various Oracle question everyday
and learn a lot from it. Thanks for your great work!

Here are some questions that bear in my mind for a long
time. Hope I can get anwser from you.

1) One Oracle Book says that DBA have to connect Oralce
server (using MTS architecure) using dedicated sever connection
in order to perform some DBA task from remote site.
How can I ensure the connection is dedicated? Is there any
setting needed in both server side and client side? Where
can I check that the connection is server dedicated.

2) What is the difference between DB_FILES and MAXDATEFILES?

3) What is the shadow process in Oracle?

4) Where can I check that rollback segment is ACTIVE or INACTIVE
   befor to taking it offline?

5) Regarding database synchronization:

   I am confused about the folloing concepts:

   checkpoint, SCN (timestamp) and redo log reference #
   recorded in datafile header and control file are checked
   (verified) during recovery process or database opening.

   What does the Oracle server exactly do with these numbers?

   Some books says SCN is used for synchronization, some says
   redo log ref#, and other says checkpoint (timestamp).

6) In which situation, one use ALTER TABLESPACE name OFFLINE
   IMMEDIATE?

7) I understand that when a database is in mounted mode only
   control file is open. One oracle book says that one can
   check v$backup view to see whether a tablespace is in backup
   mode or not (in the case that system crash during the hot
   backup) in order to take it offline before the database can
   be open with the purpose to reduce down time.

   I am confused about the statement since dynamic view is only
   available when a database is OPEN mode.

Thanks,

Leon,
and we said...
1) if you are on the server and use just an oracle_home/sid --
it'll be dedicated (no sqlnet).

In your tnsnames.ora you can also:

THINK1_DEDICATED.WORLD =
  (DESCRIPTION =
    (ADDRESS_LIST =
     (ADDRESS = (PROTOCOL = TCP)(HOST = hostname)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = THINK1.world)
    )
  )

to make it dedicated.

2) maxdatafiles is used to size your control files.  it makes
the control files a certain size and your database cannot get
more files then that without rebuilding your control files.

db_files is less then or equal to that and sets up in memory
structures that say how many open files we may have.

3) its your dedicated server.

4) dba_rollback_segs has that...

5) well, they are all used in conjunction with each other.  The
scn can be considered our "clock".  Every commit is one second
-- every commit increments it.  The redo sequence is what log we
are using or what log was current.  Checkpoints tell us what
logs we need to apply to a given file upon recovery.  Given a
failure, we can look at a datafile -- find what log sequence was
last checkpointed to it and apply all of the logs that happened
after that.

Its fairly complex.  Suggest looking at the concepts guide and
admin guide.

6)  Its an emergency thing.  It can be used to simulate failures
as well.  Hope you never have to use it  -- but its there.  Lets
say you "accidently" erased a datafile.  you could offline
immediate the tablespace -- restore from a backup and point in
time recover it....

7) v$ files are there when mounted.  for example:

SVRMGR> shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
SVRMGR> startup nomount
ORACLE instance started.
Total System Global Area                         77336560 bytes
Fixed Size                                          69616 bytes
Variable Size                                    72671232 bytes
Database Buffers                                  4505600 bytes
Redo Buffers                                        90112 bytes

SVRMGR> select * from v$backup;
FILE#      STATUS             CHANGE#    TIME
---------- ------------------ ---------- ---------
ORA-01507: database not mounted

SVRMGR> alter database mount
     2> /
Statement processed.

SVRMGR> select * from v$backup
     2> /
FILE#      STATUS             CHANGE#    TIME
---------- ------------------ ---------- ---------
         1 NOT ACTIVE                  0
         2 NOT ACTIVE                  0
         3 NOT ACTIVE                  0
         4 NOT ACTIVE                  0
         5 NOT ACTIVE                  0
         6 NOT ACTIVE                  0
         7 NOT ACTIVE                  0
         8 NOT ACTIVE                  0
8 rows selected.
SVRMGR>
Review & Followup
Rating: 4
Forcing Dedicated? March 25, 2002
Reviewer: A reader
Good description - what do you mean by forcing a dedicated connection with
ORACLE_HOME/sid ?   if you are on the box itself?

Followup:
If you are logged onto the same machine as the database server....

and you set the environment variables ORACLE_HOME, ORACLE_SID correctly...

and you do not have TWO_TASK set in the environment, then...

sqlplus scott/tiger

will connect locally, without using the network (hence it cannot be using MTS)

Rating: 4
Two tasks March 25, 2002
Reviewer: mahbuba Rahman from Sharjah,UAE
In your follow up Answer you mentioned about "Two_tasks".
What is it ? How to set it in our environment?
Please clarify with examples.

Followup:
the answer to many questions can easily be answered by using "search".

search this site for TWO_TASK, you will find the first hit is "what is two_task"
and contains the answer to your entire question.

Rating: 3
Sagi March 26, 2002
Reviewer: Sagi from India
Hi Tom.

Your answers and explanations are to the point and we all really like them.

I would like to take a movement to Thank you for the help you have extended to
all of us.

Sagi.

Rating: 3
March 26, 2002
Reviewer: ???
3) do you mean v$rollstat for active transactions?

Rating: 3
??? March 26, 2002
Reviewer: ???
oops! that for question # 4.

March 26, 2002 Charles -- Thanks for the question regarding "Airline Website Research", version 8.1.5
You Asked
Hello Tom,
I am the webmaster for Air New Zealand.  We have redeveloped our
website (design/content/backend) and it now uses a Oracle
database (8.1.5 I think) running on the Solaris OS.

I am looking for tools which I can use to develop the webpages,
we have taken a large step forward - previous to our new setup I
used dreamweaver to edit the pages which were all static html.
Now that our pages are to be dynamic (jsp) I am in an area where
I know nothing - can you recommend to me some software that I
could use?  Also, can you recommend where on the web I could go
for more information which is revelevant to designing webpages
which use Oracle?

Thanks for your help.

Sincerely,
Charles Clapshaw
and we said...
There are many ways to accomplish this -- JSP's being one of
them.  I'll point you to resources for that as well I'll broaden
the search a little to incorporate WebDB -- a tool that
generates dynamic pages (as opposed to you coding them yourself
-- for many applications, a small rapidly generated dynamic page
beats a page you have to "hand craft" to some extent).

The tool for developing JSP's in Oracle is Oracle JDeveloper
http://www.oracle.com/tools/jdeveloper/
.  If you would like to read about the process from start
to finish, you can see
http://www.oracle.com/tools/jdeveloper/documents/jsptwp/index.html?content.html
 That tutorial shows you the entire
process from start to finish.

I would also suggest you look at WebDB.  WebDB will help you
manage, index, and organize the content on your web site.  More
about WebDB -- what it does, what is looks like -- may be found
at
http://www.oracle.com/tools/webdb/
Review &
      Followup

      Rating: 3
Could not find
      the link   March 25, 2002
Reviewer:  Dharmesh
      Bangali  from India
Hello Tom,

1.Below mentioned link no more works...can u please tell me were can i find the
same help.

http://www.oracle.com/tools/jdeveloper/documents/jsptwp/index.html?content.html

2. Should I load Oracle Jserver option of Oracle8i 8.1.7, when i am planning to
load Oracle9iAS on my machine? Will the HTTPAPACHE webserver clash?

I will also load Oracle JDeveloper, Forms6i and Report6i. I am planning to
develop portal with Oracle Technologies.

It would be great if u can help me out in setting up the environment.

Thanks in advance

Dharmesh

          Followup:

goto

http://otn.oracle.com/
click on documentation -> Oracle9i JDeveloper (in the Internet Dev. Suite box)

all of the jdev docs/papers are there

you don't need jserv (its basically replaced by OC4J in 9iAS).

March 27, 2002 Shiyin -- Thanks for the question regarding
      "table access(full)", version 8.1.6

      You Asked
Hi,
dear Tom,
     I am wonderful that what's the problem with the following question:

the following sql statement:

SELECT
    CatalogINR.item_CATI_ID ,
    CATEGORY_ITEM.CATI_MODIFIER_ID ,
    CATEGORY_ITEM.CATI_POSITION_ID ,
    CATEGORY_ITEM.CATI_DEPARTMENT_ID ,
    CatalogINR.node_CATN_ID
    FROM CATEGORY_ITEM, CatalogINR
    WHERE CATEGORY_ITEM.CATI_ID = CatalogINR.item_CATI_ID

i create index on CATEGORY_ITEM.CATI_ID  and CatalogINR.item_CATI_ID

and i issued the command :

analyze table CATEGORY_ITEM compute statistics;
analyze table CatalogINR compute statistics;

but the execution plan is :

 0      SELECT STATEMENT Optimizer=CHOOSE (Cost=801 Card=81785 Bytes=15212010)

 1    0   HASH JOIN (Cost=801 Card=81785 Bytes=15212010)
 2    1     TABLE ACCESS (FULL) OF 'CATALOGINR' (Cost=52 Card=81785
Bytes=654280)

 3    1     TABLE ACCESS (FULL) OF 'CATEGORY_ITEM' (Cost=690 Card=81785
Bytes=14557730)

why the indexes were not used?

the tables each have more than 80,000 recoreds.

      and we said...
Because the fastest way to get all of the rows from this query is to do what its
doing.  Using an INDEX would be slower.

We can full scan the tables very fast using multi-block IO. We have to full scan
at least ONE of these tables (you'll be getting every row)...  Instead of doing
an INDEX RANGE SCAN 80,000 times and a table access by rowid 80,000 times -- we
simply HASH and are done with it.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t1 as select rownum my_id, a.*
from sys.source$ a;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t1
  2  select 100000+rownum, a.*
  3  from sys.source$ a where rownum <= 25000;

25000 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t2 as select * from t1;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t1 add constraint t1_pk primary
key(my_id);

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t2 add constraint t2_pk primary
key(my_id);

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t1, t2 where t1.my_id =
t2.my_id;

80493 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   NESTED LOOPS
   2    1     TABLE ACCESS (FULL) OF 'T2'
   3    1     TABLE ACCESS (BY INDEX ROWID) OF 'T1'
   4    3       INDEX (UNIQUE SCAN) OF 'T1_PK' (UNIQUE)

Statistics
----------------------------------------------------------
        326  recursive calls
         28  db block gets
     251301  consistent gets
       4673  physical reads
          0  redo size
   46012252  bytes sent via SQL*Net to client
     596056  bytes received via SQL*Net from client
       5368  SQL*Net roundtrips to/from client
          8  sorts (memory)
          0  sorts (disk)
      80493  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table t1 compute statistics;

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table t2 compute statistics;

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t1, t2 where t1.my_id =
t2.my_id;

80493 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=5245 Card=80493
Bytes=41051430)
   1    0   HASH JOIN (Cost=5245 Card=80493 Bytes=41051430)
   2    1     TABLE ACCESS (FULL) OF 'T1' (Cost=4846 Card=80493 Bytes=20525715)
   3    1     TABLE ACCESS (FULL) OF 'T2' (Cost=135 Card=80493 Bytes=20525715)

Statistics
----------------------------------------------------------
          0  recursive calls
         32  db block gets
      14431  consistent gets
        288  physical reads
          0  redo size
   46012252  bytes sent via SQL*Net to client
     596056  bytes received via SQL*Net from client
       5368  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
      80493  rows processed

look at the consistent gets and such -- an INDEX is not always the best idea,
many times it is just plain the "wrong way".
Review &
      Followup

      Rating: 4
what is the
      basis of full table scan  March 27, 2002
Reviewer:
      lakshmi narasimhan R  from Dubai, UAE
Tom

please see the following queries

RAPID/rdek>set autotrace on
RAPID/rdek>select count(*) from fapfarhdr;

  COUNT(*)
----------
      5333

1 row selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=7 Card=1)
   1    0   SORT (AGGREGATE)
   2    1     INDEX (FAST FULL SCAN) OF 'FAPFARHDR_UIND1' (UNIQUE) (Co
          st=7 Card=5333)

Statistics
----------------------------------------------------------
          0  recursive calls
          4  db block gets
         67  consistent gets
         68  physical reads
          0  redo size
        190  bytes sent via SQL*Net to client
        184  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          1  rows processed

RAPID/rdek>select count(*) from fapfarhdr where 1=0;

  COUNT(*)
----------
         0

1 row selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=7 Card=1)
   1    0   SORT (AGGREGATE)
   2    1     FILTER
   3    2       INDEX (FAST FULL SCAN) OF 'FAPFARHDR_UIND1' (UNIQUE) (
          Cost=7 Card=5333)

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

RAPID/rdek>

in both cases the CARD =5333

my doubt is

1.  even when i give a wrong where clause
    without using any of the columns still it will
    process all the rows. Why?

2.  What is CARD meant for. is it a no. of
    rows processed based on the condition we are
    giving.

          Followup:

1) it processed ZERO rows -- don't you see that in the statistics.  0 consistent
gets, 0 physical reads.  It never touched your data.  the CARD= is a
"guesstimate" by the optimizer at the number of rows possible.  It is not a fact
after the query, it is a guess before the query.

2) card = cardinality, the expected number of rows you will hit in a step.

March 28, 2002 arunima -- Thanks for the question regarding "Top
      'N' Analysis", version 8.1.7

      You Asked
Tom,

Hope you had a good vacation. Can you please help me understand what does it
mean to Perform Top 'N' Analysis. I see this topic under Creating Views in the
sql cerfication exam.

Thanks a lot.

      and we said...
it in general refers to getting the top-n rows from a result set.

Eg: find the top 3 employees ranked by salary:

  select *
    from ( select ename from emp order by sal )
   where rownum <= 3;

thats a top-n type query.
Review & Followup

      Rating: 5
Top 'N'
      query  February 22, 2002
Reviewer:  arunima

Thanks Tom.

      Rating:
      4
  March 20, 2002
Reviewer:  Shane Hu
       from NYC
If the info for the employee with highest salary is needed and the table is
huge, with millions rows, will it be expensive to do the top-1 analysis? Is
there an alternative? Will it be useful to create an index on the "order by"
column?  Thanks,

          Followup:

Yes, if there were an index on SAL (and I just noticed the query should be:

 select *
    from ( select ename from emp order by sal DESC )
   where rownum <= 3

of course) and SAL was non-nullable (or a predicate that precluded NULL sal's
was added), we would read the index "backwards" to find the top 3 and stop.  The
optimizer is "smart" about this.

      Rating: 4
a followup
      question  March 28, 2002
Reviewer:  Shane Hu
       from NYC
How can I retrive data for those whose sal is between the 4th highest and the
6th highest? I notice that the PL/SQL manual states that the only meaningful
uses of ROWNUM in a WHERE clause are
... WHERE ROWNUM < constant;
... WHERE ROWNUM <= constant;
Thus, I cannot do where rownum between 4 and 6.

My true intention is to skip top N records and retrive data from the middle. It
would be more efficient by not retriving the top N records. Do you have the
trick to do the skip?

Thanks,

Shane

          Followup:

read

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:127412348064
...

      Rating: 4
chen sun
      HK  July 16, 2002
Reviewer:  chen sun
       from Hong Kong
Hi Tom,
     How can do the following requirements in the oracle discover?
     Dimension1- by princial, channel, agent account
     Dimension2- for current month, YTD, current month     last year, YTD last
year
     Dimension3- Top 50 +ve incurred movement,Top -ve incurred movement, others
     Dimension4- new claim, old claim, all
and every dimension should display the following data :
1. gross reserve b/f
2. gross paid
3. net paid
4. gross incurred movement
5. net incurred movement

It is difficult for me to display the Top 50 +ve incurred movement and Top 50
-ve movement when I choose diff dimensions.

Thanks...

      Rating: 4
thanks
      Tom  May 22, 2003
Reviewer:  hazim
       from VN
Hi Tom
I urgently need your help!
I have tow PL/SQL blocks, as I thought they must be the same but the returned
results are different:

----------Block 1----
declare
sort_type integer :=1;
mi_id integer :=0;
n number := 100;
m number := 70;
k number := 30;
begin
execute immediate 'alter session enable parallel dml';

execute immediate 'alter table temp nologging';
insert /*append*/ into temp
(select a.srcaddr,a.dstaddr from
        (select srcaddr,dstaddr,sum(dpkts) sum_p from all_ip
         where src_as=0 and srcaddr in
            (select b.srcaddr from
                (select srcaddr,sum(dpkts) sum_p from all_ip
                where src_as = 0
                group by srcaddr
                order by sum_p desc
                ) b
             where rownum<=n
            )
        group by srcaddr,dstaddr
        order by sum_p desc
        ) a
    where rownum <=n*m
    );
end;
/
-----------------------------------------------------------------------
------Block 2-----------------------
declare
sort_type integer :=1;
mi_id integer :=0;
n number := 100;
m number := 70;
k number := 30;
begin
execute immediate 'alter session enable parallel dml';

execute immediate 'alter table top_out_mi modify partition top_out_mi_p
nologging';
insert /*append*/ into top_out_mi
 ( select
sort_type,mi_id,a.srcaddr,a.dstaddr,null,null,a.sum_p,a.sum_o,a.cf,a.sum_po from

    (select srcaddr,dstaddr,sum(dpkts) sum_p,sum(doctets) sum_o,count(srcaddr)
cf,
     sum(dpkts+doctets) sum_po from all_ip
     where src_as=0 and srcaddr in
         (select b.srcaddr from
            (select srcaddr,sum(dpkts) sum_p    from all_ip
             where src_as = 0
             group by srcaddr
             order by sum_p desc
             ) b
         where rownum<=n )
     group by srcaddr,dstaddr
     order by sum_p desc
      ) a
  where rownum <=n*m
 );
commit;
end;
/
--------------------------------------------------------------------------
The structure and content of two blocks are the same, only different in inserted
columns. My desired result is that :
 - I want to find couples (srcaddr,dstaddr) that rank top-n*m in sum_p and
srcaddr must be in the set of srcaddr(s) that rank top-n in sum_p ( as sub-query
named b)
and my question are :
1. are above blocks correct to get my target?Please guide me if there're better
ways to do that.
2. as I mentioned at the top, two blocks insert data to temp and top_out_mi, as
I thought the (srcaddr,dstaddr) in two table must the same but following query
return contrary thing :
SQL> select count(*) from top_out_mi;

  COUNT(*)
----------
      7000

SQL> select count(*) from temp;

  COUNT(*)
----------
      7000
SQL> select count(*) from top_out_mi where (srcaddr,dstaddr) not in (select
srcaddr,dstaddr from temp2);

  COUNT(*)
----------
       253 (as I thought this must be zero).
Please Help!
Thanks very much
Kind Regards.

March 28, 2002 arunima -- Thanks for the question regarding "sql
      query", version 8.1.6

      You Asked
Hi Tom,

Please help me write a query wherein I can get this result.

select * from t;

a    b
--  ---
1    11
2    11
3    22
4    33

Result (Here we need those rows where b's occurance is not more than once)

a    b
--   --
3    22
4    33

Thanks for your time

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> select a, b
  2    from t
  3   where NOT EXISTS ( select null
  4                        from t t2
  5                       where t2.b = t.b
  6                         and t2.a <> t.a )
  7  /

         A          B
---------- ----------
         3         22
         4         33

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select *
  2    from ( select a, b, count(b) over (order by b range between 0 preceding
and 0 following ) cnt
  3                     from t
  4             )
  5   where cnt = 1
  6  /

         A          B        CNT
---------- ---------- ----------
         3         22          1
         4         33          1

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select t.*
  2    from t, (select b
  3                           from t
  4                          group by b
  5                     having count(*) = 1) t2
  6   where t.b = t2.b
  7  /

         A          B
---------- ----------
         3         22
         4         33

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select *
  2    from t
  3   where (select count(*) from t t2 where t2.b = t.b ) = 1
  4  /

         A          B
---------- ----------
         3         22
         4         33

ops$tkyte@ORA817DEV.US.ORACLE.COM>

are but a couple of ways.

If b is indexes, the not exists is probably the fastest way to get the "first"
rows back.
Review & Followup

      Rating:
      5
arunima  March 29, 2002
Reviewer:
      arunima  from MI
Thanks a lot.

March 29, 2002 prashanth -- Thanks for the question regarding
      "convert column data to a row ", version 8.1.7

      You Asked
Hi

Is it possiable in a single sql to fetch a columns data in a single row.

Ex: If X is a column of integers and has the following values

1
2
3
4
null

i need to display as "1,2,3,4, null" at my fetch.

Thanks in advance.
Cheers
Prashanth

      and we said...
If and only if you know the maximum number of rows.  We can do that with decode.

OR

If you can deal with using an "array".

OR

if you can deal with a "CURSOR" type

ops$tkyte@ORA817DEV.US.ORACLE.COM> select max(decode( rownum,1,x,null ))c1,
  2             max(decode( rownum,2,x,null ))c2,
  3             max(decode( rownum,3,x,null ))c3,
  4             max(decode( rownum,4,x,null ))c4,
  5             max(decode( rownum,5,x,null ))c5
  6   from ( select * from t order by x )
  7  /

        C1         C2         C3         C4         C5
---------- ---------- ---------- ---------- ----------
         1          2          3          4

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace type myTableType as table
of number
  2  /

Type created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select cast( multiset(select * from t order
by x) as myTableType )
  2    from dual
  3  /

CAST(MULTISET(SELECT*FROMTORDERBYX)ASMYTABLETYPE)
---------------------------------------------------------------------------------
--------------------------------------------------
MYTABLETYPE(1, 2, 3, 4, NULL)

ops$tkyte@ORA817DEV.US.ORACLE.COM>

ops$tkyte@ORA817DEV.US.ORACLE.COM> select CURSOR( select * from t order by x )
  2  from dual
  3  /

CURSOR(SELECT*FROMTO
--------------------
CURSOR STATEMENT : 1

CURSOR STATEMENT : 1

         X
----------
         1
         2
         3
         4

Review &
      Followup

      Rating:
      5
Thanks  March 29, 2002
Reviewer:
      Manikandan  from Singapore
Hai Tom,

The Decode query was very much useful

Regards,
Manikandan.R.

      Rating:
      5
  March 30, 2002
Reviewer:
      prashanth  from Singapore

      Rating: 5
Ver
      Good  April 01, 2002
Reviewer:  AK
       from India

      Rating: 5
Very
      Good  April 01, 2002
Reviewer:  AK
       from India

March 29, 2002 A -- Thanks for the question regarding
      "Tune-PL/SQL Block", version 8.1.6

      You Asked
1.How to tune a PL SQL Block? Can u give clear explanation. What is the
difference between EXPLAIN PLAN and TKProf Utility?

2. What is the difference between the OFF Line Tablespace and Read Only
Tablespace?

      and we said...
1) tuning PLSQL is no different then tuning C, VB, Java, etc etc etc.

You are tuning procedural logic typically -- looking for better algorithms.
Having a solid knowledge of the language and what it offers is paramount to
tuning (eg: learn everything about the language and you'll be able to tune the
algorithms in that language).

explain plan and tkprof are for tuning SQL.  explain plan shows you what the
database would do if it were asked to run your query.  tkprof shows you what the
data DID really, when asked to run your query (cpu time, elapsed time, physical
io's, logical io's,etc).

I can say -- in my book I fully describe

o how to use tkprof
o how to use autotrace (i never use explain plan, too unwieldy, autotrace is
much better)
o how to use stats pack
o how to use dbms_profiler ( a source code profiler very useful in tuning plsql
based algorithms)
o lots more

2) offline = you cannot access it read or write
   read on = you cannot access it write.  you can READ it.
March 29, 2002 Famy -- Thanks for the question regarding "How to
      find out from which procedure the error is emanating....", version 8.1.7

      You Asked
Tom

I have a procedure , which call another procedure , which in turn calls another
procedure, and this finally call another procedure. In between these procedures
also call functions, which in turn call other functions.

Now when I run my main procedure, how should I figure out...

1.When I encounter an error, how should I know from which procedure or function
the error is coming from.
Can you give me an example and show how to code, and what to code, so that I
will know exactly where the problem is coming from.

2.Is dbms_trace package useful in any way.

Thank you

      and we said...
1) the call stack returned to the client tells you this.  Just run your
procedure in sqlplus and you'll see the entire call stack.

2) did you search for dbms_trace and read the articles on it?  do that and
you'll see....
March 29, 2002
      uma -- Thanks for the question regarding
      "Truncate and Coalesce ", version 8.1.6

      You Asked
Do I need to COALESCE the table space for the indexes after truncating the
table. My table TAB1 is in table space TAB_SPACE and indexes for that table are
in table space IND_SPACE. After truncating the table, Do I need to COALESCE the
table space TAB_SPACE and COALESCE the table space IND_SPACE? Do I need both or
neither or one?

Thanks in advance

Uma

      and we said...
In 8i, you should be using locally managed tablespaces and you never ever need
to coalesce them

So, my answer is "NO".

Even in dictionary managed tablespaces, it would be very rare to need to.  But,
it doesn't really hurt anything so feel free to do so.
March 29, 2002 Tianhua -- Thanks for the question regarding
      "sqlplus working path", version 8.1

      You Asked
Hi Tom,

I ran into this problem recently and still could not figure out why.

In one Solaris box, whenever I use ed in sqlplus, it will only use $ORACLE_PATH.
 When I unset it, everything is back to normal (using current directory).
However, on other boxes, Even ORACLE_PATH is set, it is still work normally.
all the login.sql or glogin.sql are the same.  And I use default for sqlplus.  I
search Metalink, and post question there and also in googles, so far I did not
get any satisfied answer and it still puzzles me.  Do you know why?
Thank you,

T Wu

      and we said...
Proof positive that you learn something new every day.  I had never used this
one myself.  This is what it does:

ORACLE_PATH Use:      directory for reading and writing to and
                      from SQL*Forms, SQL*Menu 5.0, SQL*Plus
            General:  can be any directory (if not set, read and
                      write from current directory)
            Example:  $HOME/oracle

I cannot get it to "not work" (so the bug would be that on some boxes, the
oracle_path is ignored, not the other way around).  If the Oracle_path is in
fact set, sqlplus should use that to read and write from, as if that were the
current directory.
Review & Followup

      Rating:
      3
  March 29, 2002
Reviewer:  Tianhua
      Wu  from Atlanta, GA
I agree.  From my experience, ORACLE_PATH is just like SQL_PATH or PATH for
unix, it just give you path/s to search when you just specify a file name.  It
should not direct where you write to, right?  In that "strange" box, if I set
ORACLE_PATH, and I do not have write permission on that path, I could not use ed
in sqlplus at all.  My problem is that I could find anything that is set
different on that box.
Thank you,

T Wu

          Followup:

for SQLPlus -- its behavior is to direct reads and writes

March 29, 2002 vrishali -- Thanks for the question regarding
      "Log_miner", version 8.1.6

      You Asked
Hi Tom,

   I set utl_file_dire parameter in init.ora and restated the
database. If I will run following query aginst sqlplus the
output is -

select value from v$parameter where name = 'utl_file_dir'

/mnt/log_mnr
---------------

Now I am trying to create dictionary.ora by executing following
command as a SYS user -

Execute dbms_logmnr_d.build Dictionary_filename  'dict1.ora',
Dictionary_location => '/mnt/log_mnr');

And I am getting following error

ORA-01309: specified dictionary file cannot be opened
ORA-06510: PL/SQL: unhandled user-defined exception
ORA-06512: at "SYS.DBMS_LOGMNR_D", line 793
ORA-06512: at line 2

could you please tell me what may be the reason?

Thanks,
Vrishali

      and we said...
Run this test:

declare
    l_output        utl_file.file_type;
begin
    l_output := utl_file.fopen( '/mnt/log_mnr',
                                'dict1.ora', 'w' );
    utl_file.fclose( l_output );
end;
/

if that fails and the UTL_FILE_DIR is set as above -- then:

o the Oracle software owner (the userid of the oracle processes)
does not have write on the /mnt/log_mnr directory.  Don't forget
-- UTL_FILE is done by the server and will be running under the
SERVERS id.

o the file /mnt/log_mnr/dict1.ora already exists but it privs
are set such that the Oracle software account cannot open it for
writing.

Review &
      Followup

      Rating: 1
"Log_miner",
      version 8.1.6  March 27, 2002
Reviewer:  Mike
      McGinty  from Charlotte, NC
The answer confirms the problem but does not give an answer for resolving it.

          Followup:

did you not read the response??

I quote:

if that fails and the UTL_FILE_DIR is set as above -- then:

o the Oracle software owner (the userid of the oracle processes) does not have

write on the /mnt/log_mnr directory.  Don't forget -- UTL_FILE is done by the
server and will be running under the SERVERS id.

o the file /mnt/log_mnr/dict1.ora already exists but it privs are set such
that
the Oracle software account cannot open it for writing.

If you cannot take that and "turn it into an answer" (eg: either give the guy
WRITE or get rid of the dict1.ora that exists), I don't know what to say....

      Rating: 3
Log
      Miner  March 27, 2002
Reviewer:  Kasturi

Hello Tom,

I have a problem, currently one of my instances are generating lots of redo....I
never had this problem couple of days back.

I made a small test: I exported "scott" user and imported into the instance
which is generating lots of redo...analyzed the log files using log miner as you
have specified in your book. But there are ton's of records and most of them are
DML activities on internal "$" tables. How do we analyze what is causing to
generate lots of redo....

Thanks

          Followup:

1) what changed.  What did you turn on in the last couple of days.  what new
things did you start using or doing.

2) how much is "lots".

3) what $ tables.

      Rating: 3
Log
      Miner  March 27, 2002
Reviewer:  Kasturi

1. I do not know exactly what has changed. However couple of weeks back i
upgraded the instance to 8.1.7.0.0 with out any problems. used
_system_trig_enabled=false...and removed that option once the upgrade was
complete. There is a control-M server running against it, but not sure what it
could have changed it.

2.Each of the redo logfile is 2M of size. and we have currently 3 online logs.
Currently the instance is generating 20-30 archivelogs per minute. For importing
"scott" schema the exp dump was around 3M, while the redo generated is 5M. Does
that mean if i want to import 3G of export dump, i will get 3G of redo
generated?

3.The main internal tables are as i queried them randomly are
COL$, IDL_SB4$,  IDL_UB1$  IDL_UB2$, KOTTD$
How do i analyze what process is generating the redo. Further there are umpteen
"commits" in the log file.

Thanks,

          Followup:

1) well that in itself would be a change (the upgrade)

2) it is not possible to determine the amount of redo generated by a dump file.
It'll probably be AT LEAST as big if not much larger (index creates take no room
in the dmp file, can generate tons of redo)

3) the IDL tables are where compilled code is stored.  Perhaps your system is
"recompiling" itself after the upgrade (although 20/30 archives per minute or
~7gig of archives a day seems extreme).

Are you really getting 7gig of archive a day unexpectedly?
Is it sustained?
What is your aq_tm_processes set to, do you run ctxsrv?

      Rating: 5
Tom is right
      !  March 28, 2002
Reviewer:  Andre Whittick
      Nasser  from Brazil
As to the original question, what Tom explained is probably your solution. Of
course, there may be some other issues like ACLs (especially on AIX and HP), but
they're inevitably related to UNIX permissioning or file inconsitencies (file
name, path, existing file with same name, etc.). If you're not a UNIX sys admin,
please ask one for help !

      Rating:
      3
  March 28, 2002
Reviewer:  Raj
       from USA
If you have defined UTL_FILE_DIR directory with a / at the end, keep this
character in the definition of the directory parameter value of the BUILD
procedure.

      Rating: 5
On Raj's
      Comment  March 28, 2002
Reviewer:  Andre
      Whittick Nasser  from Brazil
Yes Raj, both string should not conflict. They should be verbatim the same.

      Rating: 3
Log
      Miner  March 29, 2002
Reviewer:  Kasturi

Tom,

We have been trying to import a 200M export dump. And the amount of redo it
generated is more than 400M. The redo logs got switched more than 200 times.
After that also the system generates redo, but at the rate of 4-5 per minute.
Is this normal considering a size of 2M per redo log?

I am not running ctxsrv....

With this kind of rate of redo generation, How is Log Miner
helpful in analyzing the problems such as this?

Thanks for the help...

          Followup:

a 200m dmp file could generate many gigabytes of redo logs.  It all depends on
how many indexes and procedures you have there.

The IDL tables are where compliled plsql is stored, you are seeing the effects
of lots of plsql code in your dmp file being compiled, the data being inserted
and the indexes on these tables being maintained.  100% normal and acceptable
and expected.

2m log files are *tiny*.

This is perfectly normal.  There are no problems here at all.

March 29, 2002 Logan -- Thanks for the question regarding
      "Statspack: DB File Scattered Read and DB File Sequential Read", version
      8.1.6

      You Asked
Tom,

In one of the Oracle Magazine articles about 'Analyzing a Statspack Report' (Nov
2000
http://www.oracle.com/oramag/oracle/00-nov/index.html?o60tun.html
 issue, the authors say

"DB File Scattered Read and DB File Sequential Read (and other I/O-related
events, such as direct read, direct write, and log file parallel write). The two
most common I/O-related events that can appear in the Top 5 Wait Events section
are db file scattered read and db file sequential read. Each time an I/O
operation occurs, the server process waits for one of these two events to occur.
Which one it waits for depends on the optimizer's determination of the best way
to return the requested data. The db file scattered read event indicates that a
full-table scan is occurring, whereas the db file sequential read event
indicates that a single-block read is occurring. "

Look at the last statement.  I think it should be "the db file scattered read
event indicates that an index scan is occurring, whereas the db file sequential
read event indicates that a full-table scan is occurring"
Is my understanding correct?

      and we said...
the names are counter intuitive.  The article is correct.  full scans are
scattered, sequential are indexed reads.
March 29, 2002 Prakash -- Thanks for the question regarding
      "Batch processing -- send an email every half hour", version oarcle
      8i(8.1.6)

      You Asked
Hi tom,
Iam prakash. i need to write an oracle stored procedure to run a
batch job which executes automatically every half hour to send
E-mails. This batch job, retrieves all the records from a
table,(excluding the records already sent from last
retrievals)and route then thru our mail server. Our mail server
sits on a Win-2000 machine. We use oracle 8i database on a unix
box and oracle 8.1.6 client on the Windows-2000 box.

iam sorry!
MY QUESTION IS:
How do we write a oracle stored procedure which executes
automatically every half hour?. How can we configure the oracle
8.1.6 client to talk with the mail server on windows 2000?.
Thank you in advance,
Prakash pattisapu,
Msx International

      and we said...
See the DBMS_JOB package in the supplied packages guide:

http://technet.oracle.com/doc/oracle8i_816/server.816/a76936/dbms_jo2.htm#1001490
For sending email (since you have 8.1.6), if you can use SMTP to
send mail (eg: if you can send email from the unix command line
you can send mail via SMTP for sure) see

http://technet.oracle.com/doc/oracle8i_816/server.816/a76936/utl_smtp.htm#1000695
or, if you like java better, see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:255615160805
...

Review &
      Followup

      Rating: 4
which one is
      better -- unix mail or utl_smtp  March 29, 2002

Reviewer:  Nagarju
Tom,

Greetings!!!

 what is better way of sending an email. is it using unix or oracle package
utl_smtp.

1.if have to use unix mail, i have to run a Stored proc from unix and get some
data and use unix mail to send that contents as mail.

2.if i have to use utl_smtp,modify that stored proc to directly send those
contents as mail using utl_smtp

if it is the first way, can you please give simple example(command) sending an
email from unix?

if it is the second way can you give me simple example with utl_smtp

thanks

          Followup:

If you want to do it from a stored procedure, UTL_SMTP is the only .

Just follow the already supplied links for examples...

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:255615160805
...

      Rating: 4
how do we send
      the mail in unix  March 29, 2002
Reviewer:
      Manoharan
tom,

that is informative.
but how do we send mail using UNIX?
an example would help.

thanks

          Followup:

and how that relates to Oracle the database?

do a

$ man mail

read the page.  We would not use unix "mail" to send mail from plsql as that
would entail TONS more work then using utl_smtp.

      Rating: 5
how do we send
      the mail in unix   March 29, 2002
Reviewer:
      Manoharan
my requirement is to

i need to write shell script that will call a proc which returns some values,
using that info send mail using UNIX mail command..

do you still suggest utl_smtp...

          Followup:

I would call a procedure that retrieves some values and sends an email.

getting the values from the procedure to the shell script would be painful
enough, let along sending an email.

March 29, 2002 Simi -- Thanks for the question regarding "PL/SQL
      Tables vs Global Temporary Tables", version 8.1.7

      You Asked
Hi Tom,
Which one of the following statements is more efficient.

Using PL/SQL table emp_tbl
SELECT e.name, e.sal
FROM emp e, THE (select cast(emp_tbl(1,2,3) as emp_tbl) from dual) t
WHERE e.empno = t.column_value;

Using Global temp table temp_emp
SELECT e.name, e.sal
FROM emp e, temp_emp t
WHERE e.empno = t.empno;

      and we said...
did you benchmark it?  it is what I would do...

ok, i have some time, i'll do it.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace type myTableType as table
of number
  2  /
Type created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table temp;
Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create global temporary table temp ( empno
int ) on commit delete rows;
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table emp;
Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table emp as select * from scott.emp;
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table run_stats;
Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table run_stats ( runid varchar2(15),
name varchar2(80), value int );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> grant select any table to ops$tkyte;
Grant succeeded.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view stats
  2  as select 'STAT...' || a.name name, b.value
  3        from v$statname a, v$mystat b
  4       where a.statistic# = b.statistic#
  5      union all
  6      select 'LATCH.' || name,  gets
  7        from v$latch;
View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_start number;
  3
  4      l_array myTabletype;
  5  begin
  6      insert into run_stats select 'before', stats.* from stats;
  7      l_start := dbms_utility.get_time;
  8
  9      for i in 1 .. 1000
 10      loop
 11          l_array := myTableType( 7566, 7654, 7698, 7782, 7788, 7839 );
 12          for x in ( select ename, sal
 13                       from emp, TABLE( cast( l_array as myTableType ) )
 14                      where empno = column_value )
 15          loop
 16              null;
 17          end loop;
 18      end loop;
 19      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 20
 21
 22      insert into run_stats select 'after 1', stats.* from stats;
 23      l_start := dbms_utility.get_time;
 24
 25      l_array := myTableType( 7566, 7654, 7698, 7782, 7788, 7839 );
 26      for i in 1 .. 1000
 27      loop
 28          for j in 1 .. l_array.count
 29          loop
 30              insert into temp values( l_array(j) );
 31          end loop;
 32          for x in ( select ename, sal
 33                       from emp, temp
 34                      where emp.empno = temp.empno )
 35          loop
 36              null;
 37          end loop;
 38          commit;
 39      end loop;
 40      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 41
 42      insert into run_stats select 'after 2', stats.* from stats;
 43  end;
 44  /
105 hsecs
241 hsecs

PL/SQL procedure successfully completed.

so, filling the array with that many values, doing the query runs faster then
doing the same to the temp table 1000 times.... going further we see:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value-a.value run1,
c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11   order by abs( (c.value-b.value)-(b.value-a.value))
 12  /

NAME                                           RUN1       RUN2       DIFF
---------------------------------------- ---------- ---------- ----------
...
LATCH.file number translation table               0       1000       1000
STAT...table scan blocks gotten                1000       2000       1000
STAT...commit cleanout failures: block l          0       1000       1000
ost

STAT...calls to kcmgas                            0       1000       1000
STAT...table scans (short tables)              1000       2000       1000
STAT...user commits                               0       1000       1000
STAT...commit cleanouts                           0       1005       1005
LATCH.shared pool                              3168       2160      -1008
LATCH.redo writing                                3       1078       1075
LATCH.messages                                    3       1080       1077
STAT...consistent gets                         5018       7012       1994
LATCH.list of block allocation                    0       2000       2000
LATCH.dml lock allocation                         0       2002       2002
STAT...free buffer requested                      4       2055       2051
LATCH.transaction allocation                      0       3000       3000
LATCH.undo global data                         4000       7000       3000
LATCH.cache buffers lru chain                     3       4059       4056
STAT...enqueue requests                           5       5003       4998
STAT...enqueue releases                           4       5004       5000
LATCH.sort extent pool                            0       6000       6000
STAT...table scan rows gotten                 14000      20000       6000
STAT...execute count                           1005       7006       6001
LATCH.library cache                            8334      14377       6043
STAT...redo entries                              16       7013       6997
STAT...recursive calls                         7031      14034       7003
LATCH.redo allocation                            14       7098       7084
STAT...calls to get snapshot scn: kcmgss       5009      13005       7996
LATCH.row cache objects                       18100       9082      -9018
LATCH.enqueue hash chains                         6      10008      10002
STAT...consistent changes                         0      11000      11000
LATCH.enqueues                                   13      16015      16002
STAT...db block gets                          12024      31017      18993
STAT...db block changes                          27      19022      18995
STAT...session logical reads                  17042      38029      20987
LATCH.cache buffers chains                    34104     169196     135092
STAT...redo size                              21468    1072148    1050680

59 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

It used less db resources too.  So, for a modest table like this -- I would be
tempted to choose the OBJECT type...

Review &
      Followup

      Rating:
      5
Great  March 29, 2002
Reviewer:
      A reader
Thank you very much

      Rating:
      5
  March 29, 2002
Reviewer:
      A reader
Will there be any performance problem if we populate more rows into pl/sql table

          Followup:

benchmark it and see -- you have everything you need above!

March 30, 2002 Yogesh -- Thanks for the question regarding
      "Index and V$SESSION_LONGOPS", version 8.1.7

      You Asked
Whenever there is a long operation an entry is added in V$SESSION_LONGOPS.

If I issue following command

CREATE INDEX PK_TRan ON TRANS(TRANSACTION_ID) TABLESPACE TIND
/

After that I issued following command. Here the results are right. i.e. Process
gets over in said time (time_remaining column).

select * from V$SESSION_LONGOPS where SOFAR <  TOTALWORK
/

Now if I issue following command

CREATE INDEX IDX_T_TDATE_CCD_TID ON TRANSACTIONS (TDATE, CCODE, TID)
/

After that I executed once again

select * from V$SESSION_LONGOPS where SOFAR <  TOTALWORK
/

Now here I could not understand the results. In this case first it was showing
me sofar value as 10 and totalwork as 1000. Once this got over again after
firing the same qury I got something like sofar value 50 totalwork 400 after
finishing this again I fired same command. Again some similar scenario ....

So my obervation is, it do not show total time. Why this is so ? is it due to
composite index ? or any other reason ?

Plz correct me If my oberservation is wrong. How can we get total time period in
such cases. ?

I hope this explains the question.

      and we said...
It is going to be a function of how many steps it'll take.

It is not showing you the end to end time of the index create, it is showing you
the end to end time of the STEPS within the index create.

For example, I had a sort_area_size of 1m.  I created a table t with 1,000,000
rows based on all_objects.  On this table, I put an index on object_id.  This
took many TABLE SCAN followed by SORT/MERGE followed by SORT/OUTPUT steps. Each
was timed independently.

Next, I dropped that index and set my sort_area_size to 300m (large enough to
avoid a sort to disk).  This time, the ONLY thing in v$session_longops was a
TABLE SCAN, no sort/merge, no sort/output.  Since we didn't swap to disk, these
steps were avoided.

So, I'll guess -- your single key index was done in memory, your concatenated
key was not.

Review &
      Followup

      Rating: 3
More
      Questions  March 29, 2002
Reviewer:  Yogesh
       from Pune, India
Does it mean that if I set sort_area_size  to 'N'  as you mentioned, which is
sufficient for sorting etc. It would have given me statistices in one shot ?

Another observation about V$SESSION_LONGOPS,  If some query is getting executed
may be a complicated join.... which gonna take 1 hr or so... sometimes I get
entry of it in table sometime not ... What is the criteria on which it is
decided whether the entry  will be in V$SESSION_LONGOPS  or not

          Followup:

If the index build it done entirely in one step, it would have worked like the
first one did.

v$session_longops -- as i said -- is not an "end to end" thing for a statement.
It reports on the progress of the STEPS within the statement.  If a long running
query consists of thousands of discrete steps each taking less then 5 seconds or
so -- you won't see any entries.  It is a function that takes longer then 5
seconds that you will generally see in there.  Don't forget the entries come and
go as they steps complete as well.  Just because you didn't observe anything in
there doesn't mean nothing was in there -- just that you didn't look at the
exact moment.

Predicating the end of a statement is a horribly inexact science.  You can use
v$session_longops to

a) verify the statement is going (makes you feel better sometimes)
b) how far along the current step is.

March 30, 2002 Mahesh -- Thanks for the question regarding "Loop
      execution control", version 8.1.7

      You Asked
I have a FOR loop, in the midst of which i have a condition evaluation, which if
it evaluates to TRUE, i need to skip all further code execution in the loop, and
proceed to the next iteration of the loop. I looked for a CONTINUE statement in
Oracle documentation, and did not find one. The only way I have been able to do
this is to use a WHILE loop as follows, using a GOTO:

declare
   test number := 0;
   i number := 10;
begin
   <<mk_loop>>
   while (test <= i-1) loop
      test := test+1;
      if (test <= 5) then
         goto mk_loop;
      end if;
      dbms_output.put_line('test : ' || test);
   end loop mk_loop;
end;

Output as follows:
test : 6
test : 7
test : 8
test : 9
test : 10

I tried the same with a FOR loop, but that did not work since the loop index was
reinitialized, and anyway the loop index cannot be reassigned within the body of
the FOR loop.

I would prefer not to use a GO TO, and not to have to transform the FOR loop
into a WHILE loop.

Any help is much appreciated.

MK

      and we said...
instead of using a jump to avoid processing, use an if to do processing when you
want to.

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2     test number := 0;
  3     i number := 10;
  4  begin
  5     while (test <= i-1) loop
  6        test := test+1;
  7        if NOT (test <= 5)
  8        then
  9           dbms_output.put_line('test : ' || test);
 10        end if;
 11     end loop mk_loop;
 12  end;
 13
 14
 15  /
test : 6
test : 7
test : 8
test : 9
test : 10

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

Review &
      Followup

      Rating: 4
break
        March 29, 2002
Reviewer:  oralearner
       from us
1. instead of breaking the loop mentioning upper limit,
can one break the loop by mentioning time value(after 5 secs or so) in place of
upper limit.

2. or if loop lower limit 1 and upper limit 10,000 are mentioned inthe loop, and
a condition is given to break after 2 secs lets suppose, so that  the loop would
break somewhere in between 1 to 10,000.
are these two conditions possible ? ifso how?
thankyou.

          Followup:

one can program anything one wants to.  If you want to exit when:

   l_start := dbms_utility.get_time;
   for i in 1 .. 10000
   loop
      exit when ( dbms_utility.get_time-l_start > 500 );
      ....

then you most certainly may.

      Rating:
      5
break  March 31, 2002
Reviewer:
      oralearner  from us
thanku tom
that's the answer i've been looking for months.

March 30, 2002 Ted -- Thanks for the question regarding "drop
      tables in standby db", version 8.1.6

      You Asked
Hi Tom,

My standby database is in read only mode,

When I droped tables in primary database, I found the tables I droped still
existed in standby db.

is the read only mode information  not same as primary db?

best regards,

Ted.

      and we said...
If standby is in read only mode, then it is NOT applying the redo (transaction
logs).  It is frozen at the point in time you opened it.  Close it, catch it up
by putting back into recovery mode and reopen it. The tables will be gone.
March 30, 2002 Shazia -- Thanks for the question regarding "Back
      end Process of SQL", version 8

      You Asked
I am looking for some helpful hint or links to my questions

Q: Assuming I have index on table and When I write Sql query nothing
   comes up.It is a big table index is fine query is right but I
   do not get the result or any kind of error.

       My question is how this happen and how can we handle this.

Q: How can I tune the SQL? Is this something we use SQL Navigator for?

      and we said...
q) look for a bug in the way you wrote the query.  Not getting rows is NOT an
error.  Not getting rows when you expected rows is generally an error in the way
you wrote the query.

For me to comment any further, I would have to actually SEE the query,
understand what rows you are looking for and see proof that such a row exists
(eg: you would insert a row that obviously should be returned by your query --
run your query and see the row does not get returned).  A cut and paste from
SQLPlus is the way to do that.

q) tuning SQL starts with understanding all of the possible paths SQL can use.
If you want, you can use the tuning pack with OEM and let software take a crack
at it (maybe sql nav. does that, I don't know).  If you really want to be able
to tune queries yourself (i mean, if a piece of software can do it all for you
-- why do we need you?), you will read:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76992/toc.htm
....
March 30, 2002 Reddy -- Thanks for the question regarding
      "V$Recovery_file", version 8.1.5

      You Asked
Hi Tom

Today I confusing regarding my V$Recovery_file. I just queried my

SQL> Select * from v$recovery_file;
no rows selected.
SQL> Select count(*) from v$recovery_file;
 11 rows selected.
this I taught I donot have proper access.
Then
SQL> Connect Internal@psi1 as sysdba;
Password **************
connected.
SQL> Select count(*) from v$recovery_file;
11 rows selected.
SQL> Select * from v$recovery_file;
no rows selected.

Why this happing. I am getting so much confuse. If any rows contains that files
need recovery, right?

Can u tell me why this is behaving like this.

Thanks in advance

Reddy

      and we said...
You have 11 files i bet.

v$ tables are "magic".  they are special.  they are actually queries on top of
data structures mostly -- not "tables" really.

There is a potential slot in there for every file.  Since none of your files
needs media recovery, select * returns nothing.  The count(*) is actually
counting the number of slots in there.

This was identified as being a "bug" against this view (if in fact you issue
"select file# from v$recover_file", you'll get 11 rows back).

This is corrected in 816 and up.

Your workaround is to:

ops$tkyte@ORA815.US.ORACLE.COM> select count(time) from v$recover_file;

COUNT(TIME)
-----------
          0

count(time) instead of count(*)
Review &
      Followup

      Rating:
      4
v$recover_file  March 30, 2002
Reviewer:
      Reddy  from Bangalore, India
Thank u tom,

I think this "SLOTS" are Tablespace's, because I am having exactly 11
tablespaces. Is it correct.

Reddy

          Followup:

and how many files/ tablespace (one i bet).

it's for files.

March 30, 2002 Mike -- Thanks for the question regarding "Java
      debugging in the server (how?)", version 8.1.7.0

      You Asked
Our parent company has written a web enabled timecard system that stores its
data in SQL Server (no, chuckles please) which we use to log our timecards.  For
what little the appliction does it works well.  My company is married to Oracle
though and I need to get at this data on a daily basis to compare it to the
budgets for various things.

Because I'm cheap (and curious) I wrote an Java routine rather than purchase the
related gateway from Oracle.  I'm using the FreeTDS JDBC driver
(www.freetds.org) to access the relevant SQL Server timecard tables (11 in all
comprising about ~170K rows) via Java.  Ultimately I loaded the code (and
freeTDS JDBC driver) into Oracle (loadjava) and coded some PL/SQL wrappers.
Then I used dbms_job to run the code daily.

The import code runs very well from my NT with JBuilder or JDeveloper and I have
stress tested the import by running the code 30 times in a row from my NT box.
I then used loadjava to load in my gateway.class and the FreeTDS drivers.
Before uploading I turn off all System.out.println code to avoid internal output
buffer overflow errors.

Running the code in the Oracle servers JVM works well for about 6 days and then
the code hungs.  The SNMP logs show the code beginning it's run but then it
never comes back.  In diagnosing the problem the code seems to be holding a TX
lock on an Oracle table into which it is inserting rows.  The table doesn't seem
to be the same each time.  Cleaning up the run can be problematical also.  I
attempt to kill the hung session but ALTER SYSTEM KILL SESSION 'x,y' only marks
the session for kill but never kills the session (I waited 2 days once).
Eventually I have to kill -9 the SNMP process that is controlling the job.

In the SNMP logs from the job there are no errors thrown.  I have also run the
code in a loop from within SQL*Plus (calling the PL/SQL wrappers that the
dbms_job calls) and again it runs between 6-8 times before it just hangs.  It's
importing about 170,000 rows of data each execution and it takes about an 45
minutes to run so tracing this code through 6 executions doesn't sound like fun.

My question(s):
1) Can you give me any direction as to how to debug this problem?  I've thought
about populating the longops V$ tables or pipeing all System.out and System.err
calls to a file with the servers JVM.

2) Do you think I might be hitting the Java output buffer limit?  It runs
sucessfully for 6 times in a loop but around the 7th or 8th looped run it hangs.
 I've hit this before what methods do you recommend to avoid the server buffer
overrun in Java, file based output?

3) Any general help on how debug java code running in the server?  I confess
I've read the docs on debugging in the server but I still don't understand how
to connect to the debugger.  Any concrete examples you can give?

Thanks so much for your time.  Your book is still my bible and I use it almost
daily.

Mike

      and we said...
1) Using system.out.println is very safe -- it will write to a TRACE file in the
background dump destination on the server.  This can be very very useful indeed.
 There isn't any buffer issues with regards to that.  Only if you redirect
System.out using dbms_java would there be a buffer related issue in SQLPlus (and
only there).

V$SESSION_LONGOPS is an excellent choice as well (this should just be standard
in all background, long running processes).  If you have my book -- i have a
section on how to do this best.

2) It isn't a buffer over flow at all.  Not unless you've used dbms_java.  If
you skip the dbms_java call, it'll go straight into a trace file (the trace file
will be in the user_dump_destination if you run the procedure yourself, in the
background dump if jobs runs it)

3) My, I never use a debugger for anything, find them less then useful
(especially when the stupid code runs just dandy in the debugger but not out of
it or it runs differently due to timing and race conditions).  I do everything
with trace files.  I would litter the code with system.out.printlns (which you
can easily redirect to /dev/null or whatever later when you aren't debugging)
and see where it gets stuck and try to isolate that.

The TX locks you mention might be leading down the right path.  Have you looked
at  the time you get "stuck" to see if you are in actuality being BLOCKED (in
Sybase OR in Oracle -- don't forget in Sybase a simple QUERY will block easily
and often).

March 31, 2002 atul -- Thanks for the question regarding
      "Compress=y", version 8.0.4

      You Asked
Sir,

1)I know you don't like compress=Y option in export..
But if we do single big table export by compress=y option for removing
fragmentation in table.
and if this table having very large initial exent value..

And at the time of import will it give error like unable to create initial
extent..

As i think if it gives error like this solution for this is.

1)create defination of that table with changing storage option ie initial/next..
2)import with ignore=y option

Is that correct?

2)Many times you said in answers terms like tiny database,big,moderate
database..

So what according to you sizewise tiny,moderate and big databases
and for design each of them could u give me starting parameter values?
Thanks.
atul

      and we said...
1) tables do not get fragmented.

dictionary managed tablespaces get fragmented due to the use of non-equisized
extents.

It is OK to have an object with thousands of extents.  Repeat to yourself "it is
OK, it is OK".

At the time of import, you have one HUGE initial extent and you have
insufficient contigous free space.  What you need to do is either:

a) pre-create the object with a REASONABLE initial/next extent (preferably
initial=next and pctincrease = 0).  then import with ignore=y

b) get enough contigous space to deal with this absurdly large initial extent.

2) small medium and large are general terms I use and what is small to one
person is large to another.  You need to decide what is small/med/large to you.

For example, there are some people that feel a table with millions of rows is
large.  Other people feel tables with BILLIONS of records is large.  It all
depends on your perspective.

I have no starting values for this.  In a database with 90% of the tables over
50gig each in size, "small" might be a gigabyte.  In a database with 90% of the
tables under 500meg in size, large might be a gigabyte.

It is all relative.
Review & Followup

      Rating: 5
How many times
      will this come up?  March 30, 2002
Reviewer:
      Lester Burnham  from Orlando, FL
This topic seems to be an endless loop...

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:730289259844
for an interesting recent discussion on it.

Why is fragmentation/defragmentation/number of extents such a "misunderstood"
topic?  Directly from the 8i Designing and Tuning for Performance Guide:

"For dictionary-mapped tablespaces, do not let the number of extents exceed
1,000. If extent allocation is local, then do not have more than 2,000 extents.
Having too many extents reduces performance when dropping or truncating tables."

<soapbox>
So, the basic rule (as I see it) is, you don't have to keep wasting time trying
to cram everything into 1 extent, but on the other hand, don't let things get
totally out of control.  Everyone already knows (I hope) that DDL is expensive,
and should be a special case (especially in a Production system - ie, the
dropping/truncating listed above), just be reasonable about what you're doing...

I find it interesting to note that the only "performance hit" mentioned is for
dropping/truncating, not select/update/insert/delete, which I would think is
what should be going on normally, and what everyone seems to be so concerned
about.
</soapbox>

          Followup:

I just finished a benchmark recently.  I "accidently" put a big table into the
wrong tablespace (a 1m uniform extent instead of a 50m one that I had intended).

The table is in 17,722 extents.  Did I bother rebuilding it?  nope.  What was
the most frequent type of access to this table -- full scan.  Was I worried?
not a bit.  Did I lose any sleep over it (well, it was a benchmark, of course
there was lost sleep but it was due to having too much to do and too little time
to do it) -- no.  If I had to do it all over again, would i correct it?  Yes.
Worry about it after the fact -- no.

      Rating: 5
Just a
      thought....  March 30, 2002
Reviewer:  atul
       from india
Sir,

Don't be angry about discussing it again..
I myself has a good experiance of keeping maxextent value to some number ie not
to big..

By this we could have a check on table growth..
And we could tell the developers to trim the unwanted data..
Because in many cases we r keeping unwanted data in a tables ie data before (6/7
years)..

(that data can be imported and can be exported back whenever required)

so by this we could have a check over developers and can tell them to trim and
if it's not possible we could increase maxextent

          Followup:

I'm not angry -- I'm amused, bemused, confused by it all actually.

How can putting everything in as few extents as possible help you track table
growth?  Actually -- putting things in as FEW as possible makes it HARDER to
track that.  If I have a table with 5 100meg extents (and it grew to that size
over a year from 1 100meg extent) -- I would have LESS growth information then
if the table were in 50 10meg extents (i would have 5 observations vs 50).

How can a developer "trim unwanted data" from a table with few extents?  That
just doesn't even make sense.  If you have them use smaller extents, there will
be NO data to 'trim'.

      Rating: 3
Contigous
      Space.  March 30, 2002
Reviewer:
      A reader
Hi,

Tom This is with respect to a Previous post i read a Few days back at

http://asktom.oracle.com/pls/ask/f?p=4950:8:644136::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:1056598515561,%7BContigous%7D
In This you have clearly Mentioned that The Blocks within a Extent are Not
contigous but here you say that u have to make Contigous space available for the
One Large Extent that Has to be created for the Object to Be Imported.

<Quote>
b) get enough contigous space to deal with this absurdly large initial extent.
</Quote>

Your comments on this is highly regarded.

Regards,
Ganesh R

          Followup:

read that again.... i said:

So, extents are logically contigous units of allocations in files that are
physically fragmented.

you do not have enough "contigous" space in the file.  The file itself is NOT
contigous physically in the file system (one of the "main" arguments for single
extents is this supposition that the disk head will be excellently positioned on
the disk -- thats just not true)

March 31, 2002 Charlie -- Thanks for the question regarding
      "further search through the records which are NOT included in the previous
      resultset", version 8.1.7

      You Asked
Hi Tom,

How are you doing? Here is my question:

How can we further search through the records which are NOT included in the
previous resultset.  For example: table people contains SSN, age, gender and job
4 columns.  Total records: 20,000

Step 1:
select count(1) from people
where age between 20 and 50
and gender = 'M';

I got 4000 results back.  So there are 16,000 records NOT matching this
criteria.  How can I now set search domain to this 16,000 records and issue
query against them?  For instance,

select count(1) from (RECORDS NOT IN THE PREVIOUS RESULTSET)
where job = 'Sales Manager'  -- diffent where clause
and gender = 'M';

I want to do this kind of query again and again...  How can I do that?

BTW, I still want to know how many records MATCH the filter criteria at the same
time.  The following is a desired final output:

----------------------------------------------------------
FILTER_NAME   TOTAL_FOUND_RECORDS TOTAL_NOT_FOUND_RECORDS
---------------------------------------------------------
filter1       4000                16,000
filter2       6,000               10,000
filter3       8,000               2,000
filter4       1,500               500
...           ...                 ...
...           ...                 ...

Thanks!!

-Charlie

      and we said...
Just add the predicate:

select count(*)
  from people
 where job = 'Sales Manager'
   and gender = 'M'
   and NOT ( age between 20 and 50 and gender = 'M' )
/

Just add your "anti-predicate" to the end with a NOT.  As for the "total not
found", perhaps you really want to query:

select sum( case when job = 'Sales Manager'
                  and gender = 'M'
                  and NOT ( age between 20 and 50 and gender = 'M' )
             then 1
             else 0
             end ) matching,
       count(*) total
  from people;

now you will get "matching" counts and total counts -- and you subtract matching
from total to get not_found_records

Review &
      Followup

      Rating: 1
Very
      useful  March 11, 2002
Reviewer:  Charlie

Hi Tom,

Thank you for your prompt response!!

If in this table I have duplicate data, how can I remove duplicate counts in
CASE statement?

Also, since I am about to build a form to let end-user enter their filter
criteria to construct the query in my question, how can I build dynamic sql to
allow any kind of criteria?  Each criteria may be pulled out from different
tables(sure these tables have integrity relationships).

For example.  we already have

select sum( case when job = 'Sales Manager'
                  and gender = 'M'
                  and NOT ( age between 20 and 50 and gender = 'M' )
             then 1
             else 0
             end ) matching,
       count(*) total
  from people;

If the user enter "hobby = 'Reading'" in the form, how can I dynamically build
the following SQL:

SELECT SUM( CASE WHEN job = 'Sales Manager'
                  AND gender = 'M'
          AND h.HOBBY = 'Reading'  <--- new
                  AND NOT ( age BETWEEN 20 AND 50 AND gender = 'M' )
             THEN 1
             ELSE 0
             END ) matching,
       COUNT(*) total
  FROM PEOPLE p, people_hobby h <-- new table need to add
  WHERE p.ssn = h.ssn; <-- new "where" need to add

BTW, I can't use (total - matching) directly in select statement you provided to
get not_found_records, is there any workaround to get them all at one shot?

Thank you!!

-Charlie

          Followup:

how can you have duplicate data?  You are counting records that meet a criteria?
 What would define a "duplicate" in this case??

As for doing dynamic sql -- it depends on the tool you use, pretty much every
tool I've seen allows for it, you would read the docs to see how to do it.

to get the "subtraction" in the query just:

select matching, total-matching not_matching
from (  select sum( case ....... ) )

use an inline view.

      Rating: 4
very
      useful  March 11, 2002
Reviewer:  Charlie

Followup:
how can you have duplicate data?  You are counting records that meet a criteria?

 What would define a "duplicate" in this case??

-------------------------------------------------
You are right.  The following is an example I really want to say.  Sorry about
confusion.

SQL> SELECT * FROM PEOPLE;

       SSN        AGE G JOB
---------- ---------- - ------------------------------
         1         20 M Sales Manager
         2         40 M Sales Manager
         3         21 M Developer
         4         22 M Sales Manager
         5         30 F DBA
         6         60 M Sales Manager  <-----------

         7         35 M Sales Manager
         8         70 M Sales Manager
         9         60 M Sales Manager  <-----------

9 rows selected.

SSN (6 and 9) are the same except their SSN.

SQL> SELECT SUM( CASE WHEN job = 'Sales Manager'
  2                    AND gender = 'M'
  3                    AND NOT (age BETWEEN 20 AND 50 AND gender = 'M')
  4               THEN 1
  5               ELSE 0
  6               END ) matching,
  7         COUNT(*) total
  8    FROM PEOPLE;

  MATCHING      TOTAL
---------- ----------
         3          9       <-- original

SQL> SELECT SUM( CASE WHEN job = 'Sales Manager'
  2                    AND gender = 'M'
  3                    AND NOT (age BETWEEN 20 AND 50 AND gender = 'M')
  4               THEN 1
  5               ELSE 0
  6               END ) matching ,
  7         COUNT(DISTINCT age) total  <-- based on unique age
  8    FROM PEOPLE;

  MATCHING      TOTAL
---------- ----------
         3          8       <-- unique one

since SSN (6 and 9) are the same.  I want the MATCHING to return 2 instead of 3,
which
means somehow the result of CASE statement is based on UNIQUE age.  How can I do
that?

As for doing dynamic sql -- it depends on the tool you use, pretty much every
tool I've seen allows for it, you would read the docs to see how to do it.

----------------------------------
This is a tool I'm gonna build in JAVA myself.  It's kind of query builder and
is not from 3rd-party tool.  My question is how can I let people build this sort
of dynamic sql by themselves through my GUI to achieve the result in my previous
question?

Thanks again!!

-Charlie

          Followup:

you just showed how to do this --

select sum( distinct CASE ....
                       when (condition met)
                       then AGE (or whatever else means "distinct" to you)
                       else NULL )

Java, using jdbc, is 100% dynamic sql.  The only SQL you'll be doing is dynamic
SQL.  You have no choice but to do it dynically -- hence, just build the query
in the string and execute it.

I will suggest that you DEFINITELY issue:

alter session set cursor_sharing=force

as the first SQL statement in your app -- it'll force the use of bind variables,
something that would be hard for you to code otherwise.

      Rating: 4
String
      Comparition  March 31, 2002
Reviewer:  Lakshmi
      Narasimhan R  from Dubai, UAE
hello tom

I have a critical requirement.

We are having two tables

Table A - Header Info
Table B - Detail Info

We have to do revenue auditing on these table data.

Due to the massive data availability, we thought of
doing template creation.  Ie if a  document get audited,
and it will form a template.  the consecutive document
which matches the prev documents are being skipped from
auditing process to avoid re processing.

Almost more than 90% of column are forming a match.  my one
 understanding is concatenate all the colunmns as string
store it in pl sql table and match it with successive documents.

Could you sugget if there is any best way available apart.

For the information,
We are using oracle 8.1.7 with RBO.

          Followup:

sounds like a NEW question, not a followup to this one.  And since there is
insufficient detail to answer (for example, what the heck is a "template" and
why does "massive data availability" impact this process).

When I'm taking questions, feel free to add this as a new one but with lots more
detail.

March 31, 2002 Punit -- Thanks for the question regarding "A SQL
      Query", version 8.0.3

      You Asked
Could u please assist me in getting the following output using a SQL Query:

For (SNUM), Highest Order was on (ODATE) and Lowest was on (ODATE).

Hey Tom - I'm extremely sorry for not placing that info. Actually, I have a
table ORDERS with following columns :

ONUM - the Order Number
CNUM - the Customer Number (FK - CNUM of Customers Table)
SNUM - the Salesperson Number (FK - SNUM of Salespeople Table)
AMT - the Order Amount
ODATE - the Order Date

My negligience is highly regretted - Pl. excuse

Thanx,
Punit S Bhatia

      and we said...
select snum,
       substr(max( to_char( onum, 'fm000000000000000009' ) || to_char(odate)
),19),
       substr(min( to_char( onum, 'fm000000000000000009' ) || to_char(odate)
),19)
  from v
 group by snum
/

to get the ODATE associated withte MIN/MAX ONUM -- what we do is put ONUM
together with ODATE -- making ONUM "sortable" (thats the format there).

We can substr to get the value we want back from that.
Review &
      Followup

      Rating: 5
Count of "u"
      and "ur" as whole words  April 01, 2002
Reviewer:
      Mark A. Williams  from Indianapolis, IN USA
Tom:

Perhaps you could run a query to get a count of the occurances of "u" and "ur"
as whole words in questions/"follow-up" questions...  Just though it might be an
interesting statistic.

-Mark

March 31, 2002 Jim -- Thanks for the question regarding "the
      best way to revieve active sql statements on the fly", version 8.1.7.2

      You Asked
Tom,

What would be the best way to retrieve, write out to log and view sql statements
as they happen in the database ? Is there a tool or method you can suggest ?

Thanks

Jim

      and we said...
All statements forever? that is SQL_TRACE=true

The currently active statements?  search this site for showsql.sql
March 31, 2002 arunima -- Thanks for the question regarding "NOT
      IN vs !=", version 8.1.6

      You Asked
Hi Tom,

If I have two tables a and b. Here are their structures.
a
--
x   number(3)

b
--
x  number(3)

Here is the data I have in these two tables. There is no refential intergrity
between the columns of the two tables.

x  (table a)
--
1
2
3
4
5

x (table b)
--
1
2
3
4

If I write these two queries I get different answer.

Select a.x
from a ,b
where a.x <> b.x;

Select a.x
from a
where a.x NOT IN (select x from b);

Can you please explain why? Thanks for your time.

      and we said...
Select a.x
from a ,b
where a.x <> b.x;

is the same as:

select *
  from ( Select a.x a_x, b_x from a ,b )
where a_x <> b_x;

that is, in a join -- every row in A is joined with every row in B and then the
join condition is evaluated.

In your caes, the row from A with the value x=1 with joined to EVERY row in b
and we keep only the rows where a.x <> b.x (3 of them).

the not in does not do that.  It takes every row in a, looks to see if a match
exists in b and if so -- does not output it.  The not in can only return as many
rows as A has -- the join could return   number of rows in A times number of
rows in B.

Review &
      Followup

      Rating: 5
NOT IN vs
      JOIN  March 31, 2002
Reviewer:  arunima

Very clear explanation. Once again thanks a lot.

      Rating:
      4
Parag  March 31, 2002
Reviewer:
      Parag  from India
Very usefull Information.
Great Going .... Sir.

March 31, 2002 Torben -- Thanks for the question regarding
      "Change file attributes read-only", version Oracle 7

      You Asked
Hi Tom,

How can I change the file attribute read-only on an existing OS
file from PL/SQL in a stored procedure - I run Win NT / Oracle7

Torben

      and we said...
In Oracle7 -- this is difficult at best, especially on NT.

With Oracle8.0 and up we have a facility called External
Procedures.  External Procedures allow you to call a DLL as if
it where a stored procedure.  In effect, you can code your
stored procedure in C.  In Oracle8i release 8.1 and up -- you
could also just write the procedure in java -- achieving the
same effect, java has the API's to change the permissions.

In Oracle7, the best you can do is to use the DBMS_PIPE package
to create a daemon process that is waiting on a pipe for
requests to be made of it.  The daemon would block on a pipe
read until a client (a stored procedure) wrote a request on the
pipe.  The daemon would get the request -- perform the operation
and send some response back.  On Unix, running daemons is very
natural and easy.  On NT, you'll have to get something like the
NT resource toolkit to install your C program as a service to
get it to run automatically.

See

http://osi.oracle.com/~tkyte/plex/index.html
for an implementation of this pipe daemon concept I did a while
ago.

If at all possible, 8i will make this much easier via an extproc
or java stored procedure.
Review & Followup

      Rating:
      4
Parag  March 31, 2002
Reviewer:
      Parag  from India
Hi :

Can u give s small example where we can change same in Oracle 8.1.6 onwards.

Thanks a Lot

          Followup:

search for

create replace compile java source named

I have lots of java stored procedure examples here.  You would just write a
small java stored proc to do that.

March 31, 2002 sean -- Thanks for the question regarding
      "Emulating 10,000 concurrent users", version 817

      You Asked
I have been thinking of the setup of emulating many and many concurrent users. I
came across reading Jonathan Lewis'Practical Oracle8i', on the page 4:
"
If you stop to think about it for a minute you will apprecite that these
features are pretty astounding, especially when you consider that Oraccle has
published details of a bechmark emulating 10,000 concurrent users ( and I have
heard mention of one benchmark hitting 50,000 concurrent users).
"

Do you know what the becnmarks he refered to? Or what are your thoughts on the
implementation of emulating a lot of concurrent users?

Thanks

      and we said...
Well, 10k is a good sized but smallish system these days.  Our email servers
internally handle more then that on a regular basis.

The 50k benchmark is *probably* the one we did when 8i first came out with cache
fusion for OPS. We did a stress test with 50k users -- it was part of the 8i
rollout a couple of years ago.

Check out:

http://www.tpc.org/results/individual_results/HP/tpcc_hp_sd_750_es.pdf
for a number that might make you think twice ;)

To emulate lots of clients you need some load testing software like loadrunner
and the system you want to test... Make sure to beef up the clients!

March 31, 2002 Sudhanshu -- Thanks for the question regarding
      "Create Table as Select Order by", version 8.1.7

      You Asked
We have a table X having 1 Miliion records, Index on one of the column Colx (not
primary key) has the clustering factor same as # of rows. My Application selects
from this table X on the basis of Colx only, although it updates this table by
PK.

Distinct keys for this index is about 1/3 of num of Rows.

To improve performance i was thinking if i drop and create my table X, sorted on
Column Colx, using "Create table as select Order by Colx".

Another issue is that i need to do this regularly as the clustering factor for
this index will again be distorted after some time.

Will it improve the performance of my application? Is there any other
alternative to what i am thinking.

Thanks

      and we said...
Well, if this is a read mostly, insert much less frequently then select -- a
CLUSTER might be the right answer.

(if you have my book -- great detail in there on clusters, what they are, how to
use them, etc)

A cluster will allow you to make it so that all rows with the same cluster key
value are physically colocated.  You will never need to rebuild the object
again.

Before you ask "what is read mostly, how frequently can i insert, etc" --
benchmark it.  Try it out -- see how it works for you.

You might be surprised to know that you use clusters every single day -- the
data dictionary is full of them.
April 01, 2002 Priya -- Thanks for the question regarding "Open
      Cursors ", version 8.1.7

      You Asked
Hello Tom ,
       I am trying to do this .

create function f_test return number is
cursor c is select ..... ;
begin
for crec in  c loop
if <condiiton> then
do this ;
else
f_test;
end loop;
end;

 When i call this function(recursively) it hits on the Max Open cursors problem
.OPEN_CURSORS is set to 2000 .
How to resolve this problem . I did search ur site , but am not getting enough
idea .
Thanx
Priya

      and we said...
Well, if you recursive call this routine 2,000 times - it should be obvious that
you will exceed the max open cursors!  In order to recursively call F_TEST -- by
definition the cursor C is opened.  Every time you recursively call it -- you
add another.  Get 2,000 levels deep and -- well, there you go.

I personally would search for an iterative solution to the problem.  Recursion,
while very elegant, is very inefficient.  It is useful for very small problems.

Look for the iterative solution that does not require you to open 2000 cursors.
Review &
      Followup

      Rating: 3
Open
      Cursors  April 01, 2002
Reviewer:  Edward
      Jayaraj  from IA-50265
Tom,

Whenever the function calls the "f_test" will it not open
the cursor and close them while returning back?

Why would it keep the cursor open every time?

pl., explain. I know you will

Thanks for Your service

          Followup:

When it returns - yes, the cursor will be closed.

Before it returns -- no.

So, if your recursive depth is "deep", you will have many cursors opened.  Your
depth must be deep.

April 01, 2002 Krishna Prasanna -- Thanks for the question
      regarding "in case there are no rows fetched by cursor !!", version 8.1.6

      You Asked
before inserting a value in table X
i want to check whether it is already existing or not.
for that i have defined a cursor . my problem is the table may be empty.and the
code under the cusor loop will not execute in that case .how to avoid this?

-Kris

      and we said...
The right answer is to use a primary key or unique constraint on what you
believe to be the "distinct" column(s).

Then just code:

    begin
        insert into t values ( .... );
    exception
        when dup_val_on_index then
               .... code that handles that case ....
    end;

Selecting out a row to see if it is there is a really bad idea.  You could do
this as well:

    insert into t
    select :val1, :val2, ...., :valN
      from dual
     where NOT EXISTS ( select null
                          from t
                         where col1 = :val1
                           and col2 = :val2
                           ...
                           and colN = :valN );

and in plsql use sql%rowcount to see how many rows were inserted (1 = row did
not "exist", 0 = row did exist).  In other languages, use whatever facility they
have to tell you how many rows were affected.  Do this if and only if you have
some irrational fear of exceptions!

The exception approach above is the correct approach.
Review &
      Followup

      Rating:
      2
DUP_VAL_ON_INDEX is expensive  April 01, 2002

Reviewer:  Andrew  from CA, USA
Tom
I usually try to weigh up the cost of exception handling with the probability of
it happening. e.g assuming we need a row either updated or inserted, then if
it's likely that the row exists then I'd try an update first, then try an insert
only of the update fails. My conclusion is that a failed update (due to no rows
found) is much cheaper than a failed insert (due to dup_val_on_index).  Comments
please.

SQL> create table t (x number primary key, y number);
Table created.
SQL> insert into t values(1, 11);
1 row created.
SQL> commit;
Commit complete.

SQL> set serveroutput on;
SQL> DECLARE
  2     v_hsec   PLS_INTEGER := DBMS_UTILITY.get_time;
  3  BEGIN
  4     FOR i IN 1 .. 1000 LOOP
  5        -- try to update
  6        UPDATE t SET y = 22 WHERE x = 1 AND 1 = 0;
  7        IF SQL%ROWCOUNT = 0 THEN
  8           -- whoops
  9           -- INSERT INTO t ...
 10           NULL;
 11        END IF;
 12     END LOOP;
 13     DBMS_OUTPUT.put_line ('method 1 sec = ' || (DBMS_UTILITY.get_time -
v_hsec) / 100);
 14  END;
 15  /
method 1 sec = .13

SQL> DECLARE
  2     v_hsec   PLS_INTEGER := DBMS_UTILITY.get_time;
  3  BEGIN
  4     FOR i IN 1 .. 1000 LOOP
  5        BEGIN
  6           -- try to insert
  7           INSERT INTO t VALUES (1, 11);
  8        EXCEPTION
  9           WHEN DUP_VAL_ON_INDEX THEN
 10              -- whoops!
 11              -- UPDATE t ...
 12              NULL;
 13        END;
 14     END LOOP;
 15     DBMS_OUTPUT.put_line ('method 2 sec = '|| (DBMS_UTILITY.get_time -
v_hsec) / 100);
 16  END;
 17  /
method 2 sec = 1.75

          Followup:

I agree PARTIALLY with your premise (with your test, not even a little bit) --
I've posted this same piece many times myself.

In this case -- they just said "i want to check whether it exists".  Due to lack
of clarity -- I didn't make the assumption that they wanted to update -- only to
insert if the data didn't already exist.

You would, in general, want to

  update
     insert if no rows updated

when the UPDATE is most like to occur and

  insert
      update on dup val on index

when the INSERT is most likely to occur.  I don't agree with your testing
method, lets compare apples to apples and such -- updating a NO rows 1000 times
and not changing the index isn't comparable to inserting 999 times and updating
an index is it?

Doing the test in a "fair" fashion (fair being all things equal)

test 1, one row in the table that should be updated.  999 to be inserted.
Which is better???

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;
Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t (x number primary key, y
number);
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values(1, 11);
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;
Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set serveroutput on;
ops$tkyte@ORA817DEV.US.ORACLE.COM> DECLARE
  2     v_hsec   PLS_INTEGER := DBMS_UTILITY.get_time;
  3  BEGIN
  4     FOR i IN 1 .. 1000 LOOP
  5        UPDATE t SET y = i WHERE x = i;
  6        IF SQL%ROWCOUNT = 0 THEN
  7                   insert into t values ( i, i );
  8        END IF;
  9     END LOOP;
 10     DBMS_OUTPUT.put_line ('method 1 sec = ' || (DBMS_UTILITY.get_time -
v_hsec) / 100);
 11  END;
 12  /
method 1 sec = .7

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> rollback;
Rollback complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> DECLARE
  2     v_hsec   PLS_INTEGER := DBMS_UTILITY.get_time;
  3  BEGIN
  4     FOR i IN 1 .. 1000 LOOP
  5        BEGIN
  6           INSERT INTO t VALUES (i, i);
  7        EXCEPTION
  8           WHEN DUP_VAL_ON_INDEX THEN
  9              UPDATE t SET y = i WHERE x = i;
 10        END;
 11     END LOOP;
 12     DBMS_OUTPUT.put_line ('method 2 sec = '|| (DBMS_UTILITY.get_time -
v_hsec) / 100);
 13  END;
 14  /
method 2 sec = .54

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

Seems that here, the best approach is to INSERT, catch the dup val on index
and then update -- makes sense, MOST of the time the insert works!!

Now, for test two, we have 1000 rows in there, none will be inserted -- each
will be updated.  Which is better??

ops$tkyte@ORA817DEV.US.ORACLE.COM> DECLARE
  2     v_hsec   PLS_INTEGER := DBMS_UTILITY.get_time;
  3  BEGIN
  4     FOR i IN 1 .. 1000 LOOP
  5        UPDATE t SET y = i WHERE x = i;
  6        IF SQL%ROWCOUNT = 0 THEN
  7                   insert into t values ( i, i );
  8        END IF;
  9     END LOOP;
 10     DBMS_OUTPUT.put_line ('method 1 sec = ' || (DBMS_UTILITY.get_time -
v_hsec) / 100);
 11  END;
 12  /
method 1 sec = .33

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> rollback;

Rollback complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> DECLARE
  2     v_hsec   PLS_INTEGER := DBMS_UTILITY.get_time;
  3  BEGIN
  4     FOR i IN 1 .. 1000 LOOP
  5        BEGIN
  6           INSERT INTO t VALUES (i, i);
  7        EXCEPTION
  8           WHEN DUP_VAL_ON_INDEX THEN
  9              UPDATE t SET y = i WHERE x = i;
 10        END;
 11     END LOOP;
 12     DBMS_OUTPUT.put_line ('method 2 sec = '|| (DBMS_UTILITY.get_time -
v_hsec) / 100);
 13  END;
 14  /
method 2 sec = 1.28

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

Ahh -- there is it clear.  We should do the update when we believe we will in
fact be updating MOST OFTEN.

      Rating:
      3
  April 01, 2002
Reviewer:  Krishna
      Prasanna  from INDIA
thank you for the quick responce.

      Rating: 4
Conclusive
      results  April 02, 2002
Reviewer:  Andrew
       from CA, USA
Tom
Thanks for fixing my test - I appreciate your time.


Subject | Article | Last Updated (East coast time)
Merge Command in Oracle9i. This inserts when the incoming data is new and updates when primary in incoming data already there in the destination table. | article only | Apr 02, 2002 08:26 am
FIND ALL TABLES WITH NO RECORDS | article only | Apr 02, 2002 08:27 am
Database Packages. | article only | Apr 02, 2002 08:47 am
Ora App Server. | article only | Apr 02, 2002 08:50 am
Query Very Slow in Forms But Very Fast in SQL Plus | article only | Apr 02, 2002 10:37 am
guidance please !! | article only | Apr 02, 2002 10:43 am
Truncate Tables | article only | Apr 02, 2002 01:34 pm
Database Space Usage (Allocated Vs Used) | article only | Apr 03, 2002 09:47 am
Rollback Segment Error | article only | Apr 03, 2002 10:21 am
CHAR Index (vs) NUMERIC Index | article only | Apr 03, 2002 10:24 am
Setting user information from jdbc client | article only | Apr 03, 2002 10:28 am
Dealing with long datatype fields. | article only | Apr 03, 2002 04:06 pm
Actual byte uses using varchar2 - internal to Oracle | article only | Apr 03, 2002 04:07 pm
Domain Index | article only | Apr 04, 2002 11:41 am
Transforming a table from being broad and shallow to narrow and deep | article only | Apr 04, 2002 01:21 pm
Data Storage | article only | Apr 04, 2002 07:12 pm
Writting DDL_EVENT Triggers | article only | Apr 04, 2002 07:14 pm
Differnece between Rel2 and Rel3 | article only | Apr 05, 2002 09:50 am
number formatting and how to display the primary key constraint name, primary key columns and also the foreign key names and columns. | article only | Apr 05, 2002 11:42 am
Can shared pool be *too* large? | article only | Apr 05, 2002 11:47 am
Find out value of bind variables for a dynamic plsql | article only | Apr 05, 2002 12:59 pm
Performance | article only | Apr 05, 2002 01:42 pm
Multiple index queries, max number of indexes | article only | Apr 05, 2002 02:48 pm
Subqueries, Nested Views, or one big query what is best. | article only | Apr 05, 2002 03:25 pm
Serialization on session level | article only | Apr 05, 2002 03:29 pm
Invalid datatype for a sql object type table | article only | Apr 05, 2002 03:32 pm
Finction for multi characters per field | article only | Apr 05, 2002 03:37 pm
importing partition to user's default tablespace | article only | Apr 06, 2002 03:48 pm
consistant get | article only | Apr 06, 2002 07:31 pm
Multiple selections in owa_util.listprint | article only | Apr 06, 2002 07:35 pm
utl_file security issues | article only | Apr 07, 2002 11:49 am
Random Number generate | article only | Apr 07, 2002 12:22 pm
HOST Builtin | article only | Apr 07, 2002 12:26 pm
CPU Time | article only | Apr 07, 2002 03:09 pm
Tkprof output | article only | Apr 07, 2002 03:47 pm
Case within Query | article only | Apr 07, 2002 06:17 pm
Execution Plan | article only | Apr 07, 2002 07:04 pm
How to update a column as quickly as possible? | article only | Apr 08, 2002 09:33 am
SQL Standards | article only | Apr 08, 2002 04:00 pm
Username=Password Detection | article only | Apr 08, 2002 04:14 pm
Server Name of the Oracle Database | article only | Apr 08, 2002 04:18 pm
Java Communication API (javax.comm) | article only | Apr 08, 2002 08:11 pm
row(s) 1 - 42 of 42
April 02, 2002 Lokesh -- Thanks for the question regarding "Merge Command in Oracle9i. This inserts when the incoming data is new and updates when primary in incoming data already there in the destination table.", version 9.0.1.2.0
You Asked
I have 2 questions:
1. Will you be publishing the Oracle expert one-on-one including    Oracle9i
features, sometime soon?

2. The following code is a test code, that works fine. I want to implement this
in the Datawarehouse ETL process. Is it possible to  call stored procedures
after the 'WHEN MATCHED THEN' and 'WHEN NOT MATCHED'? I believe we cannot but
here's what I am trying to accomplish.

I want to call a stored procedure so that from the imcoming records, I get
counts of the duplicate records, modified records (records with same primary key
but some other columns modified), and new records. Also update the source table
to indicate that the incoming record is new, duplicate or modifed. This is
required for reporting purposes.

MERGE INTO test a
using (select * from test1) b
on (a.id=b.id)
WHEN MATCHED THEN
   update  set a.name = b.name, a.address = b.address
WHEN NOT MATCHED THEN
   insert (id, name, address) values (b.id, b.name, b.address)
/

Currently, I am accomplishing this task by having the following exception after
the insert into the test table:

insert into test (id, name, address)
values (new_rec.id, new_rec.name, new_rec.address);
-- set the flag in the source table indicating that this is New record.
Increment the NEw_counter.

WHEN DUP_VAL_ON_INDEX THEN
   /*
   -- If exact duplicate, then set the flag in the source table    -- indicating
that this is duplicate record.increment the    -- dup_counter.
   -- If modified record, then set the flag in the source table        --
indicating that this is modified record.update test, and       -- increment
modify_counter.
  */
   null;
END;
and we said...
1) nope, I'm thinking about a performance book that will cover up through 9iR2.

I would guess that more then 90% of expert one on one is 100% applicable to 9i.
It is more about what it is "missing" then anything else (meaning, it is still a
very useful book with 9i)

A book of that scope took a tremendous amount of effort -- going for something
smaller next time.  Maybe the next go around will be the "second edition" for
one on one.

2) Well, no -- that would not be 100% possible.  You could use triggers but as
you said "data warehouse", "etl" -- i'm thinking this is huge and a trigger
might not be excessively expedient.  You can benchmark it and try -- just

create table new_t ( id int primary key, data int );
create table old_t ( id int primary key, data int );

insert into old_t values ( 1, 1 );
insert into old_t values ( 2, 1 );

insert into new_t values ( 1, 1 );
insert into new_t values ( 2, 2 );
insert into new_t values ( 3, 3 );

create or replace package state_pkg
as
    g_new_cnt number := 0;
    g_upd_cnt number := 0;
    g_unchanged_cnt number := 0;
end;
/

create or replace trigger old_t_trigger
after update or insert on old_t
for each row
begin
    if ( inserting )
    then
        state_pkg.g_new_cnt := state_pkg.g_new_cnt + 1;
    else
        if ( :new.id || '/' || :new.data <> :old.id || '/' || :old.data )
        then
            state_pkg.g_upd_cnt := state_pkg.g_upd_cnt+1;
        else
            state_pkg.g_unchanged_cnt := state_pkg.g_unchanged_cnt+1;
        end if;
    end if;
end;
/

merge into old_t
using new_t
on ( old_t.id = new_t.id )
when matched then
    update set old_t.data = new_t.data
when not matched then
    insert (id,data) values(new_t.id,new_t.data);

exec dbms_output.put_line( state_pkg.g_new_cnt );
exec dbms_output.put_line( state_pkg.g_upd_cnt );
exec dbms_output.put_line( state_pkg.g_unchanged_cnt );

Alternatively, and what might go "faster" would be to do counts to see what
would happen:

REM count of records to INSERT
select count(*)
  from ( select <columns that mean "unique" to you> from new_t
         MINUS
         select <columns that mean "unique" to you> from old_t )
/

REM count of records to UPDATE + INSERT (includes the above count,
REM you need to subtract out the above count to get the records to update)
select count(*)
  from ( select * from new_t
         MINUS
         select * from old_t )
/

REM count of records to UPDATE+INSERT+NO_CHANGE -- sub out the above count
REM to find the number of unchanged records
select count(*) from new_t;

do that before you run and you'll know the number of inserts/updates and no
changes.
Review & Followup
Rating: 4
A Good Performance and Tunning Book. April 02, 2002
Reviewer: A reader
Tom,

It would be great if you would suggest us a Good  Performance and Tunning book
that u hold in high esteem. The Docs are always there apart from that.

Until your book comes into the market ;-D

Thanks.

Regards,
Ganesh R

Followup:
Here is my 2 cents on this...

99.999% of all tuning books out there are written from the perspective of an
Emergency Room (ER) doctor.  They are all "oh my gosh, we are in a fix now, lets
stabilize this patient and get them to surgery".

There are very few books that are of the tone of the general practitioner that
counsels you to "stop smoking, lose 10 pounds and exercise".

I do not read the ER books personally (well, I have but I typically end up
ranting and raving about whats in them).  I find many of them to be filled with
mysticism (myths), solutions that worked in THEIR very specific situations.  I
pretty much feel that they seem to encourage practices I discourage.  They make
you feel so comfortable as the ER doctor -- that you forget about being the
general practitioner.

I like books like (in addition to mine, I would call my existing book a
performance book as it is -- follow the directions in there and you won't BE in
the position of having to be an ER doctor in a rush to patch up the patient)
these:

o the server concepts manual (best of all -- IT'S FREE.  And since no one else
seems to read it -- its in very good condition ;)

o Jonathan Lewis's practical Oracle8i.  Understand what he says in here and
you'll be miles ahead of the rest.

o Oracle SQL High-Performance Tuning by Guy Harrison

whats different about these books is they teach you how to write performant
stuff from the get go, not tune things after the fact.  They educate you as to
how the stuff WORKS, they do not stuff you full of scripts, ratios, half truths,
misconceptions, etc.

It is my belief that if you fully understand how the database does something,
tuning is easy.  For example (taken from early in my book) if you do not
understand how Oracle's shared server (MTS) works -- and you use it improperly
-- it'll never scale in a billion years until you rewrite.  Hence, you need to
know how it works in order to develop the best practices when using it.

Don't get me wrong, I'm an ER doctor as well when I need to be.  It's a position
we get thrown into from time to time (more often then I'd like actually).
However, I don't study to be that kind of "doctor", being that kind just comes
naturally from the understanding of the database.  Most people are surprised
when I walk in "scriptless" -- I always walk out with about a dozen scripts or
so but they are unique to the very specific situation they were in.  You would
need thousands of scripts to cater to every situation -- having the knowledge to
build them specifically as you need them, that saves alot of organizing on my
part.

Rating: 4
Merge Command in Oracle9i. April 03, 2002
Reviewer: Lokesh Wuluvarana from Wisconsin, USA
Tom,
Thanks a lot! Like you mentioned, the DB trigger is something that I will not be
using for an ETL process. The second approach is better and I will investigate
more that one.

I Look forward to reading your book on Performance Tuning. I enjoyed reading
Expert One-on-one Oracle - Great Book!!!
BTW, www.bookpool.com has the best price on this book.

Thanks
Lokesh

April 02, 2002 STEVE -- Thanks for the question regarding "FIND ALL TABLES WITH NO RECORDS", version 9I
You Asked
How can find all tables with no records, is it going to involve a simple pl/sql
where I load all the table names into a cursor and then count the rows or is
there a more efficient way to do this.

tks

steve
and we said...
See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1323202417987
it outlines a technique for finding "empty" tables.
Review & Followup
Rating: 3
Another solution April 02, 2002
Reviewer: Jan Chalupa from Czech Republic
And what about simply analyze all tables in your schema:
execute dbms_utility.analyze_schema('YOUR_SCHEMA_NAME', 'ESTIMATE');
(also exists procedure dbms_utility.analyze_database) and then:

SELECT *
FROM ALL_TABLES
WHERE owner = 'YOUR_SCHEMA_NAME'
AND num_rows = 0

Followup:
dbms_utility is legacy -- use dbms_stats.

Yes, you could analyze all of the tables as well.  Brute force, works, but it
could take a bit.

Rating: 5
Very good solution! April 02, 2002
Reviewer: kiro from Europe
Hi Tom !
I have tested by myself. It is work.
I am using your show_space procedure litle changed - to work with partition
tables and indexes.
Everyone can see result:
SQL> set pagesize 100;
SQL> set linesize 100;
SQL> set serverout on;
SQL> execute show_space_my ('TB_TRANSFER_ERR','MY_USER', 'TABLE');

Object Name *********** TB_TRANSFER_ERR
Object Type *********** TABLE
Free Blocks.............................1
Total Blocks............................152
% Free in Segment........................66
Total Space In MB.......................1.19
Unused Blocks...........................46
Unused Space In Mb.......................36
Last Used Ext FileId....................7
Last Used Ext BlockId...................114578
Last Used Block.........................10
Partition Name..........................NO PARTITIONS

PL/SQL procedure successfully completed.

SQL> delete from TB_TRANSFER_ERR;

4992 rows deleted.

SQL> execute show_space_my ('TB_TRANSFER_ERR','MY_USER', 'TABLE');

Object Name *********** TB_TRANSFER_ERR
Object Type *********** TABLE
Free Blocks.............................1
Total Blocks............................152
% Free in Segment........................66
Total Space In MB.......................1.19
Unused Blocks...........................46
Unused Space In Mb.......................36
Last Used Ext FileId....................7
Last Used Ext BlockId...................114578
Last Used Block.........................10
Partition Name..........................NO PARTITIONS

PL/SQL procedure successfully completed.

SQL> commit;

Commit complete.

SQL> execute show_space_my ('TB_TRANSFER_ERR','MY_USER', 'TABLE');

Object Name *********** TB_TRANSFER_ERR
Object Type *********** TABLE
Free Blocks.............................105
Total Blocks............................152
% Free in Segment.......................69.08
Total Space In MB.......................1.19
Unused Blocks...........................46
Unused Space In Mb.......................36
Last Used Ext FileId....................7
Last Used Ext BlockId...................114578
Last Used Block.........................10
Partition Name..........................NO PARTITIONS

PL/SQL procedure successfully completed.

You can see 105(free) + 46 (above HWM) = 152(total) - 1
After that truncate table to deallocate space!

SQL> truncate table TB_TRANSFER_ERR;

Table truncated.

SQL> execute show_space_my ('TB_TRANSFER_ERR','MY_USER', 'TABLE');
Object Name *********** TB_TRANSFER_ERR
Object Type *********** TABLE
Free Blocks.............................0
Total Blocks............................16
% Free in Segment.......................0
Total Space In MB........................13
Unused Blocks...........................15
Unused Space In Mb.......................12
Last Used Ext FileId....................7
Last Used Ext BlockId...................5842
Last Used Block.........................1
Partition Name..........................NO PARTITIONS
Result is very good.

April 02, 2002 Parag -- Thanks for the question regarding "Database Packages.", version 8.1.7
You Asked
Hello Tom :

I wanted to know the Packages provided by Oracle, which is very usefull from
Developer and DBA point of view. Can u provide some Link or Article which talk
about Important Packages with some sort of Examples.

Secondly , Can u give an Small Examples of DBMS_ALERT & DBMS_PIPE. If 10 Oracle
Client has connected to Server & if DBA wanted to flash the Message to all
Connected Client with some Message Text , then how to achive the same through
the same Packages.

In Last week, I was going through some article which was talking about File
Transfer from One Machine to Another with the help of Core Oralce Coding ( with
UTL_HTTP , UTL_TCP Packages) Is it possible achive with Oracle ? If yes then can
u provde me some example ? (please)

I wanted to know all the products with their License & Pricing details, from
where do i get it ?

From which book/Internet site , I can get Differences of Features ( Developer ,
DBA) between Oralce 8i (8.1.7) and Oracle 9i.

Regards
- Parag
and we said...
Another broken keyboard.  The y's and o's seem to be missing sometimes in your
question.

In my book -- I go through with detailed examples on the packages I find most
relevant.  You can read about all supplied packages here:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76936/toc.htm
I spend about 225 pages on 17 packages I use constantly.

There is no way for a DBA to "flash" a message to all clients UNLESS you write
yourself all of the clients (eg: no sqlplus, no toad, no 3rd party apps).  The
client must be looking for the message.

If you write the client, then using DBMS_ALERT or DBMS_PIPE is somewhat trivial
-- my book has some detailed examples, the supplied packages guide has others..
If you search this site for dbms_alert dbms_pipe, you'll see some guidance on
using them as well as examples.

There is no native functionality in Oracle to do an FTP however you can use a
Java stored procedure to do the same.  Java has a rich set of "internet" classes
to do many different protocols.
http://store.oracle.com/
would be the place to go for "product" related questions.
http://technet.oracle.com/docs/products/oracle9i/content.html
would be the place to go for 9i docs -- including a new features guide.
April 02, 2002 Parag -- Thanks for the question regarding "Ora App Server.", version 8.1.7
You Asked
Dear Tom :

In one the forth-coming application, I wanted to Implement Oracle Application
Server with Oracle Database 8.1.7 or may be Oracle 9i. Can u provide some sort
of Link/Article/Example which talk about Application Server's Architecture &
Implementation/Deployment. I am very keen to use Oralce 9iAS for one of the
Intranet Site.Please help me out Sir.

If u don't mind, Sir can u give us the Overview & Architecture of Your Site ie
ASKTOM.ORACLE.COM. How u are getting Mails from various people , How do u reply
them & How u  string all articles in database.
If u can give the explanation about the Architecture then it will be great help
for me. We are planning to make such site for our Compnay with FAQ kind of
things. Please Sir.

Regards
- Parag
and we said...
Your keyboard is malfunctioning.  It is dropping the Y and O characters
frequently.

All documentation -- including architecture, examples, and so on -- is available
here:
http://technet.oracle.com/docs/content.html
OTN (The Oracle Technical Network) is a great resource for this type of
information.

see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1995355238285
for the overview/architecture of asktom.  I use UTL_SMTP to send email from the
database via a stored procedure.  So, when I answer a question -- the stored
procedure saves the answer and generates the email.
Review & Followup
Rating: 5
Stop answering April 02, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
Tom:

If the poster can't be bothered to try to at least type in a complete word, I
wonder how they might be able to understand (and implement) your response.  Of
the 8 or so responses I have read this morning, about half of the original
questions and/or feedbacks have had those abbreviations.  One might think that
certain posters are doing it on purpose.  Of course, typo's are a different
matter...

Besides, this particular question has already been answered before (the link you
provided), and is readily available through that amazing little widget called
the "Search/Archives" tab in the upper-right corner of your page...

OK, I'm off my high-horse now,

- Mark

April 02, 2002 Mirza Ahmed -- Thanks for the question regarding "Query Very Slow in Forms But Very Fast in SQL Plus", version Forms Version 5.0.6.8.0
You Asked
The problem is the query take too much time in forms but very
less time in Sql*Plus for the same parameters in forms and
sqlplus
-----------------------------------------------------------------
----------------------------------------------------------
PROCEDURE Proc2 IS
 mitm_code Varchar2(4);
 xcode     Varchar2(4);
 mare_code Varchar2(4);
 mcases    Number(18,2) :=0;
 mpcs      Number(18,2) :=0;

 Cursor hdtl_cur3 IS
  Select /*+ INDEX(VANDTL IDX_VANDTL_DATE) */ (forcing the
ptimizer to use the index)
   c.are_code, b.itm_code,
   sum(decode(b.trs_code,'IN',b.cases,0)) cases,
   sum(decode(b.trs_code,'IN',b.pcs,0)) pcs
  From  s.vandtl b, s.gen c
  Where b.post_date between :from_dt and :to_dt
    and b.reg_code between :fr_reg and :to_reg
    and b.bra_code between :fr_bra and :to_bra
    and c.are_code between :fr_area and :to_area
    and b.van_code = c.gen_code
    and nvl(b.copies,0) <> 9    --- Cancelled Documents
  Group by c.are_code, b.itm_code;

Begin
   Open hdtl_cur3;
   Loop
    Fetch hdtl_cur3 into xcode,micode,mcases,mpcs;
 (It take a long time to start the Fecth process about 15
   Minutes for 1 day)
    Exit When hdtl_cur3%NotFound;

    ---Some small Work
   EndLoop;
 Close hdtl_cur3
End;
---The Bind variables are give the following vales from the form
  :from_dt = '01/06/2000    :to_dt = '01/06/2000'
  :fr_reg  = '0' :to_reg  = 'ZZ'
  :fr_bra  = '0' :to_bra  = 'ZZZ'
  :fr_area = '0' :to_area = 'ZZZZ'
-----------------------------------------------------------------
----------------------------------------
Same Query in Sql*Plus
With the Following Parameters The Query takes only about 9
seconds

  Select /*+ INDEX(VANDTL IDX_VANDTL_DATE) */
   c.are_code, b.itm_code,
   sum(decode(b.trs_code,'IN',b.cases,0)) cases,
   sum(decode(b.trs_code,'IN',b.pcs,0)) pcs
  From  s.vandtl b, s.gen c
  Where b.post_date between '01/06/2000' and '01/06/2000'
    and b.reg_code between '0' and 'ZZ'
    and b.bra_code between '0' and 'ZZZ'
    and c.are_code between '0' and 'ZZZZ'
    and b.van_code = c.gen_code
    and nvl(b.copies,0) <> 9    --- Cancelled Documents
  Group by c.are_code, b.itm_code;
------------------------------------------------------------
Vandtl Table is a large Table
 select count(*) from vandtl;

  COUNT(*)
----------
   1453504

Description of the Vandtl Table
SQL> desc vandtl;
 Name                            Null?    Type
 ------------------------------- -------- ----
 REG_CODE                                 VARCHAR2(3)
 BRA_CODE                                 VARCHAR2(3)
 VAN_CODE                                 VARCHAR2(3)
 TRS_CODE                                 VARCHAR2(2)
 SERIAL                                   NUMBER(6)
 ITM_CODE                                 VARCHAR2(4)
 BATCH                                    NUMBER(10)
 CASES                                    NUMBER(11)
 PCS                                      NUMBER(11)
 RET_RES                                  VARCHAR2(2)
 EXTYPE                                   VARCHAR2(2)
 PRO_DATE                                 DATE
 EXP_DATE                                 DATE
 PRICE                                    NUMBER(12,2)
 POSTED                                   VARCHAR2(1)
 POST_DATE                                DATE
 COPIES                                   NUMBER(1)
-----------------------------------------------------------------

The problem is the query take too much time in forms but very
less time in Sql*Plus
and we said...
These two queries are NIGHT and DAY different.  Bind
variables and constants are worlds apart.  To see someone with
the exactly OPPOSITE problem you have (to show you bind
variables are *not* evil, they are good) see:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:336817343783
They tell of a select statment that when executed in plsql with
bind variables it runs fast - when the "same" (its not the same
really) query is executed in an anonymous block with literals
(like you did in sqlplus) it runs much slower.

So, back to your problem.  You write:

Select /*+ INDEX(VANDTL IDX_VANDTL_DATE) */ (forcing the
optimizer to use the index)

A hint is not a method to "force" the optimizer to do anything,
its a hint and may or may not be used.  In this case, it is not
using it and by looking at your specific bind variables I can
probably guess why.  You have a query with a where clause:

  Where b.post_date between :from_dt and :to_dt
    and b.reg_code between :fr_reg and :to_reg
    and b.bra_code between :fr_bra and :to_bra
    and c.are_code between :fr_area and :to_area
    and b.van_code = c.gen_code
    and nvl(b.copies,0) <> 9    --- Cancelled Documents

I'm assuming the index IDX_VANDTL_DATE is on the POST_DATE
column as well.

The optimizer looks at this and looks at the stats on the table.
 It has to assume some "default" values for the replaceable
arguments in the query (eg:  :FROM_DT, :TO_DT and so on).  It
looks at the distribution of data in these columns (eg: how many
rows are in table B as opposed to how many discrete values of
POST_DATE, REG_CODE and so on, same with table C).  It will then
pick an index on these columns based on that (your hint will try
to influence it to use the index you want but in this case, the
generic skew of the data is such that it is picking an entirely
different index alltogether or even skipping the index).  It has
no idea that you will plug into:

   b.post_date between :from_dt and :to_dt

the same date effectively turning that into:

   b.post_date = CONSTANT

It must come up with a generic plan that would work for all
possible values of the bind variables.  In your specific case,
given your literals for the dates, the correct index is the one
on the date column. However, in a subsequent run what if the
bind variables were:

  :from_dt = '01/06/1980    :to_dt = '01/06/2010'
  :fr_reg  = '0' :to_reg  = '0'
  :fr_bra  = '0' :to_bra  = '0'
  :fr_area = '0' :to_area = '0'

Now, that index on date is no longer appealing and another
should be used.

I can think of 2 solutions for you -- each is radically
different.  If your goal is to use that index at all costs, then
try the query:

  Select /*+ INDEX(VANDTL IDX_VANDTL_DATE) */ (forcing the
ptimizer to use the index)
   c.are_code, b.itm_code,
   sum(decode(b.trs_code,'IN',b.cases,0)) cases,
   sum(decode(b.trs_code,'IN',b.pcs,0)) pcs
  From  s.vandtl b, s.gen c
  Where b.post_date between :from_dt and :to_dt
    and nvl(b.reg_code,null) between :fr_reg and :to_reg
    and nvl(b.bra_code,null) between :fr_bra and :to_bra
    and nvl(c.are_code,null) between :fr_area and :to_area
    and nvl(b.van_code,null) = c.gen_code
    and nvl(b.copies,0) <> 9    --- Cancelled Documents
  Group by c.are_code, b.itm_code;

That will preclude the use of indexes on b.reg_code, b.bra_code,
c.are_code, b.van_code.  Only the index on b.post_date is
available to be used -- that will give us access to b.van_code
and we'll use theindex on c.gen_code to do the join.   You could
probably DROP the hint at that point.

The other alternative, which I would ONLY use if this query is
executed INFREQUENTLY, is to use a forms record group instead of
a cursor and build the query into a string and dynamically fill
the record group using "create|populate_group_from_query".  In
that fashion, the optimizer will see the value of the bind
variables -- optimize the statement based on those values -- and
come up with the best plan for that query (and possible a
different plan for each query submitted).  I hesitate to do this
with a query that is executed frequently however as it will tend
to mess up the shared pool.  This is more commonly done in a
data warehouse situation where there are few very large queries.
 You can see an example of this at:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:270615625768
...
Review & Followup
Rating: 3
Y use functions on columns April 02, 2002
Reviewer: Anthony Velarde from Philippines
  Select /*+ INDEX(VANDTL IDX_VANDTL_DATE) */ (forcing the
ptimizer to use the index)
   c.are_code, b.itm_code,
   sum(decode(b.trs_code,'IN',b.cases,0)) cases,
   sum(decode(b.trs_code,'IN',b.pcs,0)) pcs
  From  s.vandtl b, s.gen c
  Where b.post_date between :from_dt and :to_dt
    and nvl(b.reg_code,null) between :fr_reg and :to_reg
    and nvl(b.bra_code,null) between :fr_bra and :to_bra
    and nvl(c.are_code,null) between :fr_area and :to_area
    and nvl(b.van_code,null) = c.gen_code
    and nvl(b.copies,0) <> 9    --- Cancelled Documents
  Group by c.are_code, b.itm_code;

in your above recomendation, you suggested to use functions on the columns.
is'nt this much slower because u use functions on the columns. I have your book
and you said dont use functions on columns that is indexed because ORACLE would
not use that index right? can you explain this ?

Followup:
(your keyboard is malfunctioning, it is dropping the Y and the O from time to
time on certain words)

I PURPOSELY used the functions on the columns to bypass the indexes.  It was a
purposeful decision and I said as much:

...
That will preclude the use of indexes on b.reg_code, b.bra_code, c.are_code,
b.van_code.  Only the index on b.post_date is available to be used -- that will
give us access to b.van_code and we'll use theindex on c.gen_code to do the
join.   You could probably DROP the hint at that point.
......

It was a trick to AVOID the other indexes we DID NOT want to use.  If accessing
the object via the index in question is what you want to do -- that is one way
to make it so.

Also see the chapter on indexes in that book -- function based indexes.  Not
relevant in this example of course but you can use an index on a function as
well.

April 02, 2002 Krishna Prasanna -- Thanks for the question regarding "guidance please !!", version 8.1.6
You Asked
Hi Tom :)

This is not technical but then hope you will answer ...
I want to know the important concepts for a developer which are part of all most
all projects.I have no idea of Java , how will it effect
my programming ? (as java is implemented in oracle now).what is the role of
autonomous transactions ?

Thanks,
Kris
and we said...
Well, the entire answer takes about 1,265 pages.  I know that cause I wrote a
book on the subject.

I suggest you read the Server Concepts manual from cover to cover first and the
consider other books on the market:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/index.htm
Java is just a lanaguage, languages are a dime a dozen.  Understand the database
-- and if you are doing database development -- you'll be way ahead of the game.
Review & Followup
Rating: 4
oracle developer April 02, 2002
Reviewer: Krishna Prasanna from INDIA
thanks allot for the response.

April 02, 2002 Alison -- Thanks for the question regarding "Truncate Tables", version Version 7.3.4
You Asked
Hi Tom

I was wondering if you could help.

I am trying to truncate all the tables in a database with 0
rows.

I have done a
"select 'select count(1) from' ||table_name||';' where owner =
'schema_name';" then ran the spooled output of this file to give
me a list of all the actual rows in the tables

I have also done a
select table_name from dba_tables where num_rows = 0 and owner =
'schema_name'; this gives me approx. 3700 tables (it's a
peoplesoft environment).

I don't want to have to "analyze table ||table_name|| compute
statistics;" because of the sheer amount of tables in the
particular schema (5627).

Is there a way to run the first script (Select 'select
count(1)etc) and combine it with something which will
automatically generate the "'truncate ||table_name||;" script
for the correct tables, or a better way to do the whole thing.

Many thanks in advance

Alison
and we said...
Instead of counting each and every table -- what I would
recommend is using some builtin functions to find good CANDIDATE
tables and then count their rows.

Counting every table would be pretty resource intensive.
Instead, we can quickly find out how many blocks are on the free
list for a table, how many blocks have never been used and how
many blocks have been allocated (we don't need to analyze to get
that).  We can add the freelist blocks with the unused blocks
and if that is 1 less then allocated -- that table is
POTENTIALLY empty (we can have blocks on the freelist that
contain data).  Those are the tables we must count.  We can use
DBMS_SQL to count those tables.  Here is a routine to do just
that.  Leave it as an ANONYMOUS block -- do not make it a
procedure (roles are not active in procedures, you'll have
issues with those privs not being active in doing this
particular operation).  If you run this as SYS, it'll show you
the empty tables.  Just change my message to be your truncate
table and spool the output.  It should do what you want.  Run it
first to get comfortable with what it does and how it does it
and when you trust that it finds empty tables -- let it generate
the truncate statements.

sys@ORA734.US.ORACLE.COM> declare
  2      l_free_blks                 number;
  3      l_total_blocks              number;
  4      l_total_bytes               number;
  5      l_unused_blocks             number;
  6      l_unused_bytes              number;
  7      l_LastUsedExtFileId         number;
  8      l_LastUsedExtBlockId        number;
  9      l_LAST_USED_BLOCK           number;
 10
 10      procedure p( p_str in varchar2 )
 11      is
 12      begin
 13          dbms_output.put_line( p_str );
 14      end;
 15
 15      function get_row_cnts( p_tname in varchar2 )
 16      return number
 17      as
 18          l_theCursor     integer
 19                          default dbms_sql.open_cursor;
 20          l_columnValue   number  default NULL;
 21          l_status        integer;
 22      begin
 23          dbms_sql.parse(  l_theCursor,
 24                          'select count(*) from ' || p_tname,
 25                           dbms_sql.native );
 26
 26          dbms_sql.define_column
 27          ( l_theCursor, 1, l_columnValue );
 28          l_status := dbms_sql.execute(l_theCursor);
 29          if ( dbms_sql.fetch_rows(l_theCursor) > 0 )
 30          then
 31              dbms_sql.column_value
 32              ( l_theCursor, 1, l_columnValue );
 33          end if;
 34          dbms_sql.close_cursor( l_theCursor );
 35          return l_columnValue;
 36      end;
 37  begin
 38      for x in ( select owner, table_name
 39                   from dba_tables
 40                  where owner NOT LIKE 'SYS%' )
 41      loop
 42          dbms_space.free_blocks
 43          ( segment_owner     => x.owner,
 44            segment_name      => x.table_name,
 45            segment_type      => 'TABLE',
 46            freelist_group_id => 0,
 47            free_blks         => l_free_blks );
 48
 48          dbms_space.unused_space
 49          ( segment_owner     => x.owner,
 50            segment_name      => x.table_name,
 51            segment_type      => 'TABLE',
 52            total_blocks      => l_total_blocks,
 53            total_bytes       => l_total_bytes,
 54            unused_blocks     => l_unused_blocks,
 55            unused_bytes      => l_unused_bytes,
 56            LAST_USED_EXTENT_FILE_ID =>l_LastUsedExtFileId,
 57            LAST_USED_EXTENT_BLOCK_ID =>l_LastUsedExtBlockId,
 58            LAST_USED_BLOCK => l_LAST_USED_BLOCK );
 59
 59          if ( l_free_blks + l_unused_blocks =
 60                                 l_total_blocks-1 )
 61          then
 62              if ( get_row_cnts
 63                  ( x.owner || '.' || x.table_name ) = 0 )
 64              then
 65                  p( x.owner || '.' || x.table_name ||
 66                     ' Empty Candidate' );
 67                  p( l_free_blks || ' on freelist, ' ||
 68                     l_unused_blocks || ' never used, ' ||
 69                     l_total_blocks || ' allocated' );
 70              end if;
 71          end if;
 72      end loop;
 73  end;
 74  /
SCOTT.TKYTE_FT_TRANSFERS Empty Candidate
0 on freelist, 1 never used, 2 allocated
SCOTT.X1 Empty Candidate
1 on freelist, 0 never used, 2 allocated
SCOTT.TESTME Empty Candidate
1 on freelist, 0 never used, 2 allocated
SCOTT.P Empty Candidate
1 on freelist, 0 never used, 2 allocated
SCOTT.COMO Empty Candidate
0 on freelist, 1 never used, 2 allocated
SCOTT.MA_SODREC Empty Candidate
0 on freelist, 1 never used, 2 allocated
TKYTE.TEMP Empty Candidate
1 on freelist, 0 never used, 2 allocated
TKYTE.TEST1 Empty Candidate
0 on freelist, 1 never used, 2 allocated
TKYTE.TEST2 Empty Candidate
0 on freelist, 1 never used, 2 allocated
OPS$TKYTE.EMP Empty Candidate
0 on freelist, 1 never used, 2 allocated
OPS$TKYTE.T Empty Candidate
0 on freelist, 1 never used, 2 allocated
SCOTT.BONUS Empty Candidate
0 on freelist, 1 never used, 2 allocated
SCOTT.C Empty Candidate
1 on freelist, 0 never used, 2 allocated
TKYTE.RIATTC Empty Candidate
0 on freelist, 23299 never used, 23300 allocated

PL/SQL procedure successfully completed.
Review & Followup
Rating: 5
Powerfull Script April 02, 2002
Reviewer: Parag from India
Thanks a Lot Tom.
This is what , I am lookng from long time.

Rating: 3
truncate tables April 02, 2002
Reviewer: kit from england
I'm not a dba but keen to increase  my overall knowledge hence the reason I
really like this site.

Could you please explain how your formulae

 Adding   freelist blocks with the unused  blocks and if that is 1 less then
allocated --
that table is Potentially empty

-why 1 less is . I would have thought unused blocks = allocated blocks then
empty

please expand on

"Leave it as an ANONYMOUS block -- do not make it a procedure (roles
are not active in procedures, you'll have issues with those privs not being
active in doing this particular operation"

Followup:
There is a one block overhead typically -- so there may be one block that is
used by the table -- even if the table is empty.

for the second point:
http://osi.oracle.com/~tkyte/Misc/RolesAndProcedures.html
.

Rating: 4
And to cut the overhead even further April 03, 2002
Reviewer: Scott from Chicago, IL USA
I'd limit the amount of leeway that the count(*) has by setting rownum=1,

 23          dbms_sql.parse(  l_theCursor,
 24                          'select count(*) from ' || p_tname || ' where
rownum=1',
 25                           dbms_sql.native );

cheers,
Scott

April 03, 2002 Vishwas -- Thanks for the question regarding "Database Space Usage (Allocated Vs Used)", version 8.1.7.0
You Asked
Hi Tom

Question:
---------

Is there any SYS object which will give me MonthWise Space used by database
since database creation  ?

Oracle Database Version :

Oracle8i Enterprise Edition Release 8.1.7.0.0 - 64bit Production
With the Partitioning option
JServer Release 8.1.7.0.0 - 64bit Production

O/s : AIX  4.3

Example of Output:
------------------

Month            Allocated(MB)  Used(MB)
----------       -----------    --------
June,2001         10,900          4500
July,2001         10,900          5500
August,2001       10,900          6200
September,2001    12,900          6200

I have installed statspack since last month only (NO Excuses Tom , Earlier DBA
had not done it...nor there is any monitoring tool..)

Pls let me know a SQL which will get info above format.(Company Management needs
it...)

Thanks in Advance

Vishwas
and we said...
Answer -- no.  You would have to "snapshot this" yourself.  Very easy to do.
Just search this site for free.sql for a script -- add sysdate to it and every
run (use dbms_job to schedule for example) run that query as an "insert into
select sysdate, .... from ... "

The history is unknown, the future -- you can track.
Review & Followup
Rating: 3
Database Space Usage (Allocated Vs Used)", March 31, 2002
Reviewer: Vishwas from Singapore
Thanks TOM for quick response...

Rating: 4
By Blocks April 01, 2002
Reviewer: Irfan from Kuwait
Hi Tom,

Is it possible that for example, every month, I take the number of blocks used
by a table and then compare with the previous months blocks used and multiply
with the BLOCK SIZE to get the bytes used by a table for a month. Will it
provide an approximate result?

Thanks...

Followup:
it'll get you an exact result.

Rating: 4
Blocks calculations April 03, 2002
Reviewer: Irfan from Kuwait
We have a tablespace of size 795 MB. 3 tables reside in this tablespace. The
total space allocated is 693 MB and the remaining space is 102 MB (tablespace
level). Considering pct_free = 10, pct_used = 40,  pct_increase = 50 for all the
tables and pct_increase = 50 for this tablespace.
Blocks used by tables
Table     Last month     This month
-------------------------------------------------
 T1        88305           92400
 T2        45490           53315
 T3        25800           30195

A total of 8 MB is used for the month for T1, 15.5MB for T2, 8.5MB for T3
.......... (Block size = 2048)
The total allocated space is 693MB out of which 344MB is used (from above) and
the remaining in allocated is 349MB.
Please guide us in how many more months the remaining 349MB will be utilized?
If we consider approximately 32MB is consumed for the month by this tablespace,
Will it last for another 11 months?
Also can you please tell us when the next extents will be allocated.
Thanks so much...

Followup:
Well, this is simple math.  I can make the math MUCH MUCH more simple and would
highly recommend you do this.

USE FIXED SIZE EXTENTS.  In 8i, use locally managed tablespaces with uniform
extents.  In 8.0 and before, just get in the habit of using INITIAL=NEXT,
PCTINCREASE=0. and use the same initial for all objects in the same tablespace.

then, forcasting how much more can fit is trivial (take free space, divide by
the extent size and there you go)

forecasting WHEN the next exent will be allocated -- you need to look at history
and chart that (that is what this question was about -- how to gather this
history, you need to "snapshot" it over time so you have a history to look at)

April 03, 2002 Balaji -- Thanks for the question regarding "Rollback Segment Error", version 8.1.6
You Asked
Hi Tom,
    Can u tell me whether my understaing of the Rollback segment is Correct.
   All the applications in our Bank's are using the Oracle Database and one of
our application is using the same.Our application we process with high volumes
of records.For Insert's into the tables we use the concept of Large Rollback
Segments (Upto 1 GB) concept and we do the insert.
   First time when we run our procedures to populate the tables it is doing
fine. But Second time when we run the procedures we get the Rollback Segment
Error.

   "Unable to Allocate Next Extent"

As per my understanding all the records the Rollback Segment will be cleared
after we issue a commit and the space taken by the transaction will be available
for the reuse for other transactions.If so , why should i get this Error.

And can u tell me why the following error also occurs and how to avoid this.
 Error: ORA-01555: Snapshot too Old: Rollback Segment number 4 with name "R04"
to small.

Thanks for all the efforts
and we said...
I'll answer in reverse.

The probability of an ORA-1555 is directly proportional to the size of your
SMALLEST rollback segment (not the largest).  This error is basically saying
that you have a long running query, one that takes longer to run then it takes
for R04 to fill up and wrap around.

Your RBSs are not sized properly for your system.  They might be sized for your
update transactions -- but they need to be sized for your READ as well as WRITE
conditions.  They need to be defined larger (not sized to get potentially
larger, they must be statically larger -- meaning you must either increase their
extent size and keep their minextents at the same number OR increase their
minextents so the permanently allocated space is larger for each).

As for the unable to allocated next extent -- I'll assume that you have a commit
in your procedure.  Upon commit, you "give up" your rollback segment you got in
the set transaction use rollback segment.  You need to issue that again after
the commit.

You may be interested in my book.  In there I go over this in great detail -- I
also describe why i really don't like the "set transaction use rollback segment"
command....
Review & Followup
Rating: 5
Rollback Segment Error September 12, 2001
Reviewer: M.Balaji from Singapore
Hi Tom,
   The answer was very useful for me.May be i will buy your books to know more .

Thanks
M.Balaji

Rating: 4
"Rollback Segment Error" March 07, 2002
Reviewer: Cheema from Los Angeles
In my opinion the Space used by a Rollback Segment is not Coalesced when the
transaction attached to it is Commit(ed) but only when another transaction
attaches to it and Oracle finds that it needs more rollback space.

I formed this opinon from the following incident :
10 rollback segments(Rb1 -- Rb10) had been created in a tablespace which could
extend to 1 Gb. An application had two large transactions running serially, with
a commit in between.Each of which these needed about 800 MB.
After having shrunk all RB(s) to 10MB each during the trials error "Unable to
Allocate Next Extent"
was returned.
I found that each transaction was (randomly)being attached to a different RB so
that the second transaction could not find enough space.

Cheema

Followup:
Opinions?  We don't need opinions -- it is documented how rollback is used in
the concepts guide (and many many other books).

Your opinion, as I understand it, is not correct.

You have 10 rollback segments rb1 .. rb10.  They are in a 1gb tablespace.
Assume each rb segment uses a 512k extent size and each starts at 2 extents (way
small, just assume that for ease of walking through the example)

You started a transaction.  It got assigned (for example) to rollback segment
rb2.  It caused rb2 to grow to 800m (the rollback segment tablespace is now
809meg in size -- 800meg for rb2, 1meg for each of the remaining rbs's).

You commit. You stopped using rb2.

You begin a new transaction.  If you are the only user on the system, you most
likely will start using rb3 (round robin'ish assignment of rollback segments).
This rollback segment rb3 will grow to about 215meg in size and then FAIL.  It
fails because the sum of the sizes of the rollback segments now exceeds
1gigabyte.  Transaction fails.

It has nothing to do with the "coalescing of space in a rollback segment", it
has everything to do with how rollback segments are used and the fact that
rollback segment space is used in a big circular buffer (read the concepts guide
or my book for details).

This is why I always propose that

a) rollback segments should be sized big enough so they need not grow in the
first place (size them for your workload)

b) rollback segments should all be the SAME size

c) if you do a&b, you need never use "set transaction use rollback segment..."

Rating: 5
Deallocate space in RBS March 08, 2002
Reviewer: Irfan from Kuwait
Hi Tom,
Our normal procedure is at the end of day we move all our data to history
tables. Since our RBS grows to a larger sizes, Can you put on light your best
way to deallocate space from RBS; coz the RBS shows the same remaining
space,used space everytime we query, unless we truncate some data in history
tables. Also is it advisable to do this periodically.

We also shutdown the database daily for cold backup. Has shutdown and restart of
database has any effect on RBS?

Thanks.

Followup:
I would not "deallocate" space -- rather I would make sure each RBS was that big
from the get go.  That way I can do something once and never worry about it
again.  Your time costs many many times more then some disk space does. (others
will tell you about optimal, i don't like optimal, I like fixed size rollback
segments that are always sized for my biggest stuff.  I sleep better at night
knowing stuff won't blow up).

You should not shutdown to backup.  You should just do a hot backup.  My
reasoning:

o i don't think you should ever do an unattended shutdown.  see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2876092892845
o you are emptying out the caches, you are forcing us to redo work that's
already been done.  You are making the system slower then it should be in the
morning.

o hot backups are no harder then cold.  They are no harder to perform nor are
they any harder to restore from.

Rating: 4
RBS size frozen April 02, 2002
Reviewer: Irfan from Kuwait
Hi Tom,
Thanks for the solution. I would like to have one more guidance from yourself.
Actually we are facing some problems in RBS. We run OPS 734 with three instances
on three Unix boxes. Since 10-11 months, I don't see any changes in RBS
tablespace. The free space remains the same since long. We do lots of
transactions and do truncations eventually. Below is the total allocated and
free space. (Allocated = 195 MB, Free=.0175 MB; showing since long)
Can you please guide us what happend to our RBS, why its got like stuck or so?
Please help us what to do in this case? Will shrinking do a favor?

SVRMGR> select * from dba_data_files where tablespace_name='RBS';
FILE_NAME           FILE_ID TABLESPACE_NAME  BYTES    BLOCKS  STATUS
-------------------------------------------------------------------
/dev/rdsk/c3t3d5sb     2         RBS         204472320       99840
AVAILABLE
1 row selected

SVRMGR> select * from dba_free_space where tablespace_name='RBS';
TABLESPACE_NAME   FILE_ID    BLOCK_ID   BYTES      BLOCKS
-------------------------------------------------------------
RBS                 2          99832    18432           9
1 row selected.

Below is a list of RBS segments.
SVRMGR> select segment_name, tablespace_name, status
     2> from dba_rollback_segs;
SEGMENT_NAME            TABLESPACE_NAME                    STATUS
------------------------- ------------------------------ ----------------
SYSTEM                        SYSTEM                          ONLINE
RBS3                          SYSTEM                             OFFLINE
R01                           RBS                                  OFFLINE
R02                           RBS                                  OFFLINE
R03                            RBS                                 OFFLINE
R04                           RBS                             OFFLINE
RBS2                          SYSTEM                             OFFLINE
SE0_RBS1                      RBS                                ONLINE
SE0_RBS2                      RBS                                ONLINE
SE0_RBS3                      RBS                                ONLINE
SE0_RBS4                      RBS                                ONLINE
SE0_RBS5                      RBS                                ONLINE
SE1_RBS1                      RBS                                ONLINE
SE1_RBS2                      RBS                                ONLINE
SE1_RBS3                      RBS                                ONLINE
SE1_RBS4                      RBS                                ONLINE
SE1_RBS5                      RBS                                ONLINE
SE2_RBS1                      RBS                                ONLINE
SE2_RBS2                      RBS                                ONLINE
SE2_RBS3                      RBS                                ONLINE
SE2_RBS4                      RBS                                ONLINE
SE2_RBS5                      RBS                                ONLINE
22 rows selected.
The RBS allocated for seperate instances as:

init.ora (instance2)
rollback_segments  = (SE1_RBS1, SE1_RBS2, SE1_RBS3, SE1_RBS4, SE1_RBS5)

init.ora (instance3)
rollback_segments = (SE2_RBS1, SE2_RBS2, SE2_RBS3, SE2_RBS4, SE2_RBS5)

init.ora (instance1)
rollback_segments  = (SE0_RBS1, SE0_RBS2, SE0_RBS3, SE0_RBS4, SE0_RBS5)

Thanks very much for solving this issue.

Followup:
Unless you built your RBS's with an "optimal" (which I hate), they will only
grow and never shrink.  This is not a bad thing -- it just means that you had a
long running transaction that either:

o did lots of work and caused the rbs to extend to hold all of its work

o just didn't commit for a long time, making it so we had to allocate new
extents for the RBS instead of reusing the extent it was occupying.

Just find the "big" rbs, offline it, drop it, rebuild it if you do not like it
being "big".

Rating: 3
April 03, 2002
Reviewer: kiro from Bulgaria
Hi Tom
May this error 'ora-01555' to be from optimal size of rollback segment?
I mean if there is optimal size of rollback segment and we have commited data
Oracle trys to shrink rollback segment to optimal size and if there is long
running  query user can receive 'ora-01555'. Is shrink of rollback segment
performs "delayed block cleanout"? Please explain.
Thanls

Followup:
optimal can cause 1555's to happen more frequently -- yes.  You are getting rid
of undo other sessions might need.

shrinking an RBS and block cleanout are wholly unrelated (if you have my book --
i go into great detail on these topics, they are complex and require many pages
of text to describe)

April 03, 2002 Madhusudhana Rao -- Thanks for the question regarding "CHAR Index (vs) NUMERIC Index", version Oracle 8.1.6
You Asked
Hi Tom,

In comparison to an Index on CHAR field and Index on NUMERIC field
pls let me know which Index(whether CHAR or NUMERIC) would be faster.

Thanx & Regards,
Madhusudhana Rao.P
and we said...
They would be the same.  Numbers are stored in a packed decimal like format in
Oracle -- they are character strings.

I would suggest not using CHAR but varchar2 instead as a CHAR is simply a
varchar2 that is always blank padded out to the maximum length (there is no
efficiency of char over varchar2, char just takes more space)
Review & Followup
Rating: 4
Index On Char Column April 01, 2002
Reviewer: Ron from Columbus, OH USA
Tom,
I have a char(5) column in my Table, but when we are storing the variable length
data in this column like 'ABC','ABCD','ABCDE','AB'.We have an index on this
column and most of our selects are based on this column, As I am using TRIM
function on this column it is not using the index.I know changing it to varchar2
is one solution.But I can not do it now..Is there any alternate way to force the
sql to use the index on this column..

Thanks in advance..

Ron

Followup:
read
http://asktom.oracle.com/~tkyte/article1/index.html
char's stink, don't use them again.

Rating: 4
Index On Char Column April 01, 2002
Reviewer: Ron from Columbus, OH USA
Sorry I missed the Version in the above question.
My Database version is 8.0.5

Followup:
Oh, sorry then, you need 8i EE and up for my suggestion above.

You'll be wanting to code:

select * from table where that_column = rpad( :bind_variable, 5 );

in the meanwhile. that'll let you use the index (rpad the thing to be compared
to, don't rtrim the column in the table)

Rating: 3
April 01, 2002
Reviewer: Dave from Ohio.
Is it worth noting that in the Performance Guide and Methods at ...
http://206.204.21.139/otndoc/oracle9i/901_doc/server.901/a87503/data_acc.htm#2416
... under "Performance tips for Bitmap Indexes" it states that "Fixed-length
datatypes are more amenable to a compact bitmap representation than variable
length datatypes."?

I'll admit that I haven't benchmarked any difference but I'm guessing that
Oracle developers must have done so in order to make such a statement.

Incidentally, just above this it states that ...

"...a B-tree index on (marital_status, region, gender) is useless for queries
that only access region and gender....". Ooops.

Followup:
The entire thing is:

<quote>
 Performance Tips for Bitmap Indexes

To get optimal performance and disk space usage with bitmap indexes, consider
the following tips:

    * To make compressed bitmaps as small as possible, declare NOT NULL
constraints on all columns that cannot contain null values.

    * Fixed-length datatypes are more amenable to a compact bitmap
representation than variable length datatypes.

This is because Oracle needs to consider the theoretical maximum number of rows
that will fit in a data block when creating bitmap indexes.
</quote>

Amenable is a "fuzzy term" if you ask me.  They are trying to save space in a
bitmap index (small thing) in a datawarehouse (big thing).  With the bitmap
index -- the internal algorithms can make the best use of the bitmaps
(utilization) with a fixed field (the math is easier) but the trade off is --
the field itself is "wider"

Tell me -- would you use char(80) on a department name field where the average
size is 30 characters and you have a billion rows in the table?  I myself would
use varchar2(80) and reap the savings in size in the table itself.  The bitmap
index on the char(80) would necessarily be larger as well if you think about it.

You can benchmark this with a bitmap index on a varchar2 and another on the same
field using rpad( , some_fixed_length ) to see which is better for you.  I
didn't see any noticable differences (other then the rpad'ed one was bigger)

And yes, it looks like they forgot to update the perf guide with respect to
index skip scanning.

Rating: 4
April 02, 2002
Reviewer: Dave from Ohio
Indeed, the use of a Char data type to store 80 characters in the example you
give is crazy talk.

I think you're unlikely to try and use such a field width as a key value in a
data warehouse fact table of course. IMO -- you're not going to start a campaign
against the use of that, are "u" ;) -- a Char data type is a reasonable
alternative for short (say less than 10 characters) key values.

Followup:
I disagree.  I would NEVER use char(10) in any condition.

I do not believe it is reasonable in any circumstance, no.

Why is a char(80) or a char(30) or a char(20) "crazy".  I'm not talking crazy --
I'm talking "i have an 80 character field that has low cardinality that people
frequently query on".

It is NOT a key field, (key fields would not be candidates for bitmaps would
they).  It is an attribute.  Sure, you could factor out the attribute to a fact
table -- but you still need to index it, and teach the people how to join to it
and so on.

Rating: 4
April 02, 2002
Reviewer: Dave from Ohio
Well there's two issues here. Would you use Char data types or VarChar2's in
general, and is there any difference in performance of bitmap indexes on the two
types. I take it for granted that the second point is not relevant if a bitmap
index is inappropriate to the data.

The amount of space you waste with a Char varies, of course, from near to
num_rows*column_length at one extreme to zero at the other, and in the example
you gave it is obviously such a large wastage that I contend that it would
indeed be crazy to pick Char(80) over VarChar2(80), where you would be wasting
50bytes * 1,000,000,000.  Specifying Char(10) would be approaching crazy (or
"bad practice" if that's too strong a word) if your average length was 5, and
much less significant if your average length was 9. If it's a choice between a
varchar2(2) and a Char(2) I'd care a lot less. The craziness here has nothing to
do with the cardinality, only the space wastage.

So it's a matter of diminishing returns as the average length approaches the
maximum, and I would consider whether -- for a column on which it is appropriate
to use a bitmap index -- there is a point at which increased efficiency in the
bitmap index structure overcomes the waste of disk space in the table. "Never"
is too strong a word for my taste, and it seems trivial to suggest that a
char(10) is indeed reasonable if your average length is 10.

Followup:
The craziness is that people NEVER remember to pad out the things to be compared
to, so they run:

select * from t where fixed_length_column = :x;

Now, you have a problem -- that finds no data.  It is non-intuitive and makes
the application dependent on the field length - that should be coded as:

  fixed_length_column = rpad( :x, 10 );

but later, someone makes that field char(11) or char(15) and now the apps
broken.

I would NEVER use a char, period.  Never, the data errors that pop up due to
improper use of that type on retrieval have wasted more of my cycles then I
could ever pick up in savings on a bitmap index ;)  (eg: I've wasted more time
explaining why char's are bad, why they do what they do, why the query in
question returns the wrong answer then I could ever recoup from using them...)

I rarely say never -- but this is one of the times when I do.  Never.

Rating: 3
April 03, 2002
Reviewer: Dave from Ohio
Well, time spent educating is never time wasted. That's what I tell functional
staff anyway, and I appreciate you taking the time on this.

So sorry to keep on like this, but I must be doing something different here as I
can't reproduce your case, which as I understand it is that Char bind variables
must be RPad'ed to the correct length to match Char column types, but VarChar2
bv's do not in order to match VarChar2 column types.

Here's what I did ...

SQL> set sqlprompt '' feedback on sqlcontinue '' sqlnumber off
/* Create a test table with sample rows */
create table char_test (fixed_length_column char(4), variable_length_column
varchar2(4));

Table created.

insert into char_test values ('','');

1 row created.

insert into char_test values ('AA','AA');

1 row created.

select * from char_test;

FIXE VARI
---- ----

AA   AA

2 rows selected.

/* See how SQL handles different cases */
select * from char_test where fixed_length_column = '';

no rows selected

select * from char_test where variable_length_column = '';

no rows selected

select * from char_test where fixed_length_column = 'AA';

FIXE VARI
---- ----
AA   AA

1 row selected.

select * from char_test where variable_length_column = 'AA';

FIXE VARI
---- ----
AA   AA

1 row selected.

/* See if it's different with defined data types */
variable v_fixed char(4)
variable v_variable varchar2(4)
variable v_fixed_row_count number
variable v_variable_row_count number

begin
:v_fixed := '';
:v_variable := '';
execute immediate 'select count(0) from char_test where fixed_length_column =
:s_string'
   into :v_fixed_row_count
   using :v_fixed;
execute immediate 'select count(0) from char_test where variable_length_column =
:s_string'
   into :v_variable_row_count
   using :v_variable;
end;
/

PL/SQL procedure successfully completed.

print :v_fixed_row_count

V_FIXED_ROW_COUNT
-----------------
                0

print :v_variable_row_count

V_VARIABLE_ROW_COUNT
--------------------
                   0

begin
:v_fixed := 'AA';
:v_variable := 'AA';
execute immediate 'select count(0) from char_test where fixed_length_column =
:s_string'
   into :v_fixed_row_count
   using :v_fixed;
execute immediate 'select count(0) from char_test where variable_length_column =
:s_string'
   into :v_variable_row_count
   using :v_variable;
end;
/

PL/SQL procedure successfully completed.

print :v_fixed_row_count

V_FIXED_ROW_COUNT
-----------------
                1

print :v_variable_row_count

V_VARIABLE_ROW_COUNT
--------------------
                   1

begin
:v_fixed := 'AA ';
:v_variable := 'AA ';
execute immediate 'select count(0) from char_test where fixed_length_column =
:s_string'
   into :v_fixed_row_count
   using :v_fixed;
execute immediate 'select count(0) from char_test where variable_length_column =
:s_string'
   into :v_variable_row_count
   using :v_variable;
end;
/

PL/SQL procedure successfully completed.

print :v_fixed_row_count

V_FIXED_ROW_COUNT
-----------------
                1

print :v_variable_row_count

V_VARIABLE_ROW_COUNT
--------------------
                   0

So ...

Case1
Char(4)'' doesn't find Char(4)''
VarChar2(4)'' doesn't find VarChar(4)''

Case2
Char(4)'AA' finds Char(4)'AA'
VarChar2(4)'AA' finds VarChar2(4)'AA'

Case3
Char(4)'AA' doesn't find VarChar(4)'AA'
VarChar2(4)'AA' doesn't find Char(4)'AA'

Case4
Char(4)'AA ' finds Char(4)'AA'
VarChar2(4)'AA ' doesn't find VarChar2(4)'AA'

To summarize, it seems to me that it is the VarChar2 that is more difficult to
handle (based on Case4). I discount Case 3 because in PL/SQL the variable should
be defined as ...
   v_variable char_test.fixed_length_column%Type;
... anyway, thus getting the same type and length even if the column definition
changes.

So, is there an init parameter thing going on here? Is it an ANSI compatibility
thing?

Followup:
ops$tkyte@ORA8I.WORLD> create table t ( x varchar2(5), y char(5) );

Table created.

ops$tkyte@ORA8I.WORLD> variable a varchar2(5)
ops$tkyte@ORA8I.WORLD> exec :a := 'a'

PL/SQL procedure successfully completed.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> insert into t values ( 'a', 'a' );

1 row created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> select * from t where x = :a;

X     Y
----- -----
a     a

ops$tkyte@ORA8I.WORLD> select * from t where y = :a;

no rows selected

ops$tkyte@ORA8I.WORLD>

There is the simple example.  99.999% of all programs will bind using a VARCHAR
-- not a char (odbc, jdbc, proc, oci, whatever -- especially C based things with
null terminated strings).

Rating: 5
April 03, 2002
Reviewer: Dave from Ohio
Ah hah, I see now. So if there's not a mechanism for declaring a variable to
inherit the data type of the column, or there is some potential for this in the
future, then VarChar2's are pretty much mandatory. It all makes sense to me now.

I've previously had problems with supporting OLTP apps that used VarChar2's, as
bad coding by third-party COTS suppliers was allowing trailing spaces to be
appended unnecessarily to character strings, something that's not an issue with
Char's. It's been quite a few years since I worked on OLTP.

Followup:
In PLSQL there is a mechanism

table.column%type

In a 3gl such as C, Java, "vb" and so on -- this is impossible.  If someone
changed the database, you would have to RECOMPILE the apps with the changed
definition.

April 03, 2002 Paul -- Thanks for the question regarding "Setting user information from jdbc client", version 8.1.5
You Asked
Hi Tom,

if you run
  select username,server,osuser,machine,terminal from v$session.
it displays:
username    server    osuser       machine        terminal
USER1        NONE    jdbcuser      jdbcclient     unknown

My question is: Is there any way for a jdbc client application to set the values
for server, machine and terminal?

Thank you!

Paul Huang
and we said...
Not really.

Point 1 -- they most likely don't have a terminal (middle tier I suspect or
running on a platform like windoze that does not have the concept of a
terminal).

Point 2 -- if you use the Oracle jdbc drivers, they do set the machine name
properly, i've never seen them NOT set it.

Point 3 -- the OSUSER is the osuser running the dedicated or shared server, not
the remote OS user.

Using DBMS_APPLICATION_INFO, you can set this information manually into
v$session -- the client_info, action and module columns.

Followup to comment

I just tried it out:

$ cat Emp.java
import java.sql.*;

class Emp
{
    public static void main(String[] args) throws SQLException
    {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try
        {
            // Load Oracle driver
            DriverManager.registerDriver
            (new oracle.jdbc.driver.OracleDriver());
            String connect_string =
                "jdbc:oracle:oci8:@ora8i.us.oracle.com";

            // Connect to the local database
            conn = DriverManager.getConnection
            (connect_string,"scott", "tiger");

            // Query the employee names by job
            ps = conn.prepareStatement
                 ( "select username || ',' || server ||','||
osuser||','||machine||','||terminal " +
                    " from v$session " +
                   "where sid = ( select sid from v$mystat where rownum = 1 )"
);

            rs = ps.executeQuery();

            while (rs.next())
                System.out.println( rs.getString(1) );

            rs.close();
            ps.close();
            conn.close();
        }
        catch (Exception e)
        {
            System.out.println(e + "\n");
        }
        finally
        {
            if ( rs != null ) rs.close();
            if ( ps != null ) ps.close();
            if ( conn != null ) conn.close();
        }
    }
}

$ javac Emp.java

$ java Emp
SCOTT,DEDICATED,tkyte,aria,pts/5

followup to comment

did you use the THICK or THIN drivers.  I used OCI8.
Review & Followup
Rating: 3
DBArtisan does it July 08, 2001
Reviewer: Paul Huang from NY, USA
I did use Oracle jdbc driver when I made the select.
If you use DBArtisan, you can see the columns for OSUSER, MACHINE, TERMINAL,
PROGRAM and COMMAND are set when you bring up the monitor screen.

Rating: 3
Does it run anywhere? July 09, 2001
Reviewer: Paul Huang from NY, USA
I ran the program on NT. Here is part of the output:

MIS,DEDICATED,jdbcuser,jdbcclient,unknown
MIS,DEDICATED,jdbcuser,jdbcclient,unknown
MIS,DEDICATED,jdbcuser,jdbcclient,unknown
MIS,DEDICATED,U028071,FINANCIAL_MIS\WK,WK1CMP22_ZHIMIH
MIS,DEDICATED,U028071,FINANCIAL_MIS\WK,WK1CMP22_ZHIMIH

The 2 lines with U028071 in ODUSER column are connections started by DBArtisan.
The rest are jdbc client connections. Are there any configuration or platform
requrements needed to get what you got?

Paul,

Rating: 3
Version 8.1.6 and above only? July 09, 2001
Reviewer: Paul Huang from NY, USA
If I connect to version 8.1.6, I see the info. What about version 8.1.5?

Paul,

Rating: 3
Question on your 'Point 2' April 03, 2002
Reviewer: A reader from NJ, USA
Tom, regarding your following comment...
###
Point 2 -- if you use the Oracle jdbc drivers, they do set the machine name
properly, i've never seen them NOT set it.
###

Do oracle thin driver also always set the machine name? I have come across some
pesky 'jdbc client' type of connections (with oracle thin driver), where machine
name is not filled in. What would it take for us to get the machine name with
thin driver? Or would we have to use OCI8 instead? We use weblogic 5.1 and
oracle 8.1.6 or 9i. What would be the advantages or disadvantages of switching
from thin to OCI8.
Thanks.

Followup:
thin probably does not do it (easy to find out, take the application i have
above and try it)

there are many advantages to OCI8, features not available with thin and speed
being the two that pop into my head.

On an app server (you have 10's of these), I would definitely be using THICK oci
drivers -- on a client workstation (you have 100's or 1000's of these) I would
be using THIN.

April 03, 2002 Sudha -- Thanks for the question regarding "Dealing with long datatype fields.", version 7.3.4
You Asked
Hi Tom,

Thanks for the ref_cursor, worked perfectly. I have a nother
question for you -- How do I get part of dat from a long
datatype field ? I know I can't use substr or instr. Can this be
done ? By any method !

Thanks in advance
Sudha
and we said...
In PLSQL we can use dbms_sql to do this, for example - the
following will print out an entire long regardless of its
length:

create or replace procedure showlong( p_query in varchar2,
                                      p_name  in varchar2,
                                      p_value in varchar2 )
as
    l_cursor    integer default dbms_sql.open_cursor;
    l_n         number;
    l_long_val  varchar2(250);
    l_long_len  number;
    l_buflen    number := 250;
    l_curpos    number := 0;
begin
    dbms_sql.parse( l_cursor, p_query, dbms_sql.native );
    dbms_sql.bind_variable( l_cursor, p_name, p_value );

    dbms_sql.define_column_long(l_cursor, 1);
    l_n := dbms_sql.execute(l_cursor);

    if (dbms_sql.fetch_rows(l_cursor)>0)
    then
        loop
            dbms_sql.column_value_long(l_cursor, 1, l_buflen,
                                       l_curpos , l_long_val,
                                       l_long_len );
            l_curpos := l_curpos + l_long_len;
            dbms_output.put_line( l_long_val );
            exit when l_long_len = 0;
      end loop;
   end if;
   dbms_output.put_line( '====================' );
   dbms_output.put_line( 'Long was ' || l_curpos ||
                         ' bytes in length' );
   dbms_sql.close_cursor(l_cursor);
exception
   when others then
      if dbms_sql.is_open(l_cursor) then
         dbms_sql.close_cursor(l_cursor);
      end if;
      raise;
end showlong;
/

set echo on
begin
    showlong( 'select text from all_views where view_name = :x',
':x', 'ALL_VIEWS' );
end;
/
set echo off

So, you can use dbms_sql.column_value_long to get any piece of a
long in PLSQL.  This cannot be called from SQL.

If the LONG is always 32k or less, another option is:

create or replace procedure longsubstr( p_rowid in rowid,
                                        p_from  in number,
                                        p_for   in number )
return varchar2
as
   l_tmp long;
begin
   select LONG_COLUMN into l_tmp from T where rowid = p_rowid;

   return substr( l_tmp , p_from, p_for );
end;
/

and then you can:

select a, b, longsubstr(rowid,35,200) from T
/
Review & Followup
Rating: 3
Long datatype fields April 03, 2002
Reviewer: David from GB
Tom, how can I insert into a table the values returned by l_curpos for each row.
When I include an insert INTO table statement it only does it once.

I would like to pass as the query 'select text from all_tables' but then insert
the LONG VALUE SIZE into a separate table.

Regards

Followup:
does not compute -- I don't know what you are asking to do.  l_curpos is just a
number, an offset.

If you want to insert the "long value size" - it sounds like you want a function
that returns the size in bytes of a given long.  turn this into a function that
given a query that identifies a row -- returns the long size in bytes.   In 8i
-- if you make that an autonomous transaction, you can call it from sql in an
insert statement.

April 03, 2002 Dinh -- Thanks for the question regarding "Actual byte uses using varchar2 - internal to Oracle", version 8.1.6
You Asked
Hi Tom

What is the actual byte(OS) uses when the datatype is varchar2.
Is Vsize() the correct function?

example:

create table test (name varchar2(4000));

what is the byte size now - with no record in this table.

insert into test values('a');

what is the byte size now - with one record in this table.

insert into test values('abcdefghijklmnopqrstuvwxyz');

what is the byte size now - with two records in this table.

insert into test values('');

what is the byte size now - with addition null record in this
table.

What is the total byte(OS) uses now?

Thanks
Dinh
and we said...
vsize is very close to the right answer.  Vsize misses the
leading byte(s) on a varchar2 field.

Varchar2 fields are stored using a leading byte(s) count
(depending on length will be 1 or 2 bytes) and then the data.
So, for the non-null values above -- they will consume 2 and 27
bytes apiece.

As for the third case, when you insert NULL, it'll actually
consume 0 bytes in your case.  Trailing columns in a table that
are NULL consume no storage.  Null columns that have columns in
the row after them that are NOT NULL consume 1 byte (a flag to
say its not there).

ops$tkyte@8i> set null NULL

ops$tkyte@8i> create table t
  2  ( name varchar2(4000) )
  3  /

Table created.

ops$tkyte@8i>
ops$tkyte@8i> insert into t values ( 'a' );

1 row created.

ops$tkyte@8i> insert into t values
  2  ( 'abcdefghijklmnopqrstuvwxyz' );

1 row created.

ops$tkyte@8i> insert into t values ( '' );

1 row created.

ops$tkyte@8i>
ops$tkyte@8i> select name, vsize(name)
  2    from t
  3  /

NAME                           VSIZE(NAME)
------------------------------ -----------
a                                        1
abcdefghijklmnopqrstuvwxyz              26
NULL                           NULL
Review & Followup
Rating: 4
how is null stored internally in the database April 02, 2002
Reviewer: Kulguru
Tom

Is a null considered as Varchar2 by oracle. Why does oracle do that, what kind
of convenience or flexibility does this provide.

How much space does a null occupy. In the example you gave it is showing null
and and not 1 when the others columns in the row are not null. Can you expand on
that.

If I have to insert a null into a column can I just insert a '', ' ', or just
enter nothing. Which of these will be considered null.

Thank you

Followup:
NULL is not a varchar2 -- the "datatype" of a NULL is dependent on the COLUMN
you insert it into.  Insert a null into a number and that is a null NUMBER, not
a varchar2.  Insert a null into a date and that is a null DATE, not a varchar2.

A null either occupies 1 or 0 bytes.  say you have a table:

create table t ( a int, b int, c int, d int );

insert into t values ( 1, null, 1, null );

column B takes 1 byte.  Column d takes 0 bytes.  Trailing null columns take no
storage.

I would insert NULL to insert null.

Rating: 4
April 03, 2002
Reviewer: Pablo Rovedo from Argentina
Tom: Could you explain me this situation:

imagenes@ROP> create view pru_null_v as select null val from dual;

View created.

imagenes@ROP> desc pru_null_v

Name     Null?    Type
------------------------------------------------
VAL               VARCHAR2(0)

imagenes@ROP>

Why the type is varchar2(0)?,in this case the "datatype" of a NULL isn 't
dependent on any COLUMN.

Thanks in Advance
Pablo R.

Followup:
The default type in this case is VARCHAR2, you can use the CAST or
TO_NUMBER/TO_DATE functions to change that.

A null is zero bytes long, so there you go -- varchar2(0)

April 04, 2002 Yogesh -- Thanks for the question regarding "Domain Index ", version 8.1.7
You Asked
This is what I wrote

Hi Tom,

I have created domain index using  following statement

create index idx_news_body on NEWS(body) indextype is ctxsys.context;

This Index always get created in system TBS. I want to shift this index to new
TBS  so I use

alter index idx_news_body rebuild tablespace <new TBS>;

but oracle don't allow this, as well at the time of creation also we can't
specify TBS.

Why this is so ? and if I want to do so what has to be done ?

Plz explain

and you replied

You really might want to stop for a moment and take a second to read the docs
from cover to cover (at least browse them!)

A domain index typically isn't a single segment like a regular index. intermedia

uses a couple of tables AND indexes to store its stuff.  You can control where
they all go in the create index command.

If you read the create index command:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/inter.817/a77063/csql5.htm#19446
you'll find it takes parameters.  One of these parameters is a storage
preference.  If you jump from the create index command to storage preferences:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/inter.817/a77063/cdatadi7.htm#43016
You can see you can tell it where to put the 6 objects it creates for your
index.

NOW .......

According to you advice I have used following to create domain index Elements in
different TBS

begin
ctx_ddl.create_preference('textindex', 'BASIC_STORAGE');
ctx_ddl.set_attribute('textindex', 'I_TABLE_CLAUSE',
                        'tablespace indx storage (initial 1K)');
ctx_ddl.set_attribute('textindex', 'K_TABLE_CLAUSE',
                        'tablespace indx storage (initial 1K)');
ctx_ddl.set_attribute('textindex', 'R_TABLE_CLAUSE',
                        'tablespace indx storage (initial 1K)');
ctx_ddl.set_attribute('textindex', 'N_TABLE_CLAUSE',
                        'tablespace indx storage (initial 1K)');
ctx_ddl.set_attribute('textindex', 'I_INDEX_CLAUSE',
                        'tablespace indx storage (initial 1K)');
end;

create index i1 on Test(c1) indextype is ctxsys.context
  parameters('storage textindex ');

Here  dr$<indexname>$I,dr$<indexname>$K, dr$<indexname>$R,dr$<indexname>$N,
dr$<indexname>$X got created in TBS i.e. INDX which I specified, but the basic
index i.e. I1 still remains in SYSTEM TBS, Where I'm going wrong ? What
parameters should I add or change ?

Plz give some solution, any more info needed ?
and we said...
that is a "fake" index, it's a domain index -- it is NOT REAL.  Look
in user_extents or user_segments instead:

ops$tkyte@ORA817DEV.US.ORACLE.COM> Create table news_test(ID NUMBER, BODY CLOB)
  2  /

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2  ctx_ddl.create_preference('store_clob', 'BASIC_STORAGE');
  3  ctx_ddl.set_attribute('store_clob', 'I_TABLE_CLAUSE', 'tablespace users
storage (initial 1M)');
  4  ctx_ddl.set_attribute('store_clob', 'K_TABLE_CLAUSE', 'tablespace users
storage (initial 1M)');
  5  ctx_ddl.set_attribute('store_clob', 'R_TABLE_CLAUSE', 'tablespace users
storage (initial 1M)');
  6  ctx_ddl.set_attribute('store_clob', 'N_TABLE_CLAUSE', 'tablespace users
storage (initial 1M)');
  7  ctx_ddl.set_attribute('store_clob', 'I_INDEX_CLAUSE', 'tablespace users
storage (initial 1M)');
  8  end;
  9  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> ops$tkyte@ORA817DEV.US.ORACLE.COM> create
index idx_news_body_test on
news_test(body) indextype is ctxsys.context
  2  parameters('storage store_clob')
  3  /

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> ops$tkyte@ORA817DEV.US.ORACLE.COM> select
segment_name, segment_type,
tablespace_name, blocks
  2    from user_extents
  3   order by segment_type, segment_name
  4  /

SEGMENT_NAME                   SEGMENT_TYPE       TABLESPACE     BLOCKS
------------------------------ ------------------ ---------- ----------
DR$IDX_NEWS_BODY_TEST$X        INDEX              USERS              64
DR$IDX_NEWS_BODY_TEST$X        INDEX              USERS              64
SYS_IOT_TOP_28452              INDEX              USERS              64
SYS_IOT_TOP_28452              INDEX              USERS              64
SYS_IOT_TOP_28457              INDEX              USERS              64
SYS_IOT_TOP_28457              INDEX              USERS              64
SYS_IL0000028445C00002$$       LOBINDEX           USERS              64
SYS_IL0000028449C00006$$       LOBINDEX           USERS              64
SYS_IL0000028454C00002$$       LOBINDEX           USERS              64
SYS_LOB0000028445C00002$$      LOBSEGMENT         USERS              64
SYS_LOB0000028449C00006$$      LOBSEGMENT         USERS              64
SYS_LOB0000028454C00002$$      LOBSEGMENT         USERS              64
DR$IDX_NEWS_BODY_TEST$I        TABLE              USERS              64
DR$IDX_NEWS_BODY_TEST$I        TABLE              USERS              64
DR$IDX_NEWS_BODY_TEST$R        TABLE              USERS              64
DR$IDX_NEWS_BODY_TEST$R        TABLE              USERS              64
NEWS_TEST                      TABLE              USERS              64

17 rows selected.

There is NOTHING there!  Don't worry about it.
Review & Followup
Rating: 4
April 04, 2002
Reviewer: Palash from India
Everything as of now is working fine but there is a concern regarding the growth
of table DR$IDX_ of the type "I_TABLE".
This table is growing ata steady pace and might go out of bound very soon.
My question is how to keep the size of this table under control but there shud
not be a drop in performance?
I know I am asking for a lot.

Followup:
what is "out of bounds".

that is the main index table.  It'll get bigger as you add documents of course.
You can compact the index or rebuild it over time to reduce the size of this
table however, it'll grow again (just like an index will).

Rating: 4
domain index in system December 16, 2002
Reviewer: Scott from Australia
G'day

Thanks, that explaination is perfect! I couldn't find this in Metalink anywhere!

April 04, 2002 Sonja -- Thanks for the question regarding "Transforming a table from being broad and shallow to narrow and deep", version 8.0
You Asked
HI!

I struggling with a query on a table that look somewhat like
this:

T_hourly_meter_readings

ID number(8),
METERID varchar2(10),
MEASUREDATE varchar2(20),
VALUE00 number(10),
VALUE01 number(10),
VALUE02 number(10),
VALUE03 number(10),
VALUE04 number(10),
etc up to VALUE24

storing a value that is measured by the hour, all day, every
day, one record for each meter for each day. The MEASUREDATE
field is also not a DATE-field, but a regular text-field.

The customer want this old data to go into a new table that
looks slightly different:

T_hourly_meter_readings2

ID number(8),
METERID varchar2(10),
MEASUREDATE datetime,
VALUE varchar2(10)

so that each record contains information about a measure for
each hour:

ID   METERID    MEASUREDATE    VALUE
1    1001       200012310000   32987,23   (midnight)
2    1001       200012310100   34347,40   (1 o'clock)
3    1001       200021310200   40008,23   (2 o'clock)
etc

which would give them some billion record...

Can you help me with a SQL that could convert the text
MEASUREDATE to datetime MEASUREDATE, take the last datetime for
each date and add 1 hour to it (from 1 to 24 for each date),
take the right VALUE00, VALUE01 etc and then input everything
again into the new table?

I would be most greatful!

Sonja
and we said...
I'll make an assumption here - the ID column in the NEW table is
a new ID (surrogate key) -- it is not the ID from the old
table...

Consider this example then.  All we need is a table with 24 rows
in it representing the hours of the day.  We'll join and decode.
 I did it for 4 hours:

ops$tkyte@ORA8I.WORLD> create table t
  2  ( id number,
  3    meterid varchar2(10),
  4    measuredate varchar2(20),
  5    value00 number,
  6    value01 number,
  7    value02 number,
  8    value03 number
  9  );

Table created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> REM you would use 25, not 5 in the
following:
ops$tkyte@ORA8I.WORLD> create table hours
  2  as
  3  select rownum-1 hh
  4    from all_objects
  5   where rownum < 5;

Table created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> insert into t values
  2  ( 1, 1001, '20001231',      32987.23, 12345.67, 11111,
22222 );

1 row created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> insert into t values
  2  ( 2, 1001, '20001230',      4324.23, 3621643.67, 642542,
523234 );

1 row created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> select rownum,
  2         meterid,
  3         to_date(measuredate,'yyyymmdd')+HH/24 ,
  4         decode( HH, 0, value00,
  5                     1, value01,
  6                     2, value02,
  7                     3, value03
  8                     /* .... rest of columns */ ) value
  9    from T, hours
 10  /

    ROWNUM METERID    TO_DATE(MEASUREDATE,      VALUE
---------- ---------- -------------------- ----------
         1 1001       31-dec-2000 00:00:00   32987.23
         2 1001       30-dec-2000 00:00:00    4324.23
         3 1001       31-dec-2000 01:00:00   12345.67
         4 1001       30-dec-2000 01:00:00 3621643.67
         5 1001       31-dec-2000 02:00:00      11111
         6 1001       30-dec-2000 02:00:00     642542
         7 1001       31-dec-2000 03:00:00      22222
         8 1001       30-dec-2000 03:00:00     523234

8 rows selected.
Review & Followup
Rating: 1
Transforming a table from being broad and shallow to narrow and deep April 04, 2002
Reviewer: Kumar from USA
The question was how to create the table something like this:

ID   METERID    MEASUREDATE    VALUE
1    1001       200012310000   value00, value02...   (midnight)
2    1001       200012310100   value00, value02...   (1 o'clock)
3    1001       200021310200   value05, value08...  (2 o'clock)

Followup:
no, that wasn't the question.  Read it again.  they don't want a comma separated
list of values (perhaps the european 123.456,78 dot/comma notation confused
you).

they got exactly what they asked for.

Rating: 5
Simply Amazing April 04, 2002
Reviewer: Randy Richardson from St. Louis, MO
I use to think that, as a DBA, If I learn one new thing a day, that would be
great.  Your site makes that possible.

Thanks for a very simple solution to what I thought was a complex problem.

April 04, 2002 balraj -- Thanks for the question regarding "Data Storage", version 8.1.6.3
You Asked
Hi Tom

1.Could u please explain actually how the data is stored in the datafile and
when a particular row is requested what all procedure does oracle follow to
retrieve that row(I am not talking about the storage structues like
tablesdpaces,datafiles)...Pl take it in the concept of ROWID(How is it assigned
and how is it used to access the requested rows..(please explain with an
example)

2.How will multiple block sizes in Oracle 9i help me in managing the databse

3.Can i by means know what all SCNs took place in a particular log file.(or all
SCNs between 2 LSNs)

4.Where there is always a difference in result of these queries

select count(*) from v$session
and
select count(*) from v$process

why is this differnce and what does diff consist of assuming that V$process
containing all Server Processes and OS level processes
and we said...
1) the rowid is the physical address of a row.  It has the file/block and slot
on the block embedded in it.  Given a rowid, Oracle will open file X, seek to
block Y and read it -- retrieving row Z.

A rowid is not assigned to a row really, it is a derived attribute of a row that
is known immediately after a row is inserted.  The initial location of the row
dictates its rowid.

2) It was predominantly designed for transporting tablespaces between databases
with differing block sizes.  OLTP applications benefit from smaller block sizes
(we run all of our tpc-c's with 2k blocks).  DSS applications benefit from
larger blocks -- 8, 16, maybe even 32k.  You want to be able to take the data
from your OLTP system and transport it to your DSS system for easy
transformation (denormalization, merging and so on).  This will permit that.

3) Sort of, the first_change#, next_change# in the v$archived_log.  Bear in mind
that log #1 might have data for the transaction that was eventually assigned SCN
100 -- but so might log files 2, 3, 4, ... 50.  A transaction is not contained
in a log file.

4) they could be shared servers and dispatchers, they could be sessions that are
connected physically via a middle tier but have not yet decided to have a
session (connection pooling software would do this).

Use

select * from v$process where not exists ( select null from v$session where
paddr = v$process.addr )

to see what they are -- the program column will shed some light perhaps.
Review & Followup
Rating: 3
more clear July 02, 2001
Reviewer: A reader
The first questions should have been clarified with examples

thanks anyhow

Rating: 3
Tablespaces/Datafiles April 03, 2002
Reviewer: David from USA
I have a follow up question. Should I implement a Tablespace with multiple
datafiles to reduce contention, possibly increasing performance? I currently
have a tablespace made up of three datafiles, Oracle seems to create most
objects in the second datafile. How can I spread the workload more evenly
between the files?

Thanks!

Followup:
We typically do that at the OS level these days.  I just did a benchmark where I
had one mount point -- couple dozen disks under that mount point -- so it really
didn't matter.

We allocate space from the files in a round robin fashion.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create tablespace testing
  2  datafile '/tmp/test1.dbf' size 1m;

Tablespace created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter tablespace testing add datafile
'/tmp/test2.dbf' size 1m;

Tablespace altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter tablespace testing add datafile
'/tmp/test3.dbf' size 1m;

Tablespace altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table test_tab1 ( x int ) tablespace
testing;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table test_tab2 ( x int ) tablespace
testing;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table test_tab3 ( x int ) tablespace
testing;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select segment_name, file_id
  2    from dba_extents
  3   where segment_name like 'TEST_TAB%'
  4     and owner = user;

SEGMENT_NAME                      FILE_ID
------------------------------ ----------
TEST_TAB1                              12
TEST_TAB2                              12
TEST_TAB3                              12

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table test_tab1 allocate extent;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table test_tab2 allocate extent;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table test_tab3 allocate extent;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table test_tab1 allocate extent;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table test_tab2 allocate extent;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table test_tab3 allocate extent;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select segment_name, file_id
  2    from dba_extents
  3   where segment_name like 'TEST_TAB%'
  4     and owner = user;

SEGMENT_NAME                      FILE_ID
------------------------------ ----------
TEST_TAB1                              12
TEST_TAB1                               9
TEST_TAB1                              11
TEST_TAB2                              12
TEST_TAB2                               9
TEST_TAB2                              11
TEST_TAB3                              12
TEST_TAB3                               9
TEST_TAB3                              11

9 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

so, as the tables grow, we'll use all of the files (same is true with LMTs, not
just DMTs as above).

You can use the alter table allocate extent to direct an extent to a particular
file however ..... if you are that down and dirty, its time to get some raid
striping going on and just let it do it.

Rating: 4
What about file #10 April 04, 2002
Reviewer: Logan Palanisamy from Santa Clara, CA, USA
Tom,

Just an academic curiosity.

You created a tablespace with three files. They seem to have file numbers 9, 11
and 12. What about file #10? Did someone create another tablespace between the
time you created "testing" tablespace with the first file, and added the other
two files?

I thought the file numbers are generated sequentially.

Thanks for enlightening us all continuously with your knowledge.

Followup:
Yes, I have files 8 and 10 but not a 9:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select file_id,
  2             lead(file_id) over ( order by file_id ),
  3             lead(file_id) over ( order by file_id ) - file_id skipped
  4    from dba_data_files
  5  order by file_id
  6  /

   FILE_ID LEAD(FILE_ID)OVER(ORDERBYFILE_ID)    SKIPPED
---------- --------------------------------- ----------
         1                                 2          1
         2                                 3          1
         3                                 4          1
         4                                 5          1
         5                                 6          1
         6                                 7          1
         7                                 8          1
         8                                10          2
        10

9 rows selected.

I test in this instance constantly so it's a bit different then "normal"
sometimes.

April 04, 2002 Ajay -- Thanks for the question regarding "Writting DDL_EVENT Triggers", version version 8.1.6.1
You Asked
Do you have a full example of writing a ddl_Even trigger.  I
want to audit on a schema level any creates, alters, or drops of
any objects....and write the actual calling syntax to a table.
Oracle8 has the new feature of DDL_EVENT, and their is an
example in the SQL Reference, but it just shows a pl/sql_block
not how to extract the calling DDL.

If their is not an easier way I would end up querying v$sql_text
and looking for text with some ddl call in it.  I would then
write the text to a seperate table.

Any other ideas or examples would be appreciated.

Thanks
and we said...
There is no way to get the actual DDL (create and drops) firing
the event as far as I know.  Alters, yes.

You will not find the DDL (creates and drops) in the dynamic
performance tables (eg: v$sqlarea, v$open_cursor and so on).
DDL is handled different from SQL DML.

Drops, we don't really need the DDL -- we can get the object
name, owner and the fact we are dropping.

Alters, we can read out of v$open_cursor.

Creates, we can only audit the fact that the object is being
created.  You could read the definition of the object out of the
data dictionary and attempt to recreate the create statement but
its a lot of work for not much payback.

Here would be an example:

tkyte@THINK1.US.ORACLE.COM> create table log
  2  ( operation varchar2(25),
  3    owner     varchar2(25),
  4    name      varchar2(25),
  5    extra     varchar2(4000) );
Table created.

tkyte@THINK1.US.ORACLE.COM> create or replace trigger
ddl_trigger
  2  before create or alter or drop on SCHEMA
  3  declare
  4      l_sysevent varchar2(25);
  5  begin
  6      select ora_sysevent into l_sysevent from dual;
  7
  8      if ( l_sysevent in ('DROP','CREATE') )
  9      then
 10          insert into log
 11          select ora_sysevent, ora_dict_obj_owner,
 12                         ora_dict_obj_name, null
 13            from dual;
 14      elsif ( l_sysevent = 'ALTER' )
 15      then
 16          insert into log
 17          select ora_sysevent, ora_dict_obj_owner,
 18                         ora_dict_obj_name, sql_text
 19            from v$open_cursor
 20           where upper(sql_text) like 'ALTER%' ;
 21      end if;
 22  end;
 23  /
Trigger created.

tkyte@THINK1.US.ORACLE.COM> drop table t;
Table dropped.

tkyte@THINK1.US.ORACLE.COM> create table t ( x int );
Table created.

tkyte@THINK1.US.ORACLE.COM> alter table t add y int;
Table altered.

tkyte@THINK1.US.ORACLE.COM> select * from log;

OPERATION  OWNER NAME  EXTRA
---------- ----- ----- --------------------
DROP       TKYTE T
CREATE     TKYTE T
ALTER      TKYTE T     alter table t add y int

There are many pieces of information you may retrieve, such as
the ip address of the user doing the operation, the name of the
object the DDL is affecting, the type of object being affect,
the list of grantees being granted some privelege, and so on.

For a full list for 8.1.6 see
http://technet.oracle.com/doc/oracle8i_816/appdev.816/a76939/adg14evt.htm#1000872
...
Review & Followup
Rating: 5
Very good, thanks March 22, 2002
Reviewer: A reader from Switzerland
Never seen this system events before.

Rating: 1
URGENT HELP:Strange problem.... April 04, 2002
Reviewer: Neeti from TX USA
Hi Tom,

We have a production issue.I was trying to write a database
trigger and tested it fine. This trigger logs the ALL DDL
operation performed on a prticular schema.To keep the
activity transparent from users I wrote a package and called
it inside the trigger.I wrote a wrapper for the package.

It was working fine then I created a copy of the trigger and
by mistake compiled the copy in the base.

Now I have two trigger trigger1 and trigger2 in the base doing the same thing
and somehow the package got invalidated.

As the trigger is at the database level and both do the same
thing so whenever I try to create any object in database under any schema then
one or the other trigger is failing due to invalidation.

It's kind of mutation and I am getting the following error..
HONDEV>create table t(t number);
create table t(t number)
*
ERROR at line 1:
ORA-04045: errors during recompilation/revalidation of
VUPRE.DBUPDATE_DDL_AUDIT_TRIGGER
ORA-01031: insufficient privileges

It's not even allowing me to drop either of the trigger or any object because
the other trigger is invalid.

Please help its urgent....please please please....

Followup:
well -- no version, not much info.

Try this:

connect internal

or in 9i

connect user/pass as sysdba;

drop trigger vupre.DBUPDATE_DDL_AUDIT_TRIGGER;

without further info -- I cannot really give you much more.  There is a hidden _
parameter (contact support) that disables these triggers alltogether as well.

Rating: 1
Sorry for not sending you full info... April 04, 2002
Reviewer: Neeti from TX USA
Well we are oracle Oracle8i Enterprise Edition Release 8.1.6.2.0 and running on
Solaris. I panicked because
the trigger is not allowing anyone to create anything.

The two triggers mutate if I say

DROP TRIGGER A;

Then trigger B fails with insufficient previleges
and if I try to drop trigger B then trigger A fails
with insufficient priveleges.

I have DBA priveleges. Is there a way that I can prevent
user with alter any trigger privileges to disable this trigger.

Followup:
get internal.  do it with that.

April 05, 2002 Atul -- Thanks for the question regarding "Differnece between Rel2 and Rel3", version 8.1.7
You Asked
Hi Tom,

This is my first opportunity to ask you a question.

I have searched at many places including metalink but have not been able to find
differences between 8i Rel2 (8.1.6) and 8i Rel 3 (8.1.7). All the manuals
available on 1. Documentation CD 2. TechNet 3.Metalink 4. Oracle Store only
mentions about 8.1.6. I even opened a TAR to ask the same question but was only
told that there are not much differences. Now I know there are many differences,
can you please either point out the differences or suggest/refer/point
1. a URL
2. A PDF etc
that will clearly state the difference.

You are one of my last hopes. Any replies on this will be highly appreaicated.

Thanks in advance...
... Atul
and we said...
See
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/addendum.817/a85455/toc.htm
I went to otn -> documentation -> 8i docs -> 8i doc addendum

it's all there.
April 05, 2002 Famy -- Thanks for the question regarding "number formatting and how to display the primary key constraint name, primary key columns and also the foreign key names and columns.", version 8.1.6
You Asked
Tom

1.I have a varchar2 column which can have output like

$2,500.00 or
$25,654.00 or
$2,23,345.00

this can go up to billions.

Now this is a varchar2 column, I want to convert it to a number, and format it
with the format 999,999 , there should be no  .00.

When Iam trying to do that Iam getting a problem due to the $ sign and also the
commas.

Can you help me in this regard.

2.For a given table , I want to display the primary key constraint name, primary
key columns and also the foreign key names  and columns ( and the tables they
come from) relating to the
respective primary keys. Can you help me in showing how to retrieve this data.

Thank you
and we said...
ops$tkyte@ORA9I.WORLD> select x,
  2             to_number( x, '$999,999,999,999.99' ) x_num,
  3             to_char(to_number( x, '$999,999,999,999.99' ),'999,999,999')
x_num
  4    from t;

X                         X_NUM X_NUM
-------------------- ---------- ------------
$2,500.00                  2500        2,500
$25,654.00                25654       25,654
$2,321,345.00           2321345    2,321,345

Either or of the following will do it (add more max(decodes as needed)

ops$tkyte@ORA9I.WORLD> create table par ( x int, y int, constraint p_pk primary
key(x,y) );

Table created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> create table c1 ( a int, b int, constraint c1_fk foreign
key(a,b) references par );

Table created.

ops$tkyte@ORA9I.WORLD> create table c2 ( l int, m int, constraint c2_fk foreign
key(l,m) references par );

Table created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> select a.constraint_name, decode( a.constraint_type, 'P',
'primary', 'R', 'foreign' ), b.column_name
  2    from user_constraints a, user_cons_columns b
  3   where
  4     (( a.constraint_type = 'P' and a.table_name = 'PAR' )
  5      or
  6      ( a.constraint_type = 'R' and a.r_constraint_name  =
  7                     ( select constraint_name
  8                         from user_constraints
  9                        where constraint_type = 'P'
 10                          and table_name = 'PAR' )
 11      ) )
 12     and a.constraint_name = b.constraint_name
 13   order by a.constraint_type, a.constraint_name, b.position
 14  /

CONSTRAINT_NAME                DECODE( COLUMN_NAM
------------------------------ ------- ----------
P_PK                           primary X
P_PK                           primary Y
C1_FK                          foreign A
C1_FK                          foreign B
C2_FK                          foreign L
C2_FK                          foreign M

6 rows selected.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> select constraint_name, constraint_type,
  2         rtrim(
  3         max(decode(position,1,constraint_name,null)) || ','||
  4         max(decode(position,2,constraint_name,null)) || ','||
  5         max(decode(position,3,constraint_name,null)) || ','||
  6         max(decode(position,4,constraint_name,null)) || ','||
  7         max(decode(position,5,constraint_name,null)), ',' ) columns
  8    from (
  9  select a.constraint_name,
 10         a.constraint_type,
 11         b.column_name,
 12         b.position
 13    from user_constraints a, user_cons_columns b
 14   where
 15     (( a.constraint_type = 'P' and a.table_name = 'PAR' )
 16      or
 17      ( a.constraint_type = 'R' and a.r_constraint_name  =
 18                      ( select constraint_name
 19                          from user_constraints
 20                         where constraint_type = 'P'
 21                           and table_name = 'PAR' )
 22      ) )
 23     and a.constraint_name = b.constraint_name
 24     )
 25  group by constraint_name, constraint_type
 26  order by 2, 1
 27  /

CONSTRAINT_NAME                C COLUMNS
------------------------------ - ------------------------------
P_PK                           P P_PK,P_PK
C1_FK                          R C1_FK,C1_FK
C2_FK                          R C2_FK,C2_FK

ops$tkyte@ORA9I.WORLD>
Review & Followup
Rating: 5
Well... the transposing you are doing... April 04, 2002
Reviewer: Famy
in the second query will be more beneficial if you use the column_name, instead
of constraint_name.. just think about it

in your case the same constraint name is printed multiple number of times. where
as if we us the column_name , we can see the different column names in that
specific foreign key...

what do you say..

my two cents

Followup:
read it again - thats what I say.

The "second" query (assuming you mean the first query of the constraint
question) shows the constraint name (important and needed) as well as the column
name (equally important and relevant).

The "third" query shows the same data as the first, just in a different format.
they both show the same AMOUNT and type of information.

Rating: 5
Was working on something similar ;) April 04, 2002
Reviewer: Yogeeraj from Mauritius
Hi,

oops! Should it not have been (column_name instead of constraint name):
===========================================================
select constraint_name, constraint_type,
    rtrim(
    max(decode(position,1,column_name,null)) || ','||
    max(decode(position,2,column_name,null)) || ','||
    max(decode(position,3,column_name,null)) || ','||
    max(decode(position,4,column_name,null)) || ','||
    max(decode(position,5,column_name,null)), ',' ) columns
===========================================================

I tried the same query as a user with dba roles. It just hangs!! why?

When i replace user_xxx with dba_xxx and add the OWNER clause, it works fine!!

===========================================================
swprod@CMTDB.CMT.MU> r
  1  select constraint_name, constraint_type,
  2      substr(rtrim(
  3      max(decode(position,1,column_name,null)) || ','||
  4      max(decode(position,2,column_name,null)) || ','||
  5      max(decode(position,3,column_name,null)) || ','||
  6      max(decode(position,4,column_name,null)) || ','||
  7      max(decode(position,5,column_name,null)), ',' ),1,45) columns
  8     from (
  9   select a.constraint_name,
 10          a.constraint_type,
 11          b.column_name,
 12          b.position
 13     from dba_constraints a, dba_cons_columns b
 14    where
 15       (( a.constraint_type = 'P' and a.table_name = 'KNT100' )
 16       or
 17        ( a.constraint_type = 'R' and a.r_constraint_name  =
 18                       ( select constraint_name
 19                           from dba_constraints
 20                          where constraint_type = 'P'
 21                            and table_name = 'KNT100'
 22                       and owner='SWPROD')
 23       ) )
 24      and a.constraint_name = b.constraint_name
 25      and a.owner='SWPROD'
 26      and b.owner='SWPROD'
 27      )
 28  group by constraint_name, constraint_type
 29* order by 2, 1

CONSTRAINT_NAME                C COLUMNS
______________________________ _ _____________________________________________
PK_KNT100                      P ORDNUM,FABSEQ,KMCHCOD,SEQNUM
FK_KNT120_1                    R ORDNUM,FABSEQ,KMCHCOD,SEQNUM

Elapsed: 00:00:00.48
swprod@CMTDB.CMT.MU>

===========================================================

Thanks for your time.

best regards
Yogeeraj
PS. Will your next book include something about BACKUP and Recovery? How to
migrate to Oracle's Backup and Recovery Manager for Database backup - "Expert
one-on-one Oracle" (Page 340)? (the gist of "Oracle 8i Operating System Backup
and Recovery Guide ;) ) The way you present your material is simple and clear!!
(30mb/30MB/30mB/30Mb or K,k - scientist look at it differently ;))

Followup:
It does not just hang -- its that without the "and owner = " on DBA_* views, the
query is going to be somewhat inefficient.

The "primary key" of user_constraints is constraint_name
The "primary key" of dba_constraints is constraint_name, owner

When you changed views, in order to not only get the answer fast but in fact to
get the RIGHT ANSWER you have to put OWNER on all references.

I'm not planning on a backup and recovery book -- looking at a performance
related one :)

Rating: 5
Much better....... April 05, 2002
Reviewer: Yogeeraj from Mauritius
hi again,

Much better now:
============================================================
swprod@CMTDB.CMT.MU> r
  1  select substr(constraint_name,1,15) "Constrait Name", constraint_type,
  2      substr(rtrim(
  3      max(decode(position,1,column_name,null)) || ','||
  4      max(decode(position,2,column_name,null)) || ','||
  5      max(decode(position,3,column_name,null)) || ','||
  6      max(decode(position,4,column_name,null)) || ','||
  7      max(decode(position,5,column_name,null)), ',' ),1,45) columns, substr(r
_constraint_name,1,15) "References"
  8     from (
  9   select a.constraint_name,
 10          a.constraint_type,
 11          b.column_name,
 12          b.position,
 13     a.r_constraint_name
 14     from dba_constraints a, dba_cons_columns b
 15    where
 16       (( a.constraint_type = 'P' and a.table_name = 'KNT100' )
 17       or
 18        ( a.constraint_type = 'R' and a.r_constraint_name  =
 19                       ( select constraint_name
 20                           from dba_constraints
 21                          where constraint_type = 'P'
 22                            and table_name = 'KNT100'
 23                       and owner='SWPROD')
 24       ) )
 25      and a.constraint_name = b.constraint_name
 26      and a.owner='SWPROD'
 27      and b.owner='SWPROD'
 28      )
 29  group by constraint_name, constraint_type, r_constraint_name
 30* order by 2, 1

Constrait Name  C COLUMNS                                       References
_______________ _ _____________________________________________ _______________
PK_KNT100       P ORDNUM,FABSEQ,KMCHCOD,SEQNUM
FK_KNT120_1     R ORDNUM,FABSEQ,KMCHCOD,SEQNUM                  PK_KNT100

Elapsed: 00:00:00.86
swprod@CMTDB.CMT.MU>
============================================================
Cheers
Yogeeraj

Rating: 5
These queries are very useful April 05, 2002
Reviewer: Kulguru
Tom

If I want to put these scripts into a sql script, how should I add the set
conditions along with the script, so that whenever I execute the script those
settings take effect. Can you show an example with one of the above queries.

Further, the above (last two) queries are awesome, and will give quick details
as far as constraints are concerned. Tom, is it possible to find out what is the
kind of relationship between 2 given tables, i.e. 1 to 1 or 1 to many or many to
one , or many to many.

Do the above scripts help in that aspect in any way, or do you have another sql
to do this kind thing, or should it be done using pl/sql.

Can you kindly show us the way, in this regard.

Thank you

Followup:
Just change 'PAR' to '&1'

Then put the sql into a script and run it as:

@scriptname PAR

and that would do the same -- &1, &2, ... those are substition values passed to
the script.

If the relationship is 1:1 -- there should be ONE table (IMHO).  But anyway, you
would have to see if the foreign key columns are also involved in a Unique or
Primary key constraint on that table -- so yes ,you can find 1-1 if you write a
tricky enough query.  1:M -- that should be assumed.  M:M -- you'll NEVER see
that as that takes three tables and two 1:M relationships -- there is no such
thing as a M:M relationship really....

You can write the 1:1 vs 1:M in a sql query using subtle modifications of the
above (a challenge for the reader).  But as I said -- its probably just a waste
of time as ALL of them should be 1:M.

Rating: 5
..everything in a script April 05, 2002
Reviewer: Yogeeraj from Mauritius
hi

hope this helps..

Regards
Yogeeraj
============================================================
--check_pffkcons.sql
--YD-05/04/2002
SET VERIFY OFF;
SET DEFINE ON;
define MOWNER = '&MOWNER'
define MTABLE = '&MTABLE'
select substr(constraint_name,1,15) "Constrait Name", constraint_type,
     substr(rtrim(
     max(decode(position,1,column_name,null)) || ','||
     max(decode(position,2,column_name,null)) || ','||
     max(decode(position,3,column_name,null)) || ','||
     max(decode(position,4,column_name,null)) || ','||
     max(decode(position,5,column_name,null)), ',' ),1,45) columns,
substr(r_constraint_name,1,10) "References", substr(status,1,3) Sts
    from (
  select a.constraint_name,
         a.constraint_type,
         b.column_name,
         b.position,
    a.r_constraint_name,
    a.status
    from dba_constraints a, dba_cons_columns b
   where
      (( a.constraint_type = 'P' and a.table_name = '&&MTABLE' )
      or
       ( a.constraint_type = 'R' and a.r_constraint_name  =
                      ( select constraint_name
                          from dba_constraints
                         where constraint_type = 'P'
                           and table_name = '&&MTABLE'
                      and owner= '&&MOWNER')
      ) )
     and a.constraint_name = b.constraint_name
     and a.owner='&&MOWNER'
     and b.owner='&&MOWNER'
     )
 group by constraint_name, constraint_type, r_constraint_name, status
order by 2, 1;
SET DEFINE OFF;
============================================================

Rating: 5
Tom April 05, 2002
Reviewer: Famy
"
If the relationship is 1:1 -- there should be ONE table (IMHO).  But anyway, you
would have to see if the foreign key columns are also involved in a Unique or
Primary key constraint on that table -- so yes ,you can find 1-1 if you write a
tricky enough query.  1:M -- that should be assumed."

Can you gift us a utility which does the above.

From the above what do you mean by '1:M -- that should be assumed.'
Thank you

Followup:
Ah, come on... Can't you take up the challenge?

...
You can write the 1:1 vs 1:M in a sql query using subtle modifications of the
above (a challenge for the reader).  But as I said -- its probably just a
waste of time as ALL of them should be 1:M.
.......

;)

When I said 1:M should be assumed is that

a) M:M -- doesn't exist, we can rule that out
b) 1:1 -- shouldn't exist IMO, I rule that out
c) 1:M -- thats all that is left over!

I know I can do it -- just wanna have others think about it (SQL queries like
that can be fun sometimes!)

April 05, 2002 Beth -- Thanks for the question regarding "Can shared pool be *too* large?", version 8.1.6
You Asked
Is there a downside to having a very large shared pool?

We have a box with 4GB of memory almost completely devoted to 3 Oracle
databases.  We have quite a small shared_pool_size (100MB for one db and smaller
for the rest).

Is there a downside to allocating 1GB or so to the shared_pool in each database
for instance, since the box not being used for other applications?
and we said...
YES.  definitely.

Especially if you don't make use of bind variables.
The shared pool works best when right sized to hold what you do.  A common
"myth" (conventional wisdom) is that if you are getting ora-4031's due to not
using bind variables you must immediately increase the size of the shared pool

What that does is this:

o delays the problem a little while.
o makes the problem even WORSE when it does happen.

The larger the shared pool, the harder to manage and page the stuff in and out.
When not using bind variables, you litterally flood the shared pool with
statements that will never be reused --sending us into a frenzy of "load and age
and flush".  The bigger the shared pool -- the longer it takes to do this -- the
more you wait (the system seems to sloooowwww  way down) and then eventually, it
falls apart.

Take that same system, decrease the shared pool way down -- and there you go,
it'll managed itself in a timely fashion.

I've seen sites that didn't (refused) use bind variables in OLTP.  They actually
had a guy who would sit there and watch a quest screen all day waiting for their
2gig (yes 2gig) shared pool to get 50% utilized.  He would then alter system
flush shared pool.  That was his job.

I begged them to test a 75m shared pool -- when they finally did, guess what
happened.... (rhetorical question, you should be able to figure it out by now)

The real fix for them -- use BINDS, re-assign the guy who was paid to flush the
shared pool (heck, they didn't even script that...)

If you ask me, 100mb is NOT small, it's about right.
Review & Followup
Rating: 5
April 05, 2002
Reviewer: MaxU from Munich
Tom, thank you for clear answer!

Tom, should one use bind variables even though he/she has constants, for
example:

'select * from bla_table where level=1 and name=:nm'

if application uses such code and it's hardcoded, should we use bind variable
for column "level" too?

Thank you!

Followup:
Nope, you use bind variables on things that CHANGE from run to run of the query.

If you have my book -- I actually cover this topic in "why I don't like
cursor_sharing = force".  It over binds -- sometimes you DON'T want to bind
everything.

Only bind things that change from execution to execution.

Rating: 5
Thanks !!! April 05, 2002
Reviewer: MaxU from Munich

Rating: 4
Does this apply to MTS? May 07, 2003
Reviewer: Fred from irvine, ca
Does your recommendation about reducing size apply to MTS?

Rating: 4
September 17, 2003
Reviewer: Jean Zhou from Columbus, OH USA
I forward this page to developement managers so that they will understand the
importance of using bind variables.

April 05, 2002 Lee -- Thanks for the question regarding "Find out value of bind variables for a dynamic plsql", version 8.1.7
You Asked
Hello Tom,
I have a simple question.
How do I find out what is the current value of a bind variable used in a dynamic
plsql?

Example:

    EXECUTE IMMEDIATE 'SELECT * FROM EMP WHERE ENAME = :V_NAME' USING
V_SOME_VARIABLE;

Using an sql query, how can I see the value of V_NAME?

Thanks.
and we said...
You cannot -- in the current session there is v$sql_biEXECUTE IMMEDIATE 'SELECT
* FROM EMP WHERE ENAME = :V_NAME' USING V_SOME_VARIABLE;nd_data but the bind
data is only visible in the current session.

What I like to do in cases like this is:
  dbms_application_info.set_client_info( v_some_variable );
  EXECUTE IMMEDIATE 'SELECT * FROM EMP WHERE ENAME = :V_NAME'
      USING V_SOME_VARIABLE;

you have 3 columns in v$session you can set with dbms_application_info and there
is an interface to the v$session_longops table as well in that package.  Very
very useful!

Using that, you can see the SQL someone is executing and any useful information
they wish to share with you.
Review & Followup
Rating: 5
Thanks Tom April 05, 2002
Reviewer: Lee from NY USA
Very good idea, thank you.

April 05, 2002 Geeta -- Thanks for the question regarding "Performance", version 8.1.6
You Asked
We are working in a DataWarehouse environment and the two databases ( TEST &
Production) are identical - about 32 GB RAM ( 12 GB SGA etc) - overkill in my
opinion but it is how it is. Recently the TEST database started going extremely
slow - A simle query like select tablespace_name, .... from dba_free_space would
take 7-8 minutes to run while the same would come back instantaneously on the
Prodcution Box. After sitting with the DBA and reading lots of stuff in this
forum and elsewhere we found that the fet$ table on the TEST box had more than
114,000 rows while that on the production box had only 1900. Why should this
happen.One reason I can think of is that we are not using Locally managed
tablespaces - Am I right? Can we all tablespaces Locally managesd or are there
some restrictions?
We coalesced the tablespaces and things are looking better but it is still
slower than the production box. If the number of extents in an object is not a
performance issue then why a large number of records in the FET$ table is an
issue. I am still trying to learn Oracle and find this site a great help
(because it makes things so simple with examples).

Thanks
and we said...
Your a datawarehouse

Using 8i

and your not using locally managed tablespaces!!!

In test, you created tons of stuff and dropped tons of stuff.  Hence you have
tons of fet$ rows.

My 9i database has 2 such rows!  for system.  everything else is LMT.

You should use LMTs for everything.... Everything.

The number of extents in an object won't affect the objects with lots of extents
but you have to remember:

o the DBA_* is views.  Many times views of views of views with joins and group
by and so on

o selecting from a single DBA_* view should go pretty good

o start joining/aggregating them and all bets are off

o the database doesn't use the DBA_* views, it goes straight against fet$ and
such when necessary.  It is not impacted by the "magic" of the views

Just use LMTS, drop the DMTS (don't migrate them -- rebuild over time).  Use
uniform allocations and lose ALL storage parameters like initial, next and
pctincrease on your creates.
April 05, 2002 rd -- Thanks for the question regarding "Multiple index queries, max number of indexes", version 8.1.5
You Asked
When  a  query  has  two  or  more   equality
predicate  clauses, multiple indexes may be  used.Oracle  will  merge  the
indexes  at  run   time, returning  rows  that  are in  both  indexes,  for

example.
                    SELECT    ename
                    FROM      emp
                    WHERE     deptno=20
                    AND       job='manager'

                    Will merge the following indexes.

                    non unique index on job
                    non unique index on deptno

How are indexes merged internally by oracle. Which is more economical, a
concatanated index on job and deptno , or separate indexes on job and deptno ,
leading to merging of indexes.

Is it true that oracle uses only 5 indexes for a particular query?
what is the max number of indexes we can have on a table?
Can we have indexes on views?

Tom, without giving a link, can you illustrate for me in a layman's language as
to what a latch is, and what will happen if there are no latches provided by
oracle. can you make me understand what a latch is with simple illustratrated
examples.
and we said...
indexes are merged as you think they would be.  deptno=20 will generate a list
of candidate rowids -- job='manager' another.  These lists will be merged
together and only those rowids that are in both will actually be retrieved from
the table.

As for which is more economical -- it (as always) depends.  There are cases for
both.  In the above example, a concatenated index on deptno, job would probably
be best (least work).  However suppose EMP has 50 columns and you could put a
predicate on any TWO of them?  you want to have every combination (i don't).

If you are dealing with bitmap indexes, it almost never makes sense to have a
concatenated index -- you almost always index invidual columns.

As for "5", we will use as many indexes as we see fit, for example:

ops$tkyte@ORA817.US.ORACLE.COM> create table t ( a int, b int, c int, d int, e
int, f int, g int, h int, i int );

Table created.

ops$tkyte@ORA817.US.ORACLE.COM>
ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_a on t(a);

Index created.

ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_b on t(b);

Index created.

ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_c on t(c);

Index created.

ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_d on t(d);

Index created.

ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_e on t(e);

Index created.

ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_f on t(f);

Index created.

ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_g on t(g);

Index created.

ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_h on t(h);

Index created.

ops$tkyte@ORA817.US.ORACLE.COM> create bitmap index t_i on t(i);

Index created.

ops$tkyte@ORA817.US.ORACLE.COM>
ops$tkyte@ORA817.US.ORACLE.COM>
ops$tkyte@ORA817.US.ORACLE.COM> exec dbms_stats.set_table_stats( user, 'T',
numrows=>10000000, numblks => 10000000 );

PL/SQL procedure successfully completed.

ops$tkyte@ORA817.US.ORACLE.COM>
ops$tkyte@ORA817.US.ORACLE.COM> set autotrace on explain
ops$tkyte@ORA817.US.ORACLE.COM>
ops$tkyte@ORA817.US.ORACLE.COM> select count(*)
  2    from t
  3   where a = 1
  4     and b = 2
  5     and c = 3
  6     and d = 4
  7     and e = 5
  8     and f = 6
  9     and g = 7
 10     and h = 8
 11     and i = 9
 12  /

  COUNT(*)
----------
         0

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=10 Card=1 Bytes=100)
   1    0   SORT (AGGREGATE)
   2    1     BITMAP CONVERSION (COUNT)
   3    2       BITMAP AND
   4    3         BITMAP INDEX (SINGLE VALUE) OF 'T_A'
   5    3         BITMAP INDEX (SINGLE VALUE) OF 'T_B'
   6    3         BITMAP INDEX (SINGLE VALUE) OF 'T_C'
   7    3         BITMAP INDEX (SINGLE VALUE) OF 'T_D'
   8    3         BITMAP INDEX (SINGLE VALUE) OF 'T_E'
   9    3         BITMAP INDEX (SINGLE VALUE) OF 'T_F'
  10    3         BITMAP INDEX (SINGLE VALUE) OF 'T_G'
  11    3         BITMAP INDEX (SINGLE VALUE) OF 'T_H'
  12    3         BITMAP INDEX (SINGLE VALUE) OF 'T_I'

ops$tkyte@ORA817.US.ORACLE.COM> set autotrace off

Even if I used conventional bitmap indexes, we can join them all together:

ps$tkyte@ORA817.US.ORACLE.COM> select /*+ index_join( t ) */ count(*)
  2    from t
  3   where a = 1
  4     and b = 2
  5     and c = 3
  6     and d = 4
  7     and e = 5
  8     and f = 6
  9     and g = 7
 10     and h = 8
 11     and i = 9
 12  /

  COUNT(*)
----------
         0

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=32 Card=1 Bytes=100)
   1    0   SORT (AGGREGATE)
   2    1     VIEW OF 'index$_join$_001' (Cost=32 Card=1 Bytes=100)
   3    2       HASH JOIN
   4    3         HASH JOIN
   5    4           HASH JOIN
   6    5             HASH JOIN
   7    6               HASH JOIN
   8    7                 HASH JOIN
   9    8                   HASH JOIN
  10    9                     HASH JOIN
  11   10                       INDEX (RANGE SCAN) OF 'T_A' (NON-UNIQUE)
(Cost=11 Card=1 Bytes=100)
  12   10                       INDEX (RANGE SCAN) OF 'T_B' (NON-UNIQUE)
(Cost=11 Card=1 Bytes=100)
  13    9                     INDEX (RANGE SCAN) OF 'T_C' (NON-UNIQUE) (Cost=11
Card=1 Bytes=100)
  14    8                   INDEX (RANGE SCAN) OF 'T_D' (NON-UNIQUE) (Cost=11
Card=1 Bytes=100)
  15    7                 INDEX (RANGE SCAN) OF 'T_E' (NON-UNIQUE) (Cost=11
Card=1 Bytes=100)
  16    6               INDEX (RANGE SCAN) OF 'T_F' (NON-UNIQUE) (Cost=11 Card=1
Bytes=100)
  17    5             INDEX (RANGE SCAN) OF 'T_G' (NON-UNIQUE) (Cost=11 Card=1
Bytes=100)
  18    4           INDEX (RANGE SCAN) OF 'T_H' (NON-UNIQUE) (Cost=11 Card=1
Bytes=100)
  19    3         INDEX (RANGE SCAN) OF 'T_I' (NON-UNIQUE) (Cost=11 Card=1
Bytes=100)

Here is a very tiny extract from my book on latches (you'll have to get the book
to get the full story)

Latches and Internal Locks (Enqueues)

Latches and enqueues are lightweight serialization devices used to coordinate
multi-user access to shared data structures, objects and files. Latches are
locks which are held for very small periods of time – extremely small periods,
for example the time it takes to modify an in memory data structure. They are
used to protect certain memory structures, such as the database block buffer
cache or the library cache in the shared pool, as described previously in
Chapter 2. Latches are typically requested internally in a 'willing to wait'
mode. This means that if the latch is not available, the requesting session will
sleep for a short period of time and retry the operation later. Other latches
may be requested in an immediate mode, meaning that the process will go do
something else, rather than spin and wait for the latch to become available.
Since many requestors may be waiting for a latch at the same time, you may see
some processes waiting longer than others. Latches are assigned rather randomly,
based on the luck of the 'draw' if you will. Whichever session asks for a latch
right after it was released will get it. There is no line of latch waiters, just
a 'mob' of waiters constantly retrying.

Oracle uses atomic instructions like 'test and set' for operating on latches.
Since the instructions to set and free latches are atomic, the operating system
itself guarantees that only one process gets it. Since it is only one
instruction, it can be quite fast. Latches are held for short periods of time
and provide a mechanism for clean up in case a holder dies abnormally while
holding it. This cleaning up process would be performed by PMON.

.....
Review & Followup
Rating: 2
Multiple index queries, max number of indexes March 20, 2002
Reviewer: Ramesh Golkonda from Edinburgh, U.K
Tom,

My question is realted to multiple single column indexes.  We have got two
tables and indexes on those tables are as follows :

Table  T1
Index  REQST_N4
Index  REQST_N4

Table T2 :
Index : EVENT_I01

Data on the index REQST_N4 IS SKEWED IN THE FOLLOWING MANNER :
  TYPE = 100    (contains nearly 150,000 rows)
  TYPE = 102    (contains nearly 200,000 rows)
  TYPE = 103    (contains nearly 95,000 rows)
  TYPE = 104    (contains nearly 170,000 rows)

Data on REQST_N5 IS SKEWED IN THE FOLLOWING MANNER :
  ID = Each ID contains 15 rows

T1.ID and T1.TYPE ARE BIND VARIABLES (i.e we input these values each time we run
the query)

Select event_id
from  T1, T2
where t1.reqtype = 100  -- (index on t1.reqtype is REQST_N4)
and   t1.id = 5         -- (index on t1.reqtype is REQST_N5)
and   t2.reqid = t1.reqid
and   t2.typeid = 'AE'
Order by t2.edate

Produces following plan :

ID PARENT_ID  POSITION Query Plan
-- --------- --------- ------------------------------------------
 0                     SELECT STATEMENT   Cost =
 1         0         1   SORT ORDER BY
 2         1         1     NESTED LOOPS
 3         2         1       TABLE ACCESS BY INDEX ROWID T1
 4         3         1         AND-EQUAL
 5         4         1           INDEX RANGE SCAN REQST_N4
 6         4         2           INDEX RANGE SCAN REQST_N5
 7         2         2       TABLE ACCESS BY INDEX ROWID T2
 8         7         1         INDEX RANGE SCAN EVENT_I01

Questions :
1) Which index should I be hitting REQST_N4 OR REQST_N5 ?
2) Does the order matters ?

Thanks
RameshG

Followup:
Well, I don't see a REQST_N5 but....

REQST_N4 should be DROPPED.  It is a b*tree index on 615,000 rows that have 4
discrete values.  It is useless.

You might have a concatenated index on t1(id,reqtype) but a b*tree index on
t1(reqtype) is worthless.

Rating: 2
Mutilple indexes March 21, 2002
Reviewer: Ramesh from Edinburgh, U.K
Apologies for the mistake.

Select event_id
from  T1, T2
where t1.reqtype = 100  -- (index on t1.reqtype is REQST_N4)
and   t1.id = 5         -- (index on t1.id is REQST_N5)
and   t2.reqid = t1.reqid
and   t2.typeid = 'AE'
Order by t2.edate
----

That's what I had thought it is useless. We are are using RULE BASED optimizer
and if we had used cost-based the index REQST_N4 would not have appeared.  Funny
thing is when I turn this index off we are getting the results in the same time
as it was with this index,  this won't happen unless oracle uses bit different
algorithm than what you mentioned.  I need to give more trouble, let's assume it
is a useless index why am I getting results instantly ?

Thanks
RameshG

Followup:
why wouldn't it happen?  ANYTHING can happen.  use sql_trace+tkprof to analyze
the differences in more detail.  without something like that, I cannot really
comment.  You give me NOTHING to compare to.

Rating: 3
Multiple indexes April 05, 2002
Reviewer: Kiran from Cincinnati, OH USA
Tom,
I am struggling with the multiple indexes concept using Index Join.I have two
b-tree indexes on the table and I want to fetch the data only by joining these
two indexes with out scanning the table.These two indexes have all the columns
in my query.I tried using the hint /*+ INDEX_JOIN(TAB1 IND1 IND2) */ but It
didn't work.Could you explain how to use the Index Join on the B-tree index.What
are the INIT.ora parameters we have to set to enable this feature.

Thanks a lot,
Kiran

Followup:
Examples -- one always needs an example of what one has tried in order to
explain why something did or didn't happen.

Could be a multitude of reasons.  Most probable include:

o you tested without stats
o you tested on too small a set of data
o you have the indexes on nullable columns and included no predicates
o full scanning was BETTER then index joining and we ignored as (they are hints,
not directives!)

ops$tkyte@ORA817DEV.US.ORACLE.COM> @desc big_table
Datatypes for Table big_table

                               Data                 Data
Column Name                    Type                 Length      Nullable
------------------------------ -------------------- ----------- --------
OWNER                          VARCHAR2             30          not null
OBJECT_NAME                    VARCHAR2             30          not null
SUBOBJECT_NAME                 VARCHAR2             30          null
OBJECT_ID                      NUMBER                           not null
DATA_OBJECT_ID                 NUMBER                           null
OBJECT_TYPE                    VARCHAR2             18          null
CREATED                        DATE                 7           not null
LAST_DDL_TIME                  DATE                 7           not null
TIMESTAMP                      VARCHAR2             19          null
STATUS                         VARCHAR2             7           null
TEMPORARY                      VARCHAR2             1           null
GENERATED                      VARCHAR2             1           null
SECONDARY                      VARCHAR2             1           null

Indexes on big_table

Index                          Is
Name                           Unique COLUMNS
------------------------------ ------ --------------------------------
BIG_TABLE_IDX1                 No     OBJECT_NAME
BIG_TABLE_IDX2                 No     OBJECT_ID

Now, I have no problems with this 1,000,000 row table getting an index join
going:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select /*+ index_join( big_table
big_table_idx1 big_table_idx2 ) */ object_name, object_id
  2  from big_table
  3  where object_name like 'ABCDEF%'
  4  and object_id between 1000 and 1500
  5  /

no rows selected

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=83 Card=138 Bytes=4140)
   1    0   VIEW OF 'index$_join$_001' (Cost=83 Card=138 Bytes=4140)
   2    1     HASH JOIN
   3    2       INDEX (RANGE SCAN) OF 'BIG_TABLE_IDX2' (NON-UNIQUE) (Cost=81
Card=138 Bytes=4140)
   4    2       INDEX (RANGE SCAN) OF 'BIG_TABLE_IDX1' (NON-UNIQUE) (Cost=81
Card=138 Bytes=4140)

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

I did have to HINT it since it is the WRONG plan:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select object_name, object_id
  2  from big_table
  3  where object_name like 'ABCDEF%'
  4  and object_id between 1000 and 1500
  5  /

no rows selected

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=431 Card=138 Bytes=4140)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'BIG_TABLE' (Cost=431 Card=138
Bytes=4140)
   2    1     INDEX (RANGE SCAN) OF 'BIG_TABLE_IDX1' (NON-UNIQUE) (Cost=52
Card=138)

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

No init.ora's really needed here.

Rating: 4
Reader April 05, 2002
Reviewer: A reader
Tom,

Is there a way to get the information from the
data dictionary, which latches(names of latches) are of
type IMMEDIATE_GET and which latches (names of latches)
are WILLING_TO_WAIT type.

Also what is the criteria for latches to change from
one type to another. Do they change from one level
to another also, in the priority scheme of things

Thanks

Followup:
not that I'm aware of. Its the code in the database that decides whether its
willing to wait or not.

April 05, 2002 Mark -- Thanks for the question regarding "Subqueries, Nested Views, or one big query what is best.", version 9i
You Asked
Not quite sure how to pose this question, but I have to create a report that
only prints differences between two of our systems.

One called budget, has project then cc then revision levels.

a project has many cc's which might have many revisions, this is represented by
3 tables.

The other is called CCS and it has a project table and one detail table, which
is really cc's at the revisions level.

In the two systems, the budget might have job 1 cc #1 and it has 3 revisions. In
the ccs system job 1 cc#1 and it has 5 revisions.

The report prints differences at the cc level. So the budget system's 3
revisions for cc #1 sum up to $500, and the CCS systems 5 revisions sum up to
$600. Because they are different they print, but the print prints all the
revisions from both sides, like

project 1
cc #        revision #     budget_amt     ccs_amt
1              0              300           300
1              1               0             50
1              2              200             0
1              3                            150
1              4                            100

cc#1 total                   $500          $600

it prints only because 500 <> 600, so the entire above is printed. and if
revision #4 was no in the ccs system they would have been equal and not printed.

I want my program to have a simple query like "select * from my_compare_view
where project = 1"

I have tried to make views to get to that level, but I think that might be too
slow. And besides I ended up having nested views. meaning one view just to get
each system flattened, then another that queries those two views to combine the
two systems. Then another to do the summing to a CC level, so that I could make
another that would get the records from the combined view filtering the summed
cc level equals.

I thought about using the new WITH clause to add subqueries to a query, instead
of them being in the FROM clause, so I could try using FULL OUTER JOINS.

And I also had made a stored procedure that would take the project # and cc #
and compare the two systems via cursors, and return a varchar saying 'DIFFERENT'
or 'EQUAL'. Now this way was definitely real slow.

OK so here is the question, finally :)

Which way do you think would be the best? using speed, ease of maintainability,
and my stupidity :) as what determines best, in that order.

I apologize for the length of this post.

--is there a MAX number of revisions, some reasonable upper bound?
No can be as many as they have entered.

-- does one set of the tables alway have AT LEAST as many if not MORE revisions
then then other set of tables
No, one might have more than the other, and vice a versa. Hard to believe that
they would have two different systems, both storing almost identical data, and
them not entering the same information always. :) Management, yeah right. :)

Mark
and we said...
Given that -  i don't see an "efficient" way to do this.  It'll require full
outer joins (way bad from performance perspective).

What you can try is this (t1 and t2 are views of your respective systems to get
the data all together)

ops$tkyte@ORA9I.WORLD> select * from t1;

       CC#  REVISION# BUDGET_AMT
---------- ---------- ----------
         1          0        300
         1          1          0
         1          2        200
         2          0        300
         2          1          0
         2          2        200

6 rows selected.

ops$tkyte@ORA9I.WORLD> select * from t2;

       CC#  REVISION#    CCS_AMT
---------- ---------- ----------
         1          0        300
         1          1         50
         1          2          0
         1          3        150
         1          4        100
         2          0        300
         2          1         50
         2          2          0
         2          3        150

9 rows selected.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> select *
  2    from (
  3  select nvl(t1.cc#,t2.cc#) cc#, nvl(t1.revision#,t2.revision#) revision#,
budget_amt, ccs_amt,
  4         sum(budget_amt) over (partition by nvl(t1.cc#,t2.cc#))
tot_budget_amt,
  5         sum(ccs_amt) over (partition by nvl(t1.cc#,t2.cc#)) tot_ccs_amt
  6    from t1 full outer join t2 on ( t1.cc# = t2.cc# and t1.revision# =
t2.revision# )
  7    )
  8   where tot_budget_amt <> tot_ccs_amt
  9  /

       CC#  REVISION# BUDGET_AMT    CCS_AMT TOT_BUDGET_AMT TOT_CCS_AMT
---------- ---------- ---------- ---------- -------------- -----------
         1          0        300        300            500         600
         1          1          0         50            500         600
         1          3                   150            500         600
         1          2        200          0            500         600
         1          4                   100            500         600
Review & Followup
Rating: 5
Partitioned by and Outer Join. April 05, 2002
Reviewer: Mark Spritzler from Santa Ana, CA USA
Thanks Tom for your insightful response.

I was unfamiliar with the Partitioned by command, and your use of NVL is
something I would hav enever thought of using it for, but it reaches the best
goal of having everything in one place, no need to check at the cc level if they
match, then go to detail to print.

Thanks

Mark

April 05, 2002 Prince -- Thanks for the question regarding "Serialization on session level", version 8.1.7
You Asked
Q1:
=========
Suppose I set the transaction isolation set to serializable at the session
level.

say I have the following SQLs

Unti1:
~~~~~~~
Select count(*) from tab1;

update tab1 set col1 = 10;

commit;

Unit2:
~~~~~~~
select * from tab1;

insert into tab1 values ....

select * from tab1;

update tab1 set ....

commit;

Now, am I correct in assuming that all the stamenets in unitX including the
select is considered as a transaction.
ie, Transaction one comprises of

"Select count(*) from tab1" and "update tab1 set col1 = 10"

and Transation 2 comprises of
"select * from tab1" and "insert into tab1 values ...."

Q2:
=========

If I have the isolation set to the default (read commited), is the SELECT part
of the transaction? I guess the transaction begins with the first statement
which changes the data. Am I correct?

Thanks,
and we said...
q1) yes.

q2) yes.  the transaction technically begins with the first statement -- we
might not need a v$transaction entry until you actually modify something but
your in what is known as a transaction at that point.
April 05, 2002 Asha -- Thanks for the question regarding "Invalid datatype for a sql object type table", version 8i
You Asked
I have following code:

Create or replace type mytabletype as object
    (ID             Varchar2(14),
     TestID         Varchar2(14),
     TestResult     Number(6,2),
     DateTested     Date,
     TestType       Varchar2(10),
     TestMethod     Varchar2(10),
     NoResultReason Varchar2(14),
     CurrentConfirm Varchar2(1));

CREATE OR REPLACE
type mytable as table of mytabletype

When I try to run the statement through a procedure I get
'ORA-00904: Invalid column name'

    Open v_RefCursor FOR
       'SELECT * FROM TABLE(cast(l_data AS MyTable)) ' ||
                            'where id =  ''' || v_id || '''';

Here v_id is a parameter passed to the procedure.
Can you tell me how can I refer the field 'ID' of l_data ?

Thanks a lot.
and we said...
You have to BIND.

It is not complaining about ID, but l_data.  Also -- NEVER concatenate in values
like that, BIND them!!!!

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          l_data myTable := myTable();
  3          l_id   number;
  4  begin
  5          open :x for
  6       'SELECT *
  7          FROM TABLE(cast( :l_data AS MyTable))
  8         where id = :x' using l_data, l_id;
  9  end;
 10  /

PL/SQL procedure successfully completed.

works just fine....
Review & Followup
Rating: 4
April 05, 2002
Reviewer: Asha from Newport, RI, USA
Thanks a lot.

It was very helpful as it will solve lot many problmes of mine. Also, it was a
quick reply.

April 05, 2002 Betty -- Thanks for the question regarding "Finction for multi characters per field", version 8.1.7
You Asked
I have a field 32FLAGS that's datatype is VARCAHR2(32)
the fiels contains NNNNYNNNNYNNNNYNNNNYNNNNYNNNNYNN
What I need to know is what is in each character and display them separately as
flag-1, flag-2 ……flag-32.  I created a function for the first flag, but how do I
do the rest?

create or replace function get_flags
(aaid in number)
return varchar2
is flag_out char(1);
begin
   select substr(itflag,1,1)
     into flag_out
        from hlsys_itest
        where aa_id = aaid;
        return flag_out;
end;
/

Thanks
and we said...
just create a view:

create or replace view v
as
select a.*,
       substr( itflag, 1, 1 ) flag_1,
       substr( itflag, 2, 1 ) flag_2,
       ...
       substr( ifflag, 32, 1 ) flag_32
from hlsys_itest a
/

and use the view V to query it out.
Review & Followup
Rating: 5
Create View April 05, 2002
Reviewer: Betty from Palm Harbor, FL
That was just what I needed to finish that porject.

Thanks

April 06, 2002 -- Thanks for the question regarding "importing partition to user's default tablespace", version 8.1.7
You Asked
Hi Tom

I am struggling importing some partitions from one database to another. With
normal tables I was able to do this

1. create user in destination db with connect and resource role
2. assign a tablespace to this user
3. revoke unlimited quota on tablespaces from this new user so new objects can
be imported to his default tablespace

but with partition tables this doesnt work, it keeps on trying to import to
their original tablespace which i dont have, the only work around is by creating
a tablespace with same name?

cheers
and we said...
Multi-segment objects (tables with partitiones, IOTS with overflow segments,
tables with LOBS and so on) are not as "simple" as a single segment table.

As such, IMP will not rewrite the statement to create the object in the users
default tablespace as it would for other objects since it is highly unlikely you
want ALL segments to go into the same tablespace.

For this object, use:

imp userid=u/p tables=TABLE_NAME indexfile=TABLE_NAME.sql

edit table_name.sql, assign whatever tablespaces you want to the table --
uncomment the CREATE table and pre-create the object.

Then imp the data with ignore=y

Using INDEXFILE will just create the DDL and not do anything in the database.
Review & Followup
Rating: 5
ok got it April 06, 2002
Reviewer: A reader from madrid, kpmg spain
Thank you Tom

April 06, 2002 gaurang -- Thanks for the question regarding "consistant get", version 8.1.7
You Asked
Hi Tom,

I am working in oracle from last 5 Years.But i am confused with this
word."consistant get" ,"db block get" ," Read in current mode".

Last week my manager came to me with query,execution plan and stattistics.It
shows  2000000 consistant get and 100000 disk read(i know that),and i am unable
to answer it(consistant get).

  Can you PLEASE,PLEASE explain me in a simple techinal word meaning of above
terms.It is really confusing to me.

Thank in advance.
and we said...
consistent gets are blocks read in the regular "read" mode -- we use consistent
reads when processing queries.  These are blocks gotten AS THEY EXISTED when
your query began (we may have had to of read them from the rollback segments in
order to make them consistent).

It is part of our consistent read mechanism that uses multi-versioning:
http://technet.oracle.com/doc/oracle8i_816/server.816/a76965/c23cnsis.htm#17882
has a good discussion on that.

We get blocks either in read consistent mode (for queries) or CURRENT mode for
modifications (we need to modify the current version of the data)
Review & Followup
Rating: 4
April 06, 2002
Reviewer: A reader
Thanks tom for quick response.

 consistent read is logical read.is it good to have high consistent read?.In my
query we have 2 million consistent read.

Followup:
It depends.

If it takes 2,000,000 consistent gets to get your answer and there is no more
efficent means to get it -- its great (you got your answer, thats great)

If creating an index takes that down to 1 consistent get, then 2,000,000 is bad.

Look for tuning opportunities here.

(you can get high consistent gets on a very active system where we must apply
lots of undo to get you the consistent read.  Eg: "select
count(unindexed_column) from one_billion_row_table" where there are TONS of
inserts/updates/deletes against the table while you are querying it can lead to
higher consistent gets for the rollback processing.

Rating: 4
April 06, 2002
Reviewer: A reader
hi Tom.

you are telling "we may have had to of read them from the rollback segments in
order to make them consistent"

It is rading rollback segment.

is consistent read is 100% logical read?

Followup:
Yes, that is what I'm saying.

An interesting EXAMPLE of this is as follows.  I have about 17k records in
all_objects.  Say you do the following sequence of events:

create table t as select * from all_objects;
alter session set sql_trace=true;
select count(*) from t JUST_IN_PLUS;
variable x refcursor
begin
    open :x for select count(*) from t REF_CURSOR;
end;
/
delete from t;
commit;
print x

Now, that first count(*) will read from the table and find NONE of the blocks
need "undoing" -- they don't change while that query is going.

The second query on the other hand will find LOTS of blocks changed.  The OPEN
fixed the point in time at which the query will return the results -- the
"printing" of the query actually did all of the work (the open did NO IO,
logical or physical -- the FETCH did that).

Now, that second query will find that EVERY block it goes to read has changed,
it'll have to do a little read consistency to undo that work.  TKPROF shows us:

select count(*)
from
 t JUST_IN_PLUS

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.04       0.03        235        239         12           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.04       0.03        235        239         12           1

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 216

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE
  17224   TABLE ACCESS FULL T

SELECT COUNT(*)
FROM
 T REF_CURSOR

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        1      0.43       2.45        503      17894         12           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        3      0.44       2.45        503      17894         12           1

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 216     (recursive depth: 1)

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE
  17224   TABLE ACCESS FULL T

As you can see -- every row that we hit, we found we needed to do a little work
to "undo it" (to get it back).  Both queries returned the same answer -- just
one of them had to undo the delete in order to get it.  This was done via the
rollback segments.

A consistent read may be done via a logical or physical IO (see query one above
for example)

Rating: 4
April 06, 2002
Reviewer: A reader
Hi Tom,

Can select statement give ora-01555 error?

In your response above if select statement unable to retrive older image of
data(due to overwritten of undo information) what will happen.

Followup:
A select statement is what gives the 1555 (or the search part of an
update/delete)

A 1555 is the error you get when a select is unable to retrieve the older image
of data -- that is what happens ORA-01555.

April 06, 2002 Munzer -- Thanks for the question regarding "Multiple selections in owa_util.listprint", version 8.1.7
You Asked
Tom:

1.  I am using owa_util.listprint to create drop down menus in a web
application.  I need to let the user search multiple values and the nsearch on
those.  Fro example, all records for state='CA' or state='VA'.  I know you can
do this in HTML using
<SELECT MULTIPLE Name="i_state">

Can I add any parameter to the owa_util.listprint to do this.  I do not mean
changing the PL/SQL code but the statement.

2.  When The user submits several states to search on, how do you change your
SQL query to handle this.  DO you build a dynamic query based on the number of
states he selects.

3.  How in SQL*PLUS can I insert hard returns into a text field.
E.G., insert into table(text) values('this is line 1?? this is line 2');

4.  Does ORacle has any packages to ftp files from local disk to a remote unix
server or vice versa.

Thank you,
and we said...
1) listprint supports multiple:

PROCEDURE LISTPRINT
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 P_THECURSOR                    NUMBER(38)              IN
 P_CNAME                        VARCHAR2                IN
 P_NSIZE                        NUMBER                  IN
 P_MULTIPLE                     BOOLEAN                 IN     DEFAULT
PROCEDURE LISTPRINT
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 P_THEQUERY                     VARCHAR2                IN
 P_CNAME                        VARCHAR2                IN
 P_NSIZE                        NUMBER                  IN
 P_MULTIPLE                     BOOLEAN                 IN     DEFAULT

as is....

2) see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:110612348061
for one approach (no, dynamic sql is not necessary)

3) ( 'this is line 1' || chr(10) || 'this is line 2' );

4) no, that would be a feature of the operating system and pretty much all of
them have it.
April 07, 2002 -- Thanks for the question regarding "utl_file security issues", version 8.1.7
You Asked
Hi Tom

Utl file utilities can be used to read/write files in directories specified in
the init.ora file .

The file operations are performed by the 'Oracle' Unix user .

The downside to it is that any  database session can write/overwrite files in
these directories provided the
user has execute permissions  on the utl_file routines. I believe that in most
installations public grant is
given on the 'utl_file'  routines.

This does mean that the scott/tiger user can write/overwrite sensitive
files .

Does this issue exist and if so what is the work around

regards
J Joseph
and we said...
UTL_FILE is a generic utility to read and write files, this is true.

Only the directories specified in the UTL_FILE_DIR init.ora parameter can be
accessed.

By default, all users can use utl_file.  This is true on all installations.

If you put "sensitive files" into these directories, then use -- by default any
user can read and write them.

If you need a higher degree of control and need only READ access -- bfiles
provide that.  they use a directory object and the access to the directory
object is very granular.

If you need read/write access, a Java stored procedure is what you will want to
use.  Using DBMS_JAVA in Oracle8i release 2 (816 and up) you have extremely
granular control over who can read and write files in any directory.

The Bfiles and Java stored procedures have the additional benefit that you need
not "bounce" the server in order to open up access to a new directory path.  The
DBA or Security person can dynamically grant/revoke access to paths at runtime
as needed.
April 07, 2002 S. -- Thanks for the question regarding "Random Number generate", version 8.1.7
You Asked
Hello Tom,
I need to generate random number.
But I could find such a function in Oracle.
But there is a function to generate random number in C, PASCAL.
Is there any function or a way to generate random number?

Please tell me.

With best regards,
and we said...
Everyone should check out the supplied packages guide -- LOTS of functionality
in there (i devoted 225 pages in my book to the ones I think are the most
important day to day)

for random directly in all releases see:
http://asktom.oracle.com/~tkyte/Misc/Random.html
supplied packages guide for 8i is:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76936/toc.htm
and in 9i, there is a fips-140 cryptologically secure random key generator as
well in the dbms_obfuscation_toolkit package
April 07, 2002 Venkateswara Reddy -- Thanks for the question regarding "HOST Builtin", version 8.1.5
You Asked
Hi Tom

We have developed some applications, here we are using DYNAMIC LOV'S using HOST
Built-in like
 HOST(SQLPLUS USERNAME/PASSWORD@STRING C:\MYSCRIPT.SQL)
MYSCRIPT.SQL Containst "create or replace myview as select &1 from &2";

But now same application want to use web enabled. So i want create a lov from
any column, from any table.  How can I create an lov without host builtin in
developer 6.0

Thanks

Reddy
and we said...
forms_ddl( 'create or replace myview as select ' || some_thing || ' from ' ||
something_else );

That would be better then host HOWEVER -- its a horrible way to do it all
together.  Why not just replace the record group associated with the LOV in the
first place -- creating a group with a query.  Doing DDL over and over is
TERRIBLE.  and your users need "create view".  Just use a record group and
assign it to the lov -- creating the view is a really bad idea.
Review & Followup
Rating: 2
HOST April 08, 2002
Reviewer: Reddy from Bangalore, India
Hi tom

Thanks I solved my problem.

Reddy

April 07, 2002 Mohammed Irfan -- Thanks for the question regarding "CPU Time", version 7.3.4
You Asked
Hi Tom,

Can you please explain us to make us understand the concepts, the result we get
when tkprof is executed. For example call  count

cpu  (how the time is calculated)
elapsed
disk
query
current
rows
library cache misses
Execution Plan (The different results we get on execution).
Its a bit tough to understand from manuals. I hope you will provide with
example.

Also what is and CBO & RBO and how is one better than the other. Any examples
will do..

Thanks a lot...
and we said...
Do you have my book?  I go into GREAT details on tkprof, statspack and the other
tools like dbms_profiler available to you.

You can read:
http://technet.oracle.com/doc/oracle8i_816/server.816/a76992/ch14_str.htm#1018
its from the 816 doc set but applies 100% to 7.3

With regards to the optimizer -- read the Oracle7 Tuning guide, it goes into
detail on the CBO vs RBO.  In Oracle8i and up -- CBO is the *only* one to use.
In 7.3.4, I would use CBO when using parallel query or bitmapped indexes or
something that required it.
April 07, 2002 Jim -- Thanks for the question regarding "Tkprof output", version 8.1.7
You Asked
Hi Tom,

We a a packaged Stored Proc F1_SEL
depending on a flag it either did a SELECT with
WHERE >=
or WHERE >

The SELECT is hardcoded twice somthinge like
  IF flag = TRUE
     SELECT cols
     FROM   table
     WHERE  >=
  ELSE
     SELECT cols
     FROM   table
     WHERE  >

My questions is re the following tkprof output.
Since the counts for each SQL the stored proc ran
total up to the same as the counts for the
counts for the stored proc call itself.
How come the times don't ?
eg the total elapsed time for the SP call was 20.63
for the >= SELECT it was .19 and the > SELECT 4.25
4.25 + .19 doesn't equal 20.63 so I'm missing 15 or so seconds.

In your book  you mention the CPU time is CPU seconds
am I right in assuming that is not the same as
as measured by a wall clock?

Thanks Jim

begin  TSS_FILE01_PKG . F1_SEL (:b1 ,:b2 ,:b3 ,:b4 ,:b5 ,:b6 ,:b7 ,:b8 ,:b9 ,
  :b10 ,:b11 ) ; END ;

call  count    cpu   elapsed    disk   query    current     rows
----   ---  ---- ---------- ---------- ---------- ----------  --------
Parse  11659   5.62  5.72        0     0          0           0
Execute  11659 14.30 14.91       0     0          0           0
Fetch    0      0.00  0.00       0     0          0           0
------- ------  -------- ---------- --- ---------- ----------  ----------
total    23318 19.92  20.63      0     0          0           0

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 22  (CYBORG)
********************************************************************************

SELECT /*+ FIRST_ROWS  */FILE01_KEY,FILE01_DATA
FROM
 FILE01  WHERE FILE01_KEY >= SUBSTR(:b1,1,:b2)  AND ROWNUM = 1 ORDER BY
  FILE01_KEY

call   count    cpu    elapsed    disk   query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse    1      0.01    0.00      0          0          0           0
Execute  434    0.06    0.08      0          0          0           0
Fetch    434    0.13    0.11      0        868          0         434
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    869    0.20    0.19      0        868          0         434

Misses in library cache during parse: 1
Optimizer goal: FIRST_ROWS
Parsing user id: 22  (CYBORG)   (recursive depth: 1)

Rows     Row Source Operation
-------  ---------------------------------------------------
    434  COUNT STOPKEY
    434   TABLE ACCESS BY INDEX ROWID FILE01
    434    INDEX RANGE SCAN (object id 10665)

Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: HINT: FIRST_ROWS
    434   COUNT (STOPKEY)
    434    TABLE ACCESS   GOAL: ANALYZED (BY INDEX ROWID) OF 'FILE01'
    434     INDEX   GOAL: ANALYZED (RANGE SCAN) OF 'FILE01_IDX1' (UNIQUE)

********************************************************************************

SELECT /*+ FIRST_ROWS  */FILE01_KEY,FILE01_DATA
FROM
 FILE01  WHERE FILE01_KEY > SUBSTR(:b1,1,:b2)  AND ROWNUM = 1 ORDER BY
  FILE01_KEY

call  count   cpu  elapsed   disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse     1   0.01   0.00      0          0          0           0
Execute  11225 2.28  2.30      0          0          0           0
Fetch    11225 2.12  1.95      0      22450          0       11225
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    22451 4.41  4.25      0      22450          0       11225

Misses in library cache during parse: 1
Optimizer goal: FIRST_ROWS
Parsing user id: 22  (CYBORG)   (recursive depth: 1)

Rows     Row Source Operation
-------  ---------------------------------------------------
  11225  COUNT STOPKEY
  11225   TABLE ACCESS BY INDEX ROWID FILE01
  11225    INDEX RANGE SCAN (object id 10665)

Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: HINT: FIRST_ROWS
  11225   COUNT (STOPKEY)
  11225    TABLE ACCESS   GOAL: ANALYZED (BY INDEX ROWID) OF 'FILE01'
  11225     INDEX   GOAL: ANALYZED (RANGE SCAN) OF 'FILE01_IDX1' (UNIQUE)

********************************************************************************
and we said...
The remaining 15 seconds is time spent in your code processing the results.

You can use dbms_profiller (also in "the book") to see where that is being
spent.  The timing on the plsql is the sum of the times of all time spent in the
code -- doing sql, other routines, etc.

Consider:

declare
    l_data long;
begin
    for x in ( select * from dual )
    loop
        for i in 1 .. 30000
        loop
            l_data := rpad( 'x', i, 'x' );
        end loop;
    end loop;
end;

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.01          0          0          0           0
Execute      1      5.95       5.96          0          0          0           1
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      5.96       5.97          0          0          0           1

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 216
********************************************************************************

SELECT *
FROM
 DUAL

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.00       0.00          0          1          4           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.01       0.00          0          1          4           1

that CPU time was burned up in the PLSQL code itself.
Review & Followup
Rating: 4
Alot of time ? April 07, 2002
Reviewer: Jim
Tom

Thanks for your reply.
I used DBMS_PROFILER but it didn't give me any
places where bottlenecks are.

The stored procedure essential does either of the SELECTs
as in my original question and set a variable
in an IF test (code follows) and thats all.

      IF substr(p_input_record,1,p_length_key_entered) =
          substr(p_output_data,1,p_length_key_entered) THEN
          p_file_status := '00';
       ELSE
          p_file_status := '01';
       END IF;

Then does this mean that the extra 15 seconds is used
up by this? Seems like alot of time considering the
execution of the SELECTs only totalled around 4.44 seconds

Followup:
Lets see all of the code and the summary report given by dbms_profiler.

Rating: 4
TKPROF April 07, 2002
Reviewer: sam from usa
hi tom,
I searched to find out the complete form of TKPROF, can you help me know what
does it stand for?
sam

Followup:
I always thought it meant the "Tom Kyte PROFiler" but apparently it is more like

ToolKit PROFiler

April 07, 2002 -- Thanks for the question regarding "Case within Query", version 734
You Asked
Hello Tom,

I have a case where there is a master and detail table. Detail tables fetch
multiple records related to a master record. Now I need to filter out detail
record based on value of a column for col5

SQL> create table master ( col1 number primary key );

Table created.

SQL> create table detail ( col1 number, col2 number, col3 varchar2(5), col4
varchar2(5),
  2  constraint detail_pk primary key ( col1, col2 ),
  3  constraint detail_fk foreign key ( col1 ) references master ( col1 ));

Table created.

SQL> insert into master values ( 1 );

1 row created.

SQL> insert into master values ( 2 );

1 row created.

SQL> insert into master values ( 3 );

1 row created.

SQL> insert into detail values ( 1, 1, null, null );

1 row created.

SQL>  insert into detail values ( 1, 2, '12345', null );

1 row created.

SQL> commit;

Commit complete.

SQL> spool off

SQL> select b.col1, b.col2, b.col3, b.col4, b.col5
  2   from master a, detail b
  3   where a.col1 = b.col1;

      COL1       COL2 COL3  COL4  COL5
---------- ---------- ----- ----- -----
         1          1             1
         1          2 12345       2
         2          1             1

if the value of col5 is 2 that record has to be picked elsif the
value of col5 is 1 then that record has to selected.

The real output I need to see is

col1   col3   col4
-----  -----  -----
1       2     12345
2       1     1

I tried using union but ended up getting duplicate records.

Thanks in advance
and we said...
Well, I added col5 to detail (you didn't have one) and used these inserts (your
inserts don't match up with your query results)

ops$tkyte@ORA734.WORLD> insert into master values ( 1 );

1 row created.

ops$tkyte@ORA734.WORLD> insert into master values ( 2 );

1 row created.

ops$tkyte@ORA734.WORLD> insert into master values ( 3 );

1 row created.

ops$tkyte@ORA734.WORLD> insert into detail values ( 1, 1, null, null, 1 );

1 row created.

ops$tkyte@ORA734.WORLD> insert into detail values ( 1, 2, '12345', null, 2 );

1 row created.

ops$tkyte@ORA734.WORLD> insert into detail values ( 2, 1, null, null, 1 );

1 row created.

ops$tkyte@ORA734.WORLD> commit;

Commit complete.

ops$tkyte@ORA734.WORLD>
ops$tkyte@ORA734.WORLD> select b.col1, b.col2, b.col3, b.col4, b.col5
  2   from master a, detail b
  3   where a.col1 = b.col1
  4     and b.col5 = ( select max(col5) from detail where detail.col1=a.col1 )
  5  /

      COL1       COL2 COL3  COL4        COL5
---------- ---------- ----- ----- ----------
         1          2 12345                2
         2          1                      1

You want the row that has the "max" value for col5 for a given col1 value...
Review & Followup
Rating: 5
April 07, 2002
Reviewer: A reader
I did'nt think of that. It might just work in this case since there are only 2
unique values for col5.  I know in 8i it is possible to use CASE statements
within the SELECT For curiosity sake in V734 is there a way to filter out a
Query based column value for example if col5 = value then pick that detail
record.

Thanks

Followup:
If you only care about 1 and 2, just add a where clause:

ops$tkyte@ORA734.WORLD> select b.col1, b.col2, b.col3, b.col4, b.col5
  2   from master a, detail b
  3   where a.col1 = b.col1
  4     and b.col5 = ( select max(col5) from detail where detail.col1=a.col1 and
col5 in ( 1,2 ) )
  5  /

If that doesn't do it -- I would need a FULL example showing the worst case and
what you expect as a result.  Almost anything is possible.

April 07, 2002 -- Thanks for the question regarding "Execution Plan", version 8.1.7
You Asked
Sometimes its difficult to interpret an execution plan which runs for a couple
of pages. Pl. let me know how to interpret the following execution plan.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=7089 Card=1 Bytes=18
          4)

   1    0   SORT (UNIQUE) (Cost=7088 Card=1 Bytes=184)
   2    1     FILTER
   3    2       NESTED LOONDT (Cost=7086 Card=1 Bytes=184)
   4    3         NESTED LOONDT (Cost=11 Card=1 Bytes=150)
   5    4           NESTED LOONDT (Cost=10 Card=1 Bytes=121)
   6    5             NESTED LOONDT (Cost=7 Card=1 Bytes=81)
   7    6               NESTED LOONDT (Cost=5 Card=1 Bytes=58)
   8    7                 TABLE ACCESS (BY INDEX ROWID) OF 'NDT_JOB' (C
          ost=4 Card=1 Bytes=28)

   9    8                   INDEX (RANGE SCAN) OF 'NDTAJOB' (NON-UNIQUE
          ) (Cost=3 Card=1)

  10    7                 TABLE ACCESS (BY INDEX ROWID) OF 'NDT_PERSON_DT
          ' (Cost=1 Card=1 Bytes=30)

  11   10                   INDEX (UNIQUE SCAN) OF 'NDT_PERSON_DT'
          (UNIQUE)

  12    6               TABLE ACCESS (BY INDEX ROWID) OF 'NDT_JOB_JR' (
          Cost=2 Card=6 Bytes=138)

  13   12                 INDEX (UNIQUE SCAN) OF 'NDT_JOB_JR' (UNIQUE)
          (Cost=1 Card=6)

  14    5             TABLE ACCESS (BY INDEX ROWID) OF 'NDTRANKNODE' (C
          ost=3 Card=588 Bytes=23520)

  15   14               INDEX (RANGE SCAN) OF 'NDTCNDTRANKNODE' (NON-UNI
          QUE) (Cost=1 Card=588)

  16    4           TABLE ACCESS (FULL) OF 'NDT_SCRTY_TBL' (Cost=
          1 Card=33 Bytes=957)

  17    3         INDEX (FAST FULL SCAN) OF 'NDT_PI_ERN_BAL' (UNIQUE)
          (Cost=7075 Card=906 Bytes=30804)

  18    2       SORT (AGGREGATE)
  19   18         FILTER
  20   19           TABLE ACCESS (BY INDEX ROWID) OF 'NDT_PI_ERN_BAL'
          (Cost=7 Card=3 Bytes=78)

  21   20             INDEX (RANGE SCAN) OF 'NDTEPI_ERN_BAL' (NON-UNIQ
          UE) (Cost=6 Card=3)

  22    2       INDEX (RANGE SCAN) OF 'NDTAJOB' (NON-UNIQUE) (Cost=3 Ca
          rd=1 Bytes=21)

  23   22         SORT (AGGREGATE)
  24   23           INDEX (RANGE SCAN) OF 'NDTAJOB' (NON-UNIQUE) (Cost=
          3 Card=1 Bytes=18)

  25   22         SORT (AGGREGATE)
  26   25           INDEX (RANGE SCAN) OF 'NDTAJOB' (NON-UNIQUE) (Cost=
          3 Card=1 Bytes=20)

  27    2       TABLE ACCESS (BY INDEX ROWID) OF 'NDT_SCRTY_TBL'
          (Cost=2 Card=1 Bytes=21)

  28   27         INDEX (RANGE SCAN) OF 'NDT_SCRTY_TBL' (UNIQUE)
          (Cost=1 Card=1)

Thks
and we said...
Read it from the inside out -- find the most "indented" thing -- thats first.
Then start working up and out.

So, it looks like:

   7    6               NESTED LOONDT (Cost=5 Card=1 Bytes=58)
   8    7                 TABLE ACCESS (BY INDEX ROWID) OF 'NDT_JOB' (Cost=4
   9    8                   INDEX (RANGE SCAN) OF 'NDTAJOB' (NON-UNIQUE) (Cost=3

  10    7                 TABLE ACCESS (BY INDEX ROWID) OF 'NDT_PERSON_DT'
  11   10                   INDEX (UNIQUE SCAN) OF 'NDT_PERSON_DT' (UNIQUE)

is done first (most indented), and then that (call that subresult1) is:

   6    5             NESTED LOONDT (Cost=7 Card=1 Bytes=81)
                        SUBRESULT1  <<<========
  12    6               TABLE ACCESS (BY INDEX ROWID) OF 'NDT_JOB_JR' (Cost=2

  13   12                 INDEX (UNIQUE SCAN) OF 'NDT_JOB_JR' (UNIQUE) (Cost=1

done and then (call that subresult2) is:

   5    4           NESTED LOONDT (Cost=10 Card=1 Bytes=121)
                      SUBRESULT2
  14    5             TABLE ACCESS (BY INDEX ROWID) OF 'NDTRANKNODE' (Cost=3
  15   14               INDEX (RANGE SCAN) OF 'NDTCNDTRANKNODE' (NON-UNIQUE)

and so on -- just keep unnesting from the "inside out".  Eventually in your case
-- you find an whole another plan set at the bottom that will be evaluated from
the inside and and nested loop's joined to the stuff on the top as well.

Something that runs for a couple of pages should be no harder (or easier
actually) then something that runs for 10 lines.  It is just more to read.
April 08, 2002 chen -- Thanks for the question regarding "How to update a column as quickly as possible?", version 8.05
You Asked
Hi tom,
      I want to update a column and I write the procedures like this:

DECLARE
  v_cost NUMBER;
BEGIN
  FOR REC IN (SELECT spll.rowid,
                     spll.inventory_item_id,
                     spll.list_price
              FROM WSC_SO_PRICE_LIST_LINES SPLL FOR UPDATE) LOOP

      --GET THE COST OF THE ITEM
      v_cost := WSC_COST_ITEMNO(REC.INVENTORY_ITEM_ID);

      --UPDATE THE PRICE OF THE ITEM
      UPDATE WSC_SO_PRICE_LIST_LINES SPLL
      SET SPLL.ATTRIBUTE14 = REC.LIST_PRICE - V_COST
      WHERE SPLL.ROWID = REC.ROWID;

  END LOOP;
END;

There are more than 50,000 data in this table(WSC_SO_PRICE_LIST_LINES), It takes
more than 3 hours to update the column.
Is there any other ways to do this? and if yes, which one is the best one?
thanks.
and we said...
Well, NEVER do procedurally what can be done in a SINGLE statement.

update wsc_so_price_list_lines
  set ATTRIBUTE14 = list_price - wsc_cost_itemno(inventory_item_id);

If that takes long -- look at the implementation of wsc_cost_itemno and see why
it is so slow...  If at all possible "inline" the functionality of the plsql
procedure (don't call the plsql from sql, move the functionality of the function
into the update directly)

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t as select * from all_objects;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert /*+ append */ into t select * from
all_objects;

17168 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert /*+ append */ into t select * from
all_objects;

17168 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function wsc_cost_itemno(
p_x in number )  return number
  2  as
  3  begin
  4          return dbms_random.random;
  5  end;
  6  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set timing on
ops$tkyte@ORA817DEV.US.ORACLE.COM> update t set object_id = to_char(created,'j')
- wsc_cost_itemno(object_id);

51504 rows updated.

Elapsed: 00:00:45.16
ops$tkyte@ORA817DEV.US.ORACLE.COM>

and if attribute14 is indexed, consider dropping the index, doing the update and
rebuilding the index, index maintenance on a bulk update can be very expensive.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx on t(object_id);

Index created.

Elapsed: 00:00:16.88
ops$tkyte@ORA817DEV.US.ORACLE.COM> update t set object_id = to_char(created,'j')
- wsc_cost_itemno(object_id);

51504 rows updated.

Elapsed: 00:01:24.91
ops$tkyte@ORA817DEV.US.ORACLE.COM>

it in this case doubled the time.

Give that you have a 3 hour issue here -- I am suspicious that the code in
wsc_cost_itemno takes a LONG time to execute and you must tune it.

Consider -- if that function takes 1/4 of a second to run and you call it 50,000
times:

50000*.25 = 12,500 second = 208 minutes = 3.5 hours.....
Review & Followup
Rating: 4
April 08, 2002
Reviewer: Chen from HK
Tom, thank you very much and it is very useful to me.

April 08, 2002 Andrew -- Thanks for the question regarding "SQL Standards", version 8.1.6
You Asked
Hi Tom,
I can not find this information anywhere.

 insert into ISSYEAR
  2  select SUM(if(IF BSIS.BSIS = B, PROJMARCH+PROJJAN+PROJFEB)) AS Q1,
  3  PROJYEAR
  4  FROM BSIS, FLATFILETEST
  5  where BSIS.VARIALBE = FLATFILETEST.VARIABLE;

Is the sum(if supported in 8.1.6. ANSI/ISO SQL:1992

The error from this statement is:
ORA-00907: missing right parenthesis

8.1.6 is ANSI/ISO SQL:1992 standard.
9.i is ANSI/ISO SQL 1999 standard.

Do you know where I could get information on these standards?
and we said...
Starting with the standard, we will find that the SQL standard has four
levels:

o Entry-level: This is the level to which most vendors have complied. This
level is a minor enhancement of the predecessor standard, SQL/89. No database
vendors have been certified higher and in fact the National Institute of
Standards and Technology (NIST), the agency that used to certify for
SQL-compliance, does not even certify anymore. I was part of the team that got
Oracle7.0 NIST-certified for SQL/92 entry-level compliance in 1993.  An entry
level compliant database has the feature set of Oracle7.0.

o Transitional: This is approximately 'halfway' between entry-level and
intermediate-level as far as a feature set goes.

o Intermediate: this adds many features including (not by any means an
exhaustive list):
Dynamic SQL
Cascade DELETE for referential integrity
DATE and TIME data types
Domains
Variable length character strings
A CASE expression
CAST functions between data types

o Full: Adds provisions for (again, not exhaustive):
Connection management
A BIT string data type
Deferrable integrity constraints
Derived tables in the FROM clause
Sub-queries in CHECK clauses
Temporary tables

The entry-level standard does not include features such as outer joins, the new
inner join syntax, and so on. Transitional does specify outer join syntax and
inner join syntax. Intermediate adds more, and Full is, of course all of SQL/92.

Most books on SQL do not differentiate between the various levels leading to
confusion on the subject. They demonstrate what a theoretical database
implementing SQL FULL would look like. It makes it impossible to pick up a
SQL book, and apply what you see in the book to just any SQL database. For
example, in SQL Server the 'inner join' syntax is supported in SQL statements,
whereas in Oracle8i it is not (but 9i does). Then again, Oracle7 supports
derived tables in the FROM clause (inline views) which is part of FULL but
others do not.

But, they are both SQL-compliant databases. You can do inner joins and outer
joins in Oracle, you will just do it differently than in SQL Server. The bottom
line is that SQL will not go very far at the entry-level.

All databases implement many of the features of the higher levels but do not
fully comply with the higher levels.

Now, as to how to do your statement above, either:

select sum( case when bsis = b then PROJMARCH+PROJJAN+PROJFEB
                 else 0
            end ) as q1,
       projyear
  from ....

or more simply:

select sum( decode( bsis, b, PROJMARCH+PROJJAN+PROJFEB, 0 ) ) as q1,
       projyear
...

if the issue is "well that makes my application Oracle dependent" -- use the
CASE (thats ANSI) or use a view (best approach) and just port the views.
Review & Followup
Rating: 5
That was great!!! October 05, 2001
Reviewer: asoutar from Toronto,on
I could not find detail like that any where on the internet. Thank you very
much!!

Rating: 4
A few questions about SQL 92 April 08, 2002
Reviewer: Dusan from Czech Rep.
1) What are "Domains", at Intermediate level?
2) Which SQL 92 features are still not included into 9i?
3) Is "Cascade Update" part of SQL 92?
Thanks,
Dusan

Followup:
Oracle is certified to SQL92 entry level (as high as anyone else)

We all have features from higher levels (all being the other certified
databases).

SQL92 is a HUGE spec -- there is no way I could list the features of
intermediate, transitional and full that we do not have.  No way.  They only
thing you can count on is what is specified for entry level -- thats the "level
playing field"

Domains are like sets of values -- they are the "domain of values".

Don't know if cascade update is or not -- I have yet to see a properly designed
database where it was necessary.  I'm a believer that primary keys are imutable
-- if there is even the SLIGHTEST chance you need to update them -- they are NOT
the key, something else is.

Rating: 5
April 09, 2002
Reviewer: Helena Markova from Bratislava, Slovakia
Thank you for explanation.

Rating: 5
Thanks a lot for explanation about Standard levels !!! April 09, 2002
Reviewer: MaxU from Munich, Germany

April 08, 2002 Pablo -- Thanks for the question regarding "Username=Password Detection", version 8.1.7
You Asked
Tom:  I want to audit the users passwords, particularly I need a Store Procedure
(PL/SQL) to detect if there are cases where username = password (i.e
user="scott" pass="scott").

Thanks in Advance
Pablo R.
and we said...
You would be interested in this then:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76956/secure.htm#2232

On this page:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:258815248980

I do have a routine that returns TRUE if the given user/pass combo is correct,
FALSE otherwise.  You can do that to check existing ones but your long term
solution is the first link.
Review & Followup
Rating: 4
Need More Info April 08, 2002
Reviewer: yogesh from Pune, India
The function mentioned in the first link have three parameters, is it necessary
to have only three paramerets or we can have any number ? secondly username,
password, old_password are reserverd words or we can use any variable names
instead of them ? What I mean is when we alter username/password by using alter
will they be passed to this function as username, password and old_password ?

Followup:
You are implementing a routine that will be called by the SERVER.

This routine must accept the user/new pw/old pw AT LEAST.

This routine theoretically could accept more but they would never ever be passed
a value (the server is already written, it'll only call you with three inputs,
if you add more inputs -- they must be DEFAULTed to some value)

They are not reserved words, name them as you like.  You can call them p1, p2,
p3 if you like.  You just need three of them, in the order specified  (username
and then password and then old password)

April 08, 2002 korada -- Thanks for the question regarding "Server Name of the Oracle Database", version 8.1.6
You Asked
Tom,

how do i get the name of the server on which oracle is sitting.
i need this info in a stored procedure.

thanks
and we said...
sys@ORA817DEV.US.ORACLE.COM> select host_name from v$instance;

HOST_NAME
----------------------------------------------------------------
aria-dev

you'll need to have sys grant select on v_$instance to the owner of the
procedure to put it in a proc.
Review & Followup
Rating: 4
April 05, 2002
Reviewer: eric givler from hbg, pa
This doesn't work with Oracle 7.3.4.  You have to select machine from v$session
for one of the oracle background processes.

Followup:
Sure -- but I always answer the question based on the version supplied -- in
this case 816.

In other releases:

ops$tkyte@ORA734.WORLD> select program from v$session where program like
'%(PMON)%';

PROGRAM
------------------------------------------------
oracle@aria-dev (PMON)

will work as well -- just parse out the hostname.

Rating: 3
How about the O/S? April 08, 2002
Reviewer: Andrew from ca, usa
I hope this isn't off the point too much.
Is there any direct way to tell what O/S we are on? The aim would be to have one
pl/sql code base for both NT and Unix. e.g. /tmp/ vs \tmp\ for ULT_FILE calls
and ls vs dir for extenal O/S calls.

Followup:
ops$tkyte@ORA817DEV.US.ORACLE.COM> select dbms_utility.port_string from dual;

PORT_STRING
-------------------
SVR4-be-8.1.0

would be one way -- another might be just to look at the first character of
background_dump_destination:

  1  declare
  2  l_string varchar2(255);
  3  l_dummy  number;
  4  begin
  5     l_dummy := dbms_utility.get_parameter_value( 'background_dump_dest',
l_dummy, l_string );
  6     dbms_output.put_line( substr( l_string, 1, 1 ) );
  7* end;
ops$tkyte@ORA817DEV.US.ORACLE.COM> /
/

PL/SQL procedure successfully completed.

since that typically is in the OS specific format with a fully qualified path.

April 08, 2002 Tommaso -- Thanks for the question regarding "Java Communication API (javax.comm)", version 8.1.7
You Asked
I have to write and read data from a COM port using a PL/SQL stored procedure.
The platform is WIN2K with Oracle8i 8.1.7.
I got the API named "Java Communication API" but the installation procedure
refers to a different directory structure.
This is the installation procedure:

>>To install the Java Communications API:

>>Place the Java Communications API ZIP file, ibm-javacomm-win32->>x86.zip, in
the directory where the Runtime Environment is >>installed. If you installed to
the default directory, this is the >>C:Program FilesIBMJava13 directory.

>>Unzip the file. The following files are extracted as shown:

>>jrelibextcomm.jar
>>jreinibmcomm.dll
>>jrelibjavax.comm.properties
>>docsjavacommlicense
>>docsjavacomm
eadme.javacomm.ibm.html

How can I install the API in the Jserver of the database?
Using "loadjava" I imported the classes in the database:

loadjava -u sys/change_on_install@test D:Ora817Homejavavmlibcomm.jar -g scott
-resolve -synonym -verbose

loadjava -u sys/change_on_install@test
D:Ora817Homejavavmlibjavax.comm.properties  -g tommy -resolve -synonym -verbose

but invoking the getPortIdentifiers() method of the
javax.comm.CommPortIdentifier class, the method returns an empty Enumeration
object ( I guess that the JServer can't see the DLL).

....
static Enumeration portList;
...
portList = CommPortIdentifier.getPortIdentifiers();
if (!portList.hasMoreElements()) <-- IT'S ALWAYS TRUE
{...}

Can You help me?

Thanks in advance.

Tommaso
and we said...
We don't do JNI (java invoking native compiled code) from inside the database.
That would allow you to introduce code into the database that could be very
harmful to the database itself.  A wild pointer in that DLL could corrupt our
SGA and wipe out the data in the database.  That makes us very very nervous.

If the java is calling a DLL, it's doing JNI -- we don't do that.

You might be interested in a DLL still however used via external procedures
written in C.  If you have my book -- i have a very large chapter on how to do
that.
Review & Followup
Rating: 5
Re: Java Communication API (javax.comm) April 09, 2002
Reviewer: Tommaso Cialdella from Italy
OK - I'm going to develop an external procedure
written in C.
Thanks for your reply.


Subject | Article | Last Updated (East coast time)
Select from table with an array | article only | Apr 09, 2002 07:45 am
Create Index with parallel option | article only | Apr 09, 2002 07:50 am
pl/sql Problem | article only | Apr 09, 2002 07:53 am
Table Partioning - performance gain | article only | Apr 09, 2002 08:14 am
How long do wee need for Upgrade ? | article only | Apr 09, 2002 08:37 am
Refresh Group of Snapshots | article only | Apr 09, 2002 10:43 am
LIKE with subquery | article only | Apr 09, 2002 01:32 pm
Schema Sets | article only | Apr 09, 2002 02:11 pm
Memory used by Instances | article only | Apr 09, 2002 02:17 pm
Index-organized tables | article only | Apr 09, 2002 08:03 pm
Format Address | article only | Apr 10, 2002 08:54 am
About Temporary tables | article only | Apr 10, 2002 08:58 am
How does Read only transactions work internally | article only | Apr 10, 2002 09:48 am
controlling display properties | article only | Apr 10, 2002 10:19 am
Using indexes to order data (and avoiding order by) | article only | Apr 10, 2002 11:24 am
Blocks based on stored procedures | article only | Apr 10, 2002 04:18 pm
Numbers vs Varchar | article only | Apr 10, 2002 08:48 pm
Running sqlplus / as sysdba from Unix script in 9i | article only | Apr 10, 2002 09:52 pm
Indexing on Sequences | article only | Apr 11, 2002 08:04 am
Design | article only | Apr 11, 2002 08:15 am
Expert one-on-one Oracle new edition? | article only | Apr 11, 2002 08:17 am
V$ Tables/Backup and Recovery | article only | Apr 11, 2002 01:55 pm
Sending HTML Page thru Mail | article only | Apr 11, 2002 01:57 pm
RAW datatypes and CRC32 | article only | Apr 11, 2002 07:09 pm
Understanding statspack report | article only | Apr 11, 2002 07:10 pm
Insert CLOB in a table in remote database | article only | Apr 11, 2002 07:14 pm
Specifying hidden hints | article only | Apr 11, 2002 08:16 pm
RedHat Linux with more than 2GB memory | article only | Apr 11, 2002 09:00 pm
Export/Import. Your advice is needed. | article only | Apr 12, 2002 08:21 am
Execution of query | article only | Apr 12, 2002 08:25 am
v$open_cursor.sql_text showing "table_4_200_5e14_0_0_0 " | article only | Apr 12, 2002 08:45 am
History data and export | article only | Apr 12, 2002 08:50 am
Running O/S files from PL/SQL | article only | Apr 12, 2002 08:51 am
How to find Referral URL in Stored Procedure | article only | Apr 12, 2002 09:03 am
explicit cursors in package and procedure level | article only | Apr 12, 2002 09:06 am
Locking Problems | article only | Apr 12, 2002 09:17 am
Couple of doubts | article only | Apr 12, 2002 09:50 am
difference between oracle8i & oracle9i | article only | Apr 12, 2002 11:08 am
unique number on column | article only | Apr 12, 2002 11:10 am
LMT and DMT | article only | Apr 12, 2002 12:31 pm
query | article only | Apr 12, 2002 12:34 pm
Max # Concurrent Users in OAS PLSQL cart app? | article only | Apr 12, 2002 03:35 pm
CLOB and BLOB datatypes ! | article only | Apr 12, 2002 08:46 pm
number of extents more than 10000 | article only | Apr 13, 2002 10:11 am
XML Procedure | article only | Apr 13, 2002 10:41 am
Dynamic allocation of extents for a tablespace | article only | Apr 13, 2002 11:24 am
Using External Files in 9i | article only | Apr 13, 2002 01:57 pm
Avoiding unique constraint in inserting data | article only | Apr 13, 2002 02:09 pm
works in order by but not in where clause | article only | Apr 13, 2002 02:14 pm
Tuning SQL STEP BY STEP | article only | Apr 13, 2002 02:29 pm
CR IN SOME OF TEXT FIELDS & BACKUP OR PRODUCTION | article only | Apr 14, 2002 09:07 am
Any, Some, All | article only | Apr 14, 2002 09:36 am
how to calculate the number of queries per second | article only | Apr 14, 2002 09:37 am
Convert form Client/Server to Web Based | article only | Apr 14, 2002 10:08 am
Logical & Physical Structure of Datablock. | article only | Apr 14, 2002 06:39 pm
Oracle Installation | article only | Apr 14, 2002 06:46 pm
Moving data between two remote databases | article only | Apr 14, 2002 06:47 pm
row(s) 1 - 57 of 57
April 09, 2002 Ivan -- Thanks for the question regarding "Select from table with an array", version 8.1.7
You Asked
Hello Tom,
I have table defined like this:

SQL> desc xvalues

 Name                       Null?    Type
--------------------------- -------- ----------
 MEAS_NR                    NOT NULL NUMBER(12)
 POS_NR                     NOT NULL NUMBER(7)
 SIGNUM                              VARCHAR2(8)
 VALLIST                             WERT_LIST

SQL> desc WERT_LIST
 WERT_LIST VARRAY(200) OF NUMBER

I have to select one row from the table with one value from VArray
(not one row with all values from an array). And if possible, I dont
want to do this in PL/SQL but in simple SQL statement.
So lets say I want to select row where MEAS_NR = 1, POS_NR = 4 and
from its corresponding array item 7.

THNX, Ivan
and we said...
Well, if you need more then one row back, you can use a trick like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace type myArrayType as
varray(100) of number
  2  /

Type created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t (  x int, y myArrayType ) ;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1, myArrayType(
1,2,3,4,5,6,8888,8,9,0 ) );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 2, myArrayType(
1,2,3,4,5,6,9999,8,9,0 ) );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select x,
             substr( (select max( to_char(rownum, 'fm0000' ) || column_value  )
  2                                   from table(y)
  3                      where rownum <= 7 ) , 5 )
  4    from t
  5  /

         X SUBSTR((SELECTMAX(TO_CHAR(ROWNUM,'FM0000'
---------- -----------------------------------------
         1 8888
         2 9999

If you are going after one row, this might be better (more clear)

ops$tkyte@ORA817DEV.US.ORACLE.COM> select x, column_value
  2    from (
  3  select x, column_value, rownum rnum
  4    from t, table(y)
  5   where x = 2
  6     and rownum <= 7
  7             )
  8   where rnum = 7
  9  /

         X COLUMN_VALUE
---------- ------------
         2         9999
Review & Followup
Rating: 4
Sad but true April 09, 2002
Reviewer: Ivan Kaselj from Zagreb, Croatia
This was more than enough to realize that it is impossible to reference a field
of an array in SELECT statement.
Since I have more than one table with one to many relationships, and query must
return cca 32000 rows, this is expencive.
That is it is much easier to do it with cursor in PL/SQL and to reference a
field with cursor.Array(7)

Thanks a lot, Ivan

Followup:
Umm, what about:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select x,
             substr( (select max( to_char(rownum, 'fm0000' ) || column_value  )
  2                                   from table(y)
  3                      where rownum <= 7 ) , 5 ), other columns....
  4    from t, t2, t3, t4, ...... where join conditions/etc/etc/etc
  5  /

that gets it for you.

April 09, 2002 Gisela -- Thanks for the question regarding "Create Index with parallel option", version 8.1.6
You Asked
 Is there any improvement if i use: "create index .. unrecoverable parallel 5"
to create an index on a database on nt server with only 1 processor?, why does
parallel 5 means?

Regards
Gisela
and we said...
Parallel 5 means we'll use 5 threads of execution to create the index.

Yes, there can be an improvement depending on your disk subsystem.  An index
build requires lots of:

o reading  (no cpu from the system typically)
o sorting  (tons of cpu)
o writing  (no cpu from the system typically)

So, while some threads are reading and writing, others can be sorting.
Review & Followup
Rating: 3
threads April 08, 2002
Reviewer: Natalia from EU
Sir
Can threads be defined in more simpler words, I don't understand it very clearly
Many Thanks in advance

Natalia

Followup:
using more then one process at a time.  Doing things in parallel.

Think of it like this.  You have to write a book. The book will have 5 chapters.
 You could sit down and write the book yourself -- all 5 chapters.  That is
single threaded.

Alternatively you could find 5 people -- assign a chapter to each.  Each person
now represents a thread of execution.  You have 5 parallel threads of execution
working on the book -- the book will take only as long as it takes to write the
longest chapter now -- instead of the sums of the times to write each chapter.

Rating: 3
April 08, 2002
Reviewer: Gisela from Uruguay
Why do you use 5?  why not use more threads? wouldn 't be faster?

Thanks

Followup:
I didn't use 5, they did.

It is just a number.  change all occurrences of 5 to "n" and use your
imagination.

Would 6 be faster?  maybe, maybe not.  Why 5?  No reason other then it was
"there"

Rating: 4
ashraf April 09, 2002
Reviewer: A reader
SQL> select /*+parallel (notice,5)*/ count(1) from notice;

  COUNT(1)
----------
      1445

Elapsed: 00:00:02.04

SQL> select count(1) from notice;

  COUNT(1)
----------
      1445

Elapsed: 00:00:00.21

why thathappen

Followup:
your tableis trivially small.

Setting up parallel query involves its own overhead.

Let's take my analogy of the book again.  Obviously -- to write a book with 5
chapters, it'll go faster with 5 people rather then one.  Sure, there will be
some up front time where the 6 of you sit together and you divvy up the work -
figure out who will write chapter 1 and what chapter 1 will contain and so on.
Then you all start writing, the 6th person collects the chapters and puts them
together and you get done fast.

You on the other hand are writing a 5 sentence paragraph.  Here the overhead of
coordinating the 5 people -- divvying up the work, putting it back together far
EXCEEDS the time it would take you to just DO IT yourself.

Other things that come into play with this are:

o select count(1) from t is the same as select count(*) from t which can do an
index fast full scan on a primary key index (little data).

o select count(1) in parallel cannot unless the index itself was partitioned.
Hence it probably did a full scan on the table, not the index.  more data.

But in this case I will 100% attribute it to the fact that you have so little
data as to make this example meaningless.  Well, not 100% meaningless -- you
have shown that parallel query DOES NOT in any way shape or form assure you of
"faster answer".  PQ is just a tool -- like a bitmap index, function based
index, partitioning.  Properly applied at the correct time, it can work wonders.
 Used inappropriately -- it'll be like setting "slow=true" in your init.ora.

Rating: 4
Hi asraf April 09, 2002
Reviewer: ak from India
Hi asraf,
    Put hundread of thousands of record,and see how
beautiful the parallel is.

Ak

April 09, 2002 PARTHA -- Thanks for the question regarding "pl/sql Problem", version 8.1.6
You Asked
1) I want's to update the master table based on the values of a Dummy table,
during the process of update i have to check each column value in dummy table
with master table column value ,if dummy table column has a value then update
that particular column,else nothing, i have about 90 columns in master table and
dummy table.

If primary key value in dummy table doesn't exist in master table then do a
INSERT into master table based on Dummy table primary key record

Please provide me one example simialr to above condition.
urgent please

2) should i use sql*loader to overcome above problem, give me your opinion.

Thnaks in Advance.
and we said...
here is one approach:

ops$tkyte@ORA8I.WORLD> create table t ( id int primary key, c1 int, c2 int );

Table created.

ops$tkyte@ORA8I.WORLD> create table dummy ( id int primary key, c1 int, c2 int
);

Table created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> insert into t values ( 1, 1, 1 );

1 row created.

ops$tkyte@ORA8I.WORLD> insert into t values ( 2, 2, 2 );

1 row created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> insert into dummy values ( 2, 3, null );

1 row created.

ops$tkyte@ORA8I.WORLD> insert into dummy values ( 4, 4, 4 );

1 row created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> update t
  2     set (c1,c2) = ( select nvl(dummy.c1,t.c1), nvl(dummy.c2,t.c2)
  3                                  from dummy
  4                             where dummy.id = t.id )
  5  where exists
  6              ( select nvl(dummy.c1,t.c1), nvl(dummy.c2,t.c2)
  7                                  from dummy
  8                             where dummy.id = t.id )
  9  /

1 row updated.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> insert into t select * from dummy where not exists (
select * from t where t.id = dummy.id );

1 row created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> select * from t;

        ID         C1         C2
---------- ---------- ----------
         1          1          1
         2          3          2
         4          4          4

ops$tkyte@ORA8I.WORLD>

In 9i -- this becomes one command -- a simple MERGE will do this.
Review & Followup
Rating: 5
Update or Insert Based on Criteria October 10, 2001
Reviewer: partha
Very useful

Rating: 5
Very helpful to me. April 09, 2002
Reviewer: Ice2002 from China
In one of my PL/SQL procedure,
I use this statement:

update t1 set  c1=nvl((select dummy.c1 from t1 where t1.id=dummy.id),8);

Oracle tells me procedure have compile error.

but I use this statement in SQL*plus ,it works well.

your method give me a solution.Thank you.

Followup:
read
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3027089372477
it talks about this problem and shows the workaround you can use in 8i and
before (not needed in 9i)

April 09, 2002 Reji -- Thanks for the question regarding "Table Partioning - performance gain", version 8.1.7
You Asked
Tom:
   How much performance gain that we can get when we specify the
partition name in the query? Assume that we have a table with 4
partition and each one has 2-3 million records.

If we don't specify the partition name, would you please explain
how oracle server determines which partition to go and get the data?

If you can specify the system views or tables that Oracle server query please
specify that also.

Thx
Reji
and we said...
In general, if the answer from the query with and without the extended partition
name are the same, i would expect NO increase (or decrease) in performance.

Oracle uses predicates against the partition key to eliminate partitions from
consideration.

The tables it queries are the "real" data dictionary -- the tables upon which
the DBA_*, ALL_*, USER_* tables are built.  Too numerous to mention.
Review & Followup
Rating: 3
Could you please give example of "eliminate partitions " October 03, 2001
Reviewer: mike from Atlanta
Tom, Could you give an example in terms of perfermance gain by using the
"ELIMINATE PARTITIONS"

Followup:
consider you have a 100 gig table.

It is in 100 1 gig partitions.

You query:

select count(*) from t where x = 5 and y = 2;

X is not indexed.  Y is not indexed.

X is part of the partition key.

We can now full scan 1 1gig partition instead of 100gigs of data.  that'll be
faster.

Rating: 3
April 09, 2002
Reviewer: kiro
Hi Tom
I think that performance for Partition Tables depend on Optimizer Mode also.
Is it true that only CBO must be used with Partition Tables (Rule based do not
use benefits of Partitioning)?
Please explain?

Followup:
If you have a query that accessed a partitioned table -- you are using the CBO.
You cannot use the rule based optimizer AT ALL with partitioned tables (indexed
organized tables, tables with non-default degrees of parallelism, ..... )

You can see which optimizer a query would use using this simple test in sqlplus.
 Consider these tables:

a@ORA817DEV.US.ORACLE.COM> create table t1 ( x int primary key );
Table created.

a@ORA817DEV.US.ORACLE.COM> create table t2 ( x int primary key ) organization
index;
Table created.

a@ORA817DEV.US.ORACLE.COM> create table t3 ( x int primary key ) partition by
range(x)
  2  ( partition p1 values less than (5), partition p2 values less than (10) );
Table created.

a@ORA817DEV.US.ORACLE.COM> create table t4 ( x int primary key ) parallel;
Table created.

a@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain

a@ORA817DEV.US.ORACLE.COM> select * from t1;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'T1'

Now, that used the RBO.  How can you tell?  The lack of COST, CARD, BYTES is
the key.  Consider:

a@ORA817DEV.US.ORACLE.COM> select * from t2;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=13)
   1    0   INDEX (FAST FULL SCAN) OF 'SYS_IOT_TOP_29548' (UNIQUE) (Cost=1
Card=1 Bytes=13)

Here we see the Cost, Card, Bytes in the plan.  That is an indicator that the
CBO was used and shows that the CBO figures we'll get 1 row back from this query
(in this case).

An IOT forces the CBO regardless of ALL OTHER THINGS

a@ORA817DEV.US.ORACLE.COM> select * from t3;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=164 Bytes=2132)
   1    0   INDEX (FAST FULL SCAN) OF 'SYS_C006314' (UNIQUE) (Cost=1 Card=164
Bytes=2132)

as does a partitioned table...

a@ORA817DEV.US.ORACLE.COM> select * from t4;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=82 Bytes=1066)
   1    0   INDEX (FAST FULL SCAN) OF 'SYS_C006315' (UNIQUE) (Cost=1 Card=82
Bytes=1066)

and a table with a non-default degree of parallelism....

a@ORA817DEV.US.ORACLE.COM> select * from t1, t2 where t1.x = t2.x;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=26)
   1    0   NESTED LOOPS (Cost=1 Card=1 Bytes=26)
   2    1     INDEX (FAST FULL SCAN) OF 'SYS_IOT_TOP_29548' (UNIQUE) (Cost=1
Card=1 Bytes=13)
   3    1     INDEX (UNIQUE SCAN) OF 'SYS_C006312' (UNIQUE)

it is important to note that any query that references ANY of these objects
will find that to be true.  Here we have a heap table plus an IOT - that implies
the CBO will be used

a@ORA817DEV.US.ORACLE.COM>

April 09, 2002 Dhonny -- Thanks for the question regarding "How long do wee need for Upgrade ?", version 8.0.4
You Asked
How long do we need for doing upgrading process from Oracle 8.0.4 to 8.0.6 on
Sun Solaris 2.6, We have a plan to upgrade the version of oracle database but we
also should count how much downtime do we need first, thanks in advance.
and we said...
Well, it won't take much time at all (install software, shutdown, startup with
new software, run upgrade scripts) HOWEVER....

You should be able to answer this question yourself after you PRACTICE the
upgrade on a TEST system and RUN ALL OF YOUR applications on it to
TEST THEM and look for things that DON'T WORK.

If you don't do that, the upgrade process could take days.  Something will go
wrong somewhere.

I would give myself a week probably at least to

o install and find any issues (and time this)
o full backup  (and time this)
o migrate and find any issues (and time this)
o test all applications to find any potential issues (no need to time this,
  you'll fix these and roll them out with the upgrade)

If you don't do that, it could be a very very long week for you.

(at the very very least, do the full backup -- TEST that you can restore it and
then do the upgrade)
Review & Followup
Rating: 3
How long do we need for upgrade April 09, 2002
Reviewer: Dhonny Handayana from Indonesia
it very usefull to doing some preparation fo Upgrade process, Thank you for the
answers.

April 09, 2002 Michael -- Thanks for the question regarding "Refresh Group of Snapshots", version 8.1.7
You Asked
Hi Tom,

I have another question about refresh group snapshot.

In a referesh group, let's say, there are more than two objects. Will Oracle
make sure all objects in the refresh group refreshed suceessfully and committed
so that none of them failed refreshed while other group members finished
refresh? Will Oracle rollback the refresh of all members if one of the refresh
group member failed refresh?

Besides, I would like to know what is the main usage of Snapshot Group?

Regards,
Michael
and we said...
yes, that is exactly the sole purpose of the refresh group.  to ensure all
objects in the group are refreshed at the same time, in the same transaction,
consistently.
Review & Followup
Rating: 4
Oracle cannot do query rewrite after MV refresh April 09, 2002
Reviewer: She from US
I have 2 MV : which I refresh using a refresh group. One of them has query
rewrite enabled. After I do refresh, query rewrite doesnt work. If I drop and
recreate the same MV - it works again. Why ?

Followup:
example please.  "doesn't work" doesn't compute.  it is too vague.  Give me a
really small example to work with (lots of times, when I make up my small
examples -- I find the step or process that causes the issue in the larger
example).

April 09, 2002 Kumar -- Thanks for the question regarding "LIKE with subquery", version 8.1.7
You Asked
drop table empl;
drop table grps;
drop table usrs;
drop view unq_valc_v;
drop view unq_valn_v;
drop view empl_v;

create table empl (enum varchar2(17) not null primary key, ename varchar2(30),
sal number, addr varchar2(100));

create table grps (grp varchar2(30), valc varchar2(30), valn number);

create table usrs (usr varchar2(30), grp varchar2(30));

insert into empl values ('A1001', 'Sunday', 107, 'ABC International');
insert into empl values ('A1002', 'Monday', 100, 'ABC International');
insert into empl values ('B2003', 'Tuesday', 90, 'ABC International');
insert into empl values ('B2004', 'Wednesday', 76, 'ABC International');
insert into empl values ('D4005', 'Thrusday', 88, 'ABC International');
insert into empl values ('D4006', 'Friday', 23, 'ABC International');
insert into empl values ('D4007', 'Saturday', 55, 'ABC International');

insert into grps values ('GRP1', 'A10%', 0);
insert into grps values ('GRP2', 'B20%', 0);
insert into grps values ('GRP4', 'D40%', 0);
insert into grps values ('GRP14', 'A10%', 0);
insert into grps values ('GRP14', 'D40%', 0);

insert into usrs values ('SCOTT', 'GRP1');
insert into usrs values ('SCOTT', 'GRP2');
insert into usrs values ('SCOTT', 'GRP14');

commit;

create index grps_idx1 on grps(grp);
create index usrs_idx1 on usrs(usr);

create or replace view unq_valc_v as select distinct valc from grps g where
exists (select 'X' from usrs where usr = user and grp = g.grp);
create or replace view unq_valn_v as select distinct valn from grps g where
exists (select 'X' from usrs where usr = user and grp = g.grp);

[create or replace view empl_v as
select * from empl where enum like (select valc from grps where grps_v);]

Our existing security is based on views, but these views are hard coded and end
user has to create thousands of views. If there is any change in table then they
have to recreate all those views, all this is really very time consuming job and
I found there are some performance issue too. So I want to create these views
more dynamic and should have good performance. In following example I have one
emp table, one group and one user table. User table tells you about allowed
group and group table gives search criteria. These search criteria can be used
with LIKE clause only. After that I have created a view to get unique search
criteria and now I have to apply these unique search values on emp table. But
got confuse how to apply LIKE clause with sub query?

Need your help. I'll prefer if same solution can apply on Oracle7/8+

Thanks in advance.
and we said...
You are a primary candidate for VPD:
http://osi.oracle.com/~tkyte/article2/index.html
it is new with Oracle8i and up (not 7, not 8.0).

Otherwise, you are creating views as you are currently.  There are no real
shortcuts there.
Review & Followup
Rating: 5
gr8, but ur rep ref's VPD, wht do u mean? (:>) April 09, 2002
Reviewer: J from ma
Just kidding

Followup:
Acwonyms awe one thing, using wettews fow weguwaw wowds (u fow you and uw fow
youw ow you'we ow w u fow awe you) is something compwetewy diffewent!
Twanswation couwtesey of
http://www.rinkworks.com/dialect/
Oh, dat scwewy wabbit!  the diawectizew!

(that being the Elmer Fudd -- the only really politically correct one I could
use -- dialect translation of:

Acronyms are one thing, using letters for regular words (u for you and ur for
your or you're or r u for are you) is something completely different!

Translation courtesey of
http://www.rinkworks.com/dialect/,
the dialectizer!

VBG
YMMV
ROTFL
OTOH, maybe not ;)

Rating: 5
April 09, 2002
Reviewer: A reader
Ooh that was real funny..!! A welcome deviation from the otherwise matter of
fact tone you follow Tom. We enjoy these lighter moments....

April 09, 2002 Sue -- Thanks for the question regarding "Schema Sets", version 8.1.6
You Asked
Tom,

I am a consultant working in an environment where there is a need to quickly
create "schema sets" (multiple interlocked schemas) on one instance (usually our
DEV or TEST environment).  When I say "interlocked", I mean I have an object
(say, a PL/SQL procedure) in SCHEMA1 that references another object in SCHEMA2.
I refer to these interlocked schemas as our legacy schemas because they were
applications that were built as prototypes and over time, were pieced together
into a suite of applications that work closely together.  In order to meet the
demands of the various development teams, we create a "prefixed set" for each
team.  For example, team A gets the schema set with the following schemas
(A_SCHEMA1, A_SCHEMA2, A_SCHEMA3, A_SCHEMA4) and B gets (B_SCHEMA1, B_SCHEMA2,
B_SCHEMA3, B_SCHEMA4).

Are there tools out there (or some way) that would allow us to maintain one
"master" repository of database source code and from that, be able to
dynamically allocate storage constraints and replace the schema references (say
in the PL/SQL objects, etc) so that the schemas are properly created and
isolated from all the others?  This would save us from having to manually search
and replace or run perl regular expressions on the source code which is a bit
error prone.

Thanks in advance!
Sue
and we said...
I don't know what you me by "so that the schemas are properly created".  The
schema name is just part of the object name...

But anyway, what I've seen people do is to use

&SCHEMA..EMP

in their code instead of SCOTT.EMP.  That way, when they install their code,
they define SCHEMA=scott (or whatever) and sqlplus will replace it for you.  It
won't fix anything retroactive (i don't know of anything that could do that) but
would allow you to change the schema at the drop of a hat later on.
April 09, 2002 Irfan -- Thanks for the question regarding "Memory used by Instances", version 7.3.4
You Asked
Hi Tom,

a) What is the best way to find out the physical memory + swap  consumed by a
instance in a database, other than the operating system commands. Please guide
us on finding out seperately physical and swap.

b) Approximately how much should one instance (considering three instances
running on three unix boxes) consume memory in case if the database size is
20GB.

Thanks.
and we said...
a) well, hopefully it is not using swap.  but in any case, to find out what sort
of "swapping" you are doing -- well, thats 100% an OS thing.  We can tell you
nothing about that.

You can see the size of the SGA (v$sgastat).  You can see the amount of PGA
memory (v$sesstat) and UGA memory (same).  Thats the memory we are using.  As to
whether it is real or virtual memory -- you'll need to ask to OS that.

b) database size has ZERO bearing or relevance on memory used -- none
whatsoever.  It has everything to do with

o number of concurrent users
o how much code your coders make us use above and beyond what we use (eg: use
tons of plsql tables - we'll consume some extra ram for that and so on)
April 09, 2002 Alla -- Thanks for the question regarding "Index-organized tables", version 8.1.7
You Asked
Considering to implement index-organized tables for
tables that only have primary key in it (only plain 2 number columns).
Are there any downsides to it? Limitations? Often index rebuilts are a must
then,
isn't it?

Thank you
Alla
and we said...
Well, I find index rebuilds to be soooo far and few between that I don't even
ever really do them!  Most of the time people rebuild indexes solely because
they think they have to, not for any scientific or measurable reasons.

I would say -- test it, benchmark it.  That is what I would do (even with my
knowledge of IOT's -- i would still have to do that).

If you have my book -- i go into their structure and best uses (as well as all
other table types)....
April 10, 2002 Munzer -- Thanks for the question regarding "Format Address", version 8.1.7
You Asked
Tom:

1.  I have a table that has address infomration for vendors.  I need to create
one variable that has address information for a given variable.

TABLE VENDOR:
VENCODE   VARCHAR2(8)
NAME      VARCHAR2(50)
ADDRESS_1 VARCHAR2(50)
ADDRESS_2 VARCHAR2(50)
CITY      VARCHAR2(30)
STATE     VARCHAR2(30)
ZIP       VARCHAR2(30)
COUNTRY   VARCHAR2(30)

Would you create a function with (ven_code) as IN parameter and (address) as OUT
parameter.

How do you create the hard returns between the 4 lines?

Also, this table is in a second instance and I am using a link to get data from
it.  Can you declare variables with same type as a column in a different
instance or you have to hard code the types.

Thank you,
and we said...
I would just
create view v as
select vendor.*,
       name || chr(10) ||
       address_1 || ' ' || address_2 || chr(10) ||
       city || ', ' || state || ' ' || zip || chr(10) ||
       country formatted_address
from vendor;

and let people use V instead of Vendor.

A chr(10) is an ascii linefeed (newline).  chr(13) || chr(10) is a DOS carriage
return/linefeed.

you can define variables of a type based on a remote object.  If you create the
view, you'll find you can use the VIEW instead (the view will import the meta
data from the remote site) and it'll let you compile faster.
Review & Followup
Rating: 5
Fromat address April 01, 2002
Reviewer: Munz from Reston, USA
Tom:

Basically you are suggesting to create a view and the nuse

FOR x in (SELECT * from view where ....)
LOOP
htp.p(formatted_Address);
END LOOP;

Is this valid?

2.  How can you declare a variable to a remote column. I tried

v_name   org@link.name%type;

It does not work?

3. Can you join a table to a view the same way joining two tables together, or
joining two views together.

Thanks,

Followup:
1) yes, that is what I am saying.

It is not only valid, it makes sense, is easy to do and very common.

2)
scott@ORA815.US.ORACLE.COM> declare
  2          l_empno scott.emp.ename@ora817dev%type;
  3  begin
  4          null;
  5  end;
  6  /

PL/SQL procedure successfully completed.

3) a view is for all intents and purposes a table.  Yes, you can join a view to
a table, a table to a view, a view to a view, etc.

Rating: 5
format address April 08, 2002
Reviewer: Munz from Reston, USA
Tom:

1.  If address_1 is null I do not want chr(10).  Is there a way to base a
condition on putting a hard return based on existence of a value in field.
(bascially opposite NVL() ).

2. can you include pl/sql code in a view creation or not?

Followup:
create view v as
select vendor.*,
       name || chr(10) ||
       address_1 || ' ' || address_2 || decode(address_1, null, '', chr(10))
||
       city || ', ' || state || ' ' || zip || chr(10) ||
       country formatted_address
from vendor;

Yes, you can call a plsql function from SQL if you like, even in a view.

Rating: 5
format address April 09, 2002
Reviewer: Munz from Reston, USA
Tom:

Thanks.

I did

htp.p(decode(address_1,null,' ','<BR>');

i got an error that says decode can only be used inside SQl statement
(PLS--00204).

Do i always have to create a view?

Thanks

Followup:
or

select decode(address_1,null,' ','<br>') into l_str from dual;
htp.p( l_str );

Rating: 5
Format Address April 09, 2002
Reviewer: Munz from Reston, USA
Tom:

Do you mean:

select decode(address_1,' ',address_1||'<br>') into l_str from dual;

Would this be faster than using IF statment to test for null value of variable
and iether ptint it or not?

Followup:
No - do YOU mean to do that.

You asked how to do:

htp.p(decode(address_1,null,' ','<BR>');

which to me means "if address_1 is null, put out blank, else do a <br>"

If you need to put out address_1 || '<br>', then you mean to do what you did
right above.

Me, I would put it right in the query -- I would select decode( address_1, null,
'', address_1||'<br>' ) address_1 ...... from table

Rating: 5
ashraf April 10, 2002
Reviewer: ashraf
in form how can i validate the email address if user input an invalid email so
beside the input i make a red hint please check email address

Followup:
I don't know, you'll have to tell me what your rules are for validating an email
address.

This is a pretty simple "programming exercise".

April 10, 2002 dw -- Thanks for the question regarding "About Temporary tables", version 8.1.5
You Asked
Hi tom...

Actually, ive never created any temporary table till now, i just like to ask if
when creating the table, on what tablespace will the temporary table be stored?
i need to consider the space to make sure that the creation is successful... and
by the way, can you site an example on creating a temporary table on the
duration of a session??

Thanks and God bless..
and we said...
search for

create global temporary table

to see examples.  I have many.

When you create a temporary table -- it will not use ANY tablespace.  It will
use the TEMPORARY tablespace of the current schema when accessed at runtime.
When you create a temp table -- no space allocated.  when you insert into temp
table, the space will be gotten from your temporary tablespace (or if the
temporary table is used in a stored procedure with definer rights - the
temporary tablespace of the owner of the table)
Review & Followup
Rating: 3
What about storage April 09, 2002
Reviewer: G Rama Subramanian from Bangalore, India
Hi Tom,

If the temporary table resides in the Temporary Tablespace during runtime, it
means that the Temporary tablespace should be large enough to accommodate all
the extents that might get created if the data stored at runtime in the
temporary table is going to be huge.  Does this create any sizing issue?  We
have a temporary tablespace, that does not take this aspect into account.  I now
think that I should add sufficient space by adding a datafile to the temporary
tablespace.  Am I right?

Followup:
Yes, TEMP needs to be sized appropriately to hold these things as well.

Rating: 4
What are all the drawbacks (performance), if they hold different datatype April 09, 2002
Reviewer: Prince
Tom,

Say I have the TEMPORARY table with one varchar2 column.

create global temporary table gtd_tab1 (col1 varchar2(30)) on commit delete
rows;

Is it better to use two different TEMP tables, if I want to keep NUMBER data
type in one transaction and VARCHAR2 data type in another transaction? or is it
OK, if I just share the same table?

Thanks,

Followup:
well, that table can only hold varchar2 data, it cannot hold a number type.

If you add a number column, it'll be just fine to use the single table --
holding numbers one time, varchar2's the next.

Rating: 5
ARCHIVE LOG April 10, 2002
Reviewer: ashraf
WHAT IS THE IMPACT TEMP. TABLE ON ARCHIVE LOG.

CAN YOU GIVE EXAMPLE

Followup:
WHY DO YOU SHOUT.

It will have no extraordinary impact on the logs.  If you have my book -- I
cover this in great detail.

If all you do is INSERT and SELECT from the temp tables -- very very little redo
is generated.  We do not generate any redo to REDO the insert, but we do
generate rollback (undo) information and this undo is protected by redo.

Hence, if you INSERT into a temp table, a very little amount of UNDO will be
generated (in the event you rollback, we need to undo the insert as well).  That
UNDO will be protected by REDO.  The INSERT itself will not be.

It is only when you start massively updating or deleting this temp object that
large amounts of redo would be generated.  For example, suppose you put
1,000,000 rows into the temp table.  That insert would generate little UNDO and
correspondingly little REDO.  Now, suppose you "delete from temp_table" -- that
will generate a massive amount of UNDO (in order to rollback the delete if you
need) and that UNDO will be protected by REDO.

Since temporary tables are used almost 100% for INSERTS and SELECTS, this is
just fine.  You should rely on a COMMIT, TRUNCATE or end of SESSION to "delete"
the data and that will have no effect on the logs since the data will just be
"wiped out" -- not logged.

Rating: 5
good one on the shout , I guess you are going to teach them many things i guess. April 10, 2002
Reviewer: Guest from India

Rating: 3
Redo and Rollback on Temporary Table June 11, 2003
Reviewer: kumar from India
Tom,

We have 9iR2 on NT. When I was doing some tests on global temporary tables, I
got the following results.
-------------------------------------------
TableType           Normal    Temporary
-------------------------------------------
Redo Generated      3035912   3396384
-------------------------------------------
Rollback (blks)     13        223
-------------------------------------------
The database in running in archivelog mode. I did a normal insert into table
select * from all_objects; As per my understanding the redo generated for a
temporary table should be much less than for a normal table..and also the
rollback generated was quite dramatic.. But I am not able to understand why I
got the above results.. Further, I did the same test with direct mode insert.
This time I got the results as below:
-------------------------------------------
TableType           Normal    Temporary
-------------------------------------------
Redo Generated      3247980   224
-------------------------------------------
Rollback (blks)     1         1
-------------------------------------------

Though the second set of results was understandable I could not understand the
first test.
Can you please explain why I am getting the above results ?

Rating: 5
Yeah, I can replicate this as well! August 28, 2003
Reviewer: Martin from UK
SQL> CREATE GLOBAL TEMPORARY TABLE gtt ( a VARCHAR2(10) ) ON COMMIT PRESERVE
ROWS;

Table created.

SQL> INSERT INTO gtt SELECT 'x' FROM all_objects;

30568 rows created.

Statistics
----------------------------------------------------------
         25  recursive calls
      31273  db block gets
     139489  consistent gets
          1  physical reads
    3806280  redo size
        880  bytes sent via SQL*Net to client
        663  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
      30568  rows processed

SQL> CREATE TABLE t ( a VARCHAR2(10) );

Table created.

SQL> INSERT INTO t SELECT 'x' FROM all_objects;

30569 rows created.

Statistics
----------------------------------------------------------
        109  recursive calls
        444  db block gets
     139536  consistent gets
          0  physical reads
     389604  redo size
        880  bytes sent via SQL*Net to client
        661  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
      30569  rows processed

Why is there a massive difference in the redo generated for these statements
(given what's been said above)?

Thanks in advance

Rating: 5
Sorry, forgot to mention versions.... August 28, 2003
Reviewer: Martin from UK
SQL> SELECT * FROM v$version;

BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.3.0 - Production
PL/SQL Release 9.2.0.3.0 - Production
CORE    9.2.0.3.0       Production
TNS for 32-bit Windows: Version 9.2.0.3.0 - Production
NLSRTL Version 9.2.0.3.0 - Production

April 10, 2002 Sukumar -- Thanks for the question regarding "How does Read only transactions work internally", version 8.1.7
You Asked
Hi,

In the following example on "Read Only transaction" testing on Oracle 8.1.7
(Windows NT 4.0 platform)

The table "testread" in this example
has a column teststring char(25)

SQLPLUS Session 1
=================

commit;
insert into testread values('testdata1')
commit;

SQLPLUS Session 2
=================

commit;
set transaction read only;
select * from testread;

Results are :

teststring
-----------

testdata1

SQLPLUS Session 1 continued
===========================
insert into testread values('testdata2');
commit;

SQLPLUS Session 2 continued
===========================

select * from testread;

Results are : (again it displays the same results since we have not committed
yet for Session 2 to end the Read only transaction)

teststring
-----------

testdata1

SQLPLUS Session 3 (A brand new session to the same database)
=================
commit;
set transaction read only;
select * from testread;

Results are :

teststring
-----------

testdata1
testdata2

From this example, one can learn how Read Only isolation is neatly performed by
Oracle.

The question to you is how does Oracle accomplish "Read Consistency" in the
above scenarios.
i.e. Readonly transaction in Session 2 is different from Session 3. The row
testdata2 for the second insert was seen only by Session 3
and not by Session 2, since it was started just after the first insert of row
testdata1 (i.e. after the first insert was committed)

How does Oracle maintain consistency of the data for these 2 sessions in a
consistent fashion until you end the "read only transaction" by committing it.
i.e. Any number of selects in Session 1 before commit always returns "testdata1"
and does not include "testdata2".

Is it from the REDO logs ?

What are the sideeffects of using this. Does the inserts into the datablocks
take longer than usual, since there are multiple "read only transactions" that
are reading them.

Please explain a little bit on the internals if you can

Thanks a lot

Sukumar Alagappan
and we said...
The concepts guide does this as good as I could:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c23cnsis.htm#17882
the concept guide is the BEST document with the LEAST number of people reading
it for some reason.  I read it myself with every major release.  Invaluable.

We do this with the UNDO (not redo) information.  We just roll back work.

The inserts take no longer, but as time goes on -- it may take the read only
transaction longer and longer to roll back and eventually you may hit an
ora-01555 (search this site for 1555 or 01555 for details)
Review & Followup
Rating: 4
April 09, 2002
Reviewer: A reader

Rating: 4
This looks familiar.... April 09, 2002
Reviewer: Mark J. Bobak from Dearborn Heights, MI
Or you could just check Doc ID 338634.999 on MetaLink,
which is a forum thread where you asked the same question
and I answered it in detail...and also referred you
to the Concepts manual....;-)

-Mark

Followup:

Rating: 5
RE: This looks familiar... April 10, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
Mark:

Surely you don't expect people to actually read the Oracle documentation.
Unheard of!  I mean, what else would Tom do?

Just look at today's questions...  "svrmgrl is gone in 9i???  Gosh, what do I
do???"  Tom must be "ghost writing" a lot of applicationsl...

(Of course, this is somewhat tongue-in-cheek, as I make my fair share of
mistakes, etc...)

- Mark

Rating: 4
This looks familiar April 10, 2002
Reviewer: Mark J. Bobak from Ann Arbor, MI
Yeah, we all do make our fair share of mistakes....

But I agree with Tom that the Concepts manual is the most
valuable and overlooked piece of documentation out there.

Rating: 5
Concepts Guide Rules! September 03, 2002
Reviewer: Debi Lorraine from Davis, CA USA
A developer asked me an "obscure" question about how to achive "read
consistency" throughout a session.  We both thought of "asking Tom" but alas,
Tom was not taking any questions at the time.  A search of the archives and
asking myself, "What would Tom Do?" (WWTD) led me to the concepts guide
recommended here, and to the ultimate answer:

alter session set isolation_level = serializable;

Yes, I am convert to reading the concepts guide!  Sorry, I have strayed
~somewhat~ from the topic...

April 10, 2002 marwa -- Thanks for the question regarding "controlling display properties", version 8.1.5
You Asked
hi tom
it's a long time while trying contacting you

question 1:
-----------
i'm using developer 6i
how can i controlling the display properties of windows 98 from within the
developer 6i to specify the screen resolution by example i want my application
to run only in resolution 1024x768 on the client side.

question 2:
-----------
how can i change the registry (ex. nls_lang) during runtime so my application
can run with different settings dependent on users

thanks in advance

waiting for your reply

sincerely
marwa
and we said...
question 1) you can query the screen resolution and exit if you don't like what
they have it set it (give them a good error message)

You can do this with get_application_property -- the display width and height.

question 2) by the time your form is running, it is already too late (registry
has been read). Multiple oracle homes and other things will further complicate
this (you could really mess up the machine big time).

Since the environment will override the registry, suggest you setup scripts (bat
or cmd files) that:

@echo off
set NLS_LANG=whatever
ifrun60 .....

in order to achieve your goal.  You could have a form that does a "host" to the
right script and exits immediately or have the user click on the right "cmd/bad"
file for their language.
Review & Followup
Rating: 4
ABOUT QUESTION 2 April 10, 2002
Reviewer: MARWA ELNASHAR from EGYPT
I'VE MADE A FORM TO CALL OTHER FORMS BY PRESSING A BUTTON
I WRITE THE FOLLOWING IN THE WHEN BUTTON PRESSED TRIGGER:

host('alter SESSION
SET NLS_LANGUAGE=EGYPTIAN
NLS_TERRITORY=EGYPT');
HOST('IFRUN60 D:\EXAM\EMP.fmX SCOTT/TIGER');

HINT:
NLS_LANG IN THE REGISTRY IS AMERICAN_AMERICA.AR8MSWIN1256

AND THE DEFAULT APPEARANCE IS FROM LEFT TO RIGHT
________________________
WHEN I PRESS THE BUTTON IT CALLS THE FORM EMP
BUT IN THE SAME DIRECTION (FROM LEFT TO RIGHT) NOT (FROM RIGHT TO LEFT) AS I
WISH

I'M NOT SURE THAT I UNDERSTAND THE FOLOWING PART OF YOUR ANSWER WELL :
"in order to achieve your goal.  You could have a form that does a "host" to the

right script and exits immediately or have the user click on the right "cmd/bad"

file for their language.   "

WAITING FOR YOUR HELP
MARWA

Followup:
What I mean is you would setup a script (say a cmd file) like this:

@echo off
set NLS_LANG=AMERICAN_AMERICA.WE8ISO8859P1
ifrun60 .......

That is what your users would run to start forms.  You need to have the NLS_LANG
sertup *before* you start forms.

You can then have some "master" form that runs the correct cmd file.

April 10, 2002 Prashant -- Thanks for the question regarding "Using indexes to order data (and avoiding order by)", version 8.0.4
You Asked
I want to avoid an order by in a pro*c cursor. I know I can do
this with an appropriate index on a single table select if the
right clauses are included in the where clause and the right
fields are pulled, but I want to do something more complicated.

The select is a join on two tables. Both tables have a composite
index, in which the leading column is the join column, and I
want the data ordered by this column. The select list includes
only index columns from one table and index cloumns plus other
columns from the other table. I can make Oracle use the two
indexs by using index hints, so I have an index_ffs pull on one
table and an index access followed by table lookup on the other
table (as indicated by explain plan).

The data returns ordered (by the join column) even if I do not
put an order by clause on the select. This is true of any join
method (nl or hash join) as long as I do not use parallel. If
however, I put the order by clause in, a sort is indicated on
the explain plan and the performance is much slower. Oracle does
not seem able to avoid an explict sort, even though it seems to
me that the data *must* be ordered already.

The Question:
Given that the tables are joining on a column which is the first
column in both indexes, and that the hints force Oracle to use
these indexes, can I make the assumption that Oracle will always
return me the data ordered by the joining column even if I do
not use the order by clause, and will this be true even if I use
hash join instead of nested loop?
and we said...
Hints are just that -- Hints.  They may (and will be) ignored at
the whim of the server. Not only that -- but a fast full scan
does not return data sorted (proof by simple example below).

Your ONLY option for a correctly coded application, if you need
the data sorted, is to use an ORDER BY.  A subsequent upgrade,
someone analyzing statistics (or analyzing them differently then
they do today), data skew over time, any number of things -- can
and will change the plan (even if it works).  Never rely on a
"plan" to have the data ordered -- you can ONLY rely on order by
to order the data.

Fast full scans on the index are defined in this fashion
(designing and tuning reference):

<quote>
This is an alternative to a full table scan when the index
contains all the columns that are needed for the query, and at
least one column in the index key has the NOT NULL constraint.
Fast full scan accesses the data in the index itself, without
accessing the table. It cannot be used to eliminate a sort
operation. It reads the entire index using multiblock reads
(unlike a full index scan) and can be parallelized.
</quote>

<quote>
Using Fast Full Index Scans

The fast full index scan is an alternative to a full table scan
when there is an index that contains all the keys that are
needed for the query. A fast full scan is faster than
a normal full index scan in that it can use multiblock I/O and
can be parallelized just like a table scan. Unlike regular index
scans, however, you cannot use keys and the rows will not
necessarily come back in sorted order. The following query and
plan illustrate this feature.
</quote>

Your data is currently perhaps being returned in sorted
order TODAY but it does not have to be and will not be over
time!!!

Here is the conclusive proof of that:

ops$tkyte@DEV816> create table t
  2  ( x int not null,
  3    a char(2000) default rpad('*',2000,'*') not null,
  4    b char(2000) default rpad('*',2000,'*')
  5                  )
  6  /

Table created.

ops$tkyte@DEV816> insert into t(x) select rownum from
all_objects where rownum < 10
  2  /

9 rows created.

ops$tkyte@DEV816> create index t_idx on t(x,a)
  2  /

Index created.

ops$tkyte@DEV816> begin
  2      for i in 1 .. 5
  3      loop
  4          delete from t where x = i;
  5          commit;
  6      end loop;
  7  end;
  8  /

PL/SQL procedure successfully completed.

ops$tkyte@DEV816> begin
  2      for i in 1 .. 5
  3      loop
  4          insert into t (x) values (i+100);
  5          commit;
  6      end loop;
  7  end;
  8  /

PL/SQL procedure successfully completed.

ops$tkyte@DEV816> set autotrace on explain
ops$tkyte@DEV816> select /*+ INDEX_FFS(t t_idx) */ x from t
  2  /

         X
----------
       101
       102
       103
         6
         7
         8
         9
       104
       105

9 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=82
   1    0   INDEX (FAST FULL SCAN) OF 'T_IDX' (NON-UNIQUE)

This is why we do the explicit order by at the end -- an index
fast full scan does not return the data sorted in general!!!!

There is many dozens of ways to get the results above and it
will happen naturally as the index grows and splits over time --
an index fast full scan is most definitely NOT sorted.
Review & Followup
Rating: 5
April 10, 2002
Reviewer: A reader
hi Tom,

 Can we use index to sort data?

  We are using siebel CRM applications(6.2). and we are doing lot's of sort in
application. siebel application only use RULE base optimizer.
  In one of the table(1 million rows) we sort by
  create_date desc and activity_start_Date desc.

  it is taking long time.If i remove sorts then result are very fast.

  So can i created composite index in descending order like.

   (create_date desc,activity_start_Date desc).

 Will it improve performance on query?

 Thanks in advance.

Followup:
You need to ask a different tom -- tom siebel.

Do they support you adding indexes.

Adding indexes may affect other components of Siebel (ouch, that hurts to
type)...

Yes, you can create such an index (an index with DESC in it will only be used by
the CBO, not the RBO).

I cannot imagine what you would DO with 1 million rows though!!! that seems like
alot (about 999,900 more then i would want to see sorted on a screen myself)

Rating: 5
April 10, 2002
Reviewer: A reader
Tom,

 Why you guy's hate each other(oracle and siebel).

 If i ask anything to siebel people they tell us USE
 IBM DB2 which is better then oracle.

 You are telling me to go to tom siebel.

 Here is my response.

  Personally i feel oracle is THE BEST database(i work with DB2 around a year).

  about CRM applications, SIEBEL is much better then ORACLE CRM .it use oracle
forms which i don't like (I worked with forms about 2 year(version
3.0(Character) and 4.5(GUI) ), and i found VISUAL BASIC is the BEST for front
end applications.

 any comments?

Followup:
Well, they are the competition -- It pains me to write "sqlserver" as well.
It's supposed to be a joke.

I have to tell you to go to siebel for this one.  I cannot answer the
question.  I was not brushing you off saying "goto siebel if you want to use
that stuff".  If I could answer the question, I would.

I can say "yes, you can definitely put that index on that table, it will work
from the perspective of Oracle".

I cannot say "and your siebel software will be unaffected, their support will be
100% ok with this, and no undesirable side effects will occur".  That I cannot
say.  I quite simply do not know what siebels policy on mucking about with their
database objects are.

Siebel tells you to go IBM because IBM doesn't sell CRM software, IBM sells
services that will implement Siebel CRM.

Its been a very long time since you've seen Oracle's tools then.  Forms 4.5 was
ending about 1995 (about when the web came along).

I find the WEB to be the best for front end applications personally.  If you
want to install and patch and reinstall and maintain an application on 40,000
desktops -- thats up to you.  Me, nope, won't do it.  I haven't used client
server since about 1995 myself.  VB, never ever used that for anything at
anytime (C, wrote lots and lots of C)

April 10, 2002 Montasser -- Thanks for the question regarding "Blocks based on stored procedures ", version All
You Asked
Hi TOM
Can you explain me why it is recommended to use block based on stored procedures
in developer.
is it recommended for web applications or for all kind of applications . Please
precise the difference and the advantages.

Thanks for the help you give us.
and we said...
I did not realize is was "recommended".  It is there as a feature but it is
harder to do (gotta write code) then to just use a default block.

Some of the nice aspects:

o end users do not have to have select/insert/update/delete on the tables --
only the package that forms uses.  This means they must use your application to
access the data, they cannot ad-hoc it in sqlplus for example.

o you can add fancy auditing to the package to see what people are doing

o you control the logic in the database -- find a bug in a query, fix the
package and your application is fixed.

Downside:

o it is not as automatic as a default block, you have to write code.
Review & Followup
Rating: 4
July 10, 2001
Reviewer: A reader

Rating: 3
Learn more October 24, 2001
Reviewer: Nilton Maganha from Brasil, South America
I really would like to know what exactly I should write in each forms based in
procedure, to improve forms execution. Particulary what should I do in LOCK
procedure?

Rating: 2
Oracle Forms' Stored Procedure Data Blocks April 10, 2002
Reviewer: Geeta R from Mumbai, INDIA
i tried this,
but the query procedure which i wrote on emp table,
does not work if i pass the argument for deptno,
with out that if only one out parameter of ref cursor is
used for returning the result set it works,
what is it that i am missing while passing the argument
of deptno from data source argument, that it just does not work
I also tried where clause in form property but it does not work,
what do i have to do this to work for a in argument.
Pls help, though i know i should not aks a question in review column but then it
has not been useful comletely,
and i am unable to understand where what i am missing to pass.

Regards,
Geeta

Followup:
well besides "does not work" being about as vague as we can get.... without a
reproducible example and an explanation of

a) what you feel should happen
b) what actually does happen

there is no way anyone could help you.

Rating: 4
Some more opinions April 11, 2002
Reviewer: Paul Sharples from Exeter, UK
In addition to Tom's nice aspects, I would add:

o You can write unit tests for business rules implemented in the database.

o You can denormalise the relational data and make the Forms block work with
richer entities. This is sort of like using object views with the trigger code
moved into the stored procedures. I originally tried to base my app on object
views but our version of SQL*Plus (3.3.4) couldn't deal with objects reliably
(and SQL*Plus 8.1.5 fails to connect with a TNS:protocol adapter error, but I
digress...)

o You get to edit your business code in any number of superior alternatives to
the Forms PL/SQL Editor (Toad, SQL Navigator or - from SQL Plus - Emacs, vi or
even... Notepad!)

It's just better to architect your application with the business logic close to
the data. I see too many Forms with business rules locked into Forms program
units or shared libraries which is unavailable to the database. In fairness,
many of these were written when Forms 4 was young, but people are slow to
embrace progress. I would go so far as to suggest that anyone [developing with
Forms 5 and 8i or better] who puts business logic anywhere in a Form should be
marched into the parking lot and shot (actually, no, just flayed; everyone
deserves a second chance).

I fully endorse Tom's comment that building Forms this way is more work. If you
want to save hours of pain, remember the following:

  * If you recompile your stored package spec, reconnect
    from Forms before attempting to generate (Forms will
    crash or generate Internal Errors; restart Forms).

  * Save early and often. Save before generating,
    compiling, fetching a drink or blinking.

  * The Data Block Wizard is your friend. In particular,
    it saves you the pain of having to specify the columns
    using the property editor and it doesn't trash the
    properties of the items already in place. You find
    yourself needing to use the Wizard when you change the
    record type on which your block is based (which happens
    a lot during initial development).

IMO, the benefits of testability and maintainability outweigh these
comparitively minor inconveniences.

Cheers.

April 10, 2002 -- Thanks for the question regarding "Numbers vs Varchar", version 901
You Asked
Hi Tom

I am using Oracle 901 on solaris.
My question is as folllows:
Between the Number and Varchar datatype which one is better performance wise .
What are the pros & cons of having numbers or char/varchar as primary/joining
keys  ? Is the conception of searches on number types faster than varchar TRUE?

Thanks
and we said...
You want my opinion --

you should choose a datatype based on the data that is to be contained therein,
not based on anything else.

Are you going to put numbers in there?  then by all means - -use a number.

Is it going to be a date?  better use a DATE or TIMESTAMP.

Can it be an arbitrary string of letters, numbers, etc -- use a varchar2.

Numbers are stored as varying length strings in the database. You'll find in
general it is pretty much the same using either/or.  Pick the datatype SOLELY
based on the data contained therein.
Review & Followup
Rating: 3
Followup April 10, 2002
Reviewer: A reader
In one of your articles on keeping the varchar2 datatypes not very huge such as
varchar2(4000) you stated.
Quoting your example :
"Ok, so the code prepares a query that select 10 columns that are varchar2.
The developer, for performance would like to array fetch (very important).  They
would like to array fetch say 100 rows (very very typical).  So, we have 4000
times 10 times 100 = alsmost 4meg of RAM the developer must allocate!!"

Now if we have a number datatype " number " which has a default precision of 38.
It would to the
maximum take 22 Bytes.
And if we have a column varchar2(38) it would 38KB.

If we do the above array fetch 22*10*100= 22KB
38000*10*100= 38MB of RAM

So this is a huge difference in the amount of memory being allocated to a number
vs varchar2 datatype.

Please comment if Im right in stating the above.

Thanks

Followup:
I was talking PRO*C and other 3gls and query tools -- not plsql in that case.

with PLSQL, it will dynamically allocate just enough to hold what it needs.
Still NO EXCUSE not to define the columns with meaningful, proper lengths.

I don't get your math though.  a varchar2(38) would be 38 bytes -- not 38Kb

April 10, 2002 Alla -- Thanks for the question regarding "Running sqlplus / as sysdba from Unix script in 9i", version 9i
You Asked
In Oracle 9i svrmgrl and connect internal are no more.
I want to use sqlplus and pass it the string "/ as sysdba" to let
me log in from korn shell script and run commands as sys. What is the trick?
For example this code that works in ksh for svrmgrl
#! /usr/bin/ksh
# list last 5 archive log file names
${ORACLE_HOME}/bin/svrmgrl >/dev/null  <<EOF
connect internal
spool $DB_STATUS;
select * from v\$loghist
  where rownum < 5 ;
spool off;
exit
EOF

How can I replace it with sqlplus session with no login/password
but instead "/ as sysdba" direct login.

Thank you

Alla
and we said...
You can either:

#! /usr/bin/ksh
# list last 5 archive log file names
${ORACLE_HOME}/bin/sqlplus "/ as sysdba"  <<EOF
spool something
select * from v\$loghist
  where rownum < 5 ;
spool off;
exit
EOF

or

#! /usr/bin/ksh
# list last 5 archive log file names
${ORACLE_HOME}/bin/sqlplus /nolog <<EOF
connect / as sysdba
spool something
select * from v\$loghist
  where rownum < 5 ;
spool off;
exit
EOF
Review & Followup
Rating: 3
If not Svrmgrl then what ? April 09, 2002
Reviewer: Yogesh from Pune, India
If svrmgrl is not there in 9i, then how to manage db operations like shutdown ..
etc. I havn't used 9i till now so don't know much about it.

Followup:
umm -- sqlplus?

look at the scripts -- i'm connecting "/ as sysdba" -- that is the "internal"
account of 9i (been there for years actually).

You don't need svrmgrl in 8i -- you can startup/shutdown whatever with sqlplus.

Rating: 5
re: 9i cron April 10, 2002
Reviewer: Amit Tandon from Mumbai , India
Dear Tom ,
Thanx a lot for this info. I was also trying for such a script for starting my
9i database using cron job script.
regards
Amit Tandon

Rating: 2
Need Help April 10, 2002
Reviewer: Abdul-Taofiq,Adekeye from Nigeria,Lagos
I am currently learning Oracle and I would appreciate if you could help me with
some books.
though I got some from my friend but I thought I should mail you may be you
could be of help.
I would appreciate if you could send me any book on oracle DBA.
Thanking you,
Abdul

Followup:
Here is a pair I can recommend:
http://www.amazon.com/exec/obidos/ASIN/1861004826/
http://www.amazon.com/exec/obidos/ASIN/0201715848/
you won't be sorry with either one.  and for general beginning Oracle (for
dba/developer):

http://www.amazon.com/exec/obidos/ASIN/186100690X/
...

      Rating: 5
Great
      answer!!!  April 10, 2002
Reviewer:  Alla
       from Palo Alto, CA
Thank you Tom this solution is realy helpful.

      Rating: 5
Do you know how
      to do that in NT/2K?  April 10, 2002
Reviewer:
      Ken  from Marietta, GA
I've used similar scripts in Unix, but have not been able to do it in windows.
Do you know how?

e.g. the windows equivalent of:

sqlplus "/ as sysdba"  <<EOF
spool something
select * from v\$loghist
  where rownum < 5 ;
spool off;
exit
EOF

Thanks for all of you help!

          Followup:

I usually start with

C:\> fdisk
--> install linux or some other unix variant ;)

that works well.

seriously, i do this:

echo xxxx > x.sql
echo yyyyy >> x.sql
echo zzzz >> x.sql
sqlplus "/ as sysdba" @x
erase x.sql

it is a pain but windows doesn't do that pipeing thing so nice.

April 11, 2002 --
      Thanks for the question regarding "Indexing on Sequences", version 901

      You Asked
Hi Tom

Is it a good idea to create primary keys on columns that contain sequence
generated data?
Since the data would be moving in one direction only it would be complex to
search such a column. Am I correct?

Also I have a decision to make on the primary key for a table. I can put string
data as well as number data for it. IS there a benefit of using numbers as
compared to string data.

Thanks?

      and we said...
"Is it a good idea to create primary keys on columns that contain sequence
generated data?"

Well -- the ONLY answer to that question is a question -- is the column you are
putting the sequence value into your primary key?  If so -- it is not only a
good idea, its the "ONLY" idea.

I don't understand the "complex to search" issue?

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3345399022002
for the answer to the number vs varchar.  The answer is very similar to the
answer about primary keys -- use the DATATYPE that best represents the data
stored.  Don't choose it based on performance, choose it based on WHAT you will
be storing in there!  (the performance is basically equivalent but that is not
the relevant issue here)

April 11, 2002 -- Thanks for the question regarding "Design",
      version 901

      You Asked
Hi Tom

We are trying to design our database to accomodate dynamic alteration of our
schema by providing
user defined columns. The client should be able to add columns to tables without
altering the base
 schema. Example
If we have a customer table with the example data as follows:

Cust_ID         100
Cust_Name        Corrie & Walter Inc
Cust_Contact        Scott

Since we dont at this time what other attributes might be required during the
implementation we
provide a Cust_Extended table where we define the specific attributes such as
follows :

Attribute_ID        1001
Cust_ID            100
Attribute_Value        94041

Also we have a User_defined_Attributes table as follows

Attribute_ID        1001
Atrr_Name        ZipCode
Attr_Datatype        Number

So in this table we can add all the user defined columns that need to go in the
Cust_Extended table.

The data that would go in such tables would be transaction data in millions of
records.

My question is as follows:
Is this a good design approach as this involves a lot of transaction data. If I
have to search on these tables using joins on them wouldnt it be extremely slow?

For Example

Find the customer whose zipcode=94041 or Company='Compaq'

select cust_id from Cust_Extended where
attribute_value in ('94041','Compaq')
And Attribute_id in
(select Attribute_id from User_defined_Attributes where
atrr_name in ('ZipCode','Company'));

Schema source:

Create table customer
(Cust_ID         number,
Cust_Name        varchar2(34),
Cust_Phone    varchar2(50));

Create table Cust_Extended
(Attribute_ID        number,
Cust_ID            number,
Attribute_Value        varchar2(64));

Create table User_defined_Attributes
(Attribute_ID    number,
Atrr_Name    varchar2(54),
Attr_Datatype  varchar2(32));

insert into  Customer
Values(100,'Corrie', '6501111111');
insert into  Customer
Values(200, 'Scott', '4087879999');
insert into  Customer
Values(300,'Smith', '4153338899');

insert into User_defined_Attributes
Values(1001,'ZipCode','Number');
insert into User_defined_Attributes
Values(1002,'Company','varchar');
insert into User_defined_Attributes
Values(1003,'Region','varchar');

insert into  Cust_Extended
Values(1001,100,'94041');
insert into  Cust_Extended
Values(1002,100,'Microsoft');
insert into  Cust_Extended
Values(1001,200,'Compaq');
insert into  Cust_Extended
Values(1001,300,'HP');

Thanks
Tanu

      and we said...
I would prefer an approach that used a fixed number of trailing columns.  You
would:

create table customer(
Cust_ID         number,
Cust_Name        varchar2(xxx),
Cust_Contact     varchar2(xxxx),
field1  varchar2(4000),
field2  varchar2(4000),
...
fieldN  varchar2(4000)
)
/

Now, to "add" an attribute, you would simply use one of the fields.  You would
still use your attribute table to keep track of whats what (you still have
user_defined_attributes).  You could even go further and create a view:

create view customer_v
as
select cust_id, cust_name, cust_contact,
       to_number(field1) cust_zip_code,
       to_date( field2, 'dd/mm/yyyy' ) cust_first_contact,
.....
from customer;

and if you wanted -- you could allow them to create function based indexes on
the table (see

http://osi.oracle.com/~tkyte/article1/index.html
) so they could search:

select * from customer_v where cust_zip_code = 20175 and cust_first_contact
between sysdate-50 and sysdate;

and use an INDEX on (to_number(field1), to_date(field2,'dd/mm/yyyy')) to
retrieve the results rapidly (rather then a full scan).  That is something that
would be virtually IMPOSSIBLE to achieve with that other schema (i have a name
for that other schema, I call it the "funky data model", very generic -- very
poor performing).

Going a step further, you can even put INSTEAD of triggers on the customer_v
(you would dynamically generate them as they added/removed attributes from the
attributes table) that allows them to insert/update/delete via the view easily.

Using this technique, you could deliver something with flexibility and the
potential for performance.
Review & Followup

      Rating:
      4
  December 23, 2003
Reviewer:  a
      reader

April 11, 2002 Leonid -- Thanks for the question regarding
      "Expert one-on-one Oracle new edition?", version 9i

      You Asked
Dear Mr. Kyte,

Excuse me for non-technical question, but I just wanted to ask you about your
future plans regarding the mentioned book? It is the best book I have ever read
on Oracle, so I wanted to ask how soon you are going to update it in the light
of 9i release. I won't take your time any longer.
Thank you in advance.

Sincerely,
Leonid Roodnitsky

      and we said...
I'm planning on a performance book next.  It'll cover up through 9i R2 (not
released as of this writing) but will apply 100% to every release of Oracle from
7.3 on up.  I'd be going for a 600pager instead of a 1300 one this time....

90% of Expert one on one applies 100% to 9i.  A 9i version would be 90% the
same.  I'm going to wait until there is sufficient reason to rewrite it
April 11, 2002 J
      -- Thanks for the question regarding "V$ Tables/Backup and Recovery",
      version 8.17

      You Asked
Tom

Where can I find a list of all V$ tables and descriptions

I need to know which ones to query to display:

a) The names and locations of all control files
b)to verify all files rto be backed up are in backup mode when I use ALTER
TABLESPACE to put certain tablespaces into backup mode

Also, if a system shuts down abnormally, does Oracle perform a recovery
automatically at startup - what type of recovery is this??

      and we said...
The reference manual has all of this

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76961/toc.htm
the names and locations of the control files is in the v$parameter view -- it is
an init.ora parameter.

Hopefully you aren't copying these, you use "alter database" to get backups of
these (and backup to trace is the one you really want!)

Oracle performs instance recovery, aka "crash recovery", using only the online
redo logs.
April 11, 2002 Bhavani -- Thanks for the question regarding
      "Sending HTML Page thru Mail", version 8.1.7

      You Asked
Hi Tom,

How can we send a complete HTML Page thru Mail from Oracle?  For example, if i
want to send
http://asktom.oracle.com/
Main Page thru mail from oracle, how can i do it?

Thank you very much.

Regards
Bhavani

      and we said...
see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1739411218448
but it'll be TONS of work on your part.

You would have to:

o use utl_http to get my page
o add a "base" tag so that my image references and such work
o then send it

seems like it would be trivial to email someone the URL --- hint hint.  Don't
send them my page (or any page) as the page you send MIGHT NOT WORK, but if you
send them TO the page, it'll work just dandy.
April 11, 2002 Edgars -- Thanks for the question regarding "RAW
      datatypes and CRC32 ", version 9.0.1.3

      You Asked
Hello Tom!

I have few questions:

1. Can I do left/right binary shift on RAW datatypes?

2. Is there a built in function or something like this to implement CRC32/CRC16
on RAW data? I was trying to write Java Package, but because I have little
experience in Java I had no luck. For now my CRC32 implementation uses TABLE
that I generated in C with CRC values and PL/SQL function that does byte
searching in this table and lookups crc32 using known algorithm. Maybe this can
be done easily?

3. Where can I find some info on DBMS_JAVA package, I was searching in Oracle
Documentation, but I didn't finded normal description of it. When I try to
create and compile java source named "something" I get
java.security.AccessControlException.

Thank you!

      and we said...
1) 8 bits at a time - with utl_raw.substr.  otherwise, we really don't have a
"bit shift".  we could work something out in a java stored procedure pretty
easily I'm sure.

2) how about an MD5 checksum -- would that work?  dbms_obfuscation_toolkit has
just such a thing. Not a CRC but a checksum none the less.

3) If you have my book -- i've pulled it all together in the appendix section on
dbms_java.  Else it is in the "Java Developers Guide"

http://download-east.oracle.com/otndoc/oracle9i/901_doc/java.901/a90209/config.htm#1009372

Review &
      Followup

      Rating: 3
Meaning of CRC,
      MD5?  April 11, 2002
Reviewer:  A reader
       from Santa Clara, CA, USA
What do CRC, CRC32 and MD5 stand for?

          Followup:

http://www.acronymfinder.com/
is a great place.

CRC is the Cyclic Redundancy Code (lots of hits on that).  It's a way to
"validate" data -- modems use it lots to make sure that what comes out is what
came in.

MD5 is Message Digest 5 - it's a one way hash that can be used to verify that
the document you recieve is the one that was sent for example -- these two
things serve similar purposes.

      Rating: 4
stands
      foe  April 11, 2002
Reviewer:  Jim Kennedy
       from Willsonville, OR
CRC - cyclical reduncancy check
crc32 - cyclical reduncancy check 32 ( more accurate for large things)
MD5 - message digest (I think)

April 11, 2002 Prince -- Thanks for the question regarding
      "Understanding statspack report", version 8.1.7.2

      You Asked
Tom, I have the following piece from the statspack report.

Now I have a few of questions.

Q1:
=======
In the 1st part of the report (LLatch Miss Sources ), why there is "Library
Cache" and "Shared pool". Isn't "Library Cache" a part
of "Shared Pool"

Q2:
=======
In the second part (Part2), is "recursive cpu usage" is already included in the
"CPU used by this session".

Q3:
=======
In the third part (Part3), why there is
  "shared pool db_block_buffers             27,852,800 .."
in addition to
   "db_block_buffers            838,860,800"

Which part of "db_block_buffers" stays in "shared pool"?
or
is the "shared pool db_block_buffers" different from the normal
"db_block_buffers"?

Thank you very much.

Part1:
~~~~~~
^LLatch Miss Sources for DB: DBTEST  Instance: ORCL  Snaps: 221 -231
-> only latches with sleeps are shown
-> ordered by name, sleeps desc

                                                     NoWait             Waiter
Latch Name               Where                       Misses     Sleeps  Sleeps
------------------------ -------------------------- ------- ---------- -------
.......                  ......................
.......                  ......................
library cache            kglhdgn: child:                  0        494     246
library cache            kglpnal: child: alloc spac       0        256     291
library cache            kgllkdl: child: cleanup          0        222      45
library cache            kglhdgc: child:                  0        177     130
.......                  ......................
.......                  ......................
redo writing             kcrfsr                           0          1       0
.......                  ......................
.......                  ......................
shared pool              kghfrunp: alloc: clatch no       0      1,021       0
shared pool              kghfrunp: clatch: nowait         0        416       0
shared pool              kghalo                           0        187     594
shared pool              kghfrunp: clatch: wait           0         54     254
shared pool              kghfnd: get next extent          0         27       0
shared pool              kghfnd: min scan                 0         24       0
shared pool              kghalp                           0         13      84
shared pool              kghupr1                          0          9     572
shared pool              kghfen: not perm alloc cla       0          7      18
shared pool              kghfrunp: alloc: wait            0          6       8
shared pool              kghfrunp: no latch               0          4       0
shared pool              kghfnd: req scan                 0          1       0
=================================================================================
======

Part2:
~~~~~~

^LInstance Activity Stats for DB: DBTEST  Instance: ORCL  Snaps: 231 -241

Statistic                                    Total   per Second    per Trans
--------------------------------- ---------------- ------------ ------------
CPU used by this session                   107,579          9.1          3.8
recursive cpu usage                          7,484          0.6          0.3
=================================================================================
======

Part3:
~~~~~~

SGA breakdown difference for DB: DBTEST  Instance: ORCL  Snaps: 221 -231

Pool        Name                        Begin value      End value  Difference
----------- ------------------------ -------------- -------------- -----------
java pool   free memory                      32,768         32,768           0
shared pool DML locks                       185,280        185,280           0
.......                  ......................
.......                  ......................
shared pool db_block_buffers             27,852,800     27,852,800           0
shared pool db_block_hash_buckets         3,539,016      3,539,016           0
shared pool db_handles                      157,500        157,500           0
.......                  ......................
.......                  ......................
            db_block_buffers            838,860,800    838,860,800           0
            fixed_sga                        73,888         73,888           0
            log_buffer                      253,952        253,952           0
=================================================================================
======

      and we said...
q1) the shared pool is a big place, part of which is the library cache.  look at
v$sgastat -- there is a lot more then just the library cache in there.

q2) i believe so, yes.

q3) the shared pool (aka "variable size") is used to manage all of the other
structures.  the shared pool "block buffers" is memory used to manage the real
block buffers.

For exmaple:

...
shared pool db_block_buffers              1,496,000      1,496,000           0
...
            db_block_buffers             90,112,000     90,112,000           0
...

shows that for my 90m of block buffers, i'm using 1.4m in the shared pool to
"manage" them.  This number will go up and down depending on various features
used (eg: the cache advisor in 9i will allocate MORE memory in the shared pool
to retain statistics about block buffers and such).

With Oracle8 and up, the other entry that is there is an array of pointers to
each block in the db_block_buffers.  The value for db_block_buffers in the
shared pool can be computed as follows:

 size of db_block_buffers in shared_pool =
  db_block_buffers_parameter * (size_of_C_pointer + size_of_one_buffer_header)

The size_of_C_pointer and the size_of_buffer_header are operating system
dependent.  In other words, entry for db_block_buffers in shared_pool represents
buffer-headers (which can be referenced in X$BH).

Review &
      Followup

      Rating: 5
Great! Straight
      to the point.  April 11, 2002
Reviewer:
      Prince
Thanks for making it very clear to me.

Its so nice of you to explain things as detailed as possible, even if it is not
asked.

Did you by "managing DB_BLOCK_BUFFERS" mean db_block_buffers in shared_pool
maintains stuff like "touch count" etc (x$bh)

          Followup:

...
 In other words, entry for db_block_buffers in shared_pool represents
buffer-headers (which can be referenced in X$BH).
...

it *is* x$bh ;)

April 11, 2002 Duraiswamy -- Thanks for the question regarding
      "Insert CLOB in a table in remote database", version 8.1.7

      You Asked
I have two databases connected by dblink and have a synonymn created for the
remote table.

I try to run a stored procedure in database 1 and insert records into the table
in the remote database.

My stored procedure fails to insert because one of the field is CLOB datatype.

I select the CLOB from one of the table in database 1 and
I am able to successfully bind the clob variable to the callablestatement in
java using setObject(i, object)

If I remove the CLOB field from the insert statement then I am able to do it
successfully.

Using setObject I am able to insert successfully into a table in the same
database using the same java code.

How to insert CLOB from one database to other ?  Any examples will help.

The project requirement is that it has to move record by record into the remote
table and not all the records at one time.  Each record has to be processed
additionally after entered into the remote table. Moreover the additional
operation should be (if possible) in single transaction.

      and we said...
You have two choices

1) You'll have to READ the entire clob back into the java app then and then
stream it over to the other database.  That would stink.

2) you can use an update.  DON'T fetch the clob back to java, just get the
primary key (say column "x").  You will insert the row into the remote site,
inserting NULL for the clob.  To copy the clob from database A to database B
without having to bring the clob back to the client -- you will:

                  update t@ora817dev
                     set y = ( select y from t where x = ? )
                   where x = ?;

Here y is the clob, X is the primary key, t@ora817dev is the remote table, t is
the local table.

It is bad to have "requirements" that dictate how things must be processed.  It
is good to have a requirement that tells you the end result, dictating how that
end result must be achieved is totally backwards.

You will find this "process a row at a time in a client and insert into a remote
table" processing to be just about the slowest way to achieve your goals (not to
mention hard to maintain).  A tiny bit of plsql in your life could save you tons
of heavy duty java code!

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

Why would you fetch the data BACK to java to insert it into the remote tables?

Just

insert into remote_table select whatever_you_want from local_table;

That'll move the clob.  You cannot put a LOB locator from db1 into db2 -- it's a
pointer, that pointer is NOT valid in db2, only in db1.

Review &
      Followup

      Rating: 5
One have to use
      Pl/sql  April 11, 2002
Reviewer:  G.Singh
       from India
As mentioned by Tom A little codeing  of pl/sql will slove everything. Just make
a Procedure to do such kind of thing.  Pass it the apporprate value so that it
can transfer From DB1 to DB2 Usinglinks . This way Java won't come in between.

      Rating: 4
Insert CLOB in
      a table in remote database  April 11, 2002

Reviewer:  Duraiswamy Padmanaban  from New York, NY
      USA
This is quite helpful hint to update the CLOB.  We had to do record by record
because the remote table has primary key generated by a sequence.

Though I would love to use the following sql, in this case I cannot use

insert into remote_table select * from localtable

as I have not heard  that Oracle has any way to generate unique id on the fly.

If there is anyway that can be solved then it would be great.

Anyway, thanks Tom it was really a useful hint to do an update after insert of
rest of field.  I will try it out.

          Followup:

*triggers*

*calling PLSQL from SQL*

*insert into table select my_seq.nextval, ..... *

there are many ways...

April 11, 2002 William -- Thanks for the question regarding
      "Specifying hidden hints", version 8.1.6

      You Asked
Tom,

I found this article on Metalink (Note:92202.1). The URL is

http://support.oracle.com.sg/metalink/plsql/ml2_documents.showDocument?p_database_id=NOT&p_id=92202.1
We have a third party application in which there are SQL statements with a
number of joins and the only way we can tune this is to specify hints but
unfortunately we do not have access to the source code.

The above article is the ideal solution to our problem but it mentions that
Oracle Support do NOT support it. What I would like to know is your thoughts on
it and whether you would recommend it.

Thanks in advance

      and we said...
If you have my book, I describe exactly how to do this from start to finish.
For individual "bad" queries -- I do recommend it (the alternative being -- a
bad query right?  what is worse in this case).

They made it a little harder then it needs to be in that note, using a logon
trigger, some views, some hints in views -- you can achieve what they did
WITHOUT using sys, without updating the outline tables directly.  So, I don't
recommend their way -- but the basic idea is sound.

It is interesting to note that in 9i -- there it is a GUI plan editor that makes
this almost trivial to implement.
Review &
      Followup

      Rating: 5
Re: Specifying
      hidden hints  April 11, 2002
Reviewer:
      William  from Australia
Tom,

Thanks for your reply.

How would you implement this through views if the query is like:
select a.col1, b.col1, c.col1, d.col1, e.col1
from a, b, c, d, e
where a.col2=b.col2
and a.col3=c.col3
and a.col4=d.col4
and a.col5=e.col5
/

and the rewritten hinted query is:
select /*+ ORDERED */ a.col1, b.col1, c.col1, d.col1, e.col1
from c, a, d, b, e
where a.col2=b.col2
and a.col3=c.col3
and a.col4=d.col4
and a.col5=e.col5
/

Cheers

          Followup:

You WOULDN'T.

The queries have to match.  What you need to do is take the query:

select a.col1, b.col1, c.col1, d.col1, e.col1
from a, b, c, d, e
where a.col2=b.col2
and a.col3=c.col3
and a.col4=d.col4
and a.col5=e.col5
/

which is the one you need to "fix" and get a plan to dump out that does what you
want.  Consider, say you have an application "APP" that runs the query you want
to tune (first query above), you would do this (in 8i in 9i you would just use
OEM to edit the plan in the GUI)

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop user a cascade;
User dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> grant dba to a identified by a;
Grant succeeded.

just created a dummy user to put dummy tables a, b, c, d, e into to get them
to go the way I want...

ops$tkyte@ORA817DEV.US.ORACLE.COM> @connect a/a

a@ORA817DEV.US.ORACLE.COM> create table a (col1 int, col2 int, col3 int, col4
int, col5 int );
Table created.

a@ORA817DEV.US.ORACLE.COM> create table b (col1 int, col2 int, col3 int, col4
int, col5 int );
Table created.

a@ORA817DEV.US.ORACLE.COM> create table c (col1 int, col2 int, col3 int, col4
int, col5 int );
Table created.

a@ORA817DEV.US.ORACLE.COM> create table d (col1 int, col2 int, col3 int, col4
int, col5 int );
Table created.

a@ORA817DEV.US.ORACLE.COM> create table e (col1 int, col2 int, col3 int, col4
int, col5 int );
Table created.

a@ORA817DEV.US.ORACLE.COM> alter session set optimizer_mode=first_rows;
Session altered.

a@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain

a@ORA817DEV.US.ORACLE.COM> select a.col1, b.col1, c.col1, d.col1, e.col1
  2  from a, b, c, d, e
  3  where a.col2=b.col2
  4  and a.col3=c.col3
  5  and a.col4=d.col4
  6  and a.col5=e.col5
  7  /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=2881023 Card=37
Bytes=6253)
   1    0   MERGE JOIN (Cost=2881023 Card=37 Bytes=6253)
   2    1     SORT (JOIN) (Cost=2881021 Card=45212176 Bytes=4702066304)
   3    2       MERGE JOIN (CARTESIAN) (Cost=558175 Card=45212176
Bytes=4702066304)
   4    3         MERGE JOIN (CARTESIAN) (Cost=6807 Card=551368 Bytes=43006704)
   5    4           MERGE JOIN (CARTESIAN) (Cost=83 Card=6724 Bytes=349648)
   6    5             TABLE ACCESS (FULL) OF 'C' (Cost=1 Card=82 Bytes=2132)
   7    5             SORT (JOIN) (Cost=82 Card=82 Bytes=2132)
   8    7               TABLE ACCESS (FULL) OF 'B' (Cost=1 Card=82 Bytes=2132)
   9    4           SORT (JOIN) (Cost=6806 Card=82 Bytes=2132)
  10    9             TABLE ACCESS (FULL) OF 'D' (Cost=1 Card=82 Bytes=2132)
  11    3         SORT (JOIN) (Cost=558174 Card=82 Bytes=2132)
  12   11           TABLE ACCESS (FULL) OF 'E' (Cost=1 Card=82 Bytes=2132)
  13    1     SORT (JOIN) (Cost=3 Card=82 Bytes=5330)
  14   13       TABLE ACCESS (FULL) OF 'A' (Cost=1 Card=82 Bytes=5330)

so, pretend thats the plan we DON'T want -- we want the next plan:

a@ORA817DEV.US.ORACLE.COM> select /*+ ORDERED */ a.col1, b.col1, c.col1, d.col1,
e.col1
  2  from c, a, d, b, e
  3  where a.col2=b.col2
  4  and a.col3=c.col3
  5  and a.col4=d.col4
  6  and a.col5=e.col5
  7  /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=17 Card=37 Bytes=6253)
   1    0   MERGE JOIN (Cost=17 Card=37 Bytes=6253)
   2    1     SORT (JOIN) (Cost=15 Card=45 Bytes=6435)
   3    2       MERGE JOIN (Cost=13 Card=45 Bytes=6435)
   4    3         SORT (JOIN) (Cost=11 Card=55 Bytes=6435)
   5    4           MERGE JOIN (Cost=9 Card=55 Bytes=6435)
   6    5             SORT (JOIN) (Cost=7 Card=67 Bytes=6097)
   7    6               MERGE JOIN (Cost=5 Card=67 Bytes=6097)
   8    7                 SORT (JOIN) (Cost=3 Card=82 Bytes=2132)
   9    8                   TABLE ACCESS (FULL) OF 'C' (Cost=1 Card=82
Bytes=2132)
  10    7                 SORT (JOIN) (Cost=3 Card=82 Bytes=5330)
  11   10                   TABLE ACCESS (FULL) OF 'A' (Cost=1 Card=82
Bytes=5330)
  12    5             SORT (JOIN) (Cost=3 Card=82 Bytes=2132)
  13   12               TABLE ACCESS (FULL) OF 'D' (Cost=1 Card=82 Bytes=2132)
  14    3         SORT (JOIN) (Cost=3 Card=82 Bytes=2132)
  15   14           TABLE ACCESS (FULL) OF 'B' (Cost=1 Card=82 Bytes=2132)
  16    1     SORT (JOIN) (Cost=3 Card=82 Bytes=2132)
  17   16       TABLE ACCESS (FULL) OF 'E' (Cost=1 Card=82 Bytes=2132)

what we have to do is get the optimizer to take that first query and get that
plan.  we play around a bit and find (don't even ask me how I did this, I just
played around a bit is all, no inside knowledge, just played with the stats and
optimizer mode to get what I wanted)

a@ORA817DEV.US.ORACLE.COM> alter session set optimizer_goal=all_rows;
Session altered.

a@ORA817DEV.US.ORACLE.COM> exec dbms_stats.set_table_stats( user, 'C',
numrows=>82, numblks=>1 , avgrlen => 26);

PL/SQL procedure successfully completed.

a@ORA817DEV.US.ORACLE.COM> exec dbms_stats.set_table_stats( user, 'A',
numrows=>82, numblks=>1 , AVGRLEN => 65);

PL/SQL procedure successfully completed.

a@ORA817DEV.US.ORACLE.COM> exec dbms_stats.set_table_stats( user, 'D',
numrows=>182, numblks=>1, avgrlen => 26 );

PL/SQL procedure successfully completed.

a@ORA817DEV.US.ORACLE.COM> exec dbms_stats.set_table_stats( user, 'B',
numrows=>282, numblks=>1, avgrlen => 26 );

PL/SQL procedure successfully completed.

a@ORA817DEV.US.ORACLE.COM> exec dbms_stats.set_table_stats( user, 'E',
numrows=>382, numblks=>1, avgrlen => 26 );

PL/SQL procedure successfully completed.

a@ORA817DEV.US.ORACLE.COM>
a@ORA817DEV.US.ORACLE.COM> select a.col1, b.col1, c.col1, d.col1, e.col1
  2  from a, b, c, d, e
  3  where a.col2=b.col2
  4  and a.col3=c.col3
  5  and a.col4=d.col4
  6  and a.col5=e.col5
  7  /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=ALL_ROWS (Cost=29 Card=1314 Bytes=222066)
   1    0   MERGE JOIN (Cost=29 Card=1314 Bytes=222066)
   2    1     SORT (JOIN) (Cost=25 Card=344 Bytes=49192)
   3    2       MERGE JOIN (Cost=16 Card=344 Bytes=49192)
   4    3         SORT (JOIN) (Cost=13 Card=122 Bytes=14274)
   5    4           MERGE JOIN (Cost=9 Card=122 Bytes=14274)
   6    5             SORT (JOIN) (Cost=7 Card=67 Bytes=6097)
   7    6               MERGE JOIN (Cost=5 Card=67 Bytes=6097)
   8    7                 SORT (JOIN) (Cost=3 Card=82 Bytes=5330)
   9    8                   TABLE ACCESS (FULL) OF 'A' (Cost=1 Card=82
Bytes=5330)
  10    7                 SORT (JOIN) (Cost=3 Card=82 Bytes=2132)
  11   10                   TABLE ACCESS (FULL) OF 'C' (Cost=1 Card=82
Bytes=2132)
  12    5             SORT (JOIN) (Cost=3 Card=182 Bytes=4732)
  13   12               TABLE ACCESS (FULL) OF 'D' (Cost=1 Card=182 Bytes=4732)
  14    3         SORT (JOIN) (Cost=4 Card=282 Bytes=7332)
  15   14           TABLE ACCESS (FULL) OF 'B' (Cost=1 Card=282 Bytes=7332)
  16    1     SORT (JOIN) (Cost=4 Card=382 Bytes=9932)
  17   16       TABLE ACCESS (FULL) OF 'E' (Cost=1 Card=382 Bytes=9932)

a@ORA817DEV.US.ORACLE.COM> set autotrace off
a@ORA817DEV.US.ORACLE.COM>

that is more then close enough - it does A and C in opposite orders but so
what -- a/c or c/a -- its the same in this case, we would save this plan for
that query and away we go...

April 11, 2002 Prince -- Thanks for the question regarding
      "RedHat Linux with more than 2GB memory", version 8.1.7.2

      You Asked
Tom,

I am wondering, if I have a machine (Intel platform) running RedHat 7.2 with 2GB
memory, will any additional memory is usefull?

Assume this machine is dedicated to one Oracle DB/instance.

I believe Linux is 32bit OS and is there anyway Oracle can take advantage of, if
it is provided with more than 2G memory.

Please explain, what are the areas it can be of use if say the machine has 4GB
memory.

Thanks,

      and we said...
Oracle is a multi-process beast.

Each process can address their own chunk of memory.

So, yes, there is reason to go above 2gig.  The SGA can use some and each
process will use its own.  If you have 5000 processes that each want 1meg of ram
-- having more than 2gig of ram would be a good thing.
Review &
      Followup

      Rating:
      4
  April 11, 2002
Reviewer:
      A reader
But will it be different under windows where you have a single oracle.exe
process? Can it use more than 2GB memory?

          Followup:

Yes it can.

If you want to do this - you'll want a "windows experienced" person (need some
changes here and there to the boot.ini and such) and to contact oracle support.
They can tell you how to break through the 2gig boundary on windows versionXXX
(not putting here, too specific to each tiny little release they put out)

      Rating: 3
What kind of
      process will be benefited?  April 11, 2002

Reviewer:  Prince
Wouldn't they mostly get the memory from the SGA portion? If not, when do they
get memory from area other than SGA. (in case of sorting etc)

Assume I have 300 process and each may consume 2M (ours is dedicated server
configuration, sort_area_size & sort_area_retained_size set to 131072). Now
unless I have more processes would more memory than 3GB (approx 2GB for oracle
process + 300 * say 2MB per process ~= 3GB) be useful.

What would be the maximum value I can specify for SGA in 32bit OS. (can it be
exatly 2GB or few KB less or Few MB less???). Note: I will try this in few days,
but if you have the answer it will be much helpful.

Thank you...

Thanks.

          Followup:

it depends on the OS.  Each OS will have it's own limits on the size of shared
memory.  There are tricks on each (eg: memory windows on HP/UX before 64bit
HP/ux, relocating the SGA on solaris, etc) that let you get as much as possible
but it is between 1 and 2 gig in general.

beyond that -- 64bit Oracle is what you want.

April 12, 2002 Prince -- Thanks for the question regarding
      "Export/Import. Your advice is needed.", version 8.1.7.2

      You Asked
Tom,

I want to export all the tables into two .dmp files. (for some reason I want it
in two files).
One .dmp with all tables except a few (say LOGDATA1, LOGDATA2 tables) and
another with the remaining tables (ie LOGDATA?)

Then I want to import all the tables into another schema with the data from the
1st .dmp file (say exp1.dmp)
and without data from the second .dmp (say exp2.dmp) file

This what I am planning to do.

Export:
=============
1, do a userlevel export with rows=n
        exp / owner=SCH1 rows=n file=expddl.dmp

2, do the export of all but exception tables (LOGDATA1, LOGDATA2) with data
        exp / tables=(SCH1.TAB1, SCH1.TAB2, ...) file=exp1.dmp

3, do the export of exception tables
        exp / tables=(SCH1.LOGDATA1, SCH1.LOGDATA2, ...) file=exp2.dmp

Import:
=============
1, import the structure
        imp / file=expddl.dmp fromuser=SCH1 touser=SCH2 INDEXES=N CONSTAINTS=N
rows=n

2, import the 1st set with data
        imp / file=exp1.dmp rows=y ignore=y fromuser=SCH1 touser=SCH2
tables=(TAB1, TAB2, ...) indexes=Y constraints=Y

3, import the 2nd set without data (only the indexes, constraints)
        imp / file=exp2.dmp rows=n ignore=y fromuser=SCH1 touser=SCH2
tables=(LOGDATA1, LOGDATA2, ...) indexes=Y constraints=Y rows=n

Now my questions:
===================
During the 1st import (step 1) itself the triggers are created.
        Would they get recreated during import of the data (step 2)?
        Do I have to disable the trigger so as tables do not get populated
becazz of the existing triggers.
        (These populated tables will be imported by the import (step 2 ) as
well.

What would be the easiest and fastest way to acheive this?
        Can I create the INDEX and CONSTRAINTS after the tables are populated. I
know index can we
        separated easily using the indexfile option. How abt the Constraints?

Thank you for all your guidence.

      and we said...
Here is a "trick"

Use fine grained access control

http://asktom.oracle.com/~tkyte/article2/index.html
put a predicate policy that returns 1=0 on the two tables.  do the export (it'll
get NO data for them -  do it not as SYS or SYSDBA!)

then drop the policy and export just the two tables.

do the first import.  then drop the two EMPTY tables and import the second file.

this is better cause

o the indexes won't be there during the load
o the triggers won't be there during the load
o the constraints won't be there during the load

if they are -- load = slow.....
if they are not -- load = faster then slow....

make sense?

Review &
      Followup

      Rating: 4
This is very
      slick!.. But I have SE 8.1.7.2  April 11, 2002

Reviewer:  Prince.
I like the idea very much.

I have tested these features on EE on my local machine for a while. But we do
not have EE on our company.

Is there anyway to get the stuff done in Oracle SE?

Thank you very much for the quick response.

          Followup:

Sorry -- works only with EE.

In SE, you'll be going with plan b -- your plan.

April 12, 2002 Irina -- Thanks for the question regarding
      "Execution of query", version 8.1.7.2

      You Asked
Dear Tom,
   Thank you very much for highly professional help and sharing your
knowledge,
   your site and your great book help to advance in study of Oracle.
   I want to understand some things applyed to query execution. Most likely, all
understand them...
1. Where are the intermediate result sets located? For example:

  SELECT STATEMENT Optimizer=CHOOSE (Cost=3968 Card=9786 Bytes=1986558)
  CONCATENATION
    FILTER

      TABLE ACCESS (BY INDEX ROWID) OF IP_ACCOUNT (Cost=60 Card=10 Bytes=2030)
 | (1)
        INDEX (RANGE SCAN) OF ACCT_BY_ACC_TYPE (NON-UNIQUE) (Cost=5 Card=10)

    FILTER
      TABLE ACCESS (BY INDEX ROWID) OF IP_ACCOUNT (Cost=60 Card=10 Bytes=2030)
 | (2)
        INDEX (RANGE SCAN) OF CLIENT_NO_FOR_ACCOUNT (NON-UNIQUE) (Cost=5
Card=10)

 As I inderstand, process SQL do it. Data in (1) and (2) are reading
non-simultaneously, are result sets located in memory arrays? How this query is
executed "inside"?
 Will Oracle use temporary segment if result set of stage (1) is very large?
 I observed execution of one query with big cardinality in intermediate stages,
it was very slow, but temporarily segment wasn't used. On what resource is this
time depend (if only one user works)?
2. Is final query result stored anywhere, or process, executing query, returns
row and then returns next row, but previous is nowhere? (One process calls
FETCH,other(SQL) returns ROW and in process SQL result set doesn't exist?).

      and we said...
I'd have to see the query.

if the query was like:

select * from t where x in ( 1, 2 );

the results might exist NO WHERE but on the blocks in the buffer cache.  When
you ask for the first row, we'll get you the first "x=1" row, when you ask for
the next, we'll get the next and so on.

The results don't have to be stored ANYWHERE, in most cases, we can get the data
right from the blocks -- no intermediate storage needed.

If we do need temp, we use ram upto sort_area_size and temp exents on disk after
that.

There are WAY too many variables to make any generalizations here.

2) no. no it is not.  consider:

  select * from ONE_BILLION_ROW_TABLE;

we do not copy that table, we get the data directly from it.
Review &
      Followup

      Rating: 4
Thanks, but one
      question more...  April 11, 2002
Reviewer:
      Irina
Dear Tom, please, give additional explanation.
SELECT * FROM ip_account
WHERE acct_type = 'CARDS'
  AND client_no = DECODE(:cl_no, 0, client_no, :cl_no);

Plan: Concatenation
        Filter                         (1)
          Query IP_ACCOUNT by index1(acct_type)
        Filter                         (2)
          Query IP_ACCOUNT by index2(client_no)

As you say, results (1) and (2) may be located in buffer cash or in sort area
(concatenation will sort).
   If (in other case) intermediary result is in buffer cash and it is large, is
it possible: all blocks in buffer cash for this query are "blocked" for
rewriting and other users feel slowing down?

          Followup:

READERS never blocks WRITERS

WRITERS do not block READERS.

that is Oracle's mantra, it is a major feature.

No, the other users will not be impacted.  You will use temp, you will not block
people.

      Rating:
      4
Reader  April 11, 2002
Reviewer:
      A reader
Tom,

When a block is in BUFFER CACHE, it is part of a hash
bucket chain list and it is either in LRU list or
checkpoint list.

If the block is being  sorted , it is in PGA and when the
results are accumulated it is in UGA. How does Oracle
tracks these buffers (in PGA and UGA)

Thanks

          Followup:

Well, it is not an "or" thing in the first paragraph necessarily, it is "and/or"

When the dedicated server reads the block out of the cache, it copies it.  No
one else -- other then your session -- needs to know about this block anymore.
Your server process "keeps track" of it from then on in.

April 12, 2002 --
      Thanks for the question regarding "v$open_cursor.sql_text showing
      "table_4_200_5e14_0_0_0 "", version 8.1.7

      You Asked
I am trying to figure out why v$open_cursor.sql_text is showing things like
"table_4_xxxx_x_x_x_x", the reason being that these entries in v$open_cursor has
the most number of entries in v$open_cursor. database clients are JDBC Thin
drivers against Oracle 8.1.7.x on Solaris 8.

Here are some additional information:

* No middle tier. We are not using any App server / EJB. It is straight SQL via
JDBC Thin driver.

* The Java client is not using any temporary tables.

* The Java client is also not calling any PL/SQL procedures. It was already like
that before I started working on this.

I do use PL/SQL for the aggregation stuff ran via DBMS_JOB where we aggregate
all the data on an hourly basis. The reason PL/SQL was used because I worked on
it :) In any case, the user doing the aggregation is not the same user showing
these weird entries in sql_text.

This is the query that I use ( I assume this is what you meant by "putting the
entire query in there, not just the table name" ):

select user_name, count(1) as count, sql_text
from sys.v_$open_cursor
group by user_name, sql_text
order by count;

USER_NAME                           COUNT SQL_TEXT
------------------------------ ----------
------------------------------------------------------------
ORACLE                                  1 select user_name, count(1) as count,
sql_text from sys.v_$op
SGMASTER                                1 SELECT PROPERTY_NAME,PROPERTY_VALUE
FROM SERVICE_PROVIDER_PR
SGMASTER                                1 SELECT
PROPERTY_NAME,PROPERTY_VALUE,PLATFORM_ID FROM SERVICE
SGMASTER                                1 select * from player where
player_id=93357
SGMASTER                                1 select count(initiator_id) from Game
where initiator_id=1138
SGMASTER                                1 Select  object_name ObjectName,
object_type ObjectType  from
SGMASTER                                5 INSERT INTO player_session
(session_id, creation_time, sessi
SGMASTER                                7 table_4_200_5ee2_0_0_0
SGMASTER                               12 table_4_2000_5ed8_0_0_0
SGMASTER                               12 table_4_200_5ed8_0_0_0
SGMASTER                               12 table_e_400_5ed8_2_0_0

USER_NAME                           COUNT SQL_TEXT
------------------------------ ----------
------------------------------------------------------------
SGMASTER                               17 table_4_2000_5e14_0_0_0
SGMASTER                               17 table_4_200_5f1b_0_0_0
SGMASTER                               17 table_e_400_5f1b_3_0_0
SGMASTER                               17 table_e_400_5e14_a_0_0
SGMASTER                               17 table_4_2000_5f1b_0_0_0
SGMASTER                               17 table_4_200_5e14_0_0_0

Since these tables are not really tables ( nor are they real objects in the
sense that they are not in ALL_OBJECTS ), here is what I can just assume:

1) Parallel query execution -- I had a few tables / indexes that had a DEGREE of
default. But even after ALTERing them to be NOPARALLEL ( and even restarting the
clients using these tables ), the entries are still there.

2) Partitioned tables -- Although I have partitioned tables, these tables are
owned by another user and are not accessed from the offending client.

3) (B)LOB -- Recalling that LOBs are moved out of the table ( but still in the
tablespace ) when the size gets to 4K or more, this is the most likely
candidate, although the documentation does not specifically say how LOBs are
named when stored outside of the table.
However, I also recall reading the LOBs are named with something like SYS$xxxxx.

4) Implicit cursors -- I doubt it, since the Java client hardly makes any PL/SQL
calls. They are SELECTs, INSERTs, UPDATEs, DELETEs done using a
PreparedStatement.

Is there also any way of showing the REAL sql that is generating these
"table_4_xxx_x_x_x_x" entries in v$open_cursor?

      and we said...
Ok -- in the never ending saga of "every day you learn something new" here we go
(yes, I never really noticed them before as I don't use nested tables as a
physical storage mechanism myself).

They have to do with implicit cursors surrounding nested table types!

Here is an example (done in a clean schema, I'm the only one logged on -- so the
v$open_cursor query is "safe" -- no other sesssions owned by me)

ops$tkyte@ORA817DEV.US.ORACLE.COM> connect /
Connected.
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;
Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop type myTableType;
Type dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> disconnect
Disconnected from Oracle8i Enterprise Edition Release 8.1.7.2.0 - Production
With the Partitioning option
JServer Release 8.1.7.2.0 - Production

that cleaned everything out for us...  Now, lets reconnect and see what
happens

ops$tkyte@ORA817DEV.US.ORACLE.COM> connect /
Connected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select sql_text
  2    from v$open_cursor
  3    where user_name = user and sql_text like 'table%';

no rows selected

nothing there yet...

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace type myTableType as table
of number
  2  /

Type created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select sql_text
  2    from v$open_cursor
  3    where user_name = user and sql_text like 'table%';

SQL_TEXT
------------------------------------------------------------
table_1_0_10e_0_0_0

Ah-Hah -- there we go, we have that mysterious statement in there.  Its all
about nested table types..

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int, y myTableType )
nested table y store as y_tab;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select sql_text
  2    from v$open_cursor
  3    where user_name = user and sql_text like 'table%';

SQL_TEXT
------------------------------------------------------------
table_1_0_10e_0_0_0
table_1_0_116_0_0_0
table_1_0_112_0_0_0

and now we have MORE of them..

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1, myTableType( 1, 2,
3 ) );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select sql_text
  2    from v$open_cursor
  3    where user_name = user and sql_text like 'table%';

SQL_TEXT
------------------------------------------------------------
table_1_0_10e_0_0_0
table_1_0_116_0_0_0
table_1_0_112_0_0_0

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

         X
----------
Y
---------------------------------------------------------------------------------
--------------------------------------------------
         1
MYTABLETYPE(1, 2, 3)

ops$tkyte@ORA817DEV.US.ORACLE.COM> select sql_text
  2    from v$open_cursor
  3    where user_name = user and sql_text like 'table%';

SQL_TEXT
------------------------------------------------------------
table_1_0_10e_0_0_0
table_1_0_116_0_0_0
table_1_0_112_0_0_0

ops$tkyte@ORA817DEV.US.ORACLE.COM>

now, instead of just creating them and all, lets see what would happen day to
day:

ops$tkyte@ORA817DEV.US.ORACLE.COM> disconnect
Disconnected from Oracle8i Enterprise Edition Release 8.1.7.2.0 - Production
With the Partitioning option
JServer Release 8.1.7.2.0 - Production
ops$tkyte@ORA817DEV.US.ORACLE.COM> connect /
Connected.
ops$tkyte@ORA817DEV.US.ORACLE.COM> select sql_text
  2  from v$open_cursor
  3   where user_name = user and sql_text like 'table%';

no rows selected

ops$tkyte@ORA817DEV.US.ORACLE.COM> save a rep
Wrote file a
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

         X
----------
Y
---------------------------------------------------------------------------------
--------------------------------------------------
         1
MYTABLETYPE(1, 2, 3)

ops$tkyte@ORA817DEV.US.ORACLE.COM> @a
ops$tkyte@ORA817DEV.US.ORACLE.COM> select sql_text
  2  from v$open_cursor
  3   where user_name = user and sql_text like 'table%'
  4  /

SQL_TEXT
------------------------------------------------------------
table_1_0_10e_0_0_0
table_1_0_116_0_0_0
table_1_0_112_0_0_0

so, they are a natural side effect of using nested tables.  Now, why are the
"open" -- well, they use the same cursor sharing technique as PLSQL does.  In
PLSQL when you close a cursor -- it'll still (most likely) be there in
v$open_cursor which should really be called "v$MOST_LIKELY_STILL_open_cursor" --
the cursors for the nested table access are still there but -- they are
replaceable -- they will go away if someone else needs that slot (eg: they do
not count against your open_cursor init.ora setting)

So, thats the mystery here, they are implicit sql (magic sql apparently) to do
with nested table types.  They will go away as their slots are needed, they are
not "leaked" cursors (which is probably what you were looking for).

Review &
      Followup

      Rating: 4
But we don't
      use PL/SQL nor do we use nested tables??  April 07, 2002

Reviewer:  A reader  from Sydney, Australia

I have checked and I am pretty sure we do not use nested tables

SQL> select distinct object_type
from user_objects  2
  3
SQL> /

OBJECT_TYPE
------------------
INDEX
LOB
PROCEDURE
SEQUENCE
SYNONYM
TABLE
VIEW

I know user SYS does, but we do not use them at all:

SQL> select distinct object_type from all_objects where owner = 'SYS'
  2
SQL> /

OBJECT_TYPE
------------------
CONSUMER GROUP
FUNCTION
JAVA CLASS
JAVA RESOURCE
PACKAGE
PROCEDURE
TABLE
TYPE
VIEW

          Followup:

What does:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select owner, count(*) from dba_nested_tables
group by owner;

OWNER                            COUNT(*)
------------------------------ ----------
OPS$TKYTE                               1

ops$tkyte@ORA817DEV.US.ORACLE.COM> select owner, typecode, count(*) from
dba_types group by owner, typecode;

OWNER                          TYPECODE                         COUNT(*)
------------------------------ ------------------------------ ----------
APPS                           OBJECT                                  1
CTXSYS                         COLLECTION                              1
CTXSYS                         OBJECT                                  4
OPS$TKYTE                      COLLECTION                              1
SCOTT                          COLLECTION                              1
SYS                            COLLECTION                             10
SYS                            OBJECT                                 28
SYSTEM                         COLLECTION                              1
SYSTEM                         OBJECT                                  1
TEST                           COLLECTION                              1
TEST                           OBJECT                                  1
TYPES                          COLLECTION                              1
                               BFILE                                   1
                               BINARY ROWID                            1
.....

return (your query above is inconclusive -- nested tables are TABLES -- the
object type is TABLE).

You need not use plsql to use nested tables -- I did not use PLSQL at all in my
example, only SQL.

Also, if you use any number of features, you might be using them and not even be
aware of it.  interMedia, spatial, XML stuff -- lots of nested tables out there.

      Rating: 3
nested
      tables  April 08, 2002
Reviewer:  rinku
       from ca
hi tom
what are nested tables
rinku

          Followup:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76976/adobjint.htm#452711
covers that.

      Rating: 4
Still no nested
      tables or collections  April 08, 2002
Reviewer:
      A reader  from Sydney Australia
Hi Tom. Thanks for your replies.

Unfortunately, I still have not figured out which object is actually causing
this. ( See the results of my query below ). We are also not using any of those
other features that may cause these entries in sql_text EXCEPT for Parallel
Query and Table Partitioning. I have also checked and rechecked all of the Java
code.

SQL> select owner, count(*) from dba_nested_tables
group by owner;

  2
no rows selected

SQL> select owner, typecode, count(*) from
dba_types group by owner, typecode;

  2

OWNER                          TYPECODE                         COUNT(*)
------------------------------ ------------------------------ ----------
SYS                            COLLECTION                             10
SYS                            OBJECT                                 28
                               BFILE                                   1
                               BINARY ROWID                            1
                               BLOB                                    1
                               CANONICAL                               1
                               CFILE                                   1
                               CHAR                                    1
                               CLOB                                    1
                               CONTIGUOUS ARRAY                        1
                               DATE                                    1

OWNER                          TYPECODE                         COUNT(*)
------------------------------ ------------------------------ ----------
                               DECIMAL                                 1
                               DOUBLE PRECISION                        1
                               FLOAT                                   1
                               INTEGER                                 1
                               INTERVAL DAY TO SECOND                  1
                               INTERVAL YEAR TO MONTH                  1
                               LOB POINTER                             1
                               NAMED COLLECTION                        1
                               NAMED OBJECT                            1
                               NUMBER                                  1
                               OCTET                                   1

OWNER                          TYPECODE                         COUNT(*)
------------------------------ ------------------------------ ----------
                               OID                                     1
                               PL/SQL BINARY INTEGER                   1
                               PL/SQL BOOLEAN                          1
                               PL/SQL COLLECTION                       1
                               PL/SQL LONG                             1
                               PL/SQL LONG RAW                         1
                               PL/SQL NATURAL                          1
                               PL/SQL NATURALN                         1
                               PL/SQL PLS INTEGER                      1
                               PL/SQL POSITIVE                         1
                               PL/SQL POSITIVEN                        1

OWNER                          TYPECODE                         COUNT(*)
------------------------------ ------------------------------ ----------
                               PL/SQL RECORD                           1
                               PL/SQL REF CURSOR                       1
                               PL/SQL ROWID                            1
                               PL/SQL STRING                           1
                               POINTER                                 1
                               RAW                                     1
                               REAL                                    1
                               REF                                     1
                               SIGNED BINARY INTEGER(16)               1
                               SIGNED BINARY INTEGER(32)               1
                               SIGNED BINARY INTEGER(8)                1

OWNER                          TYPECODE                         COUNT(*)
------------------------------ ------------------------------ ----------
                               SMALLINT                                1
                               TABLE                                   1
                               TIME                                    1
                               TIME WITH TZ                            1
                               TIMESTAMP                               1
                               TIMESTAMP WITH LOCAL TZ                 1
                               TIMESTAMP WITH TZ                       1
                               UNSIGNED BINARY INTEGER(16)             1
                               UNSIGNED BINARY INTEGER(32)             1
                               UNSIGNED BINARY INTEGER(8)              1
                               VARCHAR                                 1

OWNER                          TYPECODE                         COUNT(*)
------------------------------ ------------------------------ ----------
                               VARCHAR2                                1
                               VARYING ARRAY                           1

57 rows selected.

          Followup:

do you use JMS on Oracle?  AQ (advanced queues):

ops$tkyte@ORA817DEV.US.ORACLE.COM> select type_name from dba_types
  2  where owner = 'SYS' and typecode = 'COLLECTION';

TYPE_NAME
------------------------------
AQ$_SUBSCRIBERS
AQ$_RECIPIENTS
AQ$_HISTORY
AQ$_DEQUEUE_HISTORY_T
AQ$_JMS_USERPROPARRAY
DBMS_DEBUG_VC2COLL
ODCICOLINFOLIST
ODCIRIDLIST
ODCIOBJECTLIST
ODCIARGDESCLIST

10 rows selected.

The types are all surrounding that.  My question to you -- since this is
recursive sql done by Oracle under the covers and is not really taking up a slot
in the open_cursors -- why is this such a concern?  It is normal, it is safe, it
is not harmful...

      Rating: 4
Is it really a
      nested table ?  April 09, 2002
Reviewer:  Le
      Crapaud  from Paris
Hello,
I have found the same problem.
In your example, you showed 'table_1_*' which was generated by nested table use.
I have only 'table_4_*' (not 'table_1_*'). And we don't use nested tables.
How can we know which particular object is causing this behavior ?

Thanks,

El Crapaud

          Followup:

Ok, it looks like TABLE_1_* are nested tables.

TABLE_4_* are lobs:

a@ORA817DEV.US.ORACLE.COM> create table t ( x clob );

Table created.

a@ORA817DEV.US.ORACLE.COM> select sql_text from v$open_cursor;

SQL_TEXT
------------------------------------------------------------
select sql_text from v$open_cursor

a@ORA817DEV.US.ORACLE.COM> insert into t values ( 'hello world' );

1 row created.

a@ORA817DEV.US.ORACLE.COM> select sql_text from v$open_cursor;

SQL_TEXT
------------------------------------------------------------
select sql_text from v$open_cursor

a@ORA817DEV.US.ORACLE.COM> select * from t;

X
--------------------------------------------------------------------------------
hello world

a@ORA817DEV.US.ORACLE.COM> select sql_text from v$open_cursor;

SQL_TEXT
------------------------------------------------------------
table_4_200_735d_0_0_0
select sql_text from v$open_cursor

a@ORA817DEV.US.ORACLE.COM>

it is a "lob" object.

      Rating: 5
Just Great
      !  April 09, 2002
Reviewer:  Le crapaud
       from Paris

      Rating:
      5
Thanks.  April 09, 2002
Reviewer:  John
      Salvo  from Sydney Australia
OK. So my initial asssumption is correct.

LOBs are causing these table_4_***. Now I just need to find out what are those
table_e_***. :)1

I know it is not harmful, but I need to know what these are.

          Followup:

And that would appear to be a lob that was opened for writing to:

a@ORA817DEV.US.ORACLE.COM> create table t ( x clob );
Table created.

a@ORA817DEV.US.ORACLE.COM> insert into t values ( 'hello' );
1 row created.

a@ORA817DEV.US.ORACLE.COM> begin
  2          for x in (select x from t for update)
  3          loop
  4                  dbms_lob.writeappend( x.x, length( 'hey' ), 'hey' );
  5          end loop;
  6  end;
  7  /

PL/SQL procedure successfully completed.

a@ORA817DEV.US.ORACLE.COM> select sql_text from v$open_cursor;

SQL_TEXT
------------------------------------------------------------
select sql_text from v$open_cursor
table_e_400_743b_1_0_0

a@ORA817DEV.US.ORACLE.COM>

      Rating: 4
To the original
      statment  April 12, 2002
Reviewer:  Le
      Crapaud  from Paris
Tom,
We know (thanks to you) 'table_4*' in v$open_cursor is due to a SELECT on a lob.
Fine.
But how can we extract this SELECT ? I tried to look at V$SQLTEXT but can't find
anything. That would be very useful for us.
Thanks in advance.

          Followup:

I know of no way to tie that back to the original "select" -- that select
doesn't even have to exist anymore --

o the app could close the cursor
o the sql could be aged out of the shared pool

Interestingly -- the hex number seems to be object_id of the table, consider:

a@ORA9I.WORLD> create table t ( x clob, y clob );

Table created.

a@ORA9I.WORLD>
a@ORA9I.WORLD> insert into t values ( 'eh', 'he' );

1 row created.

a@ORA9I.WORLD>
a@ORA9I.WORLD> select * from t;

X
--------------------------------------------------------------------------------
Y
--------------------------------------------------------------------------------
eh
he

a@ORA9I.WORLD>
a@ORA9I.WORLD> select sql_text from v$open_cursor;

SQL_TEXT
------------------------------------------------------------
select sql_text from v$open_cursor
table_4_200_5b22_0_0_0

11 rows selected.

a@ORA9I.WORLD> select to_number( '5b22', 'xxxx' ) from dual;

TO_NUMBER('5B22','XXXX')
------------------------
                   23330

a@ORA9I.WORLD> select object_name, object_type from user_objects where object_id
= 23330;

OBJECT_NAME                    OBJECT_TYPE
------------------------------ ------------------
T                              TABLE

a@ORA9I.WORLD>

do that over and over -- the hex number changes and is always the object id of
the base table.

      Rating: 5
cool, man
      !  April 12, 2002
Reviewer:  Le Crapaud
       from Paris

      Rating:
      4
Interesting  July 09, 2003
Reviewer:
      Doug  from CT, USA
Very interesting - I just saw some cursors in a DB
like
table_4_2000_e7a_0_0_0
table_e_400_e7a_7_0_0
table_4_200_e7a_0_0_0
and this thread helped me out in 2 minutes.

      Rating: 5
Very
      interesting!  October 28, 2003
Reviewer:  Eric
      Mortensen  from Oslo, Norway
I see these 'table_4_*' entries in Toad all the time. Always wondered what they
are, and now I know :-)

Thanks!

April 12, 2002 atul -- Thanks for the question regarding
      "History data and export", version 8.0.4

      You Asked
sir,

1)I have a big table  say a which is having data of 3 years..(2000,2001,2002)
I don't want 1 year data(2000) as our requirement need only 2 years
data(2001,2002)
But sometimes we do need data from (2000) and we need that urgently so we may
not get time to import etc...

also we want to improve performance of our daily queries..
What is your solution on this?

2)Can we exclude some tables from daily full export...

As we have lot of history tables which will not require export every day so if
we have any technique of excluding those from full export
we will save export time.

3)This is general question,
What proficiency should oracle-DBA have to be called as good DBA?

Because,in today's world new software is launching every day and it's very
difficult to get updated with new things and stick to the old things..

So what according to you DBA should know apart from oracle?

Thanks.
atul

      and we said...
1) it is called partitioning.  Using partitioning, you can easily achieve this.

You can "slide" old data out with a single command (no undo, no redo).
You can "slide" new data in very easily as well.

When you get to 8i and above, you can use transportable tablespaces even so that
you can take the year 2000 partition -- convert it into a table (so the table
with the partitions now has an EMPTY year 2000 partition).  You can "transport"
the year 2000 table AND ALL OF ITS INDEXES "off" to a CD or tape or whatever.
If you ever need it again, you just snap it back in or mount the CD.  The data
is already formatted in the database format, indexes are all there.

2) why are you exporting every day?  Export is *not* a good backup tool.  Maybe
you export once a week to "exercise" your database but you shouldn't be using
export as your backup.  Only a BACKUP should be used as your backup.

I wouldn't dream of exporting every day (i do it once a week).

you can use tables=(list to export) in order to get the ones they want.

3)
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2354713062830
Review
      & Followup

      Rating:
      4
  April 11, 2002
Reviewer:
      A reader
Tom,

You are suggesting to use transportable tablespace as a archiving tool?? Sounds
a good idea. Can you please elaborate a bit more on this (explain how to do
it)..

Thanks a lot.

          Followup:

say you have a range partitioned table.

it is partitioned by month (new partition for each month).
you use locally partitioned indexes only.

you have a requirement to keep 3 years of data online.  You need to keep 7 years
for historical purposes and must be able to put the 4 offline years back online
within a short period of time if necessary.

So, what I would do is every month I would:

switch the oldest partition with an empty table including indexes.  So, now I
just removed the oldest (37'th month) of data from my partitioned table.  I have
a full table with indexes for the 37'th month just sitting there.

I would have put each partition and its local index either in the same
tablespace or in a pair of tablespaces (for those that believe that indexes must
be in a separate tablespace, makes them feel better).  I can now set those
tablespaces READ ONLY.

I can either transport them off to tape and erase the disk image.

Or I can leave them there as tables (they are not in the partitioned table
anymore, no performance impact on that guy)
Or I can copy them to CD-WORM and leave them attached (or not) to the database.

In any case, I can restore them and their indexes in seconds by transporting
back in (or if they never left, they are already there) and swapping them back
into the partitioned table.

      Rating:
      5
  April 11, 2002
Reviewer:
      A reader
Very cool. Thanks Tom.

What do you mean by "transport them off to tape "? Is it:

copy the corresponding datafiles and the exported metadata to the tape? And then
drop the (read only) tablespace with contents.  And when you want them back,
copy the datafiles back in and import the metadata. right?

          Followup:

yes, thats what I mean

(beam me up scotty ;)  -- interestingly kirk is reported to have never actually
said that)

      Rating:
      4
Export  April 12, 2002
Reviewer:
      atul  from India
Sir,

We are exporting everyday because in case any user lost his
table we can recover his table easily with latest data available and without
troubling other users..

As you are saying using table clause in export,but as i said we are using full
export,list of tables in whole database is very big and it may increase day by
day so how to do that?

Thanks.
atul

          Followup:

how the heck does a user "lose" a table.

Use rman, you can do TSPITR (tablespace point in time recovery).

Use 9i, you have flashback query, the user can recover deleted data all by
themselves.

You could use the technique outlined:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3379060892320
here to hide the rows from the user doing the export.

April 12, 2002 --
      Thanks for the question regarding "Running O/S files from PL/SQL", version
      8.1.7

      You Asked
Hi Tom,
Your site is the first place I visit when ever I run into a problem in Oracle.
Really appreciate the help you are doing.

Q1)Running O/S files from a stored procedure
I was looking at the URL below to run O/S files from a stored procedure.

>>Can i call a Shell Unix commands from Pl/Sql or Sql Plus
>>
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:952229840241
I get the following error when I try to execute the procedure.

SQL>  exec :x := RUN_CMD('C:\TEMP\delete\exp.bat');
java.security.AccessControlException: the Permission (java.io.FilePermission
C:\TEMP\delete\exp.bat
at java.security.AccessControlContext.checkPermission(AccessControlContext.java)
at java.security.AccessController.checkPermission(AccessController.java)
at java.lang.SecurityManager.checkPermission(SecurityManager.java)
at
oracle.aurora.rdbms.SecurityManagerImpl.checkPermission(SecurityManagerImpl.java)

at java.lang.SecurityManager.checkExec(SecurityManager.java)
at java.lang.Runtime.exec(Runtime.java)
at java.lang.Runtime.exec(Runtime.java)
at java.lang.Runtime.exec(Runtime.java)
at Util.RunThis(Util:13)

PL/SQL procedure successfully completed.

What is that I am doing wrong?

Q2)Is there anyway I can execute a batch file from DBMS_JOB.  Something like
dbms_job.submit(1, 'scott/tiger @c:\temp\abc.sql;', sysdate, 'sysdate + 1/24');

Q3)Public synonym performance

In the url
>>Poor performance using public synonyms
>>
http://asktom.oracle.com/pls/ask/f?p=4950:8:674964::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:332617310610,%7BPUBLIC%7D%20and%20%7BSYNONYM%7D
The examples/queries you showed compared the CPU parse time.  I was wondering if
it is possible to show the effect public synonyms have on library cache and data
dictionary cache.

Please let me know if you have any questions.

Thanks and Regards

G.V.N.Rao
Ext. 6382

      and we said...
1) I would assume you didn't use dbms_java properly to grant the permissions:

tkyte@TKYTE9I.US.ORACLE.COM> drop user rt_test cascade;

User dropped.

tkyte@TKYTE9I.US.ORACLE.COM> grant dba to rt_test identified by rt_test;

Grant succeeded.

tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM> begin
  2    dbms_java.grant_permission
  3    ('RT_TEST',
  4     'java.io.FilePermission',
  5     'c:\temp\test.bat',
  6     'execute');
  7
  8    dbms_java.grant_permission
  9    ('RT_TEST',
 10     'java.lang.RuntimePermission',
 11     '*',
 12     'writeFileDescriptor' );
 13  end;
 14  /

PL/SQL procedure successfully completed.

tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM> @connect rt_test/rt_test
tkyte@TKYTE9I.US.ORACLE.COM> set termout off
rt_test@TKYTE9I.US.ORACLE.COM> REM GET afiedt.buf NOLIST
rt_test@TKYTE9I.US.ORACLE.COM> set termout on
rt_test@TKYTE9I.US.ORACLE.COM>
rt_test@TKYTE9I.US.ORACLE.COM> create or replace and compile
  2  java source named "Util"
  3  as
  4  import java.io.*;
  5  import java.lang.*;
  6
  7  public class Util extends Object
  8  {
  9    public static int RunThis(String args)
 10    {
 11    Runtime rt = Runtime.getRuntime();
 12    int        rc = -1;
 13
 14    try
 15    {
 16       Process p = rt.exec(args);
 17
 18       int bufSize = 4096;
 19       BufferedInputStream bis =
 20        new BufferedInputStream(p.getInputStream(), bufSize);
 21       int len;
 22       byte buffer[] = new byte[bufSize];
 23
 24       // Echo back what the program spit out
 25       while ((len = bis.read(buffer, 0, bufSize)) != -1)
 26          System.out.write(buffer, 0, len);
 27
 28       rc = p.waitFor();
 29    }
 30    catch (Exception e)
 31    {
 32       e.printStackTrace();
 33       rc = -1;
 34    }
 35    finally
 36    {
 37       return rc;
 38    }
 39    }
 40  }
 41  /

Java created.

rt_test@TKYTE9I.US.ORACLE.COM>
rt_test@TKYTE9I.US.ORACLE.COM> create or replace
  2  function RUN_CMD(p_cmd in varchar2) return number
  3  as
  4  language java
  5  name 'Util.RunThis(java.lang.String) return integer';
  6  /

Function created.

rt_test@TKYTE9I.US.ORACLE.COM>
rt_test@TKYTE9I.US.ORACLE.COM> create or replace procedure RC(p_cmd in varchar2)
  2  as
  3    x number;
  4  begin
  5    x := run_cmd(p_cmd);
  6  end;
  7  /

Procedure created.

rt_test@TKYTE9I.US.ORACLE.COM>
rt_test@TKYTE9I.US.ORACLE.COM> variable x number;
rt_test@TKYTE9I.US.ORACLE.COM> set serveroutput on
rt_test@TKYTE9I.US.ORACLE.COM> exec dbms_java.set_output(100000);

PL/SQL procedure successfully completed.

rt_test@TKYTE9I.US.ORACLE.COM>
rt_test@TKYTE9I.US.ORACLE.COM> host echo dir > c:\temp\test.bat

rt_test@TKYTE9I.US.ORACLE.COM>
rt_test@TKYTE9I.US.ORACLE.COM> exec :x := RUN_CMD('c:\temp\test.bat');
D:\oracle\ora90\DATABASE>dir
Volume in drive D has no label.
Volume Serial Number is 0212-140A
Directory of D:\oracle\ora90\DATABASE
01/14/2002  10:21a      <DIR>          .
01/14/2002  10:21a      <DIR>          ..
09/09/1998  10:31a              31,744 oradba.exe
01/14/2002  10:21a      <DIR>          archive
01/14/2002  10:24a                  45 OraDim.Log
04/11/2002  07:50a               1,536 PWDtkyte9i.ora
01/14/2002  10:28a               2,560 SPFILETKYTE9I.ORA
01/14/2002  10:28a                  46 inittkyte9i.ora
5 File(s)         35,931 bytes
3 Dir(s)  27,187,904,512 bytes free

PL/SQL procedure successfully completed.

2) you would use the java stored procedure above.  you can schedule that to run
on a recurring basis.

3) Already been done, Steve did it for us.  See his results at:

http://www.ixora.com.au/newsletter/2001_05.htm#synonyms

Review &
      Followup

      Rating:
      4
  April 12, 2002
Reviewer:  burak
      karatepe  from Istanbul , Turkiye
Hi Tom,
I try your code but get this error message;

ORA-29540: class Util does not exist
ORA-06512: at "ANT.RUN_CMD", line 0
ORA-06512: at line 1

so i query this;
SELECT OBJECT_NAME, OBJECT_TYPE FROM ALL_OBJECTS
WHERE
OBJECT_TYPE = 'JAVA SOURCE';
Util    JAVA SOURCE
Did i miss something??
Thanks...

          Followup:

you did something wrong somewhere.  a full cut and paste of the entire example
from start to finish as I do is the only thing I'll look at (too many times
people take the stuff, change it in some "minor" way and then say "it doesn't
work")

do it the way I do above -- showing the ENTIRE process.

April 12, 2002 Kishan -- Thanks for the question regarding "How
      to find Referral URL in Stored Procedure", version 8.1.7

      You Asked
Dear Tom:

One of my developers need this particular problem to be solved. They are
building a web application where they want to implement a single sign-on
solution. For this, they need to find out the referral URL in the stored
procedure. Is it possible to do so?

Also, the way they want to implement is fraught with security loop holes.
Currently, they have SAP system which authenticates the first login. After that,
they called an external application, from where they need to access Oracle
database for some other information. For this application, they pass the userid
as a parameter. Now, since anyone can get the URL, find the userid and retrieve
information which they shouldn't.

And no cookies, since they feel it'll fail in case of cookie disabled browsers.
As I'm not familiar with web development environment I'm not sure what is the
right or best way to go about this. Is there a way I can encrypt the userid /
URL on the fly and send it to the procedure and decrypt there so that whoever
sees it will still not be able to figure anything out. What is the recommended
way to implement such an application?

Thank you for your time.
Have a nice day,

Regards,
Kishan.

      and we said...
A referring URL cannot be used to help in the implementation of a single sign
on.  If you do use it -- it is TRIVIAL to defeat it.

In the stored procedure, you can use owa_util.get_cgi_env to retrieve this
information.  In the owa.sql package, you would find this list:

   begin
      /* Initialize cgi names */
      /* Keep this list in the same order as in ndwoa.h file */
      var_name(1) := 'SERVER_SOFTWARE';
      var_name(2) := 'SERVER_NAME';
      var_name(3) := 'GATEWAY_INTERFACE';
      var_name(4) := 'REMOTE_HOST';
      var_name(5) := 'REMOTE_ADDR';
      var_name(6) := 'AUTH_TYPE';
      var_name(7) := 'REMOTE_USER';
      var_name(8) := 'REMOTE_IDENT';
      var_name(9) := 'HTTP_ACCEPT';
      var_name(10) := 'HTTP_USER_AGENT';
      var_name(11) := 'SERVER_PROTOCOL';
      var_name(12) := 'SERVER_PORT';
      var_name(13) := 'SCRIPT_NAME';
      var_name(14) := 'PATH_INFO';
      var_name(15) := 'PATH_TRANSLATED';
      var_name(16) := 'HTTP_REFERER';
      var_name(17) := 'HTTP_COOKIE';

Those are the variables available to you.

Using referrer for ANYTHING other then analyzing where people come from is not
something you want to do.  It cannot be used to "secure" things.

They are just kidding themselves if they do no use cookies.  You need to encrypt
the identification of the user in a cookie using an ever changing key.

April 12, 2002 Rachelle -- Thanks for the question regarding
      "explicit cursors in package and procedure level", version 7

      You Asked
Hi Tom,

I have this 'knowledge' before that when declaring explicit cursors in package
level, you must issue a close cursor before exiting the package otherwise when
the package is executed again, the fetch to the cursor will point to the next
row and may not be the real first row. But if you declare explicit cursors in
the procedure level, even if it was not closed by issuing the Close cursor
statement and the next time the procedure is executed the fetch will point to
the first row (as if the cursor was closed previously in the procedure level).
Is this true?

      and we said...
Yes, it works just like a variable would.  Consider:

create package body foo
as
    g_number number := 0;

procedure p
is
   l_number number := 0;
begin
   g_number := g_number+1;
   l_number := l_number+1;

   l_number -- will ALWAYS be 1 at this point.  G_NUMBER will be 1, 2, 3, 4, ...

               assuming no other procedure touches it.

a cursor would work the same way -- put it outside a procedure and it's lifespan
is the session.  put it inside and its lifespan is the call.

On the second call -- it's (the procedure defined cursor) a "brand new cursor"
never before used.

In any case -- I hate explicit cursors unless you HAVE to use them (eg: it needs
to remain open across calls or it is a ref cursor).  implicit cursors are
faster, easier to code, and just "rule".

Review &
      Followup

      Rating: 5
explicit
      cursors in package and procedure level  April 14, 2002

Reviewer:  rae  from singapore
hi tom,
thanks for clearing my mind on this one.

April 12, 2002 Pablo -- Thanks for the question regarding
      "Locking Problems", version 8.1.5

      You Asked
Tom:

       We have an application that insert record into a table called "PERSONS",
there are a lot of user using this application, before to insert each record a
max(id) (id is the primary_key) value from the persons table is gotten. Also
there are batch process that insert records into the same table at the same
time, about 2000 records each. The problem is a very frecuent primary key
violation because 2 or more user get the same max, one of the user can insert
the record with the new id=max(id)+1 but the others get a primary constraint
violation. I know that this is a very bad design, a sequence would be a better
solution , isn't it?,  but the developers don't want to re-write and re-compile
the programs. My question is how can i do to avoid the locking and the primary
constraint violation at the same time using the max(id) approach?

Thanks in Advance
Pablo R.

      and we said...
alter session set rant=TRUE;
alter session set rant_level=MAXIMUM;

You cannot.

Here are the steps to fix this problem:

a) fire the existing developers.  They obviously don't use databases very often.

b) hire developers who have experience writing large scale, scalable
applications, have read the documenation and are willing to fix bugs in their
code when discovered.

Sorry to be so blunt but this is "database 101" stuff here.

Yes -- a sequence is the proper design choice.
Yes -- select max(id)+1 from table is the SINGLE WORST WAY to do this.
Yes -- using a sequence would solve your concurrency issue
Yes -- using a sequence would fix the "dup val on index" bug they have
introduced

The ONLY solution here is to FIX the application which has a very well defined
problem and an even more well defined solution.  Period.

exit;

Review &
      Followup

      Rating:
      4
  April 12, 2002
Reviewer:  Ringo Li
       from Hong Kong
I like the word of firing the non db programmer.

If the developer does not want to make use of the db feature, they can either
slove the problem on the application level (by themself) or accept the fact that
they do not know how to do it. And start to learn the DB !

Blaming the DBA is not the means to slove the problem

April 12, 2002 manoharan -- Thanks for the question regarding
      "Couple of doubts", version 8.1.7

      You Asked
I went thru u'r book.Very informative book!

I have a couple of doubts to be clarified.Could u please spend your precious
time to clarify my doubts

 1.What are all the impacts in the database while we open the database in reset
log mode.I would like to know the whole concept behind this one.(infact i could
not find any information regarding this one in your book).

 2.We have a server(oracle 7.3.4) running under AIX with audit enabled.All of my
application developers are using same oracle userid/password for a production
application.One of the table's records has been deleted by a developer.As audit
is enabled,we could find out the oracle userid.but we are not able to go further
to cath hold the culprit.Could u please advice me in this regard.(we cannot
create separate userid/passwd for each developer as per our business)

Thankx
Jeyaseelan.M

      and we said...
1) reset logs just wipes out the log files (clears them out, creates them if
they do not exist)

you do this typically after a point in time recovery -- you have to "break" the
redo chain as there is data in the redo logs that is part of a future that will
never happen.

After a resetlogs, you should do a full hot backup if you are in archivelog
mode.  Your existing backups are worthless after a resetlogs. (non-archivelog
mode backups are not affected cause you do not use them for media recovery,
archivelog mode backups are rendered useless by this operation)

2) You business requirements conflict with your security requirements then.  One
of them will lose.  Looks like in this case, security loses.

The audit trail will contain other information such as the terminal (this is
generally useless as tty's are assigned randomly) and if you are using sqlnet,
you might be able to see the ip_address in there.
Review &
      Followup

      Rating:
      4
Thankx  April 12, 2002
Reviewer:
      A reader

April 12, 2002 Nasir -- Thanks for the question regarding
      "difference between oracle8i & oracle9i", version 8

      You Asked
I am using oracle8i and I want to know what is difference
between oracle8i & oracle9i ..... I need this information on
ASAP bases.

Regards,
Nasir Danish

      and we said...
http://technet.oracle.com/products/oracle9i/index.htm
has a 9i new features summary.

Review &
      Followup

      Rating:
      1
  April 12, 2002
Reviewer:  Gerard
      Giltinan  from Newcastle Upon Tyne UK
the link referred to does not exist. I have also reported this to Oracle

          Followup:

Ok, links go away over time...  No problem.

here is the roadmap to get there for all time (as long as 9i docs are around
anyway):

goto technet.oracle.com -> documentation -> 9i database
   -> Oracle9i Database Online Documentation -> list of books -> NEW

here is the "shortcut"

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/nav/docindex.htm#index-NEW
but -- that is subject to change.

April 12, 2002 Charlie -- Thanks for the question regarding
      "unique number on column", version 8.1.7

      You Asked
Hi Tom,

I have a table T(group varchar2(3), group_id number).  If I want to generate
data like the following:

group    group_id
-----    --------
AA     1
AA     2
AA     3
AA     4
AA     5
...
AB     1
AB     2
AB     3
AB     4
AB     5
...
BC     1
BC     2
BC     3
BC     4
BC     5
BC     6
......
......

That means whenever I insert a group name into this table, the database should
generate unique number as group_id IN THIS GROUP.  How can I do that? I know if
I want to make the group_id column unique, I can just simply use "sequence".
However, in this case, the group_id should be unique across the group but not
unique for the whole group_id column.

Thanks!

      and we said...
apparently, not only "unique" but sequential

you can only do this procedurally.  You'll be assigning numbers by yourself.

I would reconsider this requirement.  I can synthesize such a number on
retrieval:

select group, row_number() over ( partition by group order by group_id )
  from t
/

that would give me that sequential number UPON RETRIEVAL but the real group_id
would just a unique sequence.
Review &
      Followup

      Rating: 2
unique not
      sequential  April 12, 2002
Reviewer:
      Charlie
Hi Tom,

I think the example I gave is a little confused.  The group_id should be unique
in the group but not necessarily sequential(allowing gaps).

group  group_id
-----  --------
AA     1
AA     2
AA     3
AA     7
AA     9
...
AB     1
AB     2
AB     3
AB     4
AB     15
...
BC     1
BC     2
BC     3
BC     4
BC     5
BC     6
......
......

I want database generate this number and store it upon insertion instead of upon
retrieval.  How can I do that?

Thanks,

          Followup:

Use a sequence then -- why do you care if the data is:

group    group_id
AA       1
AA       100
AA       200
....
AB       2
AB       101
AB       102
AB       205
....
BC       206
BC       207
BC       10000

if 1, 2, 3, 4, 15 is OK, 1, 100, 200 should be OK as should 206, 207, 10000

Just use a sequence.  A trigger can be used to "store it upon insertion" easily

      Rating:
      4
Thanks!  April 12, 2002
Reviewer:
      Charlie

April 12, 2002 Alexandre -- Thanks for the question regarding
      "LMT and DMT", version 8.1.7

      You Asked
Tom,

We have a 100Gb sized database and we´d like to migrate some DMT to LMT...
What is the best and fastest way to do it ?

dbms_space_admin.tablespace_migrate_to_local
or
create a LMT and move the objects to it.

Does it really worth to migrate DMT to LMT ?
Could you show me some examples concerning performance gain ?

      and we said...
dbms_space_admin.tablespace_migrate_to_local

is faster but not as good as moving.

100gig, should not take very long at all, thats a pretty small database.

The performance gain is in the allocation of new extents.  I love LMT's not only
cause they are faster at allocating extents but because they make fragmentation
a thing of the past -- it just doesn't happen.

Do this -- create a DMT table with minextents = 500, now do the same thing in an
LMT.   drop the tables then.  compare the speed.
Review &
      Followup

      Rating: 5
LMT and
      DMT  April 11, 2002
Reviewer:  Alexandre
      Matthes  from Rio de Janeiro - Brasil
Hi Tom,

You´re really fast to feedback us... Thanks a lot

I´ve created two tablespaces (teste-LMT and teste2-DMT)
One table into each tablespace (t - teste and t2 - teste2).
I´ve noted that Oracle was slower not just dropping the table but either
creating it into DMT...

Look,

15:30:36 SYS@DBLOCAL:RIO-MATTHES> create table t(cod number(8),name
varchar2(50))
15:31:56   2  storage(minextents 500) tablespace teste;

Table created.

Elapsed: 00:00:00.07

15:32:58 SYS@DBLOCAL:RIO-MATTHES> create table t2(cod number(8),name
varchar2(50))
15:33:06   2  storage(minextents 500) tablespace teste2;

Table created.

Elapsed: 00:00:19.03

15:34:05 SYS@DBLOCAL:RIO-MATTHES> drop table t;

Table dropped.

Elapsed: 00:00:00.08

15:34:06 SYS@DBLOCAL:RIO-MATTHES> drop table t2;

Table dropped.

Elapsed: 00:00:02.04

      Rating: 4
No support for
      LMT from 3:rd party vendor  April 12, 2002

Reviewer:  Roger Andersson  from Sweden
One of our 3:rd party software vendors states that their application don't
support using LMT. The application uses features like

SQL>  select distinct object_type from user_objects order by 1;

OBJECT_TYPE
------------------
INDEXTYPE
LIBRARY
OPERATOR
PACKAGE
PACKAGE BODY
TABLE
TYPE
TYPE BODY

8 rows selected.

Other schemas are also used for the application but these only uses "normal"
objects like tables, indexes, packages etc.

I've done tests in test environment with using LMT and it seems to work. In
production we use DMT, becasue we want to have a supported environment.

Are there any reason that the application should not work with LMT?

          Followup:

I will never counter what a 3rd party software vendor says.  You must do what
they say in general.  If they say "it isn't supported" and i say "no reason why
it shouldn't work" -- and you do it -  you won't be SUPPORTED.  That is serious,
that is bad.

You need to follow their guidelines.

April 12, 2002 Archa -- Thanks for the question regarding
      "query", version 8i

      You Asked
If a table has a column with char(20) datatype and inserted
value is less then 20 so,how to write a query to fetch data?
create table abc
(a char(20))
select a from abc where a = 'Tom';
or
select a from abc where rtrim(a) = 'Tom';

The first query also retrieves result. why?

      and we said...
A char(20) is *never* less then 20 bytes.  char fields are
ALWAYS blank padded, always.

If you select length(a) from abc, it will either be 20 or NULL
-- never any other size.

As for why the query:

select a from abc where a = 'Tom'

works -- it is because the string literal 'Tom' is "promoted" to
the type it is being compared to.  Internally, 'Tom' is cast
into a char(20) to perform the comparision -- hence, it has 17
blanks on the end and the comparision succeeds.

What is more interesting is why:

ops$tkyte@8.0> variable a varchar2(20)
ops$tkyte@8.0> exec :a := 'Tom'

PL/SQL procedure successfully completed.

ops$tkyte@8.0>
ops$tkyte@8.0> select * from abc where a = :a;

no rows selected

returns NO rows.  In this case, the char(20) field is cast to a
varchar2(20) for the comparision and since:

 'Tom                 ' <> 'Tom'

it fails.  Here you would need to code either:

ops$tkyte@8.0> select * from abc where rtrim(a) = :a;

A
--------------------
Tom

ops$tkyte@8.0> select * from abc where a = rpad(:a,20);

A
--------------------
Tom

and I would always suggest the second case -- using the rpad()
ONCE on the constant rather then rtrim'ing EVERY row in the
table.  Especially if (a) is indexed you would want to use the
second form.

Review &
      Followup

      Rating:
      5
  April 12, 2002
Reviewer:
      A reader
In the previous versions(7.3.4), the following sql would not return any rows
when a is char(20)

select a from abc where a = 'Tom';

The WHERE clause would have to be rtrim(a) = 'Tom' to return data.

Is this something new in 8i?

          Followup:

That is not true -- it has always been that:

select * from t where a = 'Tom';

would be interpreted as:

select * from t where a = 'Tom           ';

It has always been a bind variable issue -- when I bind a VARCHAR and compare to
a CHAR - it does not work:

tkyte@ORA716.WORLD> create table t ( a char(20) );

Table created.

tkyte@ORA716.WORLD>
tkyte@ORA716.WORLD> insert into t values ( 'Tom' );

1 row created.

tkyte@ORA716.WORLD>
tkyte@ORA716.WORLD> select * from t where a = 'Tom';

A
--------------------
Tom

tkyte@ORA716.WORLD>
tkyte@ORA716.WORLD> variable x varchar2(20)
tkyte@ORA716.WORLD> exec :x := 'Tom';

PL/SQL procedure successfully completed.

tkyte@ORA716.WORLD>
tkyte@ORA716.WORLD> select * from t where a = :x;

no rows selected

tkyte@ORA716.WORLD>
tkyte@ORA716.WORLD> select * from t where rtrim(a) = :x;

A
--------------------
Tom

tkyte@ORA716.WORLD> select * from t where a = rpad(:x,20);

A
--------------------
Tom

tkyte@ORA716.WORLD>

      Rating: 5
Thanks for your
      quick response  April 12, 2002
Reviewer:
      A reader

April 12, 2002 Carlos -- Thanks for the question regarding "Max
      # Concurrent Users in OAS PLSQL cart app?", version 4.0.8 / 9iAS

      You Asked
We have a PLSQL cartridge application running on OAS 4.0.8 with a handful of
users.  We want to expand the user base with the potential of greater than 1000
concurrent users executing web queries.

The listener we are using to access the app has defined
Max# of Connections = 338.

> Is this the limit for CONCURRENT users all running a query at the same time?
If the limit is exceeded does the request go into a queue or does just generate
a browser error message?

> All our listeners have the same value for this parameter.  Is 338 the max#
concurrent users (A) for EACH LISTENER or (B) TOTAL# that OAS can handle?  If
(B), then how can we configure for more?

> Is there such a thing as "ADDING A SECOND INSTANCE" of OAS on either the same
box or another box to DOUBLE our Max#?

We will upgrade to 9iAS shortly, so same questions apply there.  I notice you
are using PLSQL cartridge here on this site, and I'm sure you sometimes get
large #'s of concurrent users.  How do you do it?

      and we said...
338 is derived from the fact that 3 "files" are opened for each request.  338*4
= 1024 which is very close to the maximum of 1024 files open/process.  That is
where that limit comes from.

Anything above 338 will be rejected -- unable to open the connection (no chance
to queue)

to go over that limit, you would startup more then one OAS http daemon and
either use load balancing software to distribute the hits across the many
servers or you would "do your own" load balancing and redirect users to
different ports (different http daemons) on the same machine.

With apache, MaxClients defaults to 256 but you can set that higher.  As well,
using the ListenBacklog you may be able to queue upto 512 (os dependent)
requests (768 total) as well.  Again, with 1,000 concurrent requests, you are
probably looking at getting some load balancing software/hardware to distribute
the requests out across many http daemons.

Review &
      Followup

      Rating: 3
users logon
      database via 9iAS   April 12, 2002
Reviewer:
      Mike  from NY, NY
My impression is all users using one or two accounts, such as portal_30,
portal_public to login the backend. How do you tell/trace who are they coming
from?

          Followup:

Using the single sign on server, applications can determine the user via a
cookie.

April 12, 2002 OP
      -- Thanks for the question regarding "CLOB and BLOB datatypes !", version
      8.1.6

      You Asked
Hi Tom,

How are you ?

My Question to you is as follows :-

If you have a column of type CLOB or BLOB in a
table Is it possible to view the contents of
this column in the select statement ?

Is it possible to insert data into these columns
through insert statement ? If not ,what is the
alternative.

Is it possible to write a PL/SQL block for this.
Can a function be made which can return the value
of this type , and this function then can be included
in the select statement as ORACLE 8i supports the
function to be used as a column in the select statement.

Thanks & Regards
OP

      and we said...
See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:624423639385
for an example of how to see a portion of a blob in SQL (works
for clobs as well, you don't need utl_raw obviously for them).
dbms_lob.substr will let you see at least 4000 bytes of the
blob/clob in "straight" sql.

It answers the last part of your question as well with regards
to calling a plsql function.

Yes, you can insert upto 4000 bytes in an INSERT statement --
just

 insert into t ( clob_col ) values ( 'Hello World' );

it works.  For larger chunks, you would insert an EMPTY_CLOB()
and then using that newly created lob locator write to it in
chunks.  It would look like this:

begin
   insert into t ( clob_col ) values ( empty_clob() )
   returning clob_col into Local_Variable;

   dbms_lob.write( local_variable, .... );

end;
/

in plsql.
Review
      & Followup

      Rating:
      5
  March 22, 2001
Reviewer:  Helena
      Markova  from Bratislava, Slovakia

      Rating: 5
How about CLOB
      as parameter?  April 11, 2002
Reviewer:  Joshua
      Perry  from Las Vegas
    So, how does this apply to CLOBs as parameters to a Packaged Procedure?

    As an example, if I have a procedure that will parse & process an XML
document, how do you perform the
"CALL(SOME_PACKAGE.SOME_PROC('<XML>...</XML>'))" from the client if the XML
document is larger than 4K?

    I am using ADO to execute Procedure call from NT service against a 9i
install on SUN UNIX.  Is there a way to do this with the ADO or am I forced to
use Embedded SQL (Pro*C/C++)?  If the later, how would I perform the embedded
execution?

Thanks!!

          Followup:

The XML document can be 32k (the varchar2 limit in PLSQL is 32k, not 4000
bytes)...

If you have >32k -- i would be tempted to use a global temporary table, stream
the data into that and call a procedure to work against that clob/blob.

you can do all of this from VB. No C needed.

      Rating: 3
How about CLOB
      as parameter?  April 12, 2002
Reviewer:  Joshua
      Perry  from Las Vegas
Thanks for the response...

I appreciate what you say about VARCHAR2 being 32K, but when I make any
CALL('LITERAL') from C++/ADO where the LITERAL is larger than 4K I get the error
that string literal is too large.  When you say that VARCHAR2 is 32K does that
only apply when inside a PLSQL block/Procedure?

You used the term "stream," does this imply that there is another way to load
this XML to CLOB in the Global Temp. Table, or must I do the -- Insert
Empty_CLOB & WRITEAPPEN() method?

Thanks Again!

          Followup:

You need to use BIND VARIABLES -- for things from 1 byte to 32kbytes.

You NEED to use BIND VARIABLES regardless.

BIND VARIABLES do not have the limit of a character string literal.

      Rating: 4
Last
      Question...  April 12, 2002
Reviewer:  Joshua
      Perry  from Las Vegas
Forgive me for drawing this out, but does that then equate to input parameters
with ADO?

By the way...Your site is one of the best sources for Oracle I have encounter as
of yet!

Thanks

          Followup:

I know nothing about ADO or VB programming other then I am 100% sure it supports
bind variables.

If you look at

http://asktom.oracle.com/~tkyte/ResultSets/index.html
someone gave me a VB and/or ASP example that uses bind variables for sample
code.

April 13, 2002 sl
      -- Thanks for the question regarding "number of extents more than 10000",
      version 8.0.4

      You Asked
Hi Tom,

1. I have read from this site that you said that table with 1000 extents is not
a problem, then how many extents a table has, would be a problem? I have a table
with more than 10000 extents, is it OK? If not, what can I do?

2. I used the following sql which you provided in this site to check for
fragmentation:
        select * from dba_free_space
        where tablespace_name ='T'
        and bytes <= ( select min(next_extent)
                       from dba_segments
                       where tablespace_name = 'T')
        order by block_id;

There is no row selected, does this means there is no fragementation?

3. Since my company uses Oracle Financial Application, the database structure
was done by those Oracle Experts. I found that most of the tables have very
large INITIAL extents(some are 300M), and the NEXT extents are not equal to
INITIAL extents. The pctincrease is 50%. In this case, will these tables have a
possibility of fragmentation? Should I change the settings which Oracle Expert
had set for us?

4. Finally, is fragmentation really a problem that we have to get rid of it?

Thanks in advance,

slmak

P.S. really hard to get chance to ask you questions, I am very happy everytime
when I got the chance.

      and we said...
1) I just did a benchmark.  Put a table into a 1 meg extent sized tablespace
instead of the 50 meg one I intended.  I ended up with over 17,000 extents.  I
left it alone.  It was just fine.

Unless you have some measurable performance issue that you can identify and show
that reducing the number of extents will fix, I would just leave it alone.

The 1000 rule is for sizing, you should aim for 1000 max when choosing the
tablespace to put your object into.  (i believe ALL extents in a tablespace
should be the same size, I love locally managed tablespaces in 8i and up).

In 8.0 you have a dictionary managed tablespace.  It would litterally take hours
or longer to "de-extent" this table.

2) that means that all of the chunks of free space in tablespace T are at least
big enough to hold the next extent for any object in that tablespace.  You have
no tiny chunks that can never be used.  You are not fragemented.

3) You should leave the sizing alone on COTS applications unless told to change
it at  the direction of the vendor.  Oracle applications now use locally managed
tablespaces with no pctincreases and such.

4) if it is preventing you from allocating new objects/extents -- yes.  If you
have 500meg free in a tablespace but the chunks are all too small to do anything
with -- you have a problem.  If not, you don't.
Review &
      Followup

      Rating:
      3
  April 12, 2002
Reviewer:
      A reader
"It would litterally take hours or longer to "de-extent" this table.  "

:-- is it coalescing all the extents to one? If yes why would one has to do that
when you say that having 10000 extents per table is no problem?

And what is COTS applications? Thanks.

          Followup:

releasing those extents -- doing the recursive SQL to move them from sys.uet$ to
sys.fet$, would be extremely expensive -- that would be the issue.

If you don't release them -- no problem.

Lots of extents in a dictionary managed tablespace is a problem when it comes to

a) allocating them (takes longer) -- but you've already allocated them
b) releasing them (takes a real long time)

so, unless you are hitting some measurable, quantifiable performance issue that
can be fixed by rebuilding the table -- DON'T.

      Rating: 5
"Applications"
      tablespaces  April 13, 2002
Reviewer:  Connor
      McDonald  from UK
I've seen several "vanilla" 11i sites now and whilst they are all LMT, they also
appear to be under USER alloc.  Not as 'bad' as dictionary managed, but
certainly not as good as 'genuine' (ie uniform) LMT.

          Followup:

I think Jonathan Lewis said this best on the newsgroups recently:

http://groups.google.com/groups?selm=1018603557.28719.0.nnrp-10.9e984b29%40news.demon.co.uk&output=gplain
<quote>
The extent size is small and grows by a factor
of 8 or 16 when it grows.  So when you install
product X with 3,000 tables of which you are
going to use 600 and nobody can tell you which
ones (e.g. Applications) - then you don't waste
a huge amount of space when you install,
but the tables you do use grow to suitable
sized extents very quickly without leaving a
huge mess behind them.
</quote>

so for apps -- where the usage of the tables is driven by the features the end
users enable (in one implementation table T might be HUGE, in another TINY),
this non-uniform extent allocation strategy can be worthwhile.

April 13, 2002 --
      Thanks for the question regarding "XML Procedure", version 8.1.7

      You Asked
Hello,

Hope you are in great shape. Since I frequently get requests from customers
asking for adhoc reports on various combinations, I am mulling over the idea to
use xml code to generate reports. Since at work I hardly ever get the
opportunity to develop such things at work I have installed personal oracle
8.1.7 in home computer. The point I am coming to is generate dynamic xml code
from database based on what the end user has selected in his interface form.  Is
it possible for me to use personal oracle 8i to do that or do i need oracle 9i/
8i enterprise version for that or do i need to install xdk. do you have any
sample code that i could for the same. Any help is appreciated. Have a good day.

      and we said...
You can use 8i, goto otn and follow the links to technology->xml.  there you can
download the latest xdk into the database.

it'll have MUCH of the functionality of 9i, but not all (eg: no XMLType datatype
in 8i).

If you search for "dillon" -- he is my xml expert -- on asktom, you'll see quite
a few examples.
April 13, 2002
      Partha -- Thanks for the question regarding
      "Dynamic allocation of extents for a tablespace", version 8.1.7

      You Asked
Tom,

How should i avoid " Unable to extend tablespace error messages ", because in
our applications , some inserts and updates run longer time, when it is doing
like that, we are unable to see any database error messages, and not even in
dump file, why and what is the cause for that.

I want's to avoid "unable to extend tablespace errors".

Can you please give me your detail explaination on this.

Thanks in advance

      and we said...
the solution is simple:

o stop inserting or updating data and don't run any queries that sort (so don't
run any queries)

or

o size your tablespaces to be big enough for the job you are doing.  If you
don't know the size, let your datafiles autoextend (i let all of my datafiles
autoextend out to 2gig) and just have lots of disk sitting out there.

This question is sort of surprising.  Unable to extend seems like a common sense
message "you've run out of space".

If you are hitting max extents (you do not give the exact code so I don't know),
alter the underlying objects to allow for more extents.
Review &
      Followup

      Rating:
      3
  April 15, 2002
Reviewer:
      A reader

April 13, 2002 Irfan -- Thanks for the question regarding "Using
      External Files in 9i", version 9.0.1

      You Asked
Hi Tom,
One of the new feautures of Oracle 9i is to use external files to store
information outside the database. I would like to know how Oracle retrieves the
data and can it be displayed in Forms or how to access using SQL PL/SQL. Is it
has to be a CSV format or it can use any Excel or Word data. Please provide a
simple example for the same.

Thanks

      and we said...
simple search on asktom turns up

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2518297765453
....

it doesn't have to be CSV, has to be something sqlldr could have loaded.

No, not word, not excel.  Oracle Text (intermedia in 8i) does word and excel.
turns word/excel into other formats if you need, provides searching and such as
well.
April 13, 2002 Mohammed -- Thanks for the question regarding
      "Avoiding unique constraint in inserting data", version 8i

      You Asked
Hi Tom:
             I have some of Question for you .
1-    I am trying to do insertion for data from table to another table ,but
there is  some record that are found in the Two tables.
I did an  PL/SQL to do this process which as following

DECLARE
         cursor Testing is
                          select *
                          from   LOg_bad_calls
                          where  rownum < 30;
         dataInsertion Testing%rowtype;
       begin
           open testing ;
           loop
               fetch Testing into dataInsertion;
               Exit when testing%NOTFOUND;
               dbms_outPut.put_line(dataInsertion.RECORD_ID||'
'||dataInsertion.RECORD_TYPE);
               insert into LBC_test1 values
                                              ( dataInsertion.RECORD_ID,
                                                dataInsertion.RECORD_TYPE,

dataInsertion.TERM_SERVICES,
                                                dataInsertion.DEVICE_ID,
                                                dataInsertion.CHANNEL_USE_TYPE,
                                                dataInsertion.A_EQUIPMENT_ID,
        ..
        ..
                                              );
            commit;
--            dbms_outPut.put_line(dataInsertion.RECORD_ID||'
'||dataInsertion.RECORD_TYPE);
        end loop;
        close testing;

              --when others then
                --   raise;
  End;
/
I have an error  :
ERROR at line 1:
ORA-00001: unique constraint (ESMAELM.LBC_TEST_PK) violated
ORA-06512: at line 12
How can I avid this and if this error appear during PL/SQL Execution ,ignore it.
I tried to put this exception :
exception
              when dup_val_on_index then
                   NULL;  -- ignore it
But it did  not work .

      and we said...
the code could just be this:

begin
   for dataInsertion in ( select * from log_bad_calls where rownum < 30 )
   loop
        BEGIN
             insert into lbc_test1 values ......;
        EXCEPTION
             when dup_val_on_index then
                 null;
        END;
   end loop;
end;
/

Although:

insert into lbc_test1
select ..... from log_bad_calls where rownum < 30
and NOT EXISTS ( select null
                   from lbc_test1
                  where lbc_test1.record_id = log_bad_calls.record_id )

will work and work better if record id is unique within LOG_BAD_CALLS (and if it
isn't unique, you might just as well put random data into lbc_test1 as you have
no control really over which record may or may not be inserted from run to run)

You don't even need procedural code for this process in all probability.
April 13, 2002 Prakash -- Thanks for the question regarding
      "works in order by but not in where clause", version 8.1.7

      You Asked
Hi tom,
Could you please explain this
1)
SELECT A.ecn_number ,
             (select B.DOCUMENT_NUMBER FROM
              NDMS.NDMS_DWG_REV_ALL_VIEW B
              WHERE B.metadata_key =
                              A.NDMS_METADATA_KEY AND B.rev_metadata_key
                              = A.NDMS_REV_METADATA_KEY ) AS
DOCUMENT_NUMBER
FROM mis.ecp_ecn A
WHERE DOCUMENT_NUMBER = 'A10045'
--doesnt work gives error invalid column name
ORDER BY DOCUMENT_NUMBER --works

2)
also if someone want to add a column to index then that index has to be dropped
first and then recreated with new columns...what is the technical reason for
that.

thank you tom for all your help

      and we said...
1) it is all a matter of scoping.  the order by happens AFTER the entire select
is "done".  In the select, document_number doesn't exist yet.

Inline views are awesome for this:

select *
  from ( SELECT A.ecn_number ,
             (select B.DOCUMENT_NUMBER FROM
              NDMS.NDMS_DWG_REV_ALL_VIEW B
              WHERE B.metadata_key =
                              A.NDMS_METADATA_KEY AND B.rev_metadata_key
                              = A.NDMS_REV_METADATA_KEY ) AS DOCUMENT_NUMBER
         FROM mis.ecp_ecn A )
WHERE DOCUMENT_NUMBER = 'A10045'
ORDER BY DOCUMENT_NUMBER

2) you need to rescan the entire database table, you need to rewrite the entire
index AND the index itself may end up with a totally different number of ROWS in
it -- it is a wholly different index.  (eg: the original index on T might have 0
ROWS in it.  the new index on T with that second column might have more than
ZERO)...

It is not like adding a column to a table, that just makes it so new rows can
have that column.

The entire index structure will be 100% different.

April 13, 2002 Mohammed -- Thanks for the question regarding
      "Tuning SQL STEP BY STEP", version 8i

      You Asked
Hi Tom :
       Could Please Advice me how to start learning Tuning for SQl.and How Can I
run my SQL with least Time .I am weak in this part of SQL ,I am not concerning
now in Application Tuning or database ,I am concerning only in SQL tuning ,and
to run my SQL with minimum  Time .

With my best regards .

      and we said...
First step:

o change you goals

Second step:

o realize that tuning a query independent of the database/application is not
always possible.

Sometime the ONLY way to tune a query is to change the structure (the
application) or the database (sort_area_size for example).

You have to take a holistic approach.

So, to achieve your goal, read and memorize this book:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76992/toc.htm
and then apply the knowledge learned to different problems (that is the really
hard part -- turning the written facts into useful techniques as every problem
is slightly different from the last)

April 14, 2002 Mohammed -- Thanks for the question regarding "CR
      IN SOME OF TEXT FIELDS & BACKUP OR PRODUCTION", version 8i

      You Asked
Hi Tom :
       1- I  have filed called address in one of my Database but this filed
containing CR char
I want to remove it from the filed  I tried by doing this sql

Select
 substr(c.address,1,instr(c.address,chr(13))-1)||'
'||substr(c.address,instr(c.address,chr(13))+2,length(c.address)) Address
            from clients c

           but I faced some of records that containing more than CR char ,
           do you have any solution  for this ,I also tried to use
           select replace(c.address ,chr(13),’ ‘) Address from Clients
           but it did not work properly .

         How Can I solve this without Using PL/SQL
2- I have another Question :
   Some times when I login to database, I dout if I am in Backup or Production
Do you  have any idea to be done for clarifying or to show me where I am.
Ex. Show database; or something else .
Note DataBase name is the same .

      and we said...
1) replace ( field, chr(10), '' )

that'll replace all linefeeds with nothing.

2) change the database names.  truly bad idea don't you thing to have them the
same??

If they have the same name, same global_name, same sid, you are "hosed".  If any
of those are different -- search for connect.sql on this site to see how to set
your sqlplus prompt to the current database name -- you could use the
global_name, the sid, whatever.
Review &
      Followup

      Rating: 2
Replace
      Function  April 14, 2002
Reviewer:  Mohammed
      Al-moayed  from Yemen
for using Replace(field ,chr(10),'')
I tried that again but it did not work properly, Maybe because of Arabic Text .

          Followup:

Ok, use chr(13) -- after all that is what you were substring'ing on.

If the substr worked, replace will work just the same -- looks like you actually
want to replace

chr(13)||chr(10) with '', not just chr(10).  You have a "dos" carriage
return/linefeed, not just a linefeed.

April 14, 2002 Nag -- Thanks for the question regarding "Any,
      Some, All", version 8.1.7

      You Asked
Tom

I saw a discussion on your forum on last Thursday about the use of ANY and SOME
clauses.

You are right, we seldom use ANY or SOME or ALL.

Can you first of all tell us,

1.When should we use SOME ( you have already told us the use of ANY), and when
should we use ALL. Can you give us examples please.

2.Do you think ANY is any way better than IN  , or SOME is better than IN.

Lastly,

3.When I install oracle software I see that even when I select my D drive as the
drive where I want to install it , the installation wizard shows that it is
going use some space on my C drive ( my OS drive)to install some files.

What are the kind of these files which are installed on the C drive.

Thank you

      and we said...
1) some and any are synonymous.  if you are >= ANY, you are >= SOME.  If you =
ANY, you = SOME.

Any and Some in SQL are like "is" and "as" in PL/SQL.  They are the same.

You use them when the mood strikes you.  I use ALL in the solution to getting
the most frequently occuring month in a table.  Consider:

scott@ORA817DEV.US.ORACLE.COM> select to_char(hiredate,'MM'), count(*)
  2  from emp
  3  group by to_char(hiredate,'MM')
  4  order by 2
  5  /

TO   COUNT(*)
-- ----------
04          1
05          1
11          1
06          1
01          2
02          2
09          2
12          4

8 rows selected.

so the answer I am after is 12.  Using SQL that should work in pretty much
every database I can code:

scott@ORA817DEV.US.ORACLE.COM> select to_char( hiredate, 'MM' )
  2  from emp
  3  group by to_char( hiredate, 'MM' )
  4  having count(*) >= ALL ( select count(*) from emp
                               group by to_char(hiredate,'MM' ) )
  5  /

TO
--
12

scott@ORA817DEV.US.ORACLE.COM>

to get it.  It's a different (more set oriented) way to look at that problem.

2) benchmark it and see....

3) thats the oraInventory stuff -- a list of whats installed.
April 14, 2002 Earvin -- Thanks for the question regarding "how
      to calculate the number of queries per second", version 8.1.6

      You Asked
Hi Tom,

Very often when we are load testing our applications and databases, we are asked
to calculate the average and maximum of queries that the database can handle per
minute (or sometimes per sec).

Do you have any good ways of determining that?

Thanks.
Earvin

      and we said...
We call it benchmarking.

We cannot give you a formula to determine that, since if I build the formual
with:

select * from dual;

that'll be different from the 5 page query from heck that you want to run on
your system that'll read 5 billion blocks from disk.

Or I might pick

select count(*) from all_objects

and that'll be different from the very simple "select * from emp where empno =
:x" you want to run.

April 14, 2002 Sahapat -- Thanks for the question regarding
      "Convert form Client/Server to Web Based", version FORM 6i

      You Asked
Hi.
   In the client/server enviornment  My user use function
ora_ffi to read .dll file to runapplication but when i convert
this form to run on browser , this function is not working , so
i think maybe this function didn't support to run form in
browser , but my user must use this dll file....what can i do ,
use another function or change logic of application ?
                                                          Thank
You

Sahapat.C

      and we said...
You will probably have to change the logic of the application.

The java is running in the browser and subject to the rules
therein (the "sandbox").  Calling DLL's and such happened from
the runtime module.  In the web deployment -- the runtime module
is no long running on the client -- its on the server.  The
architecture is very different.  What you see on your screen is
ONLY UI.  The application is on the middle server tier.

If your ora_ffi functions do no user interface, they can
probably still be used successfully -- its just that they must
be run on the application server, not on the client.  That is --
they will not execute on the machine with the UI, they will
execute on the machine with the fmx file.

Review &
      Followup

      Rating:
      4
  April 21, 2001
Reviewer:  Arvind K
      Singh  from Bahrain
Yes, it helped me to know about the difference between different version of
Oracle servers and how they have been build over a time.

      Rating: 3
Good
      Reminder  June 07, 2001
Reviewer:  David
       from Harahan, LA
Good reminder about the system architecture for Web deployed apps using Oracle
Application Server.

      Rating: 4
add form server
      to 9i AS  April 14, 2002
Reviewer:  Sean Bu
       from Atlanta
I have installed the client/server for developing the form based application on
my laptop, 256 m ram and 11 gb disk. I also interesting in the 3-tier
configuration. So I did a sucessful 9iAS minimum install, which has the Oracle
http server and a portal. Now I am thinking of how to add a form erver or a
report server to the 3-tier, and keep the form develop of the clien/server. All
of them will be on the same my laptop. What should i be awere of, besides of the
hardwar resources? Thanks

          Followup:

You need to be aware that 256meg is probably not sufficient to run all of that
-- database, ias, forms, at the same time.

      Rating: 5
RE: add form
      server to 9i AS  April 14, 2002
Reviewer:  Mark
      A. Williams  from Indianapolis, IN USA
PMFJI...

In regard to "add form server to 9i AS", I believe the installation guide for
9iAS indicates that you must install the Enterprise Edition (not minimal, core,
or standard) to get the 9iAS Forms Services.

See:
http://otn.oracle.com/docs/products/ias/content.html
or:
http://otn.oracle.com/docs/products/ias/doc_library/10221install/preinst.htm#1135282
for more details.

Basically, the minimum requirements per the documentation are:
Memory: 640M
Swap/Temp: 512M (or 2x physical RAM)
Disk: 6G

HTH,

- Mark

April 14, 2002 Gautam -- Thanks for the question regarding
      "Logical & Physical Structure of Datablock.", version 8.1.7

      You Asked
Dear Tom,
Thank you very much for your valuable free service to us. I got you both book
Expert-One-One & Beginner Programmer. Both Books are really very good. I read
also Oracle Concept Menual.

But Can you PLEASE give me some more detailed information about
Datablock?[Logical & Physical Structure].

Waiting for reply.

Thanks in advance.

      and we said...
The concepts guide has a whole section on this

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c02block.htm#2595
as well, I cover it in expert one on one page 62+  as well as much of the
chapter on tables where we look at things like row migration, pctfree/used and
so on.

Review &
      Followup

      Rating: 5
Another good
      source...  April 14, 2002
Reviewer:  Mark A.
      Williams  from Indianapolis, IN USA
I would like to second Tom's nomination of the supplied Oracle documentation
(not that I think my seconding carries alot of weight, mind you).  But if you
must insist on a "commercial" book, Jonathan Lewis' book has a very good
discussion of the datablock in one of the appendices...  Of course, the
documentation on OTN is free!

HTH,

Mark

April 14, 2002 G -- Thanks for the question regarding "Oracle
      Installation", version 8.1.6

      You Asked
Tom,

Where can I find a list of Oracle file types and their uses:

Parameter
Redo Log
Archive Log
Control
Personal

Also, in a typical Oracle installation, I take it files are stored in specific
directories, what files are stored where?

bin
dbs
rdbms
lib
doc

Thanks

      and we said...
In my book -- I cover each and every type of file (plus tons more).

Don't know what a "personal" file is.

there are

parameter files
data file
temp file
control file
redo log files (which may be archived)

and that is it.  each is covered in the CONCEPTS guide as well (excellent
document all should read from cover to cover)

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/toc.htm

As for the directory structure you can just take a peek.

bin -- programs
dbs -- on unix, init.ora files, password files (database on NT)
rdbms -- bunch of other directories, most important of which is rdbms/admin --
all of the catalog scripts and other goodies.
lib -- libraries, shared libraries for client runtime and libraries to link your
own programs with.
doc -- well... read it and see ;)

Review &
      Followup

      Rating: 5
RE: Don't know
      what a "personal" file is.  April 14, 2002

Reviewer:  Mark A. Williams  from Indianapolis, IN
      USA
You know Tom, the "personal" file is the one where Oracle stores the password
hashing algorithm that people frequently ask you about.  The one that allows you
to completely crack any password in the database.

(Sorry, I'm in a funny mood after watching Phil in the Masters again...)

-Mark

April 14, 2002 Zsolt -- Thanks for the question regarding
      "Moving data between two remote databases", version 8.1.6

      You Asked
Hi,

I have two databases. The first is in internal network on a computer
that can see the Internet but it doesn't have a fix IP address.

The second is at the Internet provider and has a fix IP address.
Both computers are Linux based (Suse 7.2).

What I need is to connect every morning from the internal computer
and do some pl/sql things automatically(updates and inserts) in both ways (from
internal to external and inverse).

Is there a way to do this and how can I address the external database?

Thank you very much,

Zsolt Csillag,
Hungary

      and we said...
Since the "internet" based one has a fixed IP, create a dblink from the internal
to the external.  Now you can read/write any table in either database, even in
the same transaction.


Subject | Article | Last Updated (East coast time)
how can I do this | article only | Apr 16, 2002 07:30 am
Continuty checking | article only | Apr 16, 2002 07:33 am
Diff Between Oralce 8i & 9i | article only | Apr 16, 2002 07:37 am
Manipulate Job Queues in PL/SQL stored procedure | article only | Apr 16, 2002 07:53 am
A doubt against transport tablespace | article only | Apr 16, 2002 12:23 pm
Equivalent for EXISTS() in an IF statement? | article only | Apr 16, 2002 12:31 pm
Inserting Data Into a LONG RAW column | article only | Apr 16, 2002 09:34 pm
Generate alter table statement with existing constraints in all_constarints | article only | Apr 18, 2002 08:22 am
purchasing software using oracle database | article only | Apr 18, 2002 08:36 am
Text I/O from client | article only | Apr 18, 2002 08:53 am
some silli questions | article only | Apr 18, 2002 08:29 pm
SCN | article only | Apr 18, 2002 09:08 pm
Schema Comparison | article only | Apr 18, 2002 09:12 pm
Your approach to coding this | article only | Apr 19, 2002 08:27 am
Log in Problem | article only | Apr 19, 2002 08:32 am
Database server time sync | article only | Apr 19, 2002 12:56 pm
Collection of Questions | article only | Apr 19, 2002 01:01 pm
rownum=1 | article only | Apr 19, 2002 03:22 pm
backup of offline tablespace in Oracle 9i | article only | Apr 19, 2002 04:08 pm
Firing Sequence of Triggers in Forms - in query mode | article only | Apr 19, 2002 04:35 pm
Second max records | article only | Apr 19, 2002 06:09 pm
init.ora file | article only | Apr 21, 2002 10:06 am
Checking rowcount in ref cursor | article only | Apr 21, 2002 01:34 pm
oracle web application | article only | Apr 21, 2002 03:16 pm
Query database records and display in HTML Form | article only | Apr 22, 2002 07:28 am
Archive logs | article only | Apr 22, 2002 07:31 am
general | article only | Apr 22, 2002 07:44 am
Rename privilege | article only | Apr 22, 2002 08:15 am
SQL Query | article only | Apr 22, 2002 08:27 am
How to GROUP BY around NULLs | article only | Apr 22, 2002 09:16 am
I have an error PLS-00455: cursor 'V_FILA' cannot be used in dynamic SQL OPEN" | article only | Apr 22, 2002 09:34 am
How to delete duplicate row from table and retain one row? | article only | Apr 22, 2002 08:44 pm
Correlation between MAXLOGHISTORY & CONTROL_FILE_RECORD_KEEP_TIME | article only | Apr 22, 2002 09:00 pm
Check if a file exists on the server | article only | Apr 22, 2002 09:00 pm
row(s) 1 - 34 of 34
April 16, 2002 vivek -- Thanks for the question regarding "how can I do this", version 817
You Asked
Hi tom,

  I have a situation where I am not sure what exactly the bast scalable and eazy
solution wold be..

 can you please give me your expert advice ?

 I have one table

usertable
---------
user_id     number(10)
first_name  varchar2(50),
last_name   varchar2(50),
addr1       varchar2(50),
phno        varchar2(20),
email       varchar2(80)

This table has 3.5 mil records
(the actual table has may more fields..)

  now what I want to do find duplicates in the tables
and just mark them and this proceses should run (ideally every day  but, at this
point) every month...

  the record is dulplicate or not is based on various rules
for instance,
 1.) if same email address it is duplicate
 2.) if users first char of first name and full last_name maches
     record is dulplicate
 etc..

 here I just want to select those duplicates and if the duplicate found the the
most recent is valid so the all (old or previous)records
should me marked as duplicates..

  so, what I did is..

I intoduce another field to the table call user_val_id varcahr2(1)
in the "usertable"

so now the table is

usertable
---------
user_id     number(10)
first_name  varchar2(50),
last_name   varchar2(50),
addr1       varchar2(50),
phno        varchar2(20),
email       varchar2(80,
user_val_id number

and then created another table

user_val
--------
user_val_id number,
full_first_name varchar2(1),
full_last_name  varchar2(1),
f1_first_name  varchar2(1),
l1_last_name   varchar2(1),
email          varchar2(1),
....

what i thik is this mechenizm will help me , if some new rules needed
in the future I can just add an enry in the user_val and match it in
user_table

do you think it is a good idea ?

 There is one more problem
I don't know the query or procedure that will update the user_table
can you help me write this ?

 Thanks,
and we said...
what is the point of this second table?  what happens if you update a field in
usertable?  you have to cascade it to user_val.

3.5 million, even 35 million, even 3.5 billion records -- just keep it in one
table.  do your processing there.

I cannot tell you exactly how to write the single query (and yes, I believe it
should be a single query) that will find your dups, not having all of your rules
but it should only take a couple of minutes to find "the dups".

Actually, it would be more efficient to find the "dups" as the rows are added.
You don't have to do it in real time -- rather as a row is added, you would add
a job (dbms_job, run a stored procedure in the background after the commit) to
look for "dups" of that row.  Instead of looking for dups for 3.5 million rows
(99.99% of which probably don't change from week to week or month to month), you
would just run the "dup check" for rows that were added/modified/deleted.

That is probably what I would do.  Do it constantly and continously at the
lowest level of granularity (changed data) rather then reprocess each and every
row every time.
Review & Followup
Rating: 3
April 14, 2002
Reviewer: vivek
Hi tom,

1.) the objective

  The objective of the 2nd table (user_val) is
today their rule ( business people's rule) is
some criteria..
  tomorrow they might want to tighten there critaria and
let us say they want first 3 chars of the first name and full last name and
email

 and compare them with the old criteria..

or they what to soften their criteria
and just want to check full last name and email ..

at that point I can just add a row in the user_val table
with an id and add a column to a master table (usertable)
and run just an update and compare matached rows !!

 what do you say...

2.) update

 (I get a copy of "new rows" every week so I have to operate
my mechenism on week worth of rows)

  yes, I want to validate only new rows that are added since previous validation

  but for the first time I have to validate all of them.
and I if possible I want to do it in one stmt.

3.) there are a very few chances of updation/modification
of the row ( but,for the completeness) yes, I have to update that user_val entry
corrosponding to that row.

  can you just give me some hint how to write that query. I tried but I have not
figured out the way yet.
  I have your book, I have not come across same kind of senerio.
  plese guide me ...

Thanks,

Followup:
1) i would still do it the same exact way.

I would have a procedure that would re-verify all rows (doing the same logic the
trigger does) when I add/remove a rule.  I would verify a row upon
insert/update/delete.

as i said before:

I cannot tell you exactly how to write the single query (and yes, I believe it
should be a single query) that will find your dups, not having all of your rules

but it should only take a couple of minutes to find "the dups".

Rating: 4
here is the test data April 15, 2002
Reviewer: A reader
SQL> desc t
 Name                            Null?    Type
 ------------------------------- -------- ----
 USER_ID                                  NUMBER
 first_name                               VARCHAR2(50) last_name
                VARCHAR2(50)
 ID                                       NUMBER
 COL_4                                    VARCHAR2(70)
 VALIDATION_ID                            NUMBER
-----------------------------------------------------------

SQL> select * from t;

USER_ID first_n last_n ID COL_4                     val_id
------- ------- ------ --- ------------------------ --------
      1  allen  iverson  1 test@cbstr.local.edu
      1  allen  iverson  2 test@cbstr.local.edu
      1   kobi  brient   3 hello2@cbstr.local.edu
      1   kobi  brient   4 hello2@cbstr.local.edu
      2   kobi  brient   5 hello@cbstr.local.edu
      2  jason  kid      6 unique@cbstr.local.edu
      2   kobi  brient   7 hello@cbstr.local.edu
      3   kobi  brient   8 hello@cbstr.local.edu
      4   kobi  brient   9 hello@cbstr.local.edu

SQL> DESC USER_VAL;
 Name                            Null?    Type
 ------------------------------- -------- ----
 VALIDATION_ID                            NUMBER
 FULL_FIRST_NAME                          VARCHAR2(10)
 FULL_LAST_NAME                           VARCHAR2(10)
 F3_FIRST_NAME                            VARCHAR2(10)
 L3_LAST_NAME                             VARCHAR2(10)
 F1_FIRST_NAME                            VARCHAR2(10)
 L1_LAST_NAME                             VARCHAR2(10)
 EMAIL                                    VARCHAR2(10)

SQL> SELECT * FROM USER_VAL;

V_ID FULL_F_N FULL_L F3_F_N L3_L_N F1_F_N  L1_L_N email
---- -------- ------ ------ ------ ------- ------ -----
0     NO       NO     NO      NO   NO        NO    NO
1     NO      FULL_L  NO      NO   F1_F_N    NO   Email
2     NO      FULL_L  F3_F_N  NO   NO        NO   Email

now I have to update the the validation_id in user_table (here t) such that

 for all recored with "kobi" it will mark validation_id = 2 as duplicate based
on criteria 2 except the last one...

or please tell me if you have a better solution....

Thanks,

Followup:
Sorry -- I'm not writing your code for you -- I don't know what your special
codes mean (and you don't have to post them).

Ok -- here is what I would do....

rather then try to have some convuluted code table that is an attempt to store
some procedural logic -- I would write a procedure.  This procedure would be
called by a trigger, it could be called directly.

This procedure, with its ability to do complex procedural language, would
validate the row, setting the flag.  I could now use this procedure.

You would have to interpret the above and do whatever you like with it.

April 16, 2002 Manish -- Thanks for the question regarding "Continuty checking", version 7.3
You Asked
dear Sir,
   I have a problem while designing a application. Ihave a table having
following data

No
==
1
2
3
4
8
9
10
25
26
27

and I want the following result

minimun    maximum
=======    =======
1           4
8           10
25          27

kindly suggest sql solution.

best regards,

manish
and we said...
In 7.3, you won't get the min/max (at least not in an even marginally efficient
way).  You can get:

ops$tkyte@ORA734.WORLD> select * from t
  2   where NOT exists ( select null
  3                        from t t2
  4                       where t2.x in ( t.x-1, t.x+1 ) having count(*) = 2 )
  5   order by x
  6  /

         X
----------
         1
         4
         8
        10
        25
        27

6 rows selected.

which is basically your answer.

In 8i, I can get your answer:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select max(decode( mod(rownum,2), 1, x ))
minx,
  2         max(decode( mod(rownum,2), 0, x )) maxx
  3    from (
  4  select * from t
  5   where NOT exists ( select null
  6                        from t t2
  7                       where t2.x in ( t.x-1, t.x+1 ) having count(*) = 2 )
  8   order by x
  9         )
 10  group by decode(mod(rownum,2),0,rownum-1,rownum)
 11  /

      MINX       MAXX
---------- ----------
         1          4
         8         10
        25         27

ops$tkyte@ORA817DEV.US.ORACLE.COM>

but you need an order by in a subquery to do it (new in 8i)
Review & Followup
Rating: 4
sql April 14, 2002
Reviewer: liz from FL
hi tom
just tell me how can i improve my sql and plsql like your's
tell me a book other than suggesting going through experience

Followup:
A vulcan mind meld would be useful but not practical here.

If only I could get experience without actually having gotten the experience.

Sorry, I have no suggestions for you, you won't find things I'm doing in a book
cause that book would have to have every problem in the world in it (hence, it
is an infinite book).

Read the SQL Language reference manual we give you.  Learn every bit of syntax
and understand what it means.  Then (and this is the really hard part) apply
those FACTS to a real problem turning the facts into a solution.  There is the
experience part.  You can learn all of the facts you want but until you take
those facts and learn how to apply them 500 different ways each -- they are just
a bunch of useless facts.

Rating: 5
Greately needed April 15, 2002
Reviewer: Manish Bhavsar from Nashik, Maharashtra, India
Dear Sir,
     Thanks for ur kind and immediate support. The solution u have provided is
working fine at my end and it has solved lot of purposes at my work. I am very
greateful to u and hope that u will continue to support whenever I got held-up.
      Thanks once-again.

Best regards,

Manish

Rating: 5
Analytic functions April 15, 2002
Reviewer: Stano from Slovakia
Hi Tom,

I think it should be more efficient to deploy analytic functions in Oracle 8i
for this task, especially for bigger tables:

SQL> create table t(x)
  2  as
  3  select object_id
  4  from all_objects;

Table created.

SQL> set autotrace traceonly
SQL> select
  2    max(decode( mod(rownum,2), 1, x )) minx,
  3    max(decode( mod(rownum,2), 0, x )) maxx
  4  from (
  5    select * from t
  6    where NOT exists ( select null
  7                       from t t2
  8                       where t2.x in ( t.x-1, t.x+1 ) having count(*) = 2 )
  9    order by x )
 10  group by decode(mod(rownum,2),0,rownum-1,rownum)
 11  /

346 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (GROUP BY)
   2    1     COUNT
   3    2       VIEW
   4    3         SORT (ORDER BY)
   5    4           FILTER
   6    5             TABLE ACCESS (FULL) OF 'T'
   7    5             FILTER
   8    7               SORT (AGGREGATE)
   9    8                 TABLE ACCESS (FULL) OF 'T'

Statistics
----------------------------------------------------------
          0  recursive calls
     229308  db block gets
     630597  consistent gets
         29  physical reads
          0  redo size
       5569  bytes sent via SQL*Net to client
        655  bytes received via SQL*Net from client
         37  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
        346  rows processed

SQL> select
  2    max(decode(mod(rownum, 2), 1, x )) minx,
  3    max(decode(mod(rownum, 2), 0, x )) maxx
  4  from (
  5      select *
  6      from (
  7          select x,
  8            count(*) over (order by x range between 1 preceding and current
row) bef,
  9            count(*) over (order by x range between current row and 1
following) aft
 10          from t t)
 11      where bef = 1
 12        or aft = 1
 13      order by x)
 14  group by decode(mod(rownum, 2), 0,rownum - 1, rownum)
 15  /

346 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (GROUP BY)
   2    1     COUNT
   3    2       VIEW
   4    3         VIEW
   5    4           WINDOW (SORT)
   6    5             TABLE ACCESS (FULL) OF 'T'

Statistics
----------------------------------------------------------
          0  recursive calls
         42  db block gets
         33  consistent gets
        110  physical reads
          0  redo size
       5786  bytes sent via SQL*Net to client
        767  bytes received via SQL*Net from client
         37  SQL*Net roundtrips to/from client
          1  sorts (memory)
          1  sorts (disk)
        346  rows processed

Followup:
Agreed -- analytics are my first choice in 8i...  Was pointing out you need 8i
for the order by in the subquery in order for this to work at all.

April 16, 2002 Parag -- Thanks for the question regarding "Diff Between Oralce 8i & 9i", version Oracle 8i
You Asked
Hi Tom:

   Can u please suggest me the Diff between Oracle 8i & 9i.
   Thaks in advance.

Bye
- Parag
and we said...
See
http://technet.oracle.com/products/oracle9i/index.htm
there is a new features paper there.
Review & Followup
Rating: 4
9i new features' talk on Reston April 15, 2002
Reviewer: Sean Bu from Atlanta
I had attended your 9i talk more than a month ago in Reston. At the end you
provided the URL to your talk, unfortunatly I have lost the link. Could you
please provide it again?
Thanks

Followup:
http://asktom.oracle.com/~tkyte/9iSeminar.zip

April 16, 2002 --
      Thanks for the question regarding "Manipulate Job Queues in PL/SQL stored
      procedure", version 8.1.7

      You Asked
Hi Tom:

I have 2 stored procedures, SP_SCHEDULER and SP_TRANSACTION, that I run in the
job queues.  SP_SCHEDULER is set to run every 5 minutes while SP_TRANSACTION
runs every 2 minutes.  These 2 programs have been working very well except on
some occasions they "run" into each other since both of them perform insert,
update, and delete in a few same tables.

I tried to resolve the problem by inserting the following lines of code into the
beginning of SP_SCHEDULER to check if SP_TRANSACTION is running.  If it is, wait
until it finishes, then change its NEXT_DATE to make sure it will not start
again while SP_SCHEDULER is running.

BEGIN
    SELECT job
    INTO n_JobID
    FROM sys.user_jobs
    WHERE lower(what) = lower('acctg.SP_TRANSACTION;')
      AND this_date IS NULL
      AND this_sec IS NULL;

    WHILE n_JobID IS NULL LOOP
        SELECT job
        INTO n_JobID
        FROM sys.user_jobs
        WHERE lower(what) = lower('acctg.SP_TRANSACTION;')
          AND this_date IS NULL
          AND this_sec IS NULL;
    END LOOP;

    -- Change the NEXT_DATE value of the SP_TRANSACTION.
    DBMS_JOB.NEXT_DATE(n_JobID, SYSDATE);

..........................................
....... Process SP_SCHEDULER routines.....
..........................................

    -- Activate SP_TRANSACTION again.
    DBMS_JOB.RUN(n_JobID);

These added lines of code works in the debugger, but it fails when running in
the job queues with the following error:

    ORA-00600: internal error code, arguments: [15667], [], [], [], [], [], [],
[]

Would you tell me what I did wrong?  Please show me a better way to resolve my
problem.  I would appreciate if you can provide some sample code.

Thank you very much.

Tom

      and we said...
Well, its due to the fact that a job cannot run another job.  This has been
added to the 9iR2 documentation but this limit is not documented prior to that.

If you have two jobs that cannot run simultaneously and you have
job_queue_processes > 1 (if it were 1, this could not happen, only one job at a
time would run) I might consider OTHER approaches.

a) job_queue_processes=1, only one job runs

b) use DBMS_LOCK to allocate a well known lock by name in exclusive mode.  Make
that be the first line of each routine.  Make the lock be released upon commit.
Hold the lock for the duration of your job, when your job finishes the other job
will be able to run.

This will effectively serialize your jobs so that while BOTH might be running,
one of them will WAIT for the other to finish.  It'll "sleep".  (much better
then spinning in a loop like that.

Review &
      Followup

      Rating:
      3
job  April 14, 2002
Reviewer:
      A reader
i have a job runing every week to send email but when i shut down and restart
the database the job is running

          Followup:

yes, point is?  this sounds like you want to ask a question but it is phrased as
a statement of fact.

why is it bad or good that the job is running?  the job should be running if it
was scheduled to run.

      Rating:
      3
job  April 14, 2002
Reviewer:
      A reader
i have a job running every week to send email but when i shut down and restart
the database the job is running

      Rating:
      3
ashraf  April 15, 2002
Reviewer:
      A reader
the job running every week as i say before but for example it run every sat.
then i shutdown restart on wen. the job running which not the date of next date
but if i don't shutdown it is running every week as sechduale

          Followup:

sorry -- it seems as if you saying:

job is scheduled to run on saturday.

it does.

but if I shutdown, the job ceases to run.

If that is true -- contact support.  Your sentences just are not "readable" by
me.    I really cannot parse them.

      Rating:
      3
detail  April 16, 2002
Reviewer:
      ashraf
* job schedual to run every saturday
* i shutdown on wednesday and startup again on wednesday
the job automatic running in wednesday which  not
as schedual

ver 8i 1.7
windows 2000

          Followup:

what is your job queue processes set to in your init.ora?

here is my guess:

o it is zero
o you use alter system set job_queue_processes=1 to get it going
o when you shutdown/restart, you lost your job queue process.

April 16, 2002 Pauline -- Thanks for the question regarding "A
      doubt against transport tablespace", version 8.1.5

      You Asked
Hi Tom,
I read the some ducumentation about transport tablespace and
practiced the whole procedure between two systems successfully.
But I have some doubt of its advantages---
transporting tablespaces can be useful in the follow case:
* Transporting fact tables from OLTP system in a data warehouse.
* Perform Tablespace Point In Time Recovery
......
Transporting tablespace need to alter tablespace to read
only first. In OLTP (24 X 7)database, since we are unable to
turn the tablespaces to read only, so how to transport
tablespaces to other server? It seems like that alter tablespace
to read only is the disadvantage of it.

For performing Tablespace Point In Time Recovery, we can copy
the
hot backup files directly from system1 to system2 and recreate
control file to recover. This way won't bother system1 to alter
tablespace read only. Why we need to user transporting
tablespace? In my opion,for OLTP database, altering tablespace
to read only and then exporting that tablespace, is the big
disadvantage.

Maybe I don't fully understand Transporting Tablespace. Please
give me your thought.
Thanks.

Pauline

      and we said...
Well, first -- OTLP does not imply 24x7.  Many OLTP systems are
"9-5".

Even if it is 24x7 -- is there a small window in which this can
be done?  Probably.  If you store the data in a small enough
grouping (eg: put the table you want to transport frequently
into its own tablespace and its index(es) into either that same
or their own tablespace with no other data).  The "read only"
time to transport that single table will be purely a function of
the time it takes you to copy the datafiles (the exp + copy
should be done simultaneously).

Further, if you are "really serious" -- you might use a mirrored
database (hardware).  Perhaps triple mirrored.  You would use
"alter database suspend IO", break the mirrors (so you have a
double mirrored system + 1 extra image), "alter database resume
IO".  That takes a second (so for a second, things "pause").
You now have your database back (with mirrored disks) and you
have what looks like a "crashed" instance off to the side (the
3rd disk in the mirror).  You start up another instance on the
"crashed" copy, alter tablespace read only, exp, copy the files
and then ask your hardward to resilve the mirrors (adding the
3rd disk back in).

24x7 OLTP is only one of many configurations people run Oracle
in.  Consider the developer and the bonus this feature is.  They
need a baseline upon which to test.  In the "old days", they
would get a database to the point in time they needed it -- take
a full cold backup and then test.  When done testing, full
database restore and do it over.  Now they can export just their
data (copy only their tables/indexes) -- test, drop and
reattach, test, drop and reattach, and so on over and over and
over without bring the database down, without doing a full
restore.

consider the web database with a catalog.  The catalog is
OUTSIDE the firewall for reading.  The catalog is developed
inside the firewall via some application.  Every week, you need
to update the catalog.  Rather then dump and load -- you
transport the data.

Consider the OLTP system where orders are collected into a table
outside your firewall.  Every night they need to come INSIDE
your firewall.  So, at some point you:

create table new_batch_orders ( .... );
create index on new_batch_orders ....
add constraints whatever.....

create or replace view batch_orders
as
select * from new_batch_orders;

-- transport old_batch_orders (can be read only as no one is
using it anymore)

Not every technology is 100% applicable to every case but every
technology has its use.  It took me a while to understand why fo
example Oracle8i added the "alter database suspend/resume IO"
command.  How useless I thought.  Never thought of the triple
mirror situation.  People with really large databases use it to
create a mirror for reporting/backup/etc and then resilver the
disks.

You wont use transportable tablespaces for everything, but when
you do, you'll appreciate that they are there.

Review &
      Followup

      Rating: 5
Please give
      your openion on this.  April 16, 2002
Reviewer:
      A reader
Tom, This answer provided valuable ideas for the use of transportable
tablespaces in real life situation. Can I take your openion if we can use it the
following way:

We have a datawarehouse loaded weekly from an ODS. The ODS database is updated
2-3 times daily. Every week we take a dump of the new data from ODS to ascii
files (using PL/SQL) and ftp and load into the warehouse (using SQL*Loader). We
are now getting close on the SLA load-window. We are now thinking may be we can
use transportable tablespaces to move the data over to the warehouse. This would
need some re-design, but keeping in mind the future growth and demands probably
that is the way to go. We have the fact tables partitioned based on month now.
Can we move over the weekly data and attach it as a sub-partition to the fact
table? The question is can we move and attach weekly data without re-designing
the paritioning? If there has to be any partitioning re-design, I'd prefer that
to be on the ODS rather than the warehouse. Can I have your valuable ideas
please.

          Followup:

You would have to change the partitioning scheme to be weekly, not monthly, if
you just want to slide the data in.

April 16, 2002 Dennis -- Thanks for the question regarding
      "Equivalent for EXISTS() in an IF statement?", version 8.1.7

      You Asked
In the dialect for procedural SQL in MSSQL and Sybase, there's
a useful little idiom for checking whether rows exist on a table,
and it looks like this...

    if exists (select 'x' from foo where bar)
        /* found, do something */
    else
        /* not found, do something else */

In PL/SQL, since I can't put an EXISTS() in an IF statenment,
the nearest I can manage is something like this...

    declare
        c integer;
    begin
        select count(*) into c from foo where bar;

        if c > 0 then
            /* found, do something */
        else
            /* not found, do something else */
        end if;
    end;

...which isn't terribly elegant, and probably not fast. Is there
a neater way of coding this? (Prefarably without the need to
fetch into an intermediate variable?)

regds,
Dennis

      and we said...
  for x in ( select count(*) cnt
               from dual
              where exists ( select NULL from foo where bar ) )
  loop
        if ( x.cnt = 1 )
        then
          found do something
        else
          not found
        end if;
  end loop;

is one way (very fast, only runs the subquery as long as it "needs" to, where
exists stops the subquery after hitting the first row)

That loop always executes at least once and at most once since a count(*) on a
table without a group by clause ALWAYS returns at LEAST one row and at MOST one
row (even of the table itself is empty!)

Review &
      Followup

      Rating:
      3
Thanks  March 15, 2002
Reviewer:  Dennis
      Cook  from London, England
Looks very efficient. Shame it involves so many lines
of code, though, as I would have thought it quite a
common test.

Thanks for your help.

          Followup:

Well, actually, in many many many years of coding I think I could count on one
hand how many times I've used this construct.

I don't see the usefulness of this particular construct.  The reason -- I don't
see the point in branching based on the existence of a row in a table  WITHOUT
ACTUALLY GETTING the row itself.  I mean, don't I need the values in that row?
The existence of the row doesn't tell me very much.  In my code, I have lots of:

  begin
     select * into l_Rec from t where....;
     process....
  exception
     when no_data_found then
         processs.....
  end;

which is the same as your

begin
   if exists ( select ... )
   then
      process...
   else
      process ...
   end if;
end;

but I have access to the row itself.

      Rating: 5
RE: in many
      many many years...  March 15, 2002
Reviewer:
      Mark A. Williams  from Indianapolis, IN USA
Aw, c'mon Tom, I've got your book - your picture is on it.  It can't be *that*
many years!

- Mark

      Rating: 5
Alternate
      way  April 15, 2002
Reviewer:  Jagdish
      Malani  from USA
I am not sure if we really need to check the count in the for loop.
I think we can do as follows:

for x in ( select * from dual
           where exists
           (select * from foo where bar)
         )
loop
.... processing
end loop;

The body of the loop will execute once if the subquery is satisfied, zero times
otherwise. This way we do not need to check x.cnt 's value for 1 or 0.

Please correct me if I am wrong.

          Followup:

If you want to do an:

  if ( exists ..... )
  end if;

your approach works ( I use that myself )

If you want to do:

  if ( exists .... )
      ....
  ELSE
      .....
  end if

you need to look at count

      Rating: 5
Well, well ,
      well  April 16, 2002
Reviewer:  Famy

Tom

According to you   a 'where exists'

"only runs the subquery as long as it "needs" to, where
exists stops the subquery after hitting the first row)'

But look below, if what you have said is right then only records of only one
department should be returned.

The below query tells us that the WHERE EXISTS is returning all the departments.

Kindly clarify.

Wrote file afiedt.buf

  1  select ename, deptno from emp
  2  where exists( select null from dept
  3*                        where emp.deptno=dept.deptno)
/

ENAME         DEPTNO
---------- ---------
SMITH             20
ALLEN             30
WARD              30
JONES             20
MARTIN            30
BLAKE             30
CLARK             10
SCOTT             20
KING              10
TURNER            30
ADAMS             20
JAMES             30
FORD              20
MILLER            10

14 rows selected.

          Followup:

It runs the subquery for each row in the OUTER query.

It runs the subquery only as much as it needs to.  Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int, y char(30) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert /*+ append */ into t select 1, 'x'
from all_objects;

22843 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select 2, 'x' from dual;

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly statistics;
ops$tkyte@ORA817DEV.US.ORACLE.COM> select *
  2    from dual
  3   where exists ( select null from t where x = 1 );

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

ops$tkyte@ORA817DEV.US.ORACLE.COM> select *
  2    from dual
  3   where exists ( select null from t where x = 2 );

Statistics
----------------------------------------------------------
          0  recursive calls
          8  db block gets
        125  consistent gets
         60  physical reads
          0  redo size
        363  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

Notice the consistent gets -- since "1" is near the "front" of T, the subquery
stops right away.  I put "2" at the "end" of T, the subquery read the entire
table in that case.

If dual had N rows -- it would run the subquery N times with N different values
if I used a correlated subquery...

      Rating: 5
re:re:in many
      many years  April 16, 2002
Reviewer:  Jim
       from Wilsonville, OR USA
Mark,
While I agree with you on your evaluation of Tom's picture on the cover of his
book - his youthful appearance - you have to consider things in "Tom Years".
For us mortals Tom accomplishes a multiple of what we accomplish.  Thus
something like 20 years to us Tom accomplishes in just 5.

Hence, the in many many years refers to our time.
:-)

      Rating:
      5
  April 22, 2003
Reviewer:
      A reader
Well, here is one occasion where this costruct might be useful. I just realized
that I cannot use exists in PL/SQL when I hit a PLS-204 on this block (for
validating FK relationship before making an insert).

FOR i IN  (SELECT * FROM staging_postal_address)
   LOOP
---Many other validation blocks here...
---
---
/************************************
*FK validation.
*Check if pos_add_address_id has parent in address table.
************************************/
      IF NOT EXISTS ( SELECT NULL
                    FROM address
                   WHERE add_address_id = i.pos_add_address_id)
      THEN
         v_error_code := 'POS002';
         v_error_pk_value := i.pos_address_id;
         RAISE e_err;
      END IF;

I had to define a variable and select into it as in Dennis' post. I think it
will be quite useful to have the exists construct in pl/sql too.

Actually, I am trying to write a generic function for validating the FK
relationship. I want to pass as input variables the parent table name, child
table name, parent table pk field and child table fk field. Can you please help
me write this (it will be a 2min job for you, while may be a day for me!). Or if
you have anyother suggestion for validating this relation using an existing
oracle fn, please describe. As always thanks verymuch for your time.

April 16, 2002 Sandy -- Thanks for the question regarding
      "Inserting Data Into a LONG RAW column", version 3.3

      You Asked
hello tom,
is there anyway i can insert data into a long raw column along
with other columns such as varchar and date from a pl/sql block,
i have to load a few lakh of files in long raw format.
i used devloper 4.5 and the read_image_file function but it
gives a page fault.
can u help me?

thanx
sandy

hi tom,
  first of all thanx a lot for ur prompt reply.

 the files are image files (tiff format) and each file is around
3kb to 4kb in size and yes even a pro*c sloution would do.
i have never tried oci but wud love to try it out as well,
so if u do have the time and can manage to tell me all the three
methods(i.e pl/sql and pro*c and oci) i would be higly thankful
to u. by "few lakh" i meant around 10,00000 files have to be
uploaded to the database.

thanx
sandy

      and we said...
Ok, read_image_file should not page fault (for that, please
contact support -- a tiff file is supported).  You probably
might not want to use read_image_file anyway as that will
convert the image from TIFF into the Oracle Image Format (what
you read in will not be the same as what is stored).

You won't be able to load this easily using PLSQL natively.
Since TIFF is an ascii file -- there is a chance you could use
UTL_FILE to read in the tiff file and then use
UTL_RAW.CAST_TO_RAW to convert the varchar you read into a RAW
type and then insert it.  This will only work if the tiff files
have newlines every 1022 bytes (a limit of utl_file in 7.3,
removed in 8.0.5).  I would not suggest this path if at all
possible though, pro*c and oci will be much much faster.

To use pro*c, oci or java, see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:702825000306
the pro*c code is there, it has a pointer to a location to
download the oci code and a pointer to a java example.

Review &
      Followup

      Rating: 4
Inserting Image
      into a Long Raw  April 16, 2002
Reviewer:
      kiran  from OH USA
Tom,
My Database Version is 8.1.6.
I am inserting an image into database using a PL/SQL Procedure.My Image Column
Type is Long Raw.For some reasons I can not change this Datatype.The problem
here is pl/sql procedure is not allowing me to insert the file with size >32K,
Could you please tell me how to insert this using the pl/sql procedure. In
pl/sql I used the tablename.column%type Data type.

Thanks in advance..

          Followup:

You cannot, PLSQL is quite limited to 32k -- period, no more, no chance.

If you cannot change from LONG RAW you are

o limited to 32k in plsql
o using some other language for anything larger.

blobs -- no such limit (and the correct datatype for as long raw is depricated)

April 18, 2002 partha -- Thanks for the question regarding
      "Generate alter table statement with existing constraints in
      all_constarints", version 8.1.6

      You Asked
I want's to Generate alter table statement with existing constraints in
all_cons_columns and all_constraints.

Can you please suggest me how do i correct the following statement

SELECT 'ALTER TABLE' ||' '||A.TABLE_NAME||' '||'ADD ( CONSTRAINT'||'
'||A.CONSTRAINT_NAME||
'  '||
DECODE(B.CONSTRAINT_TYPE,'P','PRIMARY
KEY','R','REFERENCES','U','UNIQUE','C','CHEQUE')||
'  ('||A.COLUMN_NAME||' )) ;'
FROM ALL_CONS_COLUMNS A,ALL_CONSTRAINTS B
WHERE B.OWNER = 'BISS01' AND
      A.OWNER = B.OWNER AND
      A.CONSTRAINT_NAME = B.CONSTRAINT_NAME AND
      A.TABLE_NAME = B.TABLE_NAME;

Thanks in Advance

Partha

      and we said...
Here is an example of how to approach this.  The following query will, when
given a PARENT table name, produce all of the foreign key constraints for its
children tables.

column fkey format a80 word_wrapped
select
'alter table "' || child_tname || '"' || chr(10) ||
'add constraint "' || child_cons_name || '"' || chr(10) ||
'foreign key ( ' || child_columns || ' ) ' || chr(10) ||
'references "' || parent_tname || '" ( ' || parent_columns || ');' fkey
from
( select a.table_name child_tname, a.constraint_name child_cons_name,
         b.r_constraint_name parent_cons_name,
         max(decode(position, 1,     '"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 2,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 3,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 4,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 5,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 6,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 7,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 8,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 9,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,10,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,11,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,12,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,13,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,14,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,15,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,16,', '||'"'||
                substr(column_name,1,30)||'"',NULL))
            child_columns
    from user_cons_columns a, user_constraints b
   where a.constraint_name = b.constraint_name
     and b.constraint_type = 'R'
   group by a.table_name, a.constraint_name, b.r_constraint_name ) child,
( select a.constraint_name parent_cons_name, a.table_name parent_tname,
         max(decode(position, 1,     '"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 2,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 3,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 4,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 5,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 6,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 7,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 8,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 9,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,10,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,11,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,12,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,13,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,14,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,15,', '||'"'||
                substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,16,', '||'"'||
                substr(column_name,1,30)||'"',NULL))
            parent_columns
    from user_cons_columns a, user_constraints b
   where a.constraint_name = b.constraint_name
     and b.constraint_type in ( 'P', 'U' )
   group by a.table_name, a.constraint_name ) parent
where child.parent_cons_name = parent.parent_cons_name
  and parent.parent_tname = upper('&1')
/
Review &
      Followup

      Rating: 5
Generate alter
      table add Foreign key Constraints table  June 26, 2001

Reviewer:  Partha  from USA
It is very useful script for Datawarehouses

      Rating: 5
Original
      question not answered  April 18, 2002
Reviewer:
      Kulguru
Tom, I think the person posting the question , also wanted the primary key
constraints along with the foreign i.e. for the given table you should produce
the primary , and then all the foreign keys in all the other tables referencing
this parent table.

Dont you think so.

          Followup:

And, if given the example, they cannot figure out how to get the primary keys --
they should be in another line of work perhaps.

I did the hard one, you can do the easy one.  Getting the primary key is child's
play compared to generating the fkey like that.

Take the ideas I have, learn how they work, apply them to new problems.

April 18, 2002 casey -- Thanks for the question regarding
      "purchasing software using oracle database", version 8.1.7

      You Asked
Hi Tom,
I am working at a health care facility and am the only oracle person here.I
haven't worked with oracle for very long but mainly do backups and some
performance tuning.Our current security is fairly simple, privileges granted
directly to the user such as execute on certain packages/procedures and the
ability to select,insert,update,delete data based on dapartment id.
We also make use of the auditing feature in oracle and find it very useful. Our
boss is planning on contracting a group of developers to develop a software
package for us.

The developers are creating an application to be used over a wan that uses
oracle backend for approx 500 users.They are creating a typical gui and middle
tier and have a few ideas for building the database.
Even though I don't have alot of experience, I have an idea of how some things
should be implemented in the database.I have voiced my opinion on this but they
seem determined that they are correct and maybe they are, but I would sure like
your opinion on this.
Here is the idea behind their database development setup which I think is very
poor but wanted an expert oracle person to look at and see what they thought.
They will have an oracle database that contains only tables,views and
indexes.Most of the work will be in the middle tier such as constraint checking.
According to them this makes the application faster.Also they say it makes the
application more flexible,making it able to use different databases because most
of the code is in the application.Last,the security checking is done at the
application level(middle tier) and they have their own auditing features which
create their own tables in the oracle database.
Tom, are there any benefits from the above? Is it common practice to not put any
code in the database? Their thinking is database
code(procedures,packages,triggers,security,auditing etc...).This only slows
things down is this really true.They also say that this type of development is
similar to something IBM is using,how I don't know.Also the security is in the
application.Won't this mean that I have full rein on the database outside of the
application or any other user for that matter? Wouldn't this give users the
ability to get into the database through sqlplus and do whatever they want or
use an odbc link through access?I am expected to use this security setup once
implemented how can this even be considered security.Would normal software
providers offer security developed in this way?Also it doesn't seem necessary to
create an auditing system when we already have one in the database.Besides the
gui and middle tier, what benefits could we gain from such a system?

      and we said...
They are doing you a great dis-service.

Ask them -- so, if you do all of the constraint checking in the middle tier, how
will you handle the fact that Oracle does multi-versioning making it virtually
impossible for you to do things as simple as "primary key"/"foreign key"
checking?  For example, how can you make it so that one session does not delete
a parent record as another session is inserting a child record without using
declaritive integrity in the dataase?  You see, with multi-versioning, you get
concurrent activities that you cannot in most other databases leading to issues
like this.  They MUST use declaritive constraints.

Next ask them this.   How long has this really cool "middle tier" stuff been in
vogue.  What was the programming technique that was in fashion immediately prior
to that.  How about immediately prior to that?  Oh wait -- you mean the way
people develop applications changes over time -- say every 3 to 5 years?  So the
client server stuff you would have proposed to us 3 to 5 years ago would be
legacy old stuff by now?  So, in 3 to 5 years while we are still using this
system (and we have to since you put the kitchen sink into it) and you guys are
off using the coolest, newest "technology dujour" -- what will we do.

I'll tell you -- you'll be screen scraping like people do with mainframe systems
so they can "web enable" them.  I don't know what the next paradigm will be, all
i know is that it will be.

What is the one constant -- databases.  They have been there from the beginning.
 The applications, they come -- they go -- they change.

So, you are going to put the security into the middle tier?  What happens when
the users realize that "hey, I need to use Brio or Oracle Reports or Cognos or
Product X" to get the data out of the database and work with it.  Oh yeah, I
cannot do that -- why -- because if I go around the application, I lose all
security, i bypass everything like that.  My only way to the data is via the
application.  I cannot use my own data -- especially you in health care with
hipaa.  If all of the security is in the middle tier -- no ad-hoc for you.  No
in house extensions for you.  You cannot touch the data.  Ask them how they will
satisfy the hipaa requirements in this middle tier and still allow you to use
YOUR data (yes, it is YOUR data, not theirs)

They will preclude you from making use of your most valuable asset -- the data.

Tell them to benchmark a simple thing like primary key enforcement.  If this
"slows down" the database so much -- why is it that they will NEVER build a
middle tier application that can enforce (properly enforce) a primary key
constraint faster then the database?  I'll take on ANY constraint they want.
The database will do that constraint enforcement faster, better, and cheaper.
And oh yeah -- if the constraint changes over time, we can fix it ONCE (at the
data) and have it immediately be in place for EVERY application.  You don't have
to hunt it down.

Don't forget -- IBM invented the host based systems that people screen scrape
and pay consultants tons of money today to screen scrape...

Triggers, security, auditing, constraints -- they all have to be in the
database.

I will take on any process they say goes "faster" in the middle tier that has to
do with data integrity and data security.  It'll be faster, better, and cheaper
in the database.  100%

If they do it all in the middle tier, you have just lost the functionality of
your database.  No more sql for you.  there will be no sqlplus access, there
will be no odbc connections, there will be no 3rd report tools.  There will be
ONLY the application they feel like providing at the cost they desire to charge.
 It'll be lock in city.

Just my 3cents worth.  I could go on but I believe you see my perspective on
this.

I don't think they even KNOW what the database can do for them.
Review &
      Followup

      Rating: 5
purchasing
      software using oracle database  April 14, 2002

Reviewer:  casey
Thanks for the response Tom.This review was very helpful.It confirmed with me
that my line of thinking was and is correct.

      Rating: 5
Learning
      everyday  April 14, 2002
Reviewer:  Suresh
       from New York
Tom,
   You are the best.

      Rating: 4
Thanks,
      but  April 14, 2002
Reviewer:  Jean
       from France
Not up with the lingo.

"screen scrape" ???

          Followup:

screen scraping is what you do when you have a "green screen" application, a
mainframe app for example, whereby the only interface/api to the data is via a
screen that a human being must enter the values into.

To "reuse" this code, you "scrape the screen", present the form to the user in
another UI, collect the data and pretend to type it in for them.

      Rating: 5
Hear hear
      !  April 15, 2002
Reviewer:  Norbert
       from NL
This sounds all too familiar. Some 'consultant' type performs an investigation
and concludes that method X (any language will do, as long as it's new and mr
consultant is eager to do his first project on it) is the way to go. No more
code in the database, which comes in handy, because he never heard of PL/SQL,
nor Oracle now you mention.
And then, when you ARE going to develop using Java/C#/..., they can provide you
with their developers, who are also eager to try what they learned on their
courses.

      Rating: 5
But the
      database can also change  April 15, 2002
Reviewer:
      Pratap  from India
Hi Tom

I fully agree with your views.

But there is one argument I frequently come across -

The underlying database can also change. E.g. SQL server to Oracle. If I write
all the logic in the application, won't the switch over be faster with less
changes?

Thanks

Pratap

          Followup:

Here is my opinion on that (it's in my book as well)...

You make choices in life, you buy products.  Why do you buy products?  Because
they do things for you.

When you buy a car, do you ask "how hard is it to remove that engine and put a
new one in?"  -- you might want to take that BMW engine out and put a Mercedes
one in there after all (not).

Today -- you want to get things done faster/better/cheaper.  They have to be
rolled out quickly AND they must be flexible, adaptable, scalable and secure.
Put all of your logic into the application -- and you can do neither.

First of all -- you cannot break the work up.  You have one guy doing the
application and security logic (maybe you have many of them but one person must
do both).  This makes development slower, less specialization.  This makes
testing take longer (have to check security in each and every module over and
over).  This makes adding things harder (gotta do that security all over again).
 You are not flexible, you are not adaptable, it is not a scalable development
solution.

Next, you cannot adapt to change.  Client server -- 3 tier with cgi bin -- 3
tier with J2EE -- N tier with web services -- WAP -- etc etc etc.  You want to
put security into each and everyone of those every time they come along.

My basic point is:

o buy your product
o use the $#@$#@ out of it -- whichever one you buy.  we are FUNDEMENTALLY
DIFFERENT.  If you want database "independence", you need keyed read vsam files
and nothing more.  If you bought a database -- USE IT

      Rating: 4
I wonder why
      Oracle Application dont have constraints in database  April
      15, 2002
Reviewer:  A reader
Although you mentioned that everything i database is fast I wonder why many
applications such as siebel, sap, oracle applications many times (not always)
dont have relationship in the database

If I rememeber right (this was long time ago, around 2 years) Oracle
Applications 11.0.2 have 0 Foreign keys in database

          Followup:

Oracle apps makes large use of many database features.  triggers, stored
procedures, tons of stuff.  It is database dependent.

Sieble/SAP -- they have taken a database independent approach, they are not able
to use any of the features of a database beyond:

o tables
o indexes

      Rating: 5
Did those guy
      know Oracle or RDBMS  April 15, 2002
Reviewer:
      Ringo Li  from Hong Kong
To put it simple, they didn't know the Oracle at all.

Do not let them screw up your application. I did and now still suffering. what I
am now doing is to re-build all parent child constranit table by table and dig
out all concurrency problem. Guess what, our schema does not have any
declarative constraints !

Why don't you ask those consultant for some successful implemantation of Oracle
DB backed application for reference ?

I doubt if they can provide any ?

      Rating: 5
What about same
      checks being performed both at the front-end and
      database?  April 18, 2002
Reviewer:  Vijay
      Raigaonkar  from India
Hi Tom

I too am an Oracle professional for quite some time now and strongly feel that
the constraints must be there in the database. But I do not know how to counter
a typical argument or statement going in favour of "not having the constraints
in the database". The argument goes like this...

We usually require to do the validations in middle/front tier to give immediate
response to the user. Many of the validations duplicate the constraints in the
database (if those are there). This makes the overall response slower (by
whatever amount of time) than the scenerio where we do not have any constraints
in the database.

          Followup:

Fine, thats great - put the constraints in the front end for validation.
However, my argument still applies:

o the integrity of the data is job #1.  If the data is corrupted, you have not
done your job properly.

o if there is a chance of getting to the data without using your application --
without using your validation -- the data WILL BE CORRUPT.

o let me change the "IF THERE IS" to "There is", there always is.  Remember, in
the span of about a decade we've gone from:

  - host based
  - "client server" (with a slight divergence to X-Windows for a bit)
  - cgi-bin on a webserver
  - App server applications (j2ee/.net/.whatever)
  - now "web services", WAP, etc etc etc

The speed of checking the constraint in the database to the end user will *not
be measurable*.

The corrupt data, when presented to the end user *will be very visible*.

Tell me then -- which do you want?

Also -- are you sure the each and every front end

a) knows all of the validation rules?
b) should have to know all of the validation rules?
c) should be updated everytime there is a change in the validation rules?
d) CAN in fact do all of the validation rules (think PHONE interface, or IVR)

and the list goes on.  People can do whatever they want, if they want to be
maintaining that system for the rest of their natural lives -- thats great (for
them) but a dis-service to the customer (IMHO)...

I put the logic in both places, its the only way to do it.

April 18, 2002 Naima -- Thanks for the question regarding "Text
      I/O from client", version 7.3.4

      You Asked
Need the code for creating the TEXT_IO package.  Can't find it
anywhere!
Thanks

      and we said...
TEXT_IO is only supplied with Developer (aka forms).  It
requires a client side PL/SQL engine (e.g. sqlplus doesn't have
one of those).

If you have forms or procedure builder, you have text_io.  If
you do not have these tools -- text_io is not available to you.
Review &
      Followup

      Rating: 3
Text I/O in
      Forms 6i - How to handle client files during a web
      session  April 18, 2002
Reviewer:  Tommy
       from Italy
Hi Tom!

I'm developing an application with Forms 6i.
Please consider the following command line:

...
text_io.fopen('c:\tmp.txt','r')
...

In client/server mode the file "tmp.txt" is searched on the local computer (on
the client).

In web mode the file "tmp.txt" is searched on the server.

How can I open the "tmp.txt" file on the CLIENT computer, also in web mode?

Is there a way to handle local files during a web session?

Thank you in advance!

          Followup:

I'm not a forms guy at all -- I don't think so.

Try
http://otn.oracle.com/
under discussions.  there is a group for developer there.

April 18, 2002 Raj -- Thanks for the question regarding "some
      silli questions", version 8.1.7

      You Asked
Tom,

This issue is probably covered by you already. I seched the archives, but could
not find an exact answer. Please excuse me if I missed the obvious.

Let me write down my understanding about the size of a database. Kindly correct
me where I am wrong.

The size of a database can be calculated at 3 levels - 1.filesystem, 2.segment
and 3.data levels.

1. The total size occupied by the datafiles, controlfiles and redofiles on the
filesystem. This is the size we most commonly refer to as the size of a
database.

2. The Actual size allocated to the segments. This can be calculated by the
following query:

select sum(bytes)/1024/1024 MB from dba_segments;

3. The actual volume of data in the database. To find out this the tables and
indexes
has to be analyzed first. The actual data volume can be found by:

select table_name, sum(blocks)*db_block_size/1024/1024 MB from dba_tables;
select index_name, sum(leaf_blocks)*db_block_size/1024/1024 from dba_indexes;

I have a feeling that, 3. above is same as 2. Is it? Also could you please
explain the concept of high watermark in this context.

Also, when we calculate the size allocated to the segments (as in 2 above), will
that include the size of the data dictionary too? Is it possible to findout the
size of the
data dictionary seperately? Is there anything called size associated with the
objects in the system tablespace (like procedures, packages, triggers etc. Where
are they stored)?

Thank you very much for your enlightning answers.

Raj.

      and we said...
1) yes
2) yes
3) no not really, that would be the same as 2 more or less.

let's say a table has 100 blocks allocated to it.

let's say you inserted 50 blocks worth of data.  there would be 50 blocks below
the HWM and 50 above.  the HWM is the point at above which the blocks are
allocated but not yet ever used.  HWM's only go up on a segment.

If you analyze the segments, you can look at the number of blocks above and
below the HWM (also search this site for dbms_space for another way)

Just add TABLESPACE into the query -- group by tablespace and add tablespace to
the select.  the data dictionary is in system, your stuff isn't.

Yes, there is "size" associated with code -- look at *_OBJECT_SIZE views (user
and dba)
April 18, 2002 wallacel -- Thanks for the question regarding
      "SCN", version 8.1.6

      You Asked
Tom:

select checkpoint_change# from v$database;
CHECKPOINT_CHANGE#
------------------
           5120092

commit;

select checkpoint_change# from v$database;
CHECKPOINT_CHANGE#
------------------
           5120092

SCN should increase each time user submit commit, am i right?
but why it still the same?

      and we said...
The SCN will -- but not the checkpoint_change#.  Thats the last
SCN checkpointed:

system@ORACLE8I> select checkpoint_change# from v$database;

CHECKPOINT_CHANGE#
------------------
            725781

system@ORACLE8I> commit;

Commit complete.

system@ORACLE8I> select checkpoint_change# from v$database;

CHECKPOINT_CHANGE#
------------------
            725781

system@ORACLE8I> alter system checkpoint;

System altered.

system@ORACLE8I> select checkpoint_change# from v$database;

CHECKPOINT_CHANGE#
------------------
            725798

system@ORACLE8I> alter system checkpoint;

System altered.

system@ORACLE8I> select checkpoint_change# from v$database;

CHECKPOINT_CHANGE#
------------------
            725799

It'll change after a checkpoint.
Review &
      Followup

      Rating: 4
How to find out
      the current SCN of a data file?  April 18, 2002

Reviewer:  Potential  from Philadelphia, PA USA

Tom, it is very useful.

But, how can I find out the current SCN of a data file? I think they are
different in different datafiles if not synchronized.

And also, for the last 3 sql statements in your example, there is almost no
commit activity -- at least at the foreground there is no explicit commit, why
does the SCN keep increasing, sometimes by 1, sometimes more than 1? I thought
SCNs are generated when there is a commit. I am confused.

Thanks.

          Followup:

why would you need that -- a datafile doesn't really truly have an "scn" --
blocks in there might.  What would you do with this?  Anyway, look at
v$datafile.

the alter system advances the checkpoint change# as well.

      Rating:
      5
  April 18, 2002
Reviewer:
      A reader
Tom,

What is the range of an SCN number? I mean if the SCN keep on incrementing, will
it not run out of range after several years for a large OLTP system? What is the
solution to that. Will it be the next Y2K problem 50 years from now?

          Followup:

50 years, lets see.  An Oracle number is a number(38) so in order to exhaust
that in 50 years:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select rpad('9', 38, '9')/50 from dual;

                    RPAD('9',38,'9')/50
---------------------------------------
  2000000000000000000000000000000000000

ops$tkyte@ORA817DEV.US.ORACLE.COM> select
2000000000000000000000000000000000000/366 from dual;

2000000000000000000000000000000000000/366
-----------------------------------------
       5464480874316939890710382513661202

ops$tkyte@ORA817DEV.US.ORACLE.COM> select 5464480874316939890710382513661202/24
from dual;

  5464480874316939890710382513661202/24
---------------------------------------
      227686703096539162112932604735883

ops$tkyte@ORA817DEV.US.ORACLE.COM> select 227686703096539162112932604735883/60
from dual;

   227686703096539162112932604735883/60
---------------------------------------
        3794778384942319368548876745598

ops$tkyte@ORA817DEV.US.ORACLE.COM> select 3794778384942319368548876745598/60
from dual;

     3794778384942319368548876745598/60
---------------------------------------
          63246306415705322809147945760

ops$tkyte@ORA817DEV.US.ORACLE.COM>
so, if you do 63246306415705322809147945760 TPS every second for 50 years, we
would roll over.  not sure what happens when we roll over.

I seriously doubt this will be a problem.  An open resetlogs

      Rating:
      4
commit  April 18, 2002
Reviewer:
      krish  from eu
system@ORACLE8I> select checkpoint_change# from v$database;

CHECKPOINT_CHANGE#
------------------
            725781

system@ORACLE8I> commit;

if scn changes with every commit why not this time or this is not a commit for
data.

          Followup:

i fail to see how your data above supports your conclusion.

the scn changed with the commit.  Your example is not capable of showing that
however.

April 18, 2002 Sriv -- Thanks for the question regarding "Schema
      Comparison", version 8.1.6

      You Asked
Tom

Would like to know if there is any script to compare schema's
across 2 database, I would want to compare the table name,
column name, data type and also the size, if there is any
difference, i would want the script to display the db name,
table name , field name and data type and length to be
displayed.

There can be fields which is in table a of schema a and not in
table a of schema b, there can be new fields either ways, or
there can be spelling mistakes in field name.  The length can
change or datatype can be a variant.

Thanks

Sriv

      and we said...
Not that I am aware of -- perhaps something like TOAD would have
something.

It would not be hard to do.  For example, to find all tables in
database A not in B

select * from user_tables
minus
select * from user_tables@b;

To find all tables in database B not in A, just reverse the
above.

To diff the tables -- to find column mismatches would be very
similar:

  for x in ( select a.table_name from user_tables a,
                                      user_tables@b b
               where a.table_name = b.table_name )
  loop
      select column_name, datatype, ... from user_tab_columns
       where table_name = x.table_name
      MINUS
      select column_name, datatype, ... from user_tab_columns@b
       where table_name = x.table_name
      ...
   end loop;

Review &
      Followup

      Rating:
      4
  April 18, 2002
Reviewer:
      A reader
Hi tom,

IF we do like this

To find all tables in database A not
in B

select table_name,column_name,column_id from all_tab_columns
where owner = 'PUBS'
MINUS
select table_name,column_name,column_id from all_tab_columns@b
where owner = 'PUBS'

all tables in database B not in A

select table_name,column_name,column_id from all_tab_columns
where owner = 'PUBS'
MINUS
select table_name,column_name,column_id from all_tab_columns@a
where owner = 'PUBS'

will it do for me ?

          Followup:

sure, thats pretty much what I answered with more or less.

      Rating: 3
Script for
      comparing schemas "compare_schemas.sql"  April 18, 2002

Reviewer:  Logan Palanisamy  from Santa Clara, CA USA

Hello,

I use the following script to compare two schemas. The only restriction is that
they have to be in the same database. What I normally do when I want to compare
schmeas in two different database is to do a schema level exp/imp of one of the
schemas and have them both in the same db.

May be you can modify the script to compare them directly across DBs.

============== The Script Starts Here ===============
-- compare_schemas.sql

set termout on
set feedback on
accept schma1 prompt 'Enter the name of the first schema :'
accept schma2 prompt 'Enter the name of the second schema :'
col s1 noprint new_value SCHEMA1
col s2 noprint new_value SCHEMA2
select upper('&&schma1') s1, upper('&&schma2') s2 from dual;

set linesize 80
set pagesize 60
set numformat 9,999,999,999
set colsep ' '
set trimspool on
set verify off
set feedback off
set pause off
set newpage 1
set termout off

clear breaks
clear buffer
clear columns

column TODAY noprint new_value _DATE
column server noprint new_value _SERVER
column dbname noprint new_value _SID
col cyymmdd new_value yymmdd noprint

select name dbname, to_char(SYSDATE, 'Mon DD, YYYY HH:MI AM') TODAY,
to_char(sysdate, 'yymmdd') cyymmdd
from sys.v_$database;

SELECT UPPER(MACHINE) server
from v$session
where program like '%PMON%';

spool /tmp/compare_schemas.lst
set heading off
select 'Column C1 refers to: ' || '&&SCHEMA1' ||CHR(10) ||
'Column C2 refers to: ' || '&&SCHEMA2' from dual;

set heading on
set colsep ''
ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID SKIP 1 -
center 'Object Information' skip 2
col owner format a10
col tbls format 9999
col indx format 9999
col vews format 9999
col trgr format 9999
col func format 9999
col proc format 9999
col pkgs format 9999
col pkgb format 9999
col sequ format 9999
col syno format 9999
col dblnk format 99999
col clstr format 99999

select owner,
sum(decode(object_type, 'TABLE', 1, 0)) Tbls,
sum(decode(object_type, 'INDEX', 1, 0)) Indx,
sum(decode(object_type, 'VIEW', 1, 0)) Vews,
sum(decode(object_type, 'TRIGGER', 1, 0)) Trgr,
sum(decode(object_type, 'FUNCTION', 1, 0)) Func,
sum(decode(object_type, 'PROCEDURE', 1, 0)) Proc,
sum(decode(object_type, 'PACKAGE', 1, 0)) Pkgs,
sum(decode(object_type, 'PACKAGE BODY', 1, 0)) Pkgb,
sum(decode(object_type, 'SEQUENCE', 1, 0)) Sequ,
sum(decode(object_type, 'SYNONYM', 1, 0)) Syno,
sum(decode(object_type, 'DATABASE LINK', 1, 0)) Dblnk,
sum(decode(object_type, 'CLUSTER', 1, 0)) Clstr
from dba_objects
where owner in ('&&SCHEMA1', '&&SCHEMA2')
GROUP BY owner;

set colsep ' '
set numformat 99,999,999
ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Table and Index Info By Owner' skip 2

col total_cnt format 99,999 heading "Total Cnt"
col tables format 99,999 heading "Tables"
col indexes format 99,999 heading "Indexes"
col table_size heading "Tbl_Size(Kb)"
col Index_size heading "Idx_Size(Kb)"
col total_size heading "Total_Size(Kb)"

select owner,
SUM(decode(segment_type, 'TABLE', 1, 0)) Tables,
SUM(decode(segment_type, 'TABLE', bytes, 0))/1024 Table_Size,
SUM(decode(segment_type, 'INDEX', 1, 0)) Indexes,
SUM(decode(segment_type, 'INDEX', bytes, 0))/1024 Index_Size,
count(*) Total_cnt,
sum(bytes)/1024 Total_size
from dba_segments
where segment_type IN ('TABLE', 'INDEX')
and owner in ('&&SCHEMA1', '&&SCHEMA2')
GROUP BY owner;

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Mismatch in the Number of Columns' skip 2
col C1 format 999
col C2 format 999
col table_name format a25
select table_name, C1, C2
from (
select table_name,
max(decode(owner, '&&SCHEMA1', column_id, NULL)) C1,
max(decode(owner, '&&SCHEMA2', column_id, NULL)) C2
from dba_tab_columns
where owner in ('&&SCHEMA1', '&&SCHEMA2')
GROUP BY table_name) a
where a.C1 <> a.C2
or a.C1 is null
or a.C2 is null
/

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Column Mismatch' skip 2
col C1 format 99
col C2 format 99
col column_name format a25
select table_name, column_name, C1, C2
from (
select table_name, column_name,
max(decode(owner, '&&SCHEMA1', column_id, NULL)) C1,
max(decode(owner, '&&SCHEMA2', column_id, NULL)) C2
from dba_tab_columns
where owner in ('&&SCHEMA1', '&&SCHEMA2')
GROUP BY table_name, column_name) a
where a.C1 <> a.C2
or a.C1 is null
or a.C2 is null
order by table_name, c1, c2
/

-- Compare data type mismatch
ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Data Type Mismatch' skip 2
col c1 format a15
col c2 format a15
col column_name format a20
col table_name format a20
select table_name, column_name, C1, C2
from (
select tc.table_name, tc.column_name,
max(decode(tc.owner, '&&SCHEMA1', tc.data_type || '(' || decode(tc.data_type,
'NUMBER', tc.data_precision || ',' || tc.data_scale, ,
max(decode(tc.owner, '&&SCHEMA2', tc.data_type || '(' || decode(tc.data_type,
'NUMBER', tc.data_precision || ',' || tc.data_scale, 2
from dba_tab_columns tc, dba_tables t
where tc.owner in ('&&SCHEMA1', '&&SCHEMA2')
and t.owner in ('&&SCHEMA1', '&&SCHEMA2')
and tc.table_name = t.table_name
GROUP BY tc.table_name, tc.column_name) a
where a.C1 <> a.C2
order by table_name, c1, c2
/

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Trigger Status Mismatch' skip 2
col trigger_name format a25
col C1 format a2
col C2 format a2
select trigger_name, trigger_type, C1, C2
from (select trigger_name, trigger_type,
max(decode(owner, '&&SCHEMA1', decode(status, 'ENABLED', 'E', 'DISABLED', 'D',
NULL), NULL)) C1,
max(decode(owner, '&&SCHEMA2', decode(status, 'ENABLED', 'E', 'DISABLED', 'D',
NULL), NULL)) C2
from dba_triggers
where owner in ('&&SCHEMA1', '&&SCHEMA2')
GROUP BY trigger_name, trigger_type) a
where a.C1 <> a.C2
or a.C1 is null
or a.C2 is null
/

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Mismatch in Stored Procedure Size (Num of Lines)' skip 2
col name format a25
col type format a15
col C1 format 9999
col C2 format 9999
select name, type, C1, C2
from (select name, type,
max(decode(owner, '&&SCHEMA1', line, NULL)) C1,
max(decode(owner, '&&SCHEMA2', line, NULL)) C2
from dba_source
where owner in ('&&SCHEMA1', '&&SCHEMA2')
GROUP BY name, type) a
where a.C1 <> a.C2
or a.C1 is null
or a.C2 is null
/

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Mismatch in Sequences' skip 2
col Seq_Name format a20
col min_c1 format 9999
col min_c2 format 9999
col max_c1 format 999,999,999
col max_c2 format 999,999,999
col inc_c1 format 9999
col inc_c2 format 9999
select sequence_name seq_name, min_c1, min_c2, max_c1, max_c2, inc_c1, inc_c2
from (select sequence_name,
max(decode(sequence_owner, '&&SCHEMA1', min_value, NULL)) min_c1,
max(decode(sequence_owner, '&&SCHEMA2', min_value, NULL)) min_c2,
max(decode(sequence_owner, '&&SCHEMA1', max_value, NULL)) max_c1,
max(decode(sequence_owner, '&&SCHEMA2', max_value, NULL)) max_c2,
max(decode(sequence_owner, '&&SCHEMA1', increment_by, NULL)) inc_c1,
max(decode(sequence_owner, '&&SCHEMA2', increment_by, NULL)) inc_c2
from dba_sequences
where sequence_owner in ('&&SCHEMA1', '&&SCHEMA2')
GROUP BY sequence_name) a
where a.min_c1 <> a.min_c2
or a.min_c1 is null
or a.min_c2 is null
or a.max_c1 <> a.max_c2
or a.max_c1 is null
or a.max_c2 is null
or a.inc_c1 <> a.inc_c2
or a.inc_c1 is null
or a.inc_c2 is null
/

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Count of ALL the Constraints' skip 2
col owner format a10
select owner,
sum(decode(constraint_type, 'P', 1, 0)) Primary_Key,
sum(decode(constraint_type, 'R', 1, 0)) Foreign_key,
sum(decode(constraint_type, 'U', 1, 0)) Unique_key,
sum(decode(constraint_type, 'C', 1, 0)) Check_Constraint
from dba_constraints
where owner in ('&&SCHEMA1', '&&SCHEMA2')
GROUP BY owner;

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Count of all the Enabled Constraints' skip 2
select owner,
sum(decode(constraint_type, 'P', 1, 0)) Primary_Key,
sum(decode(constraint_type, 'R', 1, 0)) Foreign_key,
sum(decode(constraint_type, 'U', 1, 0)) Unique_key,
sum(decode(constraint_type, 'C', 1, 0)) Check_Constraint
from dba_constraints
where owner in ('&&SCHEMA1', '&&SCHEMA2')
and status = 'ENABLED'
GROUP BY owner;

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Count of all the Disabled Constraints' skip 2
select owner,
sum(decode(constraint_type, 'P', 1, 0)) Primary_Key,
sum(decode(constraint_type, 'R', 1, 0)) Foreign_key,
sum(decode(constraint_type, 'U', 1, 0)) Unique_key,
sum(decode(constraint_type, 'C', 1, 0)) Check_Constraint
from dba_constraints
where owner in ('&&SCHEMA1', '&&SCHEMA2')
and status = 'DISABLED'
GROUP BY owner;

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Constraint Status Mismatch' skip 2
col constraint_name format a25
col C1 format a2
col C2 format a2
col constraint_type heading Type
select constraint_name, constraint_type, C1, C2
from (select constraint_name, constraint_type,
max(decode(owner, '&&SCHEMA1', decode(status, 'ENABLED', 'E', 'DISABLED', 'D',
NULL), NULL)) C1,
max(decode(owner, '&&SCHEMA2', decode(status, 'ENABLED', 'E', 'DISABLED', 'D',
NULL), NULL)) C2
from dba_constraints
where owner in ('&&SCHEMA1', '&&SCHEMA2')
and constraint_name not like 'SYS%'
GROUP BY constraint_name, constraint_type
order by constraint_type) a
where a.C1 <> a.C2
or a.C1 is null
or a.C2 is null
/

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Constraint Column Mismatch' skip 2
col column_name format a20
col C1 format 9
col C2 format 9
-- break on constraint_name noduplicates
select constraint_name, column_name, C1, C2
from (
select constraint_name, column_name,
max(decode(owner, '&&SCHEMA1', position, NULL)) C1,
max(decode(owner, '&&SCHEMA2', position, NULL)) C2
from dba_cons_columns
where owner in ('&&SCHEMA1', '&&SCHEMA2')
and constraint_name not like 'SYS%'
and position is not null
GROUP BY constraint_name, column_name) a
where a.C1 <> a.C2
or a.C1 is null
or a.C2 is null
order by constraint_name, c1, c2
/

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Index Column Mismatch on others tables' skip 2

col idx_name format a25
col tbl_name format a25
col column_name format a20
col ordr format 999 heading order

select index_owner || '.' || index_name idx_name,
table_owner || '.' || table_name tbl_name, column_name,
column_position ordr
from dba_ind_columns
where (index_owner in ('&&SCHEMA1', '&&SCHEMA2')
or table_owner in ('&&SCHEMA1', '&&SCHEMA2'))
and index_owner <> table_owner
order by index_name, index_owner, column_position
/

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Index Column Mismatch on ones own tables' skip 2
select index_name, table_name, column_name, C1, C2
from (
select index_name, table_name, column_name,
max(decode(index_owner, '&&SCHEMA1', column_position, NULL)) C1,
max(decode(index_owner, '&&SCHEMA2', column_position, NULL)) C2
from dba_ind_columns
where index_owner in ('&&SCHEMA1', '&&SCHEMA2')
and column_position is not null
and index_owner = table_owner
GROUP BY index_name, table_name, column_name) a
where a.C1 <> a.C2
or a.C1 is null
or a.C2 is null
order by index_name, c1, c2
/

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Mismatch in the number of Synonyms' skip 2
col C1 format 999
col C2 format 999
col table_owner format a25
select table_owner, C1, C2
from (
select table_owner,
sum(decode(owner, '&&SCHEMA1', 1, NULL)) C1,
sum(decode(owner, '&&SCHEMA2', 1, NULL)) C2
from dba_synonyms
where owner in ('&&SCHEMA1', '&&SCHEMA2')
GROUP BY table_owner) a
where a.C1 <> a.C2
or a.C1 is null
or a.C2 is null
/

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Mismatch in the Privileges granted to others - Sorted By Grantee' skip 2

col C1 format 999
col C2 format 999
col grantee format a25
select grantee, C1, C2
from (
select grantee,
sum(decode(grantor, '&&SCHEMA1', 1, NULL)) C1,
sum(decode(grantor, '&&SCHEMA2', 1, NULL)) C2
from dba_tab_privs
where owner in ('&&SCHEMA1', '&&SCHEMA2')
GROUP BY grantee) a
where a.C1 <> a.C2
or a.C1 is null
or a.C2 is null
/

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Mismatch in the Privileges granted to others - Sorted By Privilege' skip
2
col C1 format 999
col C2 format 999
col privilege format a25
select privilege, C1, C2
from (
select privilege,
sum(decode(grantor, '&&SCHEMA1', 1, NULL)) C1,
sum(decode(grantor, '&&SCHEMA2', 1, NULL)) C2
from dba_tab_privs
where owner in ('&&SCHEMA1', '&&SCHEMA2')
GROUP BY privilege) a
where a.C1 <> a.C2
or a.C1 is null
or a.C2 is null
/

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Mismatch in the Privileges Received from others - Sorted By Grantor'
skip 2
col C1 format 999
col C2 format 999
col grantor format a25
select grantor, C1, C2
from (
select grantor,
sum(decode(grantee, '&&SCHEMA1', 1, NULL)) C1,
sum(decode(grantee, '&&SCHEMA2', 1, NULL)) C2
from dba_tab_privs
where grantee in ('&&SCHEMA1', '&&SCHEMA2')
GROUP BY grantor) a
where a.C1 <> a.C2
or a.C1 is null
or a.C2 is null
/

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Mismatch in the Privileges Received from others - Sorted By Privilege'
skip 2
col C1 format 999
col C2 format 999
col privilege format a25
select privilege, C1, C2
from (
select privilege,
sum(decode(grantee, '&&SCHEMA1', 1, NULL)) C1,
sum(decode(grantee, '&&SCHEMA2', 1, NULL)) C2
from dba_tab_privs
where grantee in ('&&SCHEMA1', '&&SCHEMA2')
GROUP BY privilege) a
where a.C1 <> a.C2
or a.C1 is null
or a.C2 is null
/

ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Mismatch in the Granted Roles' skip 2
col C1 format a2
col C2 format a2
col granted_role format a25
select granted_role, C1, C2
from (
select granted_role,
max(decode(grantee, '&&SCHEMA1', 'Y', NULL)) C1,
max(decode(grantee, '&&SCHEMA2', 'Y', NULL)) C2
from dba_role_privs
where grantee in ('&&SCHEMA1', '&&SCHEMA2')
GROUP BY granted_role) a
where a.C1 <> a.C2
or a.C1 is null
or a.C2 is null
/
ttitle left _DATE right 'Page: ' format 99 SQL.PNO skip 1 -
left 'Server: ' _SERVER  -
RIGHT 'SID: ' _SID skip 1 -
center 'Mismatch in the System Privileges Received' skip 2
col C1 format a2
col C2 format a2
col privilege format a25
select privilege, C1, C2
from (
select privilege,
max(decode(grantee, '&&SCHEMA1', 'Y', NULL)) C1,
max(decode(grantee, '&&SCHEMA2', 'Y', NULL)) C2
from dba_sys_privs
where grantee in ('&&SCHEMA1', '&&SCHEMA2')
GROUP BY privilege) a
where a.C1 <> a.C2
or a.C1 is null
or a.C2 is null
/
clear buffer
clear columns
clear breaks
clear computes
ttitle off
btitle off
undefine SCHEMA1
undefine SCHEMA2

spool off

      Rating: 4
Wow...I figured
      you'd recommend an Oracle product  April 18, 2002

Reviewer:  Daryl  from Carrollton, TX USA
The Oracle Change Management Pack can do all this. It
looks not only at tables, indexes, views, etc., but
also at PL/SQL (and probably Java) in the database, too.
The real trick with CMP is to tell it to ignore things like
storage parameters (schema 1 has x extents; schema 2 has
y extents; CMP will flag it as different). CMP will also
generate the SQL needed to bring the two schemas into sync.

          Followup:

well, thats a GUI and all -- I guess I never think about that part ;)

      Rating: 3
See this
      URL...  April 18, 2002
Reviewer:  Andrew
       from ca, usa
http://www.oracle.com/oramag/code/tips1997/index.html?cod07077.html
Fix the typo under "TRIGGERS MISSING IN THIS SCHEMA" section where both tables
in the query reference the same schema.

      Rating: 4
Script for
      comparing schemas "SchemaDiff.sql"  April 19, 2002

Reviewer:  BroadVision RCHU  from Houston TX USA

I'm using this one.

/*
This script compares the object definitions in the current schema
to that of a remote schema.
The remote schema is defined using a database link.
THE SCRIPT COMPARES THE FOLLOWING:
- Existence of tables
- Existence of columns
- Column definitions
- Existence of indexes
- Index definitions (column usage)
- Existence of constraints
- Constraint definitions (table, type and reference)
- Constraint column usage (for unique, primary key and foreign keys)
- Check constraint definitions
- Existence of triggers
- Definition of triggers
- Existence of procedure/packages/functions
- Definition of procedures/packages/functions
The script drops and creates a few temporary objects prefixed with
the first 3 letter combination (AAA - ZZZ) that does not conflict with any
existing objects.
*/

undef prex
undef prefx
undef a
undef thisuser
undef b
undef REMOTESCHEMA
undef REMOTEPASSW
undef connstring
undef c
undef todaysdate
variable prefx varchar2(3)
declare
i number ;
j number ;
k number ;
cnt number;
begin
 for i in ascii('A') .. ascii('Z') loop
  for j in ascii('A') .. ascii('Z') loop
   for k in ascii('A') .. ascii('Z') loop
     select count(*) into cnt from user_objects where object_name like
     chr(i)||chr(j)||chr(k)||'%';
     if cnt = 0 then
       :prefx := chr(i)||chr(j)||chr(k);
       return;
     end if;
    end loop;
   end loop;
  end loop;
end;
/
column a new_val prex
set verify off
set linesize 132
set feedback off
select :prefx a from dual;
column b new_val thisuser
select user b from dual;
column c new_val todaysdate
select to_char(sysdate,'DD-MON-YYYY HH24:MI') c from dual;
accept REMOTESCHEMA char prompt 'Enter remote username:'
accept REMOTEPASSW char prompt 'Enter remote password:' hide
accept connstring char prompt 'Enter remote connectstring:'
spool dbdiff
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT     SCHEMA DEFINITION DIFFERENCES     &todaysdate
PROMPT
PROMPT           this schema: &thisuser
PROMPT         remote schema: &remoteschema.@&connstring
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT
PROMPT
create database link &prex.lnk connect to &REMOTESCHEMA identified
by &REMOTEPASSW using '&CONNSTRING';
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT  TABLES MISSING IN THIS SCHEMA:
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
create table &prex.common_tables as
select table_name from user_TAbles@&prex.lnk
intersect
select table_name from user_tables;
select table_name from user_TAbles@&prex.lnk
minus
select table_name from &prex.common_tables;
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT TABLES MISSING IN REMOTE SCHEMA:
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select table_name from user_TAbles where table_name not like '&prex.%'
minus
select table_name from user_tables@&prex.lnk;
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT COLUMNS MISSING IN THIS SCHEMA FOR COMMON TABLES
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select table_name,column_name from user_tab_columns@&prex.lnk
where table_name in
(select table_name from &prex.common_tables)
minus
select table_name,column_name from user_tab_columns
where table_name in
(select table_name from &prex.common_tables);
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT COLUMNS MISSING IN REMOTE SCHEMA FOR COMMON TABLES
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select table_name,column_name from user_tab_columns
where table_name in
(select table_name from &prex.common_tables)
minus
select table_name,column_name from user_tab_columns@&prex.lnk
where table_name in
(select table_name from &prex.common_tables);

create table &prex.diff_cols1
( TABLE_NAME                   VARCHAR2(30),
 COLUMN_NAME                VARCHAR2(30),
 DATA_TYPE                      VARCHAR2(9),
 DATA_LENGTH                  NUMBER,
 DATA_PRECISION             NUMBER,
 DATA_SCALE                    NUMBER,
 NULLABLE                        VARCHAR2(1),
 COLUMN_ID                      NUMBER,
 DEFAULT_LENGTH            NUMBER,
 DATA_DEFAULT                 varchar2(2000));
create table &prex.diff_cols2
( TABLE_NAME                   VARCHAR2(30),
 COLUMN_NAME                VARCHAR2(30),
 DATA_TYPE                      VARCHAR2(9),
 DATA_LENGTH                  NUMBER,
 DATA_PRECISION             NUMBER,
 DATA_SCALE                    NUMBER,
 NULLABLE                        VARCHAR2(1),
 COLUMN_ID                      NUMBER,
 DEFAULT_LENGTH            NUMBER,
 DATA_DEFAULT                 varchar2(2000));
declare
cursor c1 is
select
 l.TABLE_NAME ,
 l.COLUMN_NAME,
 l.DATA_TYPE ,
 l.DATA_LENGTH,
 l.DATA_PRECISION ,
 l.DATA_SCALE ,
 l.NULLABLE,
 l.COLUMN_ID ,
 l.DEFAULT_LENGTH ,
 l.DATA_DEFAULT
from user_tab_columns l,&prex.common_tables c
where c.table_name=l.table_name ;
TYPE rec is record (
 TABLE_NAME                   VARCHAR2(30),
 COLUMN_NAME                VARCHAR2(30),
 DATA_TYPE                      VARCHAR2(9),
 DATA_LENGTH                  NUMBER,
 DATA_PRECISION             NUMBER,
 DATA_SCALE                    NUMBER,
 NULLABLE                        VARCHAR2(1),
 COLUMN_ID                      NUMBER,
 DEFAULT_LENGTH            NUMBER,
 DATA_DEFAULT                 varchar2(2000)
);
c rec;
begin
 open c1;
 loop
   fetch c1 into c;
    exit when c1%NOTFOUND;
    insert into &prex.diff_cols1 values
    (c.table_name,c.column_name,c.data_type,c.data_length,
     c.DATA_PRECISION, c.DATA_SCALE, c.NULLABLE, c.COLUMN_ID,
     c.DEFAULT_LENGTH, c.DATA_DEFAULT);
end loop;
end;
/
declare
cursor c1 is
select
 l.TABLE_NAME ,
 l.COLUMN_NAME,
 l.DATA_TYPE ,
 l.DATA_LENGTH,
 l.DATA_PRECISION ,
 l.DATA_SCALE ,
 l.NULLABLE,
 l.COLUMN_ID ,
 l.DEFAULT_LENGTH ,
 l.DATA_DEFAULT
from user_tab_columns@&prex.lnk l,&prex.common_tables c
where c.table_name=l.table_name ;
TYPE rec is record (
 TABLE_NAME                   VARCHAR2(30),
 COLUMN_NAME                VARCHAR2(30),
 DATA_TYPE                      VARCHAR2(9),
 DATA_LENGTH                  NUMBER,
 DATA_PRECISION             NUMBER,
 DATA_SCALE                    NUMBER,
 NULLABLE                        VARCHAR2(1),
 COLUMN_ID                      NUMBER,
 DEFAULT_LENGTH            NUMBER,
 DATA_DEFAULT                 varchar2(2000)
);
c rec;
begin
 open c1;
 loop
   fetch c1 into c;
    exit when c1%NOTFOUND;
    insert into &prex.diff_cols2 values
    (c.table_name,c.column_name,c.data_type,c.data_length,
     c.DATA_PRECISION, c.DATA_SCALE, c.NULLABLE, c.COLUMN_ID,
     c.DEFAULT_LENGTH, c.DATA_DEFAULT);
end loop;
end;
/
column table_name format a20
column column_name format a20
column param format a15
column local_value format a20
column remote_value format a20
set arraysize 1
set maxdata 32000
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT DIFFERENCE IN COLUMN-DEFS
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select l.table_name,l.column_name,'DATA_DEFAULT' param ,
l.DATA_DEFAULT local_value, r.DATA_DEFAULT remote_value
from &prex.diff_cols1 l, &prex.diff_cols2 r
where l.table_name=r.table_name and
      l.column_name=r.column_name and l.DATA_DEFAULT != r.DATA_DEFAULT
union
select l.table_name,l.column_name,'DATA_TYPE',l.data_type,r.data_type
from &prex.diff_cols1 l, &prex.diff_cols2 r
where l.table_name=r.table_name and
      l.column_name=r.column_name and l.data_type != r.data_type
union
select l.table_name,l.column_name,'DATA_LENGTH',to_char(l.data_length),
to_char(r.data_length)
from &prex.diff_cols1 l, &prex.diff_cols2 r
where l.table_name=r.table_name and
      l.column_name=r.column_name and l.data_length != r.data_length
union
select l.table_name,l.column_name,'DATA_PRECISION',
to_char(l.DATA_PRECISION),to_char(r.DATA_PRECISION)
from &prex.diff_cols1 l, &prex.diff_cols2 r
where l.table_name=r.table_name and
      l.column_name=r.column_name and l.DATA_PRECISION != r.DATA_PRECISION
union
select l.table_name,l.column_name,'DATA_SCALE',to_char(l.DATA_SCALE),
to_char(r.DATA_SCALE)
from &prex.diff_cols1 l, &prex.diff_cols2 r
where l.table_name=r.table_name and
      l.column_name=r.column_name and l.DATA_SCALE != r.DATA_SCALE
union
select l.table_name,l.column_name,'NULLABLE',l.NULLABLE,r.NULLABLE
from &prex.diff_cols1 l, &prex.diff_cols2 r
where l.table_name=r.table_name and
      l.column_name=r.column_name and l.NULLABLE != r.NULLABLE
union
select l.table_name,l.column_name,'COLUMN_ID',to_char(l.COLUMN_ID),
to_char(r.COLUMN_ID)
from &prex.diff_cols1 l, &prex.diff_cols2 r
where l.table_name=r.table_name and
      l.column_name=r.column_name and l.COLUMN_ID != r.COLUMN_ID
union
select l.table_name,l.column_name,'DEFAULT_LENGTH',to_char(l.DEFAULT_LENGTH),
to_char(r.DEFAULT_LENGTH)
from &prex.diff_cols1 l, &prex.diff_cols2 r
where l.table_name=r.table_name and
      l.column_name=r.column_name and l.DEFAULT_LENGTH != r.DEFAULT_LENGTH
order by 1,2
/

create table &prex.common_indexes as
select table_name, index_name from user_indexes@&prex.lnk
where table_name in (select table_name from &prex.common_tables)
intersect
select table_name, INdex_name from user_indexes
where table_name in (select table_name from &prex.common_tables);
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT INDEXES MISSING IN THIS SCHEMA FOR COMMON TABLES
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select table_name, index_name from user_indexes@&prex.lnk
where table_name in (select table_name from &prex.common_tables)
minus
select table_name, index_name from &prex.common_indexes;
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT INDEXES MISSING IN REMOTE SCHEMA FOR COMMON TABLES
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select table_name, index_name from user_indexes
where table_name in (select table_name from &prex.common_tables)
minus
select table_name, index_name from &prex.common_indexes;
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT COMMON INDEXES WITH DIFFERENT UNIQUENESS
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select a.table_name, a.index_name, a.uniqueness local, b.uniqueness remote
from user_indexes a,
         user_indexes@&prex.lnk b
where  a.index_name = b.index_name
and   a.uniqueness != b.uniqueness
and  (a.table_name, a.index_name) in
(select table_name, index_name from &prex.common_indexes);
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT INDEX COLUMNS MISSING IN THIS SCHEMA FOR COMMON INDEXES
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select index_name, column_name from user_ind_columns@&prex.lnk
where (table_name,index_name)  in
(select table_name,index_name from &prex.common_indexes)
minus
select index_name, column_name from user_ind_columns
where (table_name,index_name)  in
(select table_name,index_name from &prex.common_indexes);
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT INDEX COLUMNS MISSING IN REMOTE  SCHEMA FOR COMMON INDEXES
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select index_name, column_name from user_ind_columns
where (table_name,index_name)  in
(select table_name,index_name from &prex.common_indexes)
minus
select index_name, column_name from user_ind_columns@&prex.lnk
where (table_name,index_name)  in
(select table_name,index_name from &prex.common_indexes);
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT INDEX COLUMNS POSITIONED DIFFERENTLY FOR COMMON INDEXES
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select a.index_name, a.column_name, a.column_position local,
           b.column_position remote
           from user_ind_columns a,
                        user_ind_columns@&prex.lnk b
where  (a.table_name,a.index_name) in
(select table_name,index_name from &prex.common_indexes)
and b.index_name = a.index_name
and b.table_name = a.table_name
and a.column_name = b.column_name
and a.column_position != b.column_position;

PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT CONSTRAINTS MISSING IN THIS SCHEMA FOR COMMON TABLES
PROMPT (WORKS ONLY FOR CONSTRAINT WITH NON SYSTEM GENERATED NAMES)
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select table_name,constraint_name from user_constraints@&prex.lnk
where constraint_name not like 'SYS%' and table_name in
(select table_name from &prex.common_tables)
minus
select table_name,constraint_name from user_constraints
where constraint_name not like 'SYS%' and table_name in
(select table_name from &prex.common_tables);
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT CONSTRAINTS MISSING IN REMOTE SCHEMA FOR COMMON TABLES
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select table_name,constraint_name from user_constraints
where constraint_name not like 'SYS%' and table_name in
(select table_name from &prex.common_tables)
minus
select table_name,constraint_name from user_constraints@&prex.lnk
where constraint_name not like 'SYS%' and table_name in
(select table_name from &prex.common_tables);
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT COMMON CONSTRAINTS, TYPE MISMATCH
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select a.constraint_name,a.constraint_type local_type,
b.constraint_type remote_type
from user_constraints a, user_constraints@&prex.lnk b where
a.table_name = b.table_name and
a.constraint_name=b.constraint_name and
a.constraint_type !=b.constraint_type;
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT COMMON CONSTRAINTS, TABLE MISMATCH
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select a.constraint_name,a.table_name,b.table_name from
user_constraints a, user_constraints@&prex.lnk b,
(select z.constraint_name from
(select constraint_name, table_name from useR_constraints union
select constraint_name, table_name from user_constraints@&prex.lnk) z
group by constraint_name having count(*) >1) q
where a.constraint_name = q.constraint_name and
b.constraint_name=q.constraint_name
and a.table_name != b.table_name;
create table &prex.comcons as
select constraint_name, constraint_type, table_name
from useR_constraints
intersect
select constraint_name, constraint_type, table_name
from user_constraints@&prex.lnk;
delete from &prex.comcons where constraint_name in
(select constraint_name from &prex.comcons
group by constraint_name having count(*) > 1);
delete from &prex.comcons where constraint_name like 'SYS%';
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT  DIFFERENCES IN COLUMN USAGE FOR CONSTRAINT DEFS
PROMPT    (Unique key, Primary Key, Foreign key)
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
declare
cursor c1 is
select a.constraint_name,a.position,a.column_name,b.constraint_type
from user_cons_columns a, &prex.comcons b
where a.constraint_name=b.constraint_name
union
select a.constraint_name,a.position,a.column_name,b.constraint_type
from user_cons_columns@&prex.lnk a, &prex.comcons b
where a.constraint_name=b.constraint_name
minus
(select a.constraint_name,a.position,a.column_name,b.constraint_type
   from user_cons_columns a, &prex.comcons b
   where a.constraint_name=b.constraint_name
intersect
select a.constraint_name,a.position,a.column_name,b.constraint_type
  from user_cons_columns@&prex.lnk a, &prex.comcons b
  where a.constraint_name=b.constraint_name
);
i binary_integer;
begin
for c in c1 loop
   dbms_output.put_line('COLUMN USAGE DIFFERENCE FOR '||c.constraint_type||
            ' CONSTRAINT '||c.constraint_name);
   dbms_output.put_line('. Local columns:');
   i:=1;
   for c2 in (select column_name col
             from user_cons_columns
             where constraint_name=c.constraint_name order by position)
   loop
      dbms_output.put_line('.   '||c2.col);
  end loop;
   i:=1;
   dbms_output.put_line('. Remote columns:');
   for c3 in (select column_name col
             from user_cons_columns@&prex.lnk
             where constraint_name=c.constraint_name
             )
   loop
      dbms_output.put_line('.   '||c3.col);
  end loop;
end loop;
end;
/
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT DIFFERENCES IN CHECK CONSTRAINT DEFS
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
set serveroutput on
declare
cursor c1 is select constraint_name,constraint_type,table_name
from &prex.comcons where constraint_type='C';
cons varchar2(50);
tab1 varchar2(50);
tab2 varchar2(50);
search1 varchar2(32000);
search2 varchar2(32000);
begin
dbms_output.enable(100000);
for c in c1 loop
  select search_condition into search1 from user_constraints
   where constraint_name=c.constraint_name;
  select search_condition into search2 from user_constraints@&prex.lnk
   where constraint_name=c.constraint_name;
  if search1 != search2 then
   dbms_output.put_line('Check constraint '||c.constraint_name||
                        ' defined differently!');
   dbms_output.put_line('. Local definition:');
   dbms_output.put_line('.  '||search1);
   dbms_output.put_line('. Remote definition:');
   dbms_output.put_line('.  '||search2);
  end if;
end loop;
end;
/
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT TRIGGERS MISSING IN REMOTE SCHEMA
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select trigger_name from user_Triggers minus
select trigger_name from user_Triggers@&prex.lnk;
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT TRIGGERS MISSING IN THIS SCHEMA
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select trigger_name from user_Triggers@&prex.lnk minus
select trigger_name from user_Triggers@&prex.lnk;
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT TRIGGER DEFINITION DIFFERENCES ON COMMON TRIGGERS
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
set serveroutput on
declare
cursor c1 is select
TRIGGER_NAME,TRIGGER_TYPE,TRIGGERING_EVENT,
TABLE_NAME,REFERENCING_NAMES,rtrim(WHEN_CLAUSE,' '),STATUS,
rtrim(replace(description,'"&thisuser".',null),' ') DESCRIPTION,
TRIGGER_BODY from user_Triggers;
nam1 varchar2(30);
type1 varchar2(16);
event1 varchar2(26);
table1 varchar2(30);
ref1 varchar2(87);
when1 varchar2(2000);
status1 varchar2(8);
desc1 varchar2(2000);
body1 varchar2(32000);
type2 varchar2(16);
event2 varchar2(26);
table2 varchar2(30);
ref2 varchar2(87);
when2 varchar2(2000);
status2 varchar2(8);
desc2 varchar2(2000);
body2 varchar2(32000);
pr_head boolean;
begin
dbms_output.enable(100000);
open c1;
loop
 fetch c1 into nam1,type1,event1,table1,ref1,when1,status1,desc1,body1;
 exit when c1%notfound;
 begin
  select
  TRIGGER_TYPE,TRIGGERING_EVENT,
  TABLE_NAME,REFERENCING_NAMES,rtrim(WHEN_CLAUSE,' '),STATUS,
  rtrim(replace(description,upper('"&remoteschema".'),null),' ') DESCRIPTION,
  TRIGGER_BODY
  into type2,event2,table2,ref2,when2,status2,desc2,body2
  from user_Triggers@&prex.lnk
  where trigger_name=nam1;
  pr_head := FALSE;
  if table1 != table2 then
    dbms_output.put_line('T R I G G E R : '||nam1);
    dbms_output.put_line('-------------------------------------------------'||
                         '-----------------------');
    pr_head := TRUE;
    dbms_output.put_line('   ');
    dbms_output.put_line('DEFINED ON DIFFERENT TABLES!');
    dbms_output.put_line('.   This table_name  : '||table1);
    dbms_output.put_line('.   Remote table_name: '||table2);
  end if;
  if event1 != event2 then
    if not pr_head then
     dbms_output.put_line('T R I G G E R : '||nam1);
     dbms_output.put_line('-------------------------------------------------'||
                          '-----------------------');
     pr_head := TRUE;
    end if;
    dbms_output.put_line('   ');
    dbms_output.put_line('DEFINED FOR DIFFERENT EVENTS!');
    dbms_output.put_line('.   This event: '||event1);
    dbms_output.put_line('. Remote event: '||event2);
  end if;
  if type1 != type2 then
    if not pr_head then
     dbms_output.put_line('T R I G G E R : '||nam1);
     dbms_output.put_line('-------------------------------------------------'||
                          '-----------------------');
     pr_head := TRUE;
    end if;
    dbms_output.put_line('   ');
    dbms_output.put_line('DIFFERENT TYPES!');
    dbms_output.put_line('.   This type: '||type1);
    dbms_output.put_line('.      Remote: '||type2);
  end if;
  if ref1 != ref2 then
    if not pr_head then
     dbms_output.put_line('T R I G G E R : '||nam1);
     dbms_output.put_line('-------------------------------------------------'||
                          '-----------------------');
     pr_head := TRUE;
    end if;
    dbms_output.put_line('   ');
    dbms_output.put_line('DIFFERENT REFERENCES!');
    dbms_output.put_line('.   This ref: '||ref1);
    dbms_output.put_line('.     Remote: '||ref2);
  end if;
    if when1 != when2 then
    dbms_output.put_line('   ');
    if not pr_head then
     dbms_output.put_line('T R I G G E R : '||nam1);
     dbms_output.put_line('-------------------------------------------------'||
                          '-----------------------');
     pr_head := TRUE;
    end if;
    dbms_output.put_line('DIFFERENT WHEN CLAUSES!');
    dbms_output.put_line('.  Local when_clause:');
    dbms_output.put_line(when1);
    dbms_output.put_line('.  Remote when_clause: ');
    dbms_output.put_line(when2);
  end if;
  if status1 != status2 then
    dbms_output.put_line('   ');
    dbms_output.put_line('DIFFERENT STATUS!');
    dbms_output.put_line('.  Local status: '||status1);
    dbms_output.put_line('.  Remote status: '||status2);
  end if;
 if replace(desc1,chr(10),'') != replace(desc2,chr(10),'') then
    dbms_output.put_line('   ');
    dbms_output.put_line('DIFFERENT DESCRIPTIONS!');
    dbms_output.put_line('Local definition: ');
    dbms_output.put_line(desc1);
    dbms_output.put_line('Remote definition: ');
    dbms_output.put_line(desc2);
  end if;
  if body1 != body2 then
    dbms_output.put_line('   ');
    dbms_output.put_line('THE PL/SQL BLOCKS ARE DIFFERENT! ');
    dbms_output.put_line('   ');
  end if;
  exception when NO_DATA_FOUND then null;
  when others then raise_application_error(-20010,SQLERRM);
 end;
end loop;
end;
/
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT MISSING PROCEDURES/PACKAGES/FUNCTIONS IN REMOTE SCHEMA
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select distinct name,type from user_source minus
select distinct name,type from user_source@&prex.lnk;
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT MISSING PROCEDURES/PACKAGES/FUNCTIONS IN LOCAL SCHEMA
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select distinct name,type from user_source@&prex.lnk minus
select distinct name,type from user_source;
create table &prex.comcod as
select distinct name,type from user_source intersect
select distinct name,type from user_source@&prex.lnk;
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT PROCEDURES/PACKAGES/FUNCTIONS WITH DIFFERENT DEFINITIONS
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select distinct q.name Object_name,q.type Object_type from
(select a.name,a.type,a.line,a.text
   from user_source a, &prex.comcod b
  where a.name=b.name union
select a.name,a.type,a.line,a.text
 from user_source@&prex.lnk a, &prex.comcod b
where a.name=b.name
minus
(select a.name,a.type,a.line,a.text
   from user_source a, &prex.comcod b
  where a.name=b.name
 intersect
select a.name,a.type,a.line,a.text
  from user_source@&prex.lnk a, &prex.comcod b
 where a.name=b.name )) q;
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT  VIEWS MISSING IN THIS SCHEMA:
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
create table &prex.common_views as
select view_name from user_views@&prex.lnk
intersect
select view_name from user_views;
select view_name from user_views@&prex.lnk
minus
select view_name from &prex.common_views;
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT VIEWS MISSING IN REMOTE SCHEMA:
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
select view_name from user_views
minus
select view_name from user_views@&prex.lnk;
PROMPT
PROMPT
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PROMPT VIEWS WITH DIFFERENCES IN THE DEFINITION
PROMPT  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
declare
def1 varchar2(32000);
def2 varchar2(32000);
len1 number;
len2 number;
i number;
cursor c1 is select view_name from &prex.common_views;
begin
dbms_output.enable(100000);
for c in c1 loop
  select text,text_length into def1,len1
          from user_Views where view_name=c.view_name;
  select text,text_length into def2,len2
      from user_Views@&prex.lnk where view_name=c.view_name;
        i := 1;
  def1:=replace(def1,' ','');
  def2:=replace(def2,' ','');
  if def1 != def2 or length(def1) != length(def2) then
    dbms_output.put_line(lpad('-',35+length(c.view_name),'-'));
    dbms_output.put_line('|  '||c.view_name ||
                         '                               |');
    dbms_output.put_line(lpad('-',35+length(c.view_name),'-'));
        dbms_output.put_line('Local text_length:   ' || to_char(len1));
        dbms_output.put_line('Remote text_length):  ' || to_char(len2));
    dbms_output.put_line(' ');
        i := 1;
        while i <= length(def1) loop
           if substr(def1,i,240) != substr(def2,i,240) then
                   dbms_output.put_line('Difference at offset ' || to_char(i)
);
                   dbms_output.put_line('   local:   ' || substr(def1,i,240));
                   dbms_output.put_line('   remote:  ' || substr(def2,i,240));
       end if;
           i := i + 240;
    end loop;
  end if;
  if length(def2) > length(def1) then
         dbms_output.put_line('Remote longer than Local. Next 255 bytes:    ');
         dbms_output.put_line(substr(def2,length(def1),255));
  end if;
end loop;
end;
/
drop database link &prex.lnk;
drop table &prex.comcod;
drop table &prex.diff_cols1;
drop table &prex.diff_cols2;
drop table &prex.common_tables;
drop table &prex.common_views;
drop table &prex.ind;
drop table &prex.ind1;
drop table &prex.ind2;
drop table &prex.comcons;
spool off
set verify on
set feedback on
undef prex
undef prefx
undef a
undef thisuser
undef b
undef REMOTESCHEMA
undef REMOTEPASSW
undef connstring
undef c
undef todaysdate

April 19, 2002 Mark -- Thanks for the question regarding "Your
      approach to coding this", version 9.0.1

      You Asked
Tom:

I would like to create some code that accomplishes the following:

- Reads a list of databases (preferably from OID)
- At the top of each hour connects to each of the databases in the list and gets
the number of sessions
- Updates a table in a "master" database with the date/time, database name, and
number of sessions
- viewable in a "report format" from a browser

I am open to coding this in anything from OCI to Java to PL/SQL.  It has been
about 4 yrs. or so since I have coded actively, and back then, it wasn't as
fancy as Java in a database...

Anyway, I would appreciate any advice you could give.  As always, if am being as
clear as mud, let me know what I need to clear up.

Thanks,

Mark

      and we said...
So, in short you want to build the capabilities that exist in OEM ;)

Well, you can use the DBMS_LDAP package in the database to get PLSQL to interact
with OID to get the master list.

PLSQL can then iterate over the elements in this list and assuming the dblinks
are in place (you could dynamically generate them if need be), you can
dynamically execute:

   execute immediate
   'insert into master_table
    select sysdate, :database_name, count(*)
     from v$session@' || p_db_link_name
    using p_database_name;

You would use DBMS_JOB to schedule this job on an hourly basis in the "master"
database.
Review & Followup

      Rating:
      5
Thanks!  April 19, 2002
Reviewer:  Mark
      A. Williams  from Indianapolis, IN USA
Tom:

>So, in short you want to build the capabilities that exist in OEM ;)

Yes, pretty much :)

This is just a small part of a larger project.  The main project involves
creating a "sandbox" Oracle Management Server and evaluating OEM, RMAN, OID, and
the PL/SQL module for the webserver.  We are currently using shell scripts and
cron to run the "hourly license usage" jobs which are used for license usage
reporting.  Any other jobs are run on an ad-hoc basis.  That facility is a java
applet that presents a list of jobs to the user and the user can select which
one(s) to run.  Pretty straight-forward.  However, I thought it would be a great
idea to move everything (at least as much as possible) into the database.
(Tom's mantra and all).  Of course, this is meeting with great resistance from
"the other side".  So, I want to show how moving things into the database will
improve reliability, scalability, availability and any other "bility" that
applies.  Not being terribly fluent in java, my desire was to use pl/sql as much
as possible (plus I want to use the pl/sql module, so it is a natural fit).
Anyway, sorry to drone on...

What happened to your crusade against "u", etc...  I notice the disclaimer is
off your main page now...

I saw the patch for the "ansi join issue" is available for most UNIX platforms
as of yesterday, so that's a good thing.

Thanks again,

- Mark

April 19, 2002 -- Thanks for the question regarding "Log in
      Problem", version 8.1.6

      You Asked
Hi Tom,
I am using Oracle 8i Personal Edition 8.1.6.0.0 installed on a Windows 2000
Professional platform for academic purposes.  Every now and then, I have problem
to log in (scott as user name, and tiger as password).  I had to de-install and
re-install the software in order to be able to use it for some times.  I tried
the software on both NTFS, and FAT32 file systems, even changed the software CD
to make sure that nothing is wrong with it, but still I have the same problem.
Would you please tell me how can I solve this problem?

      and we said...
I'm just going to use this question as a way to show people how not to ask a
question (you are totally anonymous here, no need to worry about that).

It starts out well.  I have the database version (816), the operating system.

From there, it goes right downhill.

"Every now and then, I have problem to log in".

So, I'm sorry to hear that but exactly what could I do about it?  What is the
problem?  What are the symptons?  This is like going to the doctor when you are
sick and not telling him your symptons -- with holding the information they need
to make a diagnoses.  If you went to the doctor you would tell him "I have a
fever of 100, my throat is sore, my nose is stuffy and i have aches and pains".
You would not just say "i'm not functioning properly" and nothing else.

Sorry, but there is no way I can help you on this one.

Review &
      Followup

      Rating:
      5
  April 19, 2002
Reviewer:
      A reader
You got to do it more often Tom! This forum is not to be treated by anyone as an
online support. It should be for serious discussions which are benificial to
everyone.

April 19, 2002 Amit -- Thanks for the question regarding
      "Database server time sync", version 8.1.6.0.0 on NT4

      You Asked
Hi Tom.

We have a database server working alongside web-servers
and application servers.

It is important to have these servers' clocks synchronized.

Currently all the servers are NT4 servers. In the future,
The database machine will be upgraded to a Unix machine,
and the web servers will run win2000.

We are aware that there are many solutions and products
for achieving this, however- we do not know how reliable
they are when an Oracle database is involved.

What are your recommendations / are there "Oracle-Approved"
solutions for this issue ?

Thanks,
Amit

      and we said...
Nope, Oracle doesn't care what time the system clock responds
with -- we have our own internal clock called the SCN (system
commit number).

The only time I'm aware that the external clock will affect us
is during a POINT IN TIME recovery when you ask us to recover
until "12 noon".  If 12 noon happened more then once -- that
would be ambigous.  It is best to point in time recover to an
SCN (time based recovery really just finds the SCN that was
current at that point in time and does an SCN based recovery).

Review &
      Followup

      Rating: 5
Finding SCN
        April 18, 2002
Reviewer:  Rajesh Narkar
       from Mumbai
How to find SCN at a given time for incomplete recovery? Is there any method?

Thanks Tom for helping us to solve problems at no cost.I'm really grateful to
you.With Best Wishes.Bye.

          Followup:

logminer.

      Rating:
      4
  April 18, 2002
Reviewer:
      A reader
"If 12 noon happened more then once -- that would be ambigous. "

I am curious to know how Oracle handles point-in-time recovery when the
day-light saving time change happens. For example on 4th Oct, there can be
2.30AM twice. If I do a point-in-time recovery till 2.30AM how will Oracle
handle it?

          Followup:

it'll recover to the FIRST 2:30am it finds and stop.  you would need to use an
SCN based PITR to recover to the second 2:30am.

      Rating:
      4
sever  April 18, 2002
Reviewer:
      ruby  from us
hello!  what is difference between database server, and application sever

          Followup:

http://otn.oracle.com/
has lots of stuff on it.  You would be most interested in the links under
documentation.

They are fundementally different things.  database - place to store data, stored
procedures, triggers, data integrity constraints, and the like

app server -- place to get html pages, run some java, etc.  no data stored here.

      Rating: 3
twice?
      how  April 18, 2002
Reviewer:  C.J
       from ON
hi there,
         how could 2.30 can occur twice on a day.
C.J

          Followup:

daylight savings.  spring forward, fall back.

In the fall, 2:30am happens twice in a night in many places.

Before timezones (9i new feature), 2:30am would occur twice on those days.

      Rating:
      3
  April 19, 2002
Reviewer:  kiro
       from Bulgaria
Hi Tom
You said:
'The only time I'm aware that the external clock will affect us is during a
POINT
IN TIME recovery when you ask us to recover until "12 noon" '.

What about submiting a job? Changed time can disturb job execution. I had such a
case.

          Followup:

There you go.  A job that should run "hourly" would (when run at 2am) schedule
itself to run at 3am.  But when its run at 2am, the clock goes back to 1am.
Hence the job will not run hourly that night, there will be a 2 hour gap that
time.

Another subtle case.

      Rating:
      4
  April 19, 2002
Reviewer:
      A reader
How much synch are "sysdate from Oracle" and
"date" from Unix

Thanks

          Followup:

if "oracle" is running on the "unix", they are the same.  We get the time from
the OS.  We don't have our own clock for telling wall clock time.

      Rating:
      3
  April 19, 2002
Reviewer:
      A reader
Having the clocks in sync can, however, also affect operations in distributed
databases.  (Loved your book by the way.)

      Rating: 4
A Brief
      Info  April 19, 2002
Reviewer:  Irfan
       from Kuwait
Tom, How can we define:
daylight savings.  spring forward, fall back

Thanks

          Followup:

You don't have to. The "timezone" people already have.

the "spring forward, fall back" is a saying we have.

Every spring, we set our clocks forward an hour (spring forward). So, it stays
light an hour later.  It used to be for farmers, to make better use of their
time.  In the fall, we set them back to normal (fall back).

Not everyone does it so to some people out there, this daylight savings issue
doesn't make sense.

April 19, 2002 Chris -- Thanks for the question regarding
      "Collection of Questions", version 817

      You Asked
Tom,

1) How can I monitor the effectiveness of Intermedia text indexes ?
2) Are there any Oracle tools that are available to monitor Oracle rdbms
performance during load/stress testing ?
3) Are you aware of weblogic jdbc connection issues when using v8173 binaries
against v805 ?
4) Are you aware of any performance tuning tips for weblogic/oracle architecture

Thanks & Regards

Chris

      and we said...
1) don't know what you are looking for there. effectiveness of what?  If you
mean performance, benchmark with and without them and see what happens.

2) statspack is a popular one.  The OEM (enterprise manager) tuning/performance
pack is another.

3) nope, but then again I do not use weblogic personally.

4) make sure the java programmers have some idea about

   a) what a database is
   b) what concurrency is and how to develop concurrent applications (eg:
      use a sequence number not select max(id) and such)
   c) what a database is
   d) that bind variables *are in fact crucial, critical and must be used*
   e) most importantly -- what a database is.
   f) the fact that stored procedures are *not* evil incarnate

99.999% of database performance is in the design and implementation.  Not the
app server.

April 19, 2002 Mike -- Thanks for the question regarding
      "rownum=1", version 8.0.6

      You Asked
Tom,
Thanks for answering my questions.

I have a query, Oracle does the range scan on the index. There are the columns
matching all the columns of the index. Since the query only returns 1 row for
the particalar query, I compared to using ROWNUM=1 and without ROWNUM=1. With
ROWNUM=1 performanced a lot better against the without. I am not fully
understanding the oberseved, except that Oracle put a stop key for the ROWNUM=1

select * from
GENERAL_LEDGER_DETAIL
WHERE
 NVL(PROPERTY_CATEGORY_CODE,'-') = NVL('?','-')
AND NVL(REIMB_SOURCE_CODE,  '-') = NVL(null,'-')
AND NVL(ALLOTMENT_CODE,'-') = NVL('1013','-')
AND   NVL(FC_SYMBOL,'-') = NVL( 'US','-')
AND NVL(PROGRAM_NO,'-') = NVL(null,'-')
  AND FOA_CODE = '13'
AND NVL(DISB_STATION_SYMBOL,'-') = NVL('5570','-')
AND   NVL(FUND_ACCT_NO,'-') = NVL('R2AA1301','-')
AND NVL(MOA_CODE,'-') = NVL('C2','-')
    AND NVL(EOR_CODE,'-') = NVL('2540','-')
---
AND GL_ACCT_NUM =   '3100.05'
AND APPROP_SYMBOL = '2020'
AND   APPROP_FISCAL_YEAR = '2001'
AND APPROP_DEPT_CODE = '21'
AND   APPROP_LIMITATION = '0000'
AND APPROP_TRANS_DEPT = 'NA'
AND ACCT_PERIOD = '200108'
AND OPR_AGNCY_CODE = '57'
AND   FSN_CODE = '09038'

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.06       0.40          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      5.94      19.50       1080       8459          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      6.00      19.90       1080       8459          0           1

Misses in library cache during parse: 1
Optimizer goal: RULE
Parsing user id: 486  (TOOLMAN)

Rows     Row Source Operation
-------  ---------------------------------------------------
   8399  TABLE ACCESS BY INDEX ROWID GENERAL_LEDGER_DETAIL
   8400   INDEX RANGE SCAN (object id 125898)

Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: RULE
   8399   TABLE ACCESS   GOAL: ANALYZED (BY INDEX ROWID) OF
              'GENERAL_LEDGER_DETAIL'
   8400    INDEX   GOAL: ANALYZED (RANGE SCAN) OF
               'GENERAL_LEDGER_DETAIL_NDX2' (NON-UNIQUE)

********************************************************************************

select * from
GENERAL_LEDGER_DETAIL
WHERE rownum =1 and
 NVL(PROPERTY_CATEGORY_CODE,'-') = NVL('?','-')
AND NVL(REIMB_SOURCE_CODE,  '-') = NVL(null,'-')
AND NVL(ALLOTMENT_CODE,'-') = NVL('1013','-')
AND   NVL(FC_SYMBOL,'-') = NVL( 'US','-')
AND NVL(PROGRAM_NO,'-') = NVL(null,'-')
  AND FOA_CODE = '13'
AND NVL(DISB_STATION_SYMBOL,'-') = NVL('5570','-')
AND   NVL(FUND_ACCT_NO,'-') = NVL('R2AA1301','-')
AND NVL(MOA_CODE,'-') = NVL('C2','-')
    AND NVL(EOR_CODE,'-') = NVL('2540','-')
---
AND GL_ACCT_NUM =   '3100.05'
AND APPROP_SYMBOL = '2020'
AND   APPROP_FISCAL_YEAR = '2001'
AND APPROP_DEPT_CODE = '21'
AND   APPROP_LIMITATION = '0000'
AND APPROP_TRANS_DEPT = 'NA'
AND ACCT_PERIOD = '200108'
AND OPR_AGNCY_CODE = '57'
AND   FSN_CODE = '09038'

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.01          0          0          0           0
Execute      1      0.01       0.01          0          0          0           0
Fetch        2      0.13       0.18          0        459          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.15       0.20          0        459          0           1

Misses in library cache during parse: 0
Optimizer goal: RULE
Parsing user id: 486  (TOOLMAN)

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  COUNT STOPKEY
    453   TABLE ACCESS BY INDEX ROWID GENERAL_LEDGER_DETAIL
    453    INDEX RANGE SCAN (object id 125898)

Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: RULE
      1   COUNT (STOPKEY)
    453    TABLE ACCESS   GOAL: ANALYZED (BY INDEX ROWID) OF
               'GENERAL_LEDGER_DETAIL'
    453     INDEX   GOAL: ANALYZED (RANGE SCAN) OF
                'GENERAL_LEDGER_DETAIL_NDX2' (NON-UNIQUE)

      and we said...
Well, we didn't realize in the first query that there would only be one row --
so we had to look at ALL of the *possible* rows to find that out.

In this particular example, with rownum = 1, we had to look at 453 rows in the
index to find the single row you wanted.  We were able to stop after finding the
first row.  Without rownum = 1, there were 8400 index entries that MIGHT have
satisfied your predicate (but did not).  We had to look at them all.

You will find that sometimes the rownum = 1 performs exactly the same (the last
row in the index we scan is in fact the one you wanted) and sometimes it is much
much faster (the first row in the index is the one you want).

Since you KNOW you are getting A row and only want A row -- using rownum = 1 is
a good idea and on average will result in better performance as we will stop
scanning as soon as we find a candidate entry.

Another option is to create a larger index -- with more columns -- to make the
index itself more selective.  For example, an index on these columns:

AND GL_ACCT_NUM =   '3100.05'
AND APPROP_SYMBOL = '2020'
AND   APPROP_FISCAL_YEAR = '2001'
AND APPROP_DEPT_CODE = '21'
AND   APPROP_LIMITATION = '0000'
AND APPROP_TRANS_DEPT = 'NA'
AND ACCT_PERIOD = '200108'
AND OPR_AGNCY_CODE = '57'
AND   FSN_CODE = '09038'

might make the query instantaneous in BOTH cases as the index range scan might
consider one or two entries instead of 8400.
Review &
      Followup

      Rating:
      4
  April 12, 2002
Reviewer:
      A reader
Hi tom

 I have same problem, if i run query without rownum=1 it is taking 90 Second,
With rownum=1 the result comes in no time(very fast).

SIEBEL use rule base optimizer.

Thanks

SELECT
      T6.POSITION_ID,
      T11.COUNTRY,
      T1.PR_POSTN_ID,
      T8.SALES_ORG_CD,
      T1.LAST_UPD,
      T10.ROW_ID,
      T1.PR_REP_SYS_FLG,
      T16.CONFLICT_ID,
      T1.X_ORIG_ORG_ID,
      T1.DIVISION,
      T16.LAST_UPD,
      T1.X_CUST_NUM,
      T5.CHRCTR_ID,
      T1.X_EARLY_CLOSE_STATE,
      T6.ROW_ID,
      T13.ADDR,
      T1.X_SUB_STATUS,
      T2.LOGIN,
      T11.STATE,
      T13.CITY,
      T13.STATE,
      T4.PR_ADDR_ID,
      T1.DISA_CLEANSE_FLG,
      T3.ROW_ID,
      T1.PR_SHIP_PER_ID,
      T1.CREATED_BY,
      T1.X_INTL_INDICATOR,
      T10.COUNTY,
      T1.X_BILL_TO_OVERRIDE_FLG,
      T1.LOC,
      T1.X_EARLY_FLD_CLOSE_DT,
      T3.COUNTY,
      T13.COUNTRY,
      T1.PR_REP_DNRM_FLG,
      T6.ACCNT_LOC,
      T1.X_RECENT_MHORDER_YR,
      T1.PR_MKT_SEG_ID,
      T1.X_MAIN_PT_FLG,
      T1.CONFLICT_ID,
      T1.X_ORG_ID,
      T1.PR_ADDR_ID,
      T1.PR_TERR_ID,
      T1.MAIN_PH_NUM,
      T1.PR_REP_MANL_FLG,
      T1.PAR_OU_ID,
      T1.CURR_PRI_LST_ID,
      T1.X_LAST_YR_LISTED,
      T10.ZIPCODE,
      T13.X_STREET_NAME,
      T1.CREATED,
      T11.ZIPCODE,
      T11.CITY,
      T1.X_RECENT_MHORDER_STATUS,
      T1.PR_SRV_AGREE_ID,
      T16.CREATED,
      T6.ACCNT_NAME,
      T1.X_REVISOR_FLG,
      T1.ROW_ID,
      T4.X_ORG_ID,
      T1.DEDUP_TOKEN,
      T1.X_BAD_ADDR_FLG,
      T16.ATTRIB_04,
      T1.LOCATION_LEVEL,
      T11.COUNTY,
      T1.PR_OU_TYPE_ID,
      T1.PAR_DUNS_NUM,
      T1.X_INACTIVE_REASON_CD,
      T5.ROW_ID,
      T13.X_STREET_NUM,
      T15.NICK_NAME,
      T16.ATTRIB_16,
      T1.DUNS_NUM,
      T1.BU_ID,
      T3.X_STREET_NAME,
      T14.ROW_ID,
      T14.NAME,
      T1.BASE_CURCY_CD,
      T16.CREATED_BY,
      T1.REGION,
      T1.X_LAST_CHANGES_DATE,
      T1.MAIN_FAX_PH_NUM,
      T6.ROW_STATUS,
      T1.NAME,
      T1.X_RATING_CHG_DT,
      T1.X_PAST_DUE_AMT,
      T12.ROW_ID,
      T1.X_ORIG_LID,
      T16.ROW_ID,
      T10.COUNTRY,
      T12.NAME,
      T7.NAME,
      T11.ADDR,
      T1.X_MAIN_EMAIL,
      T1.MODIFICATION_NUM,
      T10.X_STREET_NAME,
      T3.ADDR,
      T1.X_PR_PROOF_ADDR_ID,
      T1.PR_INDUST_ID,
      T11.ROW_ID,
      T10.STATE,
      T16.PAR_ROW_ID,
      T1.URL,
      T1.X_STATE_CLOSING_DATE,
      T1.X_LISTING_ID,
      T12.PR_EMP_ID,
      T10.X_STREET_NUM,
      T10.ADDR,
      T1.X_PROMOTION_CODE,
      T9.CITY,
      T1.X_BAD_ADDR_USER,
      T1.X_RATING,
      T6.ASGN_SYS_FLG,
      T3.COUNTRY,
      T4.NAME,
      T1.PR_BL_ADDR_ID,
      T1.PR_REP_ASGN_TYPE,
      T1.CUST_STAT_CD,
      T1.X_EARLY_LAST_CHG_DT,
      T13.ROW_ID,
      T1.LAST_UPD_BY,
      T3.ZIPCODE,
      T3.STATE,
      T13.COUNTY,
      T16.ATTRIB_07,
      T1.PR_BL_PER_ID,
      T16.ATTRIB_47,
      T1.X_BAD_ADDR_DATE,
      T2.NICK_NAME,
      T10.CITY,
      T1.X_ACCNT_ID,
      T1.X_INACTIVE_DT,
      T3.CITY,
      T13.ZIPCODE,
      T11.X_STREET_NAME,
      T1.PR_SYN_ID,
      T1.X_CREDIT_STATUS,
      T6.ASGN_DNRM_FLG,
      T1.PR_SHIP_ADDR_ID,
      T16.MODIFICATION_NUM,
      T3.X_STREET_NUM,
      T1.X_PR_PROOF_PER_ID,
      T16.LAST_UPD_BY,
      T1.X_PR_DNS_ID,
      T1.X_ACCNT_LEVEL,
      T6.ASGN_MANL_FLG,
      T12.ROW_ID,
      T11.X_STREET_NUM
   FROM
       SIEBEL.S_ORG_EXT T1,
       SIEBEL.S_EMPLOYEE T2,
       SIEBEL.S_ADDR_ORG T3,
       SIEBEL.S_ORG_EXT T4,
       SIEBEL.S_ACCNT_CHRCTR T5,
       SIEBEL.S_ACCNT_POSTN T6,
       SIEBEL.S_CHRCTR T7,
       SIEBEL.S_ORG_INT T8,
       SIEBEL.S_ADDR_ORG T9,
       SIEBEL.S_ADDR_ORG T10,
       SIEBEL.S_ADDR_ORG T11,
       SIEBEL.S_POSTN T12,
       SIEBEL.S_ADDR_ORG T13,
       SIEBEL.S_ORG_EXT_XM T14,
       SIEBEL.S_EMPLOYEE T15,
       SIEBEL.S_ORG_EXT_X T16
   WHERE
      T1.X_BAD_ADDR_USER = T15.LOGIN (+) AND
      T1.BU_ID = T8.ROW_ID (+) AND
      T1.PAR_OU_ID = T4.ROW_ID (+) AND
      T4.PR_ADDR_ID = T9.ROW_ID (+) AND
      T1.ROW_ID = T16.PAR_ROW_ID (+) AND
      T1.X_PR_PROOF_ADDR_ID = T13.ROW_ID (+) AND
      T1.PR_ADDR_ID = T3.ROW_ID (+) AND
      T1.PR_MKT_SEG_ID = T5.ROW_ID (+) AND
      T5.CHRCTR_ID = T7.ROW_ID AND
      T1.PR_SHIP_ADDR_ID = T10.ROW_ID (+) AND
      T1.PR_BL_ADDR_ID = T11.ROW_ID (+) AND
      T1.PR_POSTN_ID = T12.ROW_ID AND
      T1.PR_POSTN_ID = T6.POSITION_ID AND
      T1.ROW_ID = T6.OU_EXT_ID AND
      T12.PR_EMP_ID = T2.ROW_ID (+) AND
      T1.X_PR_DNS_ID = T14.ROW_ID (+) AND
      (T7.NAME = 'Corporate') AND
      (T1.BU_ID = '0-R9NH')  AND
      ROWNUM=1

          Followup:

well, getting the first row takes no time.

getting the 90th row must take lots.

You certainly give me *nothing* to go on here.  No tkprof's, no list of indexes,
nothing.  In response to that -- i can only give you "nothing".

Things like this:

 T1.PR_MKT_SEG_ID = T5.ROW_ID (+) AND
      T5.CHRCTR_ID = T7.ROW_ID AND

do make me raise my eyebrow.  It would be IMPOSSIBLE for T5 to be outer joined
to like that (eg: to make up a row in T5) and to have t5.chrctr_id (which
would be null) equal t7.  So the (+) on T5 is capable only of slowing down the
query, not adding any results whatsoever to it.

T1 -> T15+
T1 -> T8+
T1 -> T4+
T4 -> T9+
T1 -> T16+
T1 -> T13+
T1 -> T3+
T1 -> T5+
T5 -> T7 ****
T1 -> T10+
T1 -> T11+
T1 -> T12
T1 -> T6
T12 -> T2+
T1 -> T14+

predicate on T7
precicate on T1

So, looking at that -- only an index on T1.BU_ID could be used to access this.
An index on T7.NAME could not really since T5 joins to T7 but T1 OUTER JOINS to
T5  -- we cannot drive the query from T7, only from T1 (so your outer join
conundrum above might be negatively affecting the performance of this query by
its very design).

Then again, neither of name or bu_id might be indexed or selective enough so
getting the last row involves a full scan of T1 and index reads all over the
place.

      Rating: 3
pipelined
      execution  April 12, 2002
Reviewer:  Mikito
      Harakiri
Tom is right, not giving even a plan is not nice.

I'm assuming (those siebel queries all the same;-) the following:

1. the plan is a lots of nested loops with unique and range join indexes
2. huge cardinality of the result set
3. client dispalys 20 rows and ignores the rest of the result set

In that condition there is not much difference between:
i. explicitly telling the server that you don't need more than 20 rows, or
ii. executing the query, opening the result set, getting first 20 rows, and
closing the result set.

However, if you try to compare performance of both queries on server, then there
would be a difference, just because the server would try to get all the result
set.

The key to performance of this query is the absence of _blocking_ nodes on the
plan. Blocking nodes need to build the full intermediatory relation, before the
execution flow can advance to the parent node. Nested loops are not blocking, as
you start with the innermost table, find mathing one or few rows from the
joining table, and then can join to the rest of the tables one-by-one so that
the execution would return first rows from the result set long before the end of
the innermost table being fully scanned.

      Rating:
      5
  April 19, 2002
Reviewer:
      A reader
Hi tom,

Your response to my previous question helps me a lot.Thanks for your help.

Here is another one.

Here i am trying to get contact info (SIEBEL.S_CONTACT)
and stisfying all needed condition.
So you can call it driving table.

Siebel is only using RULE base optimizer.

My Porblem is last query condition T1.WORK_PH_NUM LIKE '404%464%5647%'.

IF a remove this condition it work very fast.

In S_CONTACT we have above 1 million records.

I created Index on WORK_PH_NUM but it is not using that index but it is using
'S_CONTACT_U3'  index.

In siebel we can only change where clause!!!

SOORY FOR LONG DESCRIPTION.

SQL> l
  1  SELECT
  2        T4.CONFLICT_ID,
  3        T6.POSTN_ID,
  4        T1.PR_POSTN_ID,
  5        T1.LAST_UPD,
  6        T1.PR_REP_SYS_FLG,
  7        T2.STATE,
  8        T1.MID_NAME,
  9        T4.ROW_ID,
 10        T1.X_PR_CONTACT_TYPE_ID,
 11        T6.ROW_ID,
 12        T4.CREATED,
 13        T1.DISA_CLEANSE_FLG,
 14        T1.EMAIL_ADDR,
 15        T1.CREATED_BY,
 16        T4.MODIFICATION_NUM,
 17        T1.X_GOVT_FLG,
 18        T7.ROW_ID,
 19        T1.X_RATING,
 20        T3.ZIPCODE,
 21        T1.X_CONTACT_FLG,
 22        T1.EMP_ID,
 23        T3.CITY,
 24        T3.COUNTY,
 25        T1.PR_REP_DNRM_FLG,
 26        T13.PR_BL_ADDR_ID,
 27        T1.LAST_NAME,
 28        T4.ATTRIB_34,
 29        T13.X_MAIN_PT_FLG,
 30        T1.CONFLICT_ID,
 31        T6.CON_FST_NAME,
 32        T1.X_POSITION_TYPE,
 33        T1.X_MIDDLE_NAME,
 34        T1.X_BIOG_DATA,
 35        T2.ROW_ID,
 36        T1.X_ADMIT_YR,
 37        T1.X_ADDR_OVERRIDE_FLG,
 38        T1.PR_REP_MANL_FLG,
 39        T5.LOGIN,
 40        T3.STATE,
 41        T1.PR_PER_ADDR_ID,
 42        T1.CREATED,
 43        T13.X_ACCOUNT_NAME,
 44        T1.X_ISLN_NUM,
 45        T13.PR_SRV_AGREE_ID,
 46        T13.PR_SHIP_ADDR_ID,
 47        T1.ROW_ID,
 48        T13.X_ORG_ID,
 49        T11.LOGIN,
 50        T8.NAME,
 51        T1.X_BAD_ADDR_FLG,
 52        T1.DEDUP_TOKEN,
 53        T1.FST_NAME,
 54        T13.X_SUB_STATUS,
 55        T4.LAST_UPD_BY,
 56        T1.X_INACTIVE_REASON_CD,
 57        T12.NICK_NAME,
 58        T13.NAME,
 59        T9.NAME,
 60        T3.ROW_ID,
 61        T6.CON_MID_NAME,
 62        T1.PRIV_FLG,
 63        T13.LOC,
 64        T10.ROW_ID,
 65        T3.ADDR,
 66        T6.ROW_STATUS,
 67        T1.BIRTH_DT,
 68        T3.X_STREET_NAME,
 69        T2.ZIPCODE,
 70        T1.X_RATING_CHG_DT,
 71        T13.PR_BL_PER_ID,
 72        T3.COUNTRY,
 73        T1.X_PRACTICE_AREA,
 74        T1.COMMENTS,
 75        T9.ROW_ID,
 76        T1.X_RATING_INVG_YR,
 77        T13.PR_ADDR_ID,
 78        T1.ASST_PH_NUM,
 79        T1.MODIFICATION_NUM,
 80        T8.PR_EMP_ID,
 81        T1.X_ATTORNEY_FLG,
 82        T5.NICK_NAME,
 83        T4.PAR_ROW_ID,
 84        T7.NAME,
 85        T1.PR_OPTY_ID,
 86        T2.COUNTY,
 87        T3.X_STREET_NUM,
 88        T13.ROW_ID,
 89        T1.X_LISTING_ID,
 90        T1.X_LAW_SCHOOL,
 91        T13.INTEGRATION_ID,
 92        T1.X_PROMOTION_CODE,
 93        T2.CITY,
 94        T6.CON_LAST_NAME,
 95        T1.X_BAD_ADDR_USER,
 96        T1.X_NON_LEGAL_FLG,
 97        T13.PAR_OU_ID,
 98        T1.X_INDIV_ID,
 99        T13.PR_SHIP_PER_ID,
100        T4.ATTRIB_34,
101        T1.OWNER_PER_ID,
102        T4.LAST_UPD,
103        T1.OU_MAIL_STOP,
104        T1.WORK_PH_NUM,
105        T1.LAST_UPD_BY,
106        T1.PREF_COMM_MEDIA_CD,
107        T2.ADDR,
108        T1.X_BAD_ADDR_DT,
109        T4.ATTRIB_40,
110        T1.X_YEAR_BORN,
111        T1.CON_ASST_NAME,
112        T1.X_INACTIVE_DT,
113        T1.X_PR_ALT_PHONE_TYPE_ID,
114        T1.PR_DEPT_OU_ID,
115        T4.CREATED_BY,
116        T1.SEX_MF,
117        T1.X_PRACTICE_GROUP,
118        T10.ATTRIB_04,
119        T2.COUNTRY,
120        T1.FAX_PH_NUM,
121        T8.ROW_ID,
122        T1.PER_TITLE,
123        T1.X_DNS_FLG,
124        T1.X_PR_DNS_ID,
125        T1.JOB_TITLE,
126        T13.X_ACCNT_LEVEL
127     FROM
128         SIEBEL.S_CONTACT T1,
129         SIEBEL.S_ADDR_ORG T2,
130         SIEBEL.S_ADDR_PER T3,
131         SIEBEL.S_CONTACT_X T4,
132         SIEBEL.S_EMPLOYEE T5,
133         SIEBEL.S_POSTN_CON T6,
134         SIEBEL.S_CONTACT_XM T7,
135         SIEBEL.S_POSTN T8,
136         SIEBEL.S_CONTACT_XM T9,
137         SIEBEL.S_CONTACT_XM T10,
138         SIEBEL.S_EMPLOYEE T11,
139         SIEBEL.S_EMPLOYEE T12,
140         SIEBEL.S_ORG_EXT T13
141     WHERE
142        T1.X_BAD_ADDR_USER = T12.LOGIN (+) AND
143        T1.EMP_ID = T11.ROW_ID (+) AND
144        T1.PR_DEPT_OU_ID = T13.ROW_ID (+) AND
145        T1.ROW_ID = T4.PAR_ROW_ID (+) AND
146        T1.X_PR_ALT_PHONE_TYPE_ID = T10.ROW_ID (+) AND
147        T13.PR_ADDR_ID = T2.ROW_ID (+) AND
148        T1.X_PR_DNS_ID = T9.ROW_ID (+) AND
149        T1.PR_PER_ADDR_ID = T3.ROW_ID (+) AND
150        T1.PR_POSTN_ID = T8.ROW_ID AND
151        T1.PR_POSTN_ID = T6.POSTN_ID AND T1.ROW_ID = T6.CON_ID AND
152        T8.PR_EMP_ID = T5.ROW_ID (+) AND
153        T1.X_PR_CONTACT_TYPE_ID = T7.ROW_ID (+) AND
154        ((T1.LAST_NAME >= ' ') AND
155        (T1.BU_ID = '0-R9NH') AND
156        (T1.PRIV_FLG = 'N')) AND
157*       (T1.WORK_PH_NUM LIKE '404%464%5647%')
SQL> /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   NESTED LOOPS (OUTER)
   2    1     NESTED LOOPS (OUTER)
   3    2       NESTED LOOPS
   4    3         NESTED LOOPS (OUTER)
   5    4           NESTED LOOPS (OUTER)
   6    5             NESTED LOOPS (OUTER)
   7    6               NESTED LOOPS (OUTER)
   8    7                 NESTED LOOPS
   9    8                   NESTED LOOPS (OUTER)
  10    9                     NESTED LOOPS (OUTER)
  11   10                       NESTED LOOPS (OUTER)
  12   11                         NESTED LOOPS (OUTER)
  13   12                           TABLE ACCESS (BY INDEX ROWID) OF '
          S_CONTACT'

  14   13                             INDEX (RANGE SCAN) OF 'S_CONTACT
          _U3' (UNIQUE)

  15   12                           TABLE ACCESS (BY INDEX ROWID) OF '
          S_ORG_EXT'

  16   15                             INDEX (UNIQUE SCAN) OF 'S_ORG_EX
          T_P1' (UNIQUE)

  17   11                         TABLE ACCESS (BY INDEX ROWID) OF 'S_
          EMPLOYEE'

  18   17                           INDEX (UNIQUE SCAN) OF 'S_EMPLOYEE
          _P1' (UNIQUE)

  19   10                       TABLE ACCESS (BY INDEX ROWID) OF 'S_CO
          NTACT_XM'

  20   19                         INDEX (UNIQUE SCAN) OF 'S_CONTACT_XM
          _P1' (UNIQUE)

  21    9                     TABLE ACCESS (BY INDEX ROWID) OF 'S_CONT
          ACT_XM'

  22   21                       INDEX (UNIQUE SCAN) OF 'S_CONTACT_XM_P
          1' (UNIQUE)

  23    8                   TABLE ACCESS (BY INDEX ROWID) OF 'S_POSTN'
  24   23                     INDEX (UNIQUE SCAN) OF 'S_POSTN_P1' (UNI
          QUE)

  25    7                 TABLE ACCESS (BY INDEX ROWID) OF 'S_CONTACT_
          XM'

  26   25                   INDEX (UNIQUE SCAN) OF 'S_CONTACT_XM_P1' (
          UNIQUE)

  27    6               TABLE ACCESS (BY INDEX ROWID) OF 'S_EMPLOYEE'
  28   27                 INDEX (UNIQUE SCAN) OF 'S_EMPLOYEE_P1' (UNIQ
          UE)

  29    5             TABLE ACCESS (BY INDEX ROWID) OF 'S_ADDR_PER'
  30   29               INDEX (UNIQUE SCAN) OF 'S_ADDR_PER_P1' (UNIQUE
          )

  31    4           TABLE ACCESS (BY INDEX ROWID) OF 'S_ADDR_ORG'
  32   31             INDEX (UNIQUE SCAN) OF 'S_ADDR_ORG_P1' (UNIQUE)
  33    3         TABLE ACCESS (BY INDEX ROWID) OF 'S_POSTN_CON'
  34   33           INDEX (RANGE SCAN) OF 'S_POSTN_CON_U1' (UNIQUE)
  35    2       TABLE ACCESS (BY INDEX ROWID) OF 'S_EMPLOYEE'
  36   35         INDEX (RANGE SCAN) OF 'S_EMPLOYEE_U1' (UNIQUE)
  37    1     TABLE ACCESS (BY INDEX ROWID) OF 'S_CONTACT_X'
  38   37       INDEX (RANGE SCAN) OF 'S_CONTACT_X_U1' (UNIQUE)

Statistics
----------------------------------------------------------
       3547  recursive calls
          0  db block gets
     886224  consistent gets
      97586  physical reads
          0  redo size
       4896  bytes sent via SQL*Net to client
       2157  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
         54  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> spool off

Thanks,

          Followup:

try

T1.WORK_PH_NUM||'' LIKE '404%464%5647%'

      Rating:
      5
  April 19, 2002
Reviewer:
      A reader
Thanks a lot tom

It is working. you are THE best.

      Rating:
      4
Explain  April 19, 2002
Reviewer:
      A reader
Hi, Tom,

I didn't get it:
...||'' like ....
By that way it will spped up the query?

Please expalin.

Thanks

          Followup:

When they added the predicate - query went slow.  Without it -- goes fast.

Predicate must have picked up on an index -- index does not imply "i'm faster"
(rbo is INDEX happy).  By concatenating '' to the column -- we made it so the
optimizer cannot use an index on that column (it is "as if the predicate were
not there" for the RBO).

Very common "trick" to avoid an index.

April 19, 2002 potential -- Thanks for the question regarding
      "backup of offline tablespace in Oracle 9i", version 9i

      You Asked
I read "Oracle 9i User-Managed backup and recovery guide". We can make backup of
offline tablespace and datafile. It is neither cold backup nor hot backup we
talked a lot.

My question are:

1. Under which circumstance, will you recommend this type of backup? Please
explain the pros and cons.

2. Why do we need to "alter system archive log current" right we are done with
the backup of an offline tablespace. I think we do need to archive the current
online redo log just to recover other tablespaces which are online when we
backup this offline one. But there should not be any redo generated for this
offline tablespace because there is no read/write to the datafiles. Is that
right?

If yes, I just assume that I can go ahead backup offline tablespaces one by one
and at the very end of the backup procedure do "alter system archive log
current", e.g.,

ALTER TABLESPACE ts1 offline normal;
host cp /d1/ts1.dbf /d2/ts1.dbf;
ALTER TABLESPACE ts1 online;
ALTER TABLESPACE ts2 offline normal;
host cp /d1/ts2.dbf /d2/ts2.dbf;
ALTER TABLESPACE ts2 online;
ALTER SYSTEM ARCHIVE LOG CURRENT;

Is it O.K.?

Thanks, Tom.

      and we said...
1) i would never do it, nor would I do a cold backup.

Hot backups allow for continous availability.
They are NO harder then cold.
The only have benefits.
Cold/offline only have downsides.

2) we don't realize we don't need that archive -- it seems obvious to you but we
just check data file headers.  We think we need it.  You don't need to archive
it really, it'll get archived eventually.

Just backup HOT, don't even consider other approaches.  If you are in archivelog
mode (which you must be) just backup HOT, period.
April 19, 2002 Famy -- Thanks for the question regarding "Firing
      Sequence of Triggers in Forms - in query mode", version Forms 6i

      You Asked
Can you kindly tell us if the below mentioned firing sequence of triggers( query
related triggers)is correct
1.Pre_query
2.Pre_select
3.on_select
4.Post_select
5.When_clear_block
6.on_fetch
7.post_change
8.post_query

Thank you

      and we said...
Sorry -- i've no idea.  You should really NOT rely on the firing order of
triggers in any case, other then form/block/item.  They really are not defined
beyond the form/block/item level.

Dependencies like that must be avoided.  Just like in the database -- if you
have 5 "before" triggers -- it is "random" as to the order of their firing.  It
is allowed to change from run to run!

Review &
      Followup

      Rating: 1
I disagree with
      you  April 19, 2002
Reviewer:  Famy

Tom, dissapointing answer from you

I totally disagree with you, if forms the order of triggers( in a case where all
the give triggers are used) will be the same as given.

I wanted to confirm finally with you. What do you mean , is it possible for post
query to fire before pre query-, noppe.

Can you substantiate your answer please.

          Followup:

Like I said -- i don't know, I don't program forms.  I would never consider
relying on the order of the triggers firing.

Is it documented (the order)?

If not, don't rely on it -- consider what happens when the next release changes
the order.

Sorry to disappoint, I just don't like to MISLEAD.  It matters not if the
triggers happen to fire in order X in this release, if you do not see it
documented that "triggers fire in this order" -- don't rely on it.  Only seems
'sensible' to me.

April 19, 2002 --
      Thanks for the question regarding "Second max records", version 8.1.6

      You Asked
Hi, TOm

How to query the record with second largest value of PK from a table.

Say,

Col1(PK)      Col2
-------------------
1             abc
20            def
33            edfg
.             ...
.             ...
.             ...
.             fjdgf     <========= what i want
n             fgfgf

I want the result of record with the second largest value as
indicated above(Pk is asc).

Thanks

      and we said...
select *
  from ( select t*, rownum rnum
           from ( select t.*
                    from t
                   order by PK desc ) t
          where rownum <= 2
       )
 where rnum = 2;

 will get that.
Review & Followup

      Rating: 3
two sql
        April 19, 2002
Reviewer:  chao_ping
       from china
the same question, look at the following different sql, they get the same
result, but with different performance:
Explain shows  SQL-1 with less cost,while in actual running test, most time, it
is SQL-2 runs faster:

SQL-1:
SQL>  SELECT
  2  a.TX_TransactionID,a.TX_Party_TX_TransactionID_fk,
  3  a.TX_TransactionAmount,a.TX_TransactionSettlementAmount
  4  FROM eachpay.TX_Transaction a
  5  WHERE
  6   a.PA_AccountID = 9799
  7  and a.TX_Status = 1005
  8  and NOT EXISTS
  9  (
 10   SELECT b.TX_TransactionID
 11   FROM eachpay.TX_Transaction b
 12   WHERE
 13   b.PA_AccountID = 9799
 14   and
 15   b.TX_Status = 1005
 16   and b.TX_TransactionID < a.TX_TransactionID
 17  )
 18   ;
Elapsed: 00:00:00.51

SQL-2:
SQL> select TX_TransactionID,TX_Party_TX_TransactionID_fk,
  2  TX_TransactionAmount,TX_TransactionSettlementAmount
  3   from
  4  (
  5  SELECT
  6  TX_TransactionID,TX_Party_TX_TransactionID_fk,
  7  TX_TransactionAmount,TX_TransactionSettlementAmount, rownum rn
  8  FROM eachpay.TX_Transaction
  9  WHERE
 10   PA_AccountID = 9739
 11   and TX_Status = 1005
 12   order by tx_transactionid
 13   )
 14   where rn=1;

Elapsed: 00:00:00.21
Added:
there is an index on (pa_accountid,tx_status) and one index on tx_transactionid

How can i decide which to use?
The first is now in our production system and i want to rewrite it, but not sure
of the overall performance gain?
Thanks.

          Followup:

#2 is "wrong".  It DOES NOT get the same result (if it just happens to be
getting the same -- you are getting *lucky*.  It is not going to always return
the same!!!)

You want to code:

select *
  from ( select *
           from tx_transaction
          where pa_accountid = :bv1
            and tx_status = :bv2
          order by tx_transactionid )
 where rownum = 1;

the way you have it we will:

find 1 row
order it

you want to

order them
take the first.

Then use the query I wrote, I believe it'll be better then the correlated
subquery.  We have top-n optimizations for just such a query.

April 21, 2002 Munzer -- Thanks for the question regarding
      "init.ora file", version 8.1.7

      You Asked
Tom:

1.  As a developer is there a way in SQL*PLUS or unix to look at the init.ora
file to see how database instance is configured?

2.  Is this only file you need to check for instance configuration?

3.  If instance is for development, what kind of standards would you use to
determine if it is under or over configured.  Does oracle recommend a standard
configuration?

4. If there is an application server do you also look at the configuration there
and in which file?

I mean the system is composed of 30-40 tables.
WHat do you need to know to know optimal configuration.

Thank you,

      and we said...
1) show parameter  does that.

2) pretty much, there is networking stuff like the listener.ora, sqlnet.ora,
protocol.ora -- and those are available only via the file system on the server.

3) It is the same for development as for test.  There are no standards -- every
configuration is slightly different.  There are dozens of considerations when
sizing/configuring and "development" vs "production" have never been in my list
(number of users, amount of ram, what else is one the machine, etc etc are).

4) there are lots -- I don't admin the app server myself but there are the
Apache configuration files to deal with.

30-40 tables -- there again is a piece of information that is not terribly
useful in sizing a system.  30-40 tables used by 10,000 people or 30-40 tables
used by 5 people.  OLTP or DSS or Mixed.  1 row/table or 1billion rows/table.
Etc Etc Etc.

April 21, 2002 Madhusudhanan -- Thanks for the question
      regarding "Checking rowcount in ref cursor ", version 8.1.6

      You Asked
Dear Tom,

I'm having a small problem while checking row count parameter in a refcursor.

I have written a stored procedure that accepts parameters and returns
refcursors; if no candidate rows are found, then an error code is returned to
the calling program as the ref cursor. The same cursor variable is used to
retrun the rowset or error code.

To check if any rows are returned, I use the ROWCOUNT attribute of the cursor
variable. Rowcount is not available till I do the first fetch. But this fetch
removes the first row from the recordset, in case any rows are present. The
'OUT' variable returned to the calling program has one row less than actual. How
to prevent this?  Is there any other better way to check if rows are present?

Presently, I work around by opening again. But surely this won't hold out for
bigger data sets and complex queries.

create or replace PROCEDURE Get_Emp_Rows (EmpCur IN OUT GenPack.GenCurTyp, Nstr
Varchar2) IS
cname   Emp.Name%type;

BEGIN
  OPEN EmpCur FOR SELECT name FROM Emp where name = Nstr;
  FETCH EmpCur into cname;
    DBMS_OUTPUT.PUT_LINE(cname);  --displays first row for test
    IF EmpCur%rowcount=0 then
       OPEN EmpCur FOR SELECT 'W001' from dual;
    else
       OPEN EmpCur FOR SELECT name FROM Emp where name = Nstr;
    End If;
END Get_Emp_Rows;

Thanks for any input.

regards
-Madhu

      and we said...
In this case, I would change the requirment.

You have a classic chicken and egg problem here.  You cannot tell if the cursor
will return data until it actually does and if it you actually check, the caller
will not see that data.

I would (truly -- I would) return the ref cursor and if the CALLER feels that it
is an error to not fetch a single row -- let them raise the error.

Your approach wastes a ton of resources as you pointed out yourself and doesn't
seem to buy you anything.  Here is my premise on that:

Suppose you returned error when ZERO rows.  Caller must code something like:

   cursor = stored procedure call
   if error ( do something about this ERROR condition )
   fetch cursor
   loop while cursor not empty
         process
         fetch cursor
   end loop

I suggest they code:

   cursor = stored procedure call
   fetch cursor
   if no data found ( do something about this ERROR condition )
   loop while cursor not empty
        .....

Same effect -- you write less code, they write less code, everyone wins!

Review
      & Followup

      Rating: 4
That'll
      work  April 21, 2002
Reviewer:
      Madhusudhanan  from India
Thanks Tom. I'll try to move ahead on that

regards
Madhu

April 21, 2002 Munzer -- Thanks for the question regarding
      "oracle web application", version 8.1.7

      You Asked
Tom:

If you are designing an html application using PL/SQL.  Let us say you had to
create a list for six different people.  Each list has a different SQL query
associated with it.

1.  Would you design one page with six different cursor and then pass  a
parameter from the calling page to execute the right query based on person.
This involves several IF statements.

2. or would you create six pages each has one SQL query with no if conditions.

Do you know which would give better performance and would be easier to maintain.

Thank you,

      and we said...
Or 3)

Use DYNAMIC sql, you would  either retrieve the query from a lookup table or
construct the query on the fly based on the parameters.

That is what I do.
Review & Followup

      Rating: 5
oracle web
      application  April 21, 2002
Reviewer:  Munz
       from Reston, USA
Tom:

Do you mean:

1.   Create a temp table (emp_code,query) and then based on person select query
and then executing that using DBMS_SQL.

2.  Building it dynamically.  Is it also using dbms_Sql.  have you provided
examples before for this.

THank you,

          Followup:

1) that is one method.  There is also native dynamic sql.

2) have you searched for dbms_sql?  lots of examples.

April 22, 2002 Bhavesh -- Thanks for the question regarding
      "Query database records and display in HTML Form", version 1.0.2.0.0

      You Asked
Hi Tom,
    Hope you must be doing fine. I am having some problem in querying records in
html form using PLSQL Server pages.
In my application form I have four fields.
1. Text Field
2. Drop down list (fetched from database)
3. Drop down list (having fixed values).
4. Multiple selection drop down list.

I have been able to insert records into tables using this form but the problem
is when I query this form as a html I can get the text field value by using
To get the user name (for item 1 above) I use
    htp.tableRowOpen;
    htp.tableData( '<FONT size="2">' ||
        'Username ' ||
        htf.img( '/img/required.gif', calign => 'texttop', calt => 'required',
cattributes => ' width="5" height="5" /') ||
        '</FONT>', calign => 'right', cattributes => ' width="150"');
    htp.tableData( '&nbsp;', cattributes => ' width="10"');
    htp.tableData( '<INPUT name="p_user_id" size="15" value='||p_user_id||'>' ||
        htf.italic( '<FONT size="2">' ||
        'between 6 and 15 characters (no spaces)' ||
        '</FONT>'));
    htp.tableRowClose;

Here p_userid is fetched from database and the value is assigned.

For item No. 2 what I am doing is

        dbms_application_info.set_client_info(p_comp_id);
    htp.tableRowOpen;
    htp.tableData( '<FONT size="2">' ||
        'Company &nbsp;&nbsp;&nbsp;&nbsp;' ||
        '</FONT>', calign => 'right', cattributes => ' width="150"');
    htp.tableData( '<FONT size="2">' ||
        '&nbsp;' ||
        '</FONT>', cattributes => ' width="10"');
         htp.p('<td>');
         owa_util.listprint ('select comp_id, comp_name,
             decode(comp_id, USERENV(''client_info''), ''Y'', null) from
compmast
             order by comp_name', 'p_comp_id', 1, FALSE);
         htp.p('</td>');
    htp.tableRowClose;
and I am getting the company id.

The problem is to display correct values for item no. 3 and 4.
For 3 what I have is

    dbms_application_info.set_client_info(p_type);
    htp.tableRowOpen;
    htp.tableData( '<FONT size="2">' ||
        'User Type&nbsp; &nbsp;&nbsp;&nbsp;' ||
        '</FONT>', calign => 'right', cattributes => ' width="150"');
    htp.tableData( '<FONT size="2">' ||
        '&nbsp;' ||
        '</FONT>', cattributes => ' width="10"');
       htp.tableData( '<FONT size="2">' ||
           htf.formSelectOpen( cname => 'p_type', nsize => '1' ) ||
           htf.formSelectOption( 'External', cselected => 'TRUE', cattributes =>
' value="E"') ||
           htf.formSelectOption( 'Internal', cattributes => ' value="I"') ||
           htf.formSelectClose,
           '</FONT>');
    htp.tableRowClose;

Now whenever I do a query it is giving be default value External as selected
even if the type is having a value Internal.

For item No. 4 I am using utl_array to store the data but again when I query it
in html form I am not sure how to display the values returned from database as
selected values.

Thanks a lot in advance.

Regards

Bhavesh

      and we said...
Ok, hopefully this small demo helps you out.  You can run it here:

http://asktom.oracle.com/pls/ask/demo_pkg.testing

The code is:

create or replace package demo_pkg
as
    type myArray is table of varchar2(2000) index by binary_integer;
    empty myArray;

    procedure testing( p_user_id in varchar2 default null,
                       p_comp_id in varchar2 default '7839',
                       p_type    in varchar2 default 'E',
                       p_other   in myArray  default empty );
end;
/

create or replace package body demo_pkg
as

procedure testing( p_user_id in varchar2 default null,
                   p_comp_id in varchar2 default '7839',
                   p_type    in varchar2 default 'E',
                   p_other   in myArray  default empty )
as
    l_dummy long;
begin

    htp.p( 'inputs passed in are:<br>' );
    htp.p( 'user_id = ' || p_user_id || '<br>' );
    htp.p( 'type = ' || p_type || '<br>' );
    for i in 1 .. p_other.count
    loop
        htp.p( 'p_other(' || i || ') = ' || p_other(i) || '<br>' );
        l_dummy := l_dummy || '/' || p_other(i) || '/';
    end loop;
    htp.hr;

    htp.p( '<form action=demo_pkg.testing method=post>' );
    htp.p( '<table>' );
    htp.tableRowOpen;
    htp.tableData( '<FONT size="2">' ||
        'Username ' ||
        htf.img( '/img/required.gif', calign => 'texttop', calt => 'required',
                  cattributes => ' width="5" height="5" /') ||
        '</FONT>', calign => 'right', cattributes => ' width="150"');
    htp.tableData( '&nbsp;', cattributes => ' width="10"');
    htp.tableData( '<INPUT name="p_user_id" size="15" value='||p_user_id||'>' ||
        htf.italic( '<FONT size="2">' ||
        'between 6 and 15 characters (no spaces)' ||
        '</FONT>'));
    htp.tableRowClose;

    dbms_application_info.set_client_info(p_comp_id);
    htp.tableRowOpen;
    htp.tableData( '<FONT size="2">' ||
        'Company &nbsp;&nbsp;&nbsp;&nbsp;' ||
        '</FONT>', calign => 'right', cattributes => ' width="150"');
    htp.tableData( '<FONT size="2">' ||
        '&nbsp;' ||
        '</FONT>', cattributes => ' width="10"');
         htp.p('<td>');
         owa_util.listprint ('select empno, ename,
             decode(empno, USERENV(''client_info''), ''Y'', null) from
              scott.emp
             order by ename', 'p_comp_id', 1, FALSE);
         htp.p('</td>');
    htp.tableRowClose;

    dbms_application_info.set_client_info(p_type);
    htp.tableRowOpen;
    htp.tableData( '<FONT size="2">' ||
        'User Type&nbsp; &nbsp;&nbsp;&nbsp;' ||
        '</FONT>', calign => 'right', cattributes => ' width="150"');
    htp.tableData( '<FONT size="2">' ||
        '&nbsp;' ||
        '</FONT>', cattributes => ' width="10"');
       htp.p( '<FONT size="2"><td>' );
         owa_util.listprint ('select ''E'', ''External'',
             decode( USERENV(''client_info''), ''E'', ''TRUE'', null) from dual
             union all
                              select ''I'', ''Internal'',
             decode( USERENV(''client_info''), ''I'', ''TRUE'', null) from
dual',
                              'p_type', 1, FALSE);
    htp.p( '</td></FONT>');
    htp.tableRowClose;

    htp.tableRowOpen;
    htp.tableData( '<FONT size="2">' ||
        'Multi-select list&nbsp;&nbsp;&nbsp;&nbsp;' ||
        '</FONT>', calign => 'right', cattributes => ' width="150"');
    htp.tableData( '<FONT size="2">' ||
        '&nbsp;' ||
        '</FONT>', cattributes => ' width="10"');
         htp.p('<td>');
         owa_util.listprint(
           owa_util.bind_Variables( 'select user_id, username,
                    decode( sign(instr(:x, ''/''||user_id||''/'')), 1, 1, null )
                                      from all_users', ':x', l_dummy ),
              'p_other', 5, TRUE);
         htp.p('</td>');
    htp.tableRowClose;

    htp.p( '</table>' );

    htp.p( '<input type=submit>' );
    htp.p( '</form>' );
end;

end;
/

it shows how to

o collect
o pass
o and use

each of the types of fields you are interested in.
Review &
      Followup

      Rating: 5
Thanks a
      million  April 10, 2002
Reviewer:  Bhavesh
       from Singapore
As usual, nothing is impossible at ask Tom. I am short of words to express my
sincere thanks for providing this solution. When I asked this question in
metalink, the status has been Work in Progress for past one week and here I got
the answer in hours. Thanks once again. Tom, You are just GREAT!!!

      Rating: 5
Can you show
      how to cater for Radio Button ?  April 18, 2002

Reviewer:  Bhavesh  from Singapore
Hi Tom,
    Excellent stuff, but can you please tell us how can we make use of Radio
buttons also. Thanks in advance.
Best Regards

Bhavesh

          Followup:

Radio buttons are no different really then a text field.

Just do them and you'll see.  They are no different then input type=text in this
case.

      Rating: 5
Thanks again
      but...  April 19, 2002
Reviewer:  Bhavesh
       from Singapore
Hi Tom,
    Thanks for the followup. But I am still not sure how to set the
cselected="TRUE" value after fetching the record from database. Say for example
I have the radio button having three values viz
Manager Developer User
Now how can i set the cselected attributes over here . Following is the part of
code for your kind perusal.

dbms_application_info.set_client_info(p_usr_role);
htp.tableRowOpen;
htp.tableData( '<FONT size="2">' ||
    'Role &nbsp;&nbsp;&nbsp;&nbsp;' ||
    '</FONT>', calign => 'right', cattributes => ' width="150"');
htp.tableData( '<FONT size="2">' ||
    '&nbsp;' ||
    '</FONT>', cattributes => ' width="10"');
htp.tableData( htf.formRadio( cname => 'P_USR_ROLE', cvalue => 'MANAGER',
cchecked => 'TRUE') ||
    'Manager ' ||
    htf.formRadio( cname => 'P_USR_ROLE', cvalue => 'DEVELOPER') ||
    'Developer' ||
    htf.formRadio( cname => 'P_USR_ROLE', cvalue => 'USER') ||
    'User');
htp.tableRowClose;

Thanks once again.
Best Regards

Bhavesh

          Followup:

htf.formRadio( cname => 'p_usr_role',
               cvalue => 'MANAGER',
               cchecked => owa_util.ite( p_usr_role = 'MANAGER', 'TRUE', NULL )
             ) ||htf.formRadio( cname => 'p_usr_role',
               cvalue => 'DEVELOPER',
               cchecked => owa_util.ite( p_usr_role = 'DEVELOPER', 'TRUE', NULL
)
             ) ||.....

owa_util.ite is a nifty little "boolean" evaluator.  You want TRUE when the
usr_role = some value, NULL otherwise.  ite does that for you (ite =
IF/THEN/ELSE)

      Rating: 5
Thanks once
      Again  April 21, 2002
Reviewer:  Bhavesh
       from Singapore
Hi Tom,
   I am short of words to express my thanks for such a wonderful and quick help.

In fact the document what I have for PL/SQL Web Toolkit reference does not have
this owa_util.ite function. Can you please provide me some link where by I can
find the new added owa_util/owa_Sec etc functions.
Best Regards

Bhavesh

          Followup:

describe the package.

Check out the package spec (select text from all_source where name = 'OWA_UTIL'
and type = 'PACKAGE' )

April 22, 2002 S.
      -- Thanks for the question regarding "Archive logs", version 8.1.7

      You Asked
Hello Tom,
Please verify me , if I am wrong.
Consider the following scenario :
I have Database server with archive log mode.
I perfomed Cold back up on 10th April in Tape.
Then No modification in Database i.e. no table created, no table modification.
I also Backed up  Archive log files  and control files every day in tape but no
cold back up because I backed up
on 10th April.
On 17th April Hard disk was crashed.
I installed Oracle again.
Restore the datafiles( April 10) from Tape.
I have archive log files till 16th April. There was no transaction on 17th
April.
will I get data till 16th April from archive log files?
Will database collect data from archive logs when I will recover Database?
What type of recover should I perform?

Please clear me in details.

Thank you, Tom.

With regards,

      and we said...
You will perform a complete recovery (thats what type of recovery)

You will get all of the data you have archives for.

You do not say what disk crashed, what files where lost.  If you lost the
current redo log files -- you may have lost some transactions from them (in
which case you'll be doing a point in time or cancel based recovery).

If you lost all of the current control files, you will have to perform some
operations specially as well.

Read:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76993/toc.htm
In your case, chapter 6 especially - find the case that mirrors your situation.

I'm always amazed that people wait until it actually happens to try and figure
out what to do about it (recovery that is).  Amazed.

Wish you luck.  Hopefully you have been doing backups correctly.  Now is the
worst time in the world to figure out that you missed some file(s)

Review &
      Followup

      Rating: 3
Hard Disk
      Crash  April 21, 2002
Reviewer:  S. Rahman
       from Bangladesh
My hard disk was totally crashed. Everything of hard disk was erased.
My question is :
If I have all datafiles of 2/3 days back but I have latest control files, latest
archive logs , would I be able to recover till latest Data?
Is it not necessary to have latest Datafiles for recover?
But I need latest control files and archive logs.
Am I right?

With regards,

          Followup:

You would be missing the contents of the unarchived ONLINE REDO log file(s).

You would be able to recover upto the point in time of the last redo log file
you have, yes.

You do not *need* the latest control file, we can work around that (read the
chapter I sent you to, one of the sections just happens to be entitled "
Recovering After the Loss of Control Files").

You need the archives definitely.  That is the source of your transactions.  You
need a backup of the datafiles and all archives that happened during/since the
backup.

April 22, 2002 sreepad -- Thanks for the question regarding
      "general", version 8.1.6

      You Asked
hi tom,

u have asked me not to ask more than 2/3 questions
before.
but what to do i don't find u frequently. if u still
feel u would answer only
three questions.no probs.. i don't mind asking again
when i find u. Its my fervent
hope i am not misquoted. i really appreciate u...

1. I have read the following in the oracle mannual
"Some operations, such as MOVE PARTITION and DROP
TABLE PARTITION,
mark all partitions of a global index unusable. You
can rebuild the
entire index by rebuilding each partition individually
using the ALTER
INDEX REBUILD PARTITION statement. You can perform
these rebuilds concurrently.
You can also simply drop the index and re-create it. "
what is global index? is it the common index for all
partitions?

2.In ur previous solutions u created a table
partitioned with timestamp and id as
columns. you were exchanging partitions from
partitioned table to fy_1999 table.
how to deal if the table has primary keys,unique keys,
some check constraints and
triggers?
As soon as the database starts i want to check the
month and year if the month has
changed i want to run a script . how to do that? can v
do that way bcoz we cant rely
on system date? what is the best method ?

3.In sql plus to stop long running query we give
ctrl+c what is the alternative of
sql*plus worksheet.

4. The version 7.3 mean that database version is 7 and
pl/sql is 2.3!
   what does 8.1.7 mean?

5. i want to run some procedures after database starts
and before database
shutsdown.    how to do that.

thanx
sree

      and we said...
1) see

http://technet.oracle.com/doc/oracle8i_816/server.816/a76965/c09parti.htm#430195
for an overview of local and global indexes.

A local index is an index that is equi-partitioned with the table.  That is, all
of the index entries in a given partition point to row entries in a SINGLE table
partition.  There is a 1-1 relationship between index partitions and table
partitions.

A global index is an index that is partitioned DIFFERENTLY then the table.
There may be more, less or the same number of partitions in the index as are in
the table.  Most importantly, the index entries in a single index partition
might point to rows in any and all partitions (hence any operation that affects
a given partition might affect ANY and ALL global index partitions -- hence, if
you drop or split a table partitions -- ALL global index partitions are
affected.  With a local index -- only the single local index partition is
affected -- not the entire index)

My book has a pretty good explaination of this as well -- as well as when you
might use a local vs global and vice versa.

2) If the table has primary keys, and you are doing the rolling (sliding) window
I think you are referring to -- you must have the primary key as part of the
partition key (in order to be able to locally partition the data).  Hence, the
data only needed be unique within the table partition itself.  Therefore, when
you create the table to exchange with the partition -- you'll just have a
primary key on that table.

Same with constraints -- you'll just have a constraint on the table you are
going to exchange with.

triggers -- they are on the table itself, when you switch the partition with the
table, the table that is now a partition will get the trigger from the table
itself.  You do not need to concern yourself about the trigger.

There is an ON STARTUP trigger you can use.  You won't "run a script", you'll
run a procedure (better then a script).

3) use sqlplus ;)   sqlplus worksheet doesn't support the ctl-c because it is
just a "gui" layer on top of sqlplus.

4) Starting with 8.0 we aligned all version numbers.  In 7.3 we had plsql 2.3,
net 2.3, and so on.  In 8.0 we had plsql 8.0, net8.0 and so on.

Oracle 8i release 3 is version 8.1.7.  8.1.7 means you have Oracle8i release 3.
(release 2 was 816, release 1 was 815)

5) use the startup/shutdown triggers.  see

http://technet.oracle.com/doc/oracle8i_816/appdev.816/a76939/adg14evt.htm#998000
....
Review
      & Followup

      Rating: 4
Unique Index
      and Unique Constraint  April 22, 2002
Reviewer:
      Senthil Kumar Srinivasan  from Chennai, India
Hi Tom,
What is the Relationship between Unique Index and Unique Constraint? How does
creating an UNIQUE INDEX on a column(having no UNIQUE Constraint) of a Table
help in Data Manipulation?

Thanks in Advance,

          Followup:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3051352977204
addresses that.

April 22, 2002 --
      Thanks for the question regarding "Rename privilege", version 8.1.7

      You Asked
Hi,
We need to grant privileges to a user so that the user can rename objects in
others schema. How can that be achieved?

Thanks

      and we said...
A documented preq for this is:

<quote>
Prerequisites
The object must be in your own schema.</quote>

So, how to do this?  A stored procedure in the users schema can be used to
convey this "privelege" as follows:

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop user a cascade;

User dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> grant create session to a identified by a;

Grant succeeded.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> @connect scott/tiger
ops$tkyte@ORA817DEV.US.ORACLE.COM> set termout off
scott@ORA817DEV.US.ORACLE.COM> REM GET afiedt.buf NOLIST
scott@ORA817DEV.US.ORACLE.COM> set termout on
scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> create or replace procedure rename_obj(
p_old_name in varchar2, p_new_name in varchar2 )
  2  is
  3  begin
  4          execute immediate 'rename ' || p_old_name || ' to ' || p_new_name;
  5  end;
  6  /

Procedure created.

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> grant execute on rename_obj to a;

Grant succeeded.

scott@ORA817DEV.US.ORACLE.COM> grant select on dept to a;

Grant succeeded.

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> @connect a/a
scott@ORA817DEV.US.ORACLE.COM> set termout off
a@ORA817DEV.US.ORACLE.COM> REM GET afiedt.buf NOLIST
a@ORA817DEV.US.ORACLE.COM> set termout on
a@ORA817DEV.US.ORACLE.COM>
a@ORA817DEV.US.ORACLE.COM> set linesize 70
a@ORA817DEV.US.ORACLE.COM> desc scott.dept
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 DEPTNO                                       NUMBER(2)
 DNAME                                        VARCHAR2(14)
 LOC                                          VARCHAR2(13)

a@ORA817DEV.US.ORACLE.COM> desc scott.testing_dept
ERROR:
ORA-04043: object scott.testing_dept does not exist

a@ORA817DEV.US.ORACLE.COM> exec scott.rename_obj( 'DEPT', 'TESTING_DEPT' );

PL/SQL procedure successfully completed.

a@ORA817DEV.US.ORACLE.COM> desc scott.dept
ERROR:
ORA-04043: object scott.dept does not exist

a@ORA817DEV.US.ORACLE.COM> desc scott.testing_dept
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 DEPTNO                                       NUMBER(2)
 DNAME                                        VARCHAR2(14)
 LOC                                          VARCHAR2(13)

a@ORA817DEV.US.ORACLE.COM> exec scott.rename_obj( 'TESTING_DEPT', 'DEPT' );

PL/SQL procedure successfully completed.

a@ORA817DEV.US.ORACLE.COM> desc scott.dept
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 DEPTNO                                       NUMBER(2)
 DNAME                                        VARCHAR2(14)
 LOC                                          VARCHAR2(13)

a@ORA817DEV.US.ORACLE.COM> desc scott.testing_dept
ERROR:
ORA-04043: object scott.testing_dept does not exist

a@ORA817DEV.US.ORACLE.COM>

April 22, 2002 Pratap -- Thanks for the question regarding "SQL
      Query", version 8.1.6

      You Asked
Hi Tom

I have the following table -

travel
    source char(1)
    destination char(1)
    distance number

Data in the table is like this -

source destination distance
A      Z           3
A      B           3
B      C           3
C      Z           3
A      F           3

I want the maximum distance between A to Z following any
of the routes.

Like From A => B is 3
A => B => C => Z is 9

Can this be done using a single SQL statement.

SQL's

create table travel
(
    source        char(1),
    destination    char(1),
    distance    number
);
insert into travel values ( 'A', 'Z', 3 );
insert into travel values ( 'A', 'B', 3 );
insert into travel values ( 'B', 'C', 3 );
insert into travel values ( 'C', 'Z', 3 );
insert into travel values ( 'A', 'F', 3 );

Thanks and regards

Pratap

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> select *
  2    from (
  3  select source,
  4         to_number(substr( info, 1, 7 )) distance,
  5         to_number(substr( info, 8, 6 )) number_of_hops,
  6         substr( info, 14 ) destination
  7    from ( select source,
  8                  (select to_char( sum(distance), 'fm0000009' ) || max(
to_char(level,'fm000009') || destination )
  9                     from travel t2
 10                    start with t2.rowid = t1.rowid
 11                  connect by prior destination = source) info
 12             from travel t1
 13            where source = 'A' )
 14   where substr(info,14) = 'Z'
 15   order by 2 DESC nulls last
 16             )
 17   where rownum = 1
 18  /

S   DISTANCE NUMBER_OF_HOPS DES
- ---------- -------------- ---
A          9              3 Z

will do it.  bit of a trick, but it should work ;)

Review &
      Followup

      Rating: 5
"SQL Query",
      version 8.1.6  April 22, 2002
Reviewer:  Ganesh
      Sundaresan  from Cincinnati, OH
Simply superb..!!

      Rating:
      5
  April 22, 2002
Reviewer:
      A reader
Awesome... It is just amazing how much you can do in a single SQL statement.
Thanks Tom for displaying once again the power of Oracle.

      Rating:
      5
Thanks!  April 22, 2002
Reviewer:
      Pratap Deshmukh  from India
Thanks Tom!

April 22, 2002 Art -- Thanks for the question regarding "How to
      GROUP BY around NULLs", version 8.1.7

      You Asked
Hello, Tom.

If I have a table t

  a       b       c
-----   -----   -----
  XYZ      30      20
  XYZ      30      11
  ABC      22      40
  ABC              50
  DEF              72
  DEF              27

then how can I do a GROUP BY such that the output is

  a       b     SUM(c)
-----   -----   ------
  XYZ      30       31
  ABC      22       90
  DEF               99

instead of

  a       b     SUM(c)
-----   -----   ------
  XYZ      30       31
  ABC      22       40
  ABC               50
  DEF               99

?

Thank you.

      and we said...
select a, max(b), sum(c) from t group by a;

but beware, if you have:

 a       b       c
-----   -----   -----
  XYZ      30      20
  XYZ      30      11
  ABC      22      40
  ABC      33      40
  ABC              50
  DEF              72
  DEF              27

it'll fold the values for B when a = abc -- giving you 33 as the value for B.
If that is not what you want - don't know what to tell you.
April 22, 2002 Carlos -- Thanks for the question regarding "I
      have an error PLS-00455: cursor 'V_FILA' cannot be used in dynamic SQL
      OPEN"", version 8.1.7

      You Asked
Hello TOM:

->I have a package that content something like that:

PACKAGE "P_MONITOR_COTIZACION"    IS
   type rec_list_cotizacion  is record
    (
        v01 cotizacion_enca.icod_emp%type,
        v02 cotizacion_enca.ncod_cotenc%type,
        v03 cotizacion_enca.ccod_cli%type,
        v04 cotizacion_enca.ianno_per%type,
        v05 cotizacion_enca.imes_per%type,
        v06 cotizacion_enca.ncod_cotenc%type,
        v07 cotizacion_enca.ncod_cotenc%type,
        v08 cotizacion_enca.ncod_cotenc%type,
        v19 cotizacion_enca.nimp_tot_n_cotenc%type,
        v10 cotizacion_enca.nimp_e_tot_cotenc%type,
        v11 cotizacion_enca.ffech_entr_cotenc%type,
        v12 cotizacion_enca.ffech_fin_validez_cotenc%type,
        v13 cotizacion_enca.ccod_situacion%type
    );

   type cur_list_cotizacion is ref cursor return rec_list_cotizacion;
end;

->Then, I have a store procedure like that:

Procedure SP_PRUEBA
(parm_query         in varchar,
 v_fila             in out p_monitor_cotizacion.cur_list_cotizacion
)
is
Begin

Open v_fila for parm_query;

Exception
  WHEN NO_DATA_FOUND THEN
    CLOSE v_fila;
  WHEN OTHERS THEN
    RETURN;
end;

->Then I have an error like that:
"(1):PLS-00455: cursor 'V_FILA' cannot be used in dynamic SQL OPEN"

->what could i do? I want to return thought that store procedure, that type of
cursor, that content that record....

->Please is there a way to do that?, because I need to return a record from a
store procedure, using a varchar as a query...

Thanks...

      and we said...
change
 type cur_list_cotizacion is ref cursor return rec_list_cotizacion;

into:

 type cur_list_cotizacion is ref cursor;

and you are done.  It will work then.

You cannot dynamically open a strongly typed ref cursor as you have.  The
compiler cannot do the type checking since the query is dynamic.

Review &
      Followup

      Rating: 5
Thank you
      !!!  April 22, 2002
Reviewer:  Carlos
       from Peru
If I change
 type cur_list_cotizacion is ref cursor return rec_list_cotizacion;

into:

 type cur_list_cotizacion is ref cursor;

-->It will work, but if I need to return that record, is there any way to change
it?:

 type cur_list_cotizacion is ref cursor;

-->to

 type cur_list_cotizacion is ref cursor return rec_list_cotizacion;

-->After open the cursor, something like that:

type v_fila_r is ref cursor;
v_fila_1 v_fila_r;
begin
Open v_fila_1 for parm_query;

v_fila:=v_fila_1 --This is my objective.
end;

Thanks anyway.

          Followup:

No, if you want dynamic sql, you will be using a weak ref cursor.

April 22, 2002 Gururaj -- Thanks for the question regarding "How
      to delete duplicate row from table and retain one row? ", version

      You Asked
Could you tell me,

How to  delete duplicate row
from table and retain one row?
I have no primary key for the table.

I am using 8 and 8i.

Lastname    firstname  telphonenumber
-------------------------------------
scott       tiger      641-098-2222
scott       tiger      641-098-2222
scott       tiger      641-098-2222
Demo        Test       222-222-3333

Now I should be able to delete two scotts and retain one scott.

Hope this is clear enough to solve the
problem.

Thanks in advance
Gururaj

      and we said...
ops$tkyte@8i> select * from t
  2  /

LASTNAME                  FIRSTNAME
TELEPHONENUMBER
------------------------- -------------------------
-------------------------
scott                     tiger                     641-098-2222
scott                     tiger                     641-098-2222
scott                     tiger                     641-098-2222
Demo                      Test                      222-222-3333

ops$tkyte@8i>
ops$tkyte@8i> delete from T t1
  2   where rowid <> ( select min(rowid)
  3                      from T t2
  4                     where t1.lastname = t2.lastname
  5                       and t1.firstname = t2.firstname
  6                       and t1.telephonenumber =
t2.telephonenumber )
  7  /

2 rows deleted.

ops$tkyte@8i> select * from t
  2  /

LASTNAME                  FIRSTNAME
TELEPHONENUMBER
------------------------- -------------------------
-------------------------
scott                     tiger                     641-098-2222
Demo                      Test                      222-222-3333

best to have an index on at least some of the columns.  Beware
of NULLS.  Each NULL will be considered UNIQUE unless you change
the predicate to be:

where ( t1.lastname = t2.lastname OR ( t1.lastname is null and
t2.lastname is null ) )
  and ...

but that will preclude the use of an index on the columns you do
this to (it'll be slow)

Review &
      Followup

      Rating: 3
How to avoid
      rowids?  April 22, 2002
Reviewer:  Sergey
       from Russia
This, surely, works fine when we use the uniqueness of rowids. But haw can we do
this without rowids, rownums, procedures, triggers and any data modifications.
In other words - with just one delete statement? Is that possible at all?

          Followup:

You need something to save "a row".

You need some criteria to pick the row you DON'T want to delete.

So no, without rowid or something like it -- you cannot do this.

      Rating: 5
How do you
      insert data  April 22, 2002
Reviewer:  Famy

You get so many questions per day, Iam very sure you have automated scripts to
create test tables and insert data into them.

How do you do it. For example int he above example, did you manually insert
data.

Or did you use a routine to insert data into the 3 columns.

Thank you

          Followup:

I cut and pasted their inputs -- they gave it to me, i didn't make it up.

That would be an amazing program that could generate the data exactly as they
gave it to me ;)

I'm *really* good with vi.  People freak out when I pull up vi on my laptop
running under windows in character mode -- but I haven't seen a gui editor that
lets me go faster yet.

      Rating:
      5
  April 23, 2002
Reviewer:  Claudio
       from Argentina

      Rating: 5
VI ROCKS..
      nothing can beat VI :) well so does TOM KYTES.. no one can beat u tom
      :)  July 18, 2003
Reviewer:  DD  from
      MN, USA

April 22, 2002 Saibabu -- Thanks for the question regarding
      "Correlation between MAXLOGHISTORY & CONTROL_FILE_RECORD_KEEP_TIME",
      version 8.1.7

      You Asked
Hi,Iam really confused after going through numureous documents on these
parameters.I have some doubts to be clarified :

1)What is significance of MAXLOGHISTORY parameter in non-OPS environments.
2)If Control_file_record_keep_time=0,then upto what point recovery can go back
under RMAN backups and disk hot backups.
3)In one of our 8.1.7.1 Databases,MAXLOGHISTORY is at 1405.
count from v$loghist=1405
count from v$archived_log=1393
value from v$controlfile_record_section=1393 for Archived Logs.
My assumption is counts between v$loghist and v$archived_log should match.What
are the reasons for it.
control_file_record_keep_time=7
select min(completion_time) from v$archived_log=22-AUg-2001.(?????)
If control_file_record_keep_time says to keep records for 7 days,why Iam seeing
the records from 22-AUG-2001.Please explain.

4)In another 8.1.6 databases,
MAXLOGHISTORY=4991
control_file_record_keep_time=7
count from v$archived_log=8783
count from v$loghist=4991
value from v$controlfile_record_section=8783 for Archived Logs.
If MAXLOGHISTory will increase dynamically when
control_file_record_keep_time<>0,then the above counts should match.
I don't understand how MAXLOGHISTORY is less than count from v$archived_log.
In this database,select min(completion_time) from
v$archived_log=28-SEP-2001,which is expected.

5) What can be done if ORA-19571 error comes up while backing up archived logs
using RMAN.How can I backup those logs which are missing from control file.

You help is very much aprreciated.
Thanks
Sai.

      and we said...
1) well -- the documentation seems clear on this to me:

<quote>
Note: This parameter is useful only if you are using Oracle with the Parallel
Server option in parallel mode, and archivelog mode enabled.
</quote>

so, it would be saying that is has not significance outside of OPS/RAC.  It
controls the number of log history records available on v$log_history.

2) well, it depends.  RMAN usually uses a recovery catalog, not the control
file.  If you are using RMAN with just the control file and no recovery catalog,
it'll totally depend on how fast you cycle through and reuse reusable records in
the control file.  In any case -- you can always recover manually regardless
(you do not lose the ability to recover, we just might not be able to supply you
the filenames)

3 & 4) they shouldn't be the same.  loghist (log_history is the prefered view)
is controled by the maxloghistory.  v$archived_log is controlled by the
control_file_record_keep_time -- those records are among the circularly reusable
records.  We'll dynamically expand the control file to hold 7 days worth of
archive log information for you (if you have it set to 7).  On the 8th day, we
just overwrite the old records and reuse their slots.

So, one is retained by a physical count (maxloghistory) and shows the history of
the logs.

The other is retained by a number of days -- keep the history for the last N
days -- and will cause the control file to grow as needed.

As for why you are seeing really old records in v$archived log -- you must have
had a 7 day period in which you generated LOTS of archives, more then you did in
the recent past.  That caused this section to grow larger to hold it.  We won't
shrink it back -- hence you have more space then you need to hold your current 7
days worth of information.  The 7 is a MINIMUM retention time.  It does not mean
we purge records older then 7 days.

5) set your control_file_record_keep_time to be at least one day GREATER then
the period of time between your backups, else there is a chance that an archive
record gets aged out during the backup which thoroughly confuses the situation.
Review &
      Followup

      Rating:
      4
Reader  April 16, 2002
Reviewer:
      A reader
Tom,
Could you elaborate on this:
"
In any case -- you can always recover manually regardless
(you do not lose the ability to recover, we just might not be able to supply you

the filenames)
"

It implies as though, you can recover the database, even if you lose the
recovery catalog, and the target database, as long as you know the names of the
data sets that have
backed up using RMAN.

Thanks

          Followup:

yes.

bits are bits and bytes are bytes after all.  if you can access the backups....

      Rating:
      5
Reader  April 16, 2002
Reviewer:
      A reader
Tom,

I am still a bit unclear

The backup sets are in RMAN special format. Therefore we
have to use RMAN command to either *restore* and recover
OR *recover*. The command line syntax does not specify
the location of backup set or name of backup set. How
to supply the information to RMAN command

Thanks

          Followup:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76990/rmanreco.htm#441496
you'll get a control file out and then you can use the control file with rman to
get the rest.

      Rating:
      4
Reader  April 17, 2002
Reviewer:
      A reader
Tom,

This is an absolute extremely useful Recovery scenario.

If we can know the process involved in accomplishing this,
we can test this scenario before bringing a Production
database go alive. Could you outline in a high level,
steps involved to perform this.

Thanks

      Rating:
      4
  April 22, 2002
Reviewer:
      A reader
Tom,

"

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76990/rmanreco.htm#441496
"

Could you give us the Title of the article. The URL only
gets to the main login frame

Thanks

          Followup:

That link -- if you wait for the page to load -- will take you to:

....
Restoring the Control File from a Backup Set Without Using RMAN

You must use a non-standard procedure to restore a control file from an RMAN
backup set in the following situations:

    * You are using a pre-8.0.5 version of RMAN to restore a database when more
than one database with the same name is registered in the recovery catalog (see
"Restoring When Multiple Databases Share the Same Name" for a discussion of this
problem).

    * You are not using a recovery catalog, and your only control file backup is
in an RMAN backup set.
............

you are the second * there.

      Rating:
      5
Reader  May 02, 2002
Reviewer:
      A reader
Tom,

I was able to try out this scenario successfully

One caveat though:
If you backup using RMAN (ex. backup database ), the
controlfile is backed up with system datafile (default).
The control file from this backup set, when restored, is
unaware of the names of the backup sets

Probable solutions:
1. Subsequent to RMAN backup of database and archivelogs,
   backup the control file in its own backup piece

2. Use the logfile from the RMAN to determine the names
   of the constituent file names of backup set.
   Use the PROCEDURES "restoredatafileto" and
   "restoreArchivedlog" to restore datafiles and
   archive logs

In either case, recover is conventional recover
SQL>recover database using backup controlfile until ....
SQL>alter database open resetlogs;

Your comments please

Thanks

      Rating: 5
Review my
      script....  February 17, 2003
Reviewer:
      Sikandar Hayat Awan  from Pindi Bhattian - Pakistan

I am using the following scripts to take full backup with RMAN on Oracle 8.1.7
(Win 2000). Please suggest me if I am doing any thing wrong or any improvement?

***I am using control file instead of catalog.***

--------RMAN_Backup.bat

--delete the previous backup files as it is moved to dat.
!del F:\HotBackup\*.* /q

rman target internal/oracle nocatalog @d:\cglmis\sikandar\rman\full_back.txt
log=d:\cglmis\sikandar\rman\of_back.log

sqlplus internal/oracle @d:\cglmis\sikandar\rman\ctrlfile.sql

net send sikandar 'ORAPT backup Completed......'

echo on

Exit

--------Full_backup.txt
run {
     allocate channel c1 type disk;
     backup database format 'f:\hotbackup\oraptdb_%d_%t_%u.bus';
}

------ctrlfile.sql
alter system archive log current;

alter database backup controlfile to trace;

alter database backup controlfile to 'f:\hotbackup\ctrl_OF.ora';

Exit;

+++++++
I am taking full backup on daily basis please also suggest me that
incremental/commulative will be the good option as the data is increasing. Any
drawback of commulative backups at the time of restore (time is not my
consideration at the moment) as I want to make sure 100% recovery of data.

For reference I am using online manuals and Oracle RMAN Pocket Reference by Darl
Kuhn (O'REILLY).

April 22, 2002 Pranav -- Thanks for the question regarding
      "Check if a file exists on the server", version 8.1.7, 9

      You Asked
I am looking for a way to check if a file exists on the server.  I thought of
using utl_file.  But there doesn't seem to be a function that just checks if the
file exists.  So I thought of opening the file in read mode and check for
exeception like Invalid_Path and No_Data_Found.
  Is there any better or easier way of knowing if a file exists??

      and we said...
You can use BFILES and DBMS_LOB.FILEEXISTS to do that.
Review &
      Followup

      Rating: 5
Thanks a
      lot  April 22, 2002
Reviewer:  Pranav Shah
       from CT USA
Just to add to the question what if you had to check for multiple files.  Is
there anything that will allow me to put all the filenames or should I just
repeat DBMS_LOB.FILEEXISTS for each.

          Followup:

Use fileexists for each.

      Rating: 4
File
      exists...woohoo  February 04, 2003
Reviewer:
      Will Cook  from Dripping Springs, Texas
I needed to check for the existence of a graphics file which was referenced in
the database.  The UTL_FILE open of course bombed on me.  The bfiles and
dbms_lob.fileexists saved me.  Thanks a lot!

      Rating: 2
Maybe not
      woohoo  February 04, 2003
Reviewer:  Will
      Cook  from Dripping Springs, Texas
The simple solution doesn't really seem to work.  I have sent a detailed
question to Tom and when I receive his reply and test it I will update this
questions page.


Subject | Article | Last Updated (East coast time)
recompiling a package | article only | Apr 23, 2002 07:39 am
Disable Default Sorting | article only | Apr 23, 2002 08:01 am
Reorganisation | article only | Apr 23, 2002 08:05 am
performance with index | article only | Apr 23, 2002 08:08 am
Unwanted Reparsing | article only | Apr 23, 2002 08:30 am
Reading Data in Clobs that are > 32767 K | article only | Apr 23, 2002 11:01 am
Cleaning TEMP segments | article only | Apr 23, 2002 01:19 pm
Maximum CPU which caused the slow processes on the whole box. | article only | Apr 23, 2002 01:43 pm
security, package in web application | article only | Apr 23, 2002 01:52 pm
Migrating from 8.1.7 to 9i | article only | Apr 23, 2002 02:54 pm
Oracle Application Server | article only | Apr 23, 2002 03:10 pm
Open Cursor Issue | article only | Apr 23, 2002 04:19 pm
Row Pointer in FETCH | article only | Apr 23, 2002 04:23 pm
Query benchmarking and create table question | article only | Apr 23, 2002 08:20 pm
8.1.7 vs 8.1.6 | article only | Apr 23, 2002 08:26 pm
OPTIMIZER_INDEX_COST_ADJ under RBO for partitioned tables | article only | Apr 24, 2002 07:18 am
Index | article only | Apr 24, 2002 07:49 am
Index usage and Null Values | article only | Apr 24, 2002 08:11 am
Index usage and nulls | article only | Apr 24, 2002 08:14 am
Using SELECT statement in stored procedures -- returning result sets from stored procedures. | article only | Apr 24, 2002 04:27 pm
sql query | article only | Apr 24, 2002 04:29 pm
first_value when doing joins with multiple tables | article only | Apr 24, 2002 08:56 pm
ora-4068 | article only | Apr 25, 2002 07:12 am
Granting Select Privilege to System Views | article only | Apr 25, 2002 07:27 am
why would you trace an exception | article only | Apr 25, 2002 06:34 pm
AskTom Architecture | article only | Apr 25, 2002 06:41 pm
Finding maxextents for tablespaces | article only | Apr 25, 2002 06:42 pm
Multi Tasking in PL/SQL | article only | Apr 25, 2002 06:50 pm
Truncate changes next extent automatically | article only | Apr 26, 2002 11:19 am
SQL Query | article only | Apr 26, 2002 11:34 am
commonts on your knowledge example | article only | Apr 26, 2002 01:09 pm
Sql Query | article only | Apr 26, 2002 04:20 pm
ORACLE TO SQLSERVER | article only | Apr 27, 2002 08:32 am
Standby database using RMAN | article only | Apr 27, 2002 08:34 am
TKPROF | article only | Apr 27, 2002 08:41 am
Error in executing external procedure | article only | Apr 27, 2002 08:43 am
BLOB Updation | article only | Apr 27, 2002 08:53 am
Is there a performace penalty for very large rollback segments? | article only | Apr 27, 2002 05:30 pm
insert bulk data from java program | article only | Apr 27, 2002 07:04 pm
Is it required any patches | article only | Apr 28, 2002 09:52 am
Security Issues | article only | Apr 28, 2002 10:02 am
Improve calls of before/after STATMENT trigger with Bulk binds use | article only | Apr 28, 2002 10:04 am
Centralized Security | article only | Apr 28, 2002 10:17 am
Internationalization | article only | Apr 28, 2002 10:25 am
Ref Cursors and Parsing | article only | Apr 28, 2002 10:32 am
Indexes and Cursors | article only | Apr 28, 2002 10:40 am
CBO and views | article only | Apr 28, 2002 11:24 am
File system upload,FGAC | article only | Apr 28, 2002 03:38 pm
Ways to simulate high OLTP | article only | Apr 28, 2002 05:04 pm
Select uploaded file problem | article only | Apr 28, 2002 08:59 pm
which is the best way to export file from a bulk table | article only | Apr 29, 2002 07:26 am
Compare SQLCOPY, CREATE TABLE, INSERT, BULK COLLECT and BULK INSERT. | article only | Apr 29, 2002 07:32 am
Java Connection In Oracle. | article only | Apr 29, 2002 07:47 am
Archive table data ... | article only | Apr 29, 2002 08:17 am
I forgot passwrod in oracle 9i. | article only | Apr 29, 2002 09:53 am
COUNT FUNCTION WITH JOINS | article only | Apr 29, 2002 12:46 pm
How to convert a .fmb file into text file. | article only | Apr 29, 2002 12:47 pm
List of values in for loop | article only | Apr 29, 2002 09:21 pm
RAID | article only | Apr 29, 2002 09:32 pm
row(s) 1 - 59 of 59
April 23, 2002 parker -- Thanks for the question regarding "recompiling a package", version 8.1.7
You Asked
Hi Tom
Good Morning, how are you.
The following is my question.

1.If changes are made to procedures within a package, is it neccessary to
recompile the package.

2.When changes are made to a package , is it necessary to recompile the
application.

3.If definition of the objects accesses by a package changes , is it necessary
to recompile the package?

4.If additional variables, or global variables added to a package definition is
it necessary to recompile the package?

What are the scenarios wherein a package needs to be recompiled?

Thanks
and we said...
1) if you change the package -- yes you must recompile it otherwise we will not
see the code.  do you mean:

"if i change a package body do I have to compile the spec?"

if so, then the answer is NO, you do not compile the spec unless you change the
interface (change inputs into the plsql procedures or add a procedure that must
be exposed via the spec)

2) no

3) no, it'll recompile itself on the next execution.

4) you compile the spec only when you change the spec.  you compile the body
only when you change the body.  do not compile a spec because a body has
changed, do not compile a body because the spec has changed.

compile only when you need to.

the word "package" is ambigous.  there are specs and bodys.  a package means
"both" but coding wise you treat them separately.
Review & Followup
Rating: 4
How to access global variables from another database April 22, 2002
Reviewer: Pullaiah Y from Chicago, USA
Hi Tom,

I have some generic packages, and global variables in them. I created them in
different databases. Now, my question is, how can I access the global variable
in one database from another database.

Hope I am clear with my question.

Thanks,
Pullaiah

Followup:
scott@ORA815.US.ORACLE.COM> create synonym demo_pkg_817 for
demo_pkg@ora817dev.us.oracle.com;

Synonym created.

scott@ORA815.US.ORACLE.COM> exec dbms_output.put_line( demo_pkg_817.x );
BEGIN dbms_output.put_line( demo_pkg_817.x ); END;

*
ERROR at line 1:
ORA-06550: line 1, column 42:
PLS-00512: Implementation Restriction: 'DEMO_PKG_817.X': Cannot directly access
remote package variable or cursor
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored

You cannot -- you'll need to add "getter" and "setter" functions/procedures to
your pacakge to GET and SET these globals.

Rating: 5
Getter & setter April 23, 2002
Reviewer: G Singh from India
Getter & setter function are used in java. How do you set them here in pl/Sql
(Package).
Tom can u Please explain with an example.
Thanks
G Singh

Followup:
really -- you cannot figure this out?  sigh, it is exactly the same as in java.
You would put a "get" function in the package that simply "returns global".  You
would put a "set" procedure in the package that simply does "global :=
passed_in_value".

Rating: 2
ora-4068 April 23, 2002
Reviewer: Anthony Velarde from PHILIPPINES
on your answers on questions number:

2) no

3) no, it'll recompile itself on the next execution.

if that so, why do we have ORA-4062???

TNX
NOTNA

Followup:
and what environment are you running in.  A little more detail would be somewhat
useful.

April 23, 2002 Shefali -- Thanks for the question regarding "Disable Default Sorting", version 8.1.7
You Asked
Hi Tom,

I have the following query:

create table semp (emp_id int, emp_name varchar2(50));

insert into semp values (1, 'ABC');
insert into semp values (2, 'XYZ');
insert into semp values (3, 'LMN');
insert into semp values (4, 'PQR');

select * from semp;

    EMP_ID EMP_NAME
---------- -----------
         1 ABC
         2 XYZ
         3 LMN
         4 PQR

select emp_name from semp where emp_id in (3,1,4,2);
EMP_NAME
-----------
ABC
XYZ
LMN
PQR

The above query returns a result set based on the timestamp the records were
inserted into the table.

The result set I want is based on what is specified in th IN Clause
i.e.

EMP_NAME
---------
LMN
ABC
PQR
XYZ

How can this be achieved, Basically I need to disable the default sort method of
Oracle.

Thanks,
Shefali
and we said...
There is no such THING as a "default sort".  The data is *not* coming out in the
order of insertion.  That is a common myth -- but it is not so.

In order to get data out in a specific order -- you MUST use an order by.  Else
the data comes out in any order it wishes.

You can:

select *
  from emp
 where emp_id in ( 3, 1, 4, 2 )
 order by decode(emp_id, 3, 1, 1, 2, 4, 3, 2, 4 );
Review & Followup
Rating: 5
April 23, 2002
Reviewer: reader
excellent solution..!!!

April 23, 2002 atul -- Thanks for the question regarding "Reorganisation", version 8.0.4
You Asked
Sir,

After doing lot of searching to your site.I found that You are not in favour of
reorganisation.

Is there anything ie like performance degradation after reoraganisation..

As per as table reoraganisation ie if we have 10,000,00 records in a table in
which 5,000,00 we dont require often so we can take those rows export to tape
and import table again..

Which i consider be benificial in increasing performance..

Is that be same for indexes?

Thanks.
atul
and we said...
Yes, I have examples where performance will decrease after a reorg.  Reorgs can
and do cause performance nosedives.

Most common reason -- human error.  You "miss" something.  An index goes
missing.

Less common reason -- the index structure is a complex data structure.  Most all
indexes get into a steady state after a while.  By reorging it, you are undoing
all of the splits and moves it has done.  Now we have to redo that work again.
You sound like you really want partitioning, to age data.  That would make
things easier for you.

But yes, if you are going to remove 50% of the data, it would most likely be
faster to:

create table new_table as select <rows you want to keep>.  do this nologging and
in parallel.

rename old_table to archive_me;
rename new_table to old_table;

index old_table using nologging/parallel

export archive_me.

and avoid the delete alltogether.
April 23, 2002 -- Thanks for the question regarding "performance with index", version 8.1.7
You Asked
Hi ,tom

       Is there any way to estimate the performance increased by adding index
?(I don't want to create index before I know it's useful.Since my DB is very
huge)

     Best Regards     Daniel
and we said...
Yes, it is called benchmarking in a test environment.

Adding a single index can cause many side effects.

o query plans of existing queries will change -- some for the better (maybe)
some for the worse (maybe) some for no change at all (maybe)

o modifications to the table will take longer as the index must be maintained

In order to avoid these side effects, you need a test environment where changes
like this can be safely introduced, tested to destruction and then deployed.

I would hope that you would have a very clear idea as to the benefit of adding
this index before even considering it.
Review & Followup
Rating: 4
Performance with Index April 23, 2002
Reviewer: George Spears from Dayton, OH
Just as one additional comment:
In 9i (yes, I realize you said you had 8.1.7), there is a functionality within
the tuning pack for Oracle Enterprise Manger called the Virtual Index Wizard.
This allows an individual to ask the question:  If I create this index (and you
describe the index), how will it affect a SPECIFIC query?  I tested this out
while running a large Oracle benchmark (almost 9 TB) and the Virtual Index
Wizard was near instantaneous, and told me how beneficial the index would be.
(aka - it wouldn't be used, said query would run 14% faster, etc...)

April 23, 2002 Ral -- Thanks for the question regarding "Unwanted Reparsing", version 8.1.7
You Asked
Tom,

We are having a problem with a query from which the following SQL has been
extracted. The application is written in C++ and communicates with Oracle via
OCI. It appears that the query is re-parsed everytime it's executed, even though
bind variables are used for values passed in by the program. The query returns
the correct values but, because of the reparsing, it is incredible inefficient.

The most suspicious thing to me is the use of a bind variable (i.e., :1) to pass
in a format to the TO_CHAR function to convert the retrieved date to a string. I
do not know why this would cause a problem, but I'm guessing it might be doing
so. I have not been able to find any information or examples of using a bind
variable in the SELECT clause of a statement, so I'm asking if you have any idea
if this could be causing the re-parsing, and if so why?

SELECT S.STAFF_ID, ..., TO_CHAR(S.INACTIVE_DT, :1)
  FROM STAFF_LOCAL S, STAFF_ASSIGN SA
WHERE SA.PAT_SEQ = :2
  ...
  AND SA.CARE_RELN_CODE = :3
  ...
ORDER BY SA.START_DDT DESC

Thanks for any help you can provide,
and we said...
The problem is in the C code.

When you program in C, you are the ones that tell us exactly when and how often
to parse.

Your program must be parsing this before each execution instead of parsing it
once per session and just binding/executing it over and over.

You need to look at your code and instead of coding:

   parse
   bind
   execute
   close

code

   if ( first_time )
   {
       parse
       first_time = false
   }
   bind
   execute
April 23, 2002 Dean -- Thanks for the question regarding "Reading Data in Clobs that are > 32767 K", version 8.1.7.2.0
You Asked
Hello Tom,

I am faced with the challenge of dealing with CLOB values that are
greater than 32767 characters in length.  For example, I have to read a record's
CLOB value which has a length = 946056k.  When I test the
dbms_lob.read package it fails when the amount is > 32767.  If this is
the case, how can one use this function to read the chunks of a CLOB
which has a length > 32767k.

Any help on this will be greatly appreciated.

Thanks,

Dean

DBMS_LOB defines the following constants:
-------------------------------------------------------------------
  file_readonly CONSTANT BINARY_INTEGER := 0;
 lob_readonly  CONSTANT BINARY_INTEGER := 0;

lob_readwrite CONSTANT BINARY_INTEGER := 1;
     lobmaxsize CONSTANT INTEGER        := 4294967295;
                call CONSTANT PLS_INTEGER    := 12;
          session CONSTANT PLS_INTEGER    := 10;
-------------------------------------------------------------------

DBMS_LOB uses the following Parameters:

Parameter  Descrption:
-----------------------------------------
lob_loc:  Locator for the LOB to be read.
amount:  Number of bytes (for BLOBs) or characters (for CLOBs) to read,
or number that were read.
   offset: Offset in bytes (for BLOBs) or characters (for CLOBs) from
the start of the LOB (origin: 1).
   buffer: Output buffer for the read operation.

Please Note:  The following block of code errors out because the amount
is > 32767.

declare
   c_text_long varchar2(32767);
           lobloc CLOB;
           buffer VARCHAR2(32767);
         amount number               := 4294967295;  -- clob max size in
bytes!
            offset INTEGER          := 100;
    lob_length integer          := 0;
begin
   select text_long
     into lobloc
   from scopus.notes_clob
 where note_id = 147787;

 dbms_lob.read(lobloc,amount,offset,buffer);

exception
   when others then
      dbms_output.put_line('SQLCODE:'||SQLCODE);
      dbms_output.put_line('SQLERRM:'||SQLERRM);
end;
/
and we said...
PLSQL is limited to 32k -- other languages (C, java, etc) are not.

In PLSQL, you will process the CLOB in chunks, 32k at a time.  You will not have
a big "in memory variable" is is the string that is the CLOB.

Even in C or Java -- you would not typically do that.  Think of the CLOB as you
would a file (it is atually built ontop of file libraries).  Think of it like
you opened a 5 megabyte text file.  You would process that file by reading a
line, seeking to a position, reading N bytes.  Do the same with a LOB.
Review & Followup
Rating: 2
Reading Clobs > 32K April 23, 2002
Reviewer: Dean Hogan from MA, USA
Tom, thanks for your prompt response.  Would you be able to give me an example
of pl/sql code, which processes clobs in 32k chunks?

Followup:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:711825134415
it is working on a bfile with RAW data but the concept is the same.  use
dbms_lob.substr or dbms_lob.read to get the piece you want, process it and do
something with it and move onto the next piece.

April 23, 2002 Leo -- Thanks for the question regarding
      "Cleaning TEMP segments", version 8.1.5

      You Asked
Hi Tom,
  Thanks for giving me a chance to ask a question.

   This is regarding the High CPU used by SMON.  What are the steps i should
consider to make the SMON to do his job properly on cleaning up the TEMP
segments? (things like making the PCTINCREASE=1 on tablespaces)

Thanks.
Leo.

      and we said...
#1 -- use a true temporary tablespace with tempfiles.  SMON no longer cleans up.
 No longer has to.  Stop using permanent tablespaces for temporary tablespaces.

#2 -- get rid of pctincrease=1.  In fact, get rid of dictionary managed
tablespaces, use locally managed.  Now SMON no longer needs to coalesce free
adjacent extents ever again.

pctincrease=1 is a *bad* *bad* *bad* idea.
Review &
      Followup

      Rating: 4
How about
      before 8.1.7   April 23, 2002
Reviewer:  Lou
       from New York
Great information. But..But.. Any tips for cleaning the temp space before 8i/9i.

Thanks

          Followup:

use temporary tablespaces.  They don't need "cleaning".

April 23, 2002 --
      Thanks for the question regarding "Maximum CPU which caused the slow
      processes on the whole box.", version 8.1.7.2.5

      You Asked
I have a quick issue and I need some advise on this.

I have an Oracle server running windows 2000 server with 933 MHZ and 1.2 G of
memory. I have 6 to 8 databases running on this box and the total memory
allocated to these databases are about 800M.

everytime when someone do and insert or update to anyone of these databases, I
look at the CPU and it's max out 100% and every body else experience the
performance issues on the whole box even on some other databases located on the
same box.

I looked at the swap file and It's not bad at all. So what could be the problem
???

It couldn't be the poor query b/c it's only a single insert statement to insert
data into the database.  I spoke to our ADMIN guys and they said that b/c we
only have one processor so that everytime anyone doing something would use up
that one processor.  To me, that makes no sense b/c it mean that every body else
had to wait till the process finish ???

Please give me some advise and how to solve this problem.

thanks

      and we said...
What do you mean "whats the problem".  You are doing something intensive, on a
teeny tiny way overloaded box (6-8 databases on a box that is sized for ONE
database at best)....

Listen to your admin -- they speak the truth.

You have a shared resource -- a CPU.  You run an intensive process that needs
lots of CPU.  You have lots of other stuff that needs CPU.  You have a very very
finite amount of CPU.  All of a sudden, people have to WAIT to gain access to
this very finite resource -- they run SLOWER.  They don't STOP, they run slower.
 They don't have to wait until the process finishes, but they WILL wait for
their turn on the CPU.  My laptop is almost as powerful as this server.  The
desktop I have is infinitely more powerful (and I wouldn't think of using it for
a server).

If you have IDE drives on this small machine, its even worse as the disk IO (and
inserts/updates being very disk intensive) will require the use of the system
CPU (other drive technologies don't suffer from this).

I would

a) not run more then one database on this machine.  run a single instance, you
need not have a database per application, use a schema per application.

b) give serious consideration to getting a bigger box.  you are cpu bound.

Review &
      Followup

      Rating: 3
Database on ONE
      Computer  January 19, 2002
Reviewer:  Sazzadur
      Rahman Tusar  from Bangladesh
Tom is right, I agree with him.
Why many databes on ONE Computer? why?

      Rating:
      5
  April 23, 2002
Reviewer:
      A reader
Hi Tom,

 I wan't to find out which user OR which process OR which query is  using
Maximum CPU time.

 Can you help me.

 What are the meaning of the following V$SYSSTAT
 parameter.

recursive cpu usage
OS Wait-cpu (latency) time
OS Other system trap CPU time
OS System call CPU time
OS User level CPU time
parse time cpu

Thanks,

          Followup:

Well, you want the impossible then.

They all have different lifespans, you cannot compare them really.

In 9i, we track CPU by query in the V$SQL view -- before that, cpu/query doesn't
exist.

A user -- thats in v$sesstat for the life of the session.

A process -- thats available from the OS.

Not that knowing "which of the above is using the maximum CPU time" means
anything.  Consider the query that was parsed a week ago, verses the shared
server started yesterday versus the user logged in for 5 hours -- how can you
begin to compare their CPU times???  To further confuse it, the user logged in
for 5 hours may (or may not) have contributed to the CPU time for a given
process and may (or may not) have executed the query with lots of CPU time.  You
are double/triple counting things.

I don't know what you would do with that info.

See the Oracle8i REFERENCE, there is an appendix with the statistics
description.

April 23, 2002 Vikas -- Thanks for the question regarding
      "security, package in web application ", version 8.1.7

      You Asked
Hi Tom,

Q.1 I am developing a web application using mod_plsql of oracle 8.1.7. I have a
problem.How can I prevent the execution of owa_util.showsource from the browser.
Because any user can give the url like
http://myservername/pls/dadname/owa_util.showsource?cname=
anyprocname and any one can issue a
ddl command also and drop any thing from my database like

http://myservername/pls/dadname/dbms_utility
exec_ddl_statement?parse_string=drop procedure p1

Or any thing like above which can be dangerous. In one way how can I prevent
user from executing any sys owned packaged procedure or function.

I know it may be not appropriate to ask how you do it at your site. But for an
example if you can let me know then, it will be more helpful.

Q.2 What is importance of packages in developing a mod_plsql based web
application. My understanding is that, kindly correct if I am wrong, I should
use package for those procedure and function, which are called together in one
request. Because after a request the connection is lost. (for a stateless dad).
And in any way I will first try to develop my application for stateless dad
until some requirement arises for which I cannot use stateless dad.

Q.3 Is there any way in pl/sql to handle a variable which hold the data more
than 32k.
For example. I have a variable var1, reads through a sql file(using utl_file)
which has a script to creates a package. The script data is more than 32k. I
concat each line read by utl_file in to var1. but it will raise an exception
when the value is more than 32k. after collecting all the data I want to use
EXECUTE IMMDIATE var1.

I think I cannot EXECUTE IMMDIATE var1 if the value is more than 32k. correct me
If I am wrong. Kindly Let me know the solutions. I can do it by running os
command sqlpllus.exe through a pl/sql program but that uses java. Is there in
another method of doing it.

I searched your site for q1 but could not get the answer for it but I got
listing which shows how to prevent user logging to administration pages that is
very helpful.

      and we said...
q1) by default, showsource would not be accessible.  You must have enabled it
(or who ever configured your setup)

See

http://technet.oracle.com/docs/products/ias/doc_library/1022doc_otn/apps.102/a90099/secure.htm#1005658
for how to setup exclusion lists.

<quote>

If this parameter is not specified, the default list is sys.*, dbms_*, utl_*,
owa_util.showsource, and owa_util.cellsprint
</quote>

so, someone must have setup another list that doesn't exclude these.

q2) you should use packages period.  For all things.  Stateless or stateful -
doesn't matter.

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:286816015990
for why.

q3) You should load up an array.  You will use DBMS_SQL which can parse an array
using

PROCEDURE PARSE
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 C                              NUMBER(38)              IN
 STATEMENT                      TABLE OF VARCHAR2(256)  IN
 LB                             NUMBER(38)              IN
 UB                             NUMBER(38)              IN
 LFFLG                          BOOLEAN                 IN
 LANGUAGE_FLAG                  NUMBER(38)              IN

see also

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1023031268045

Review &
      Followup

      Rating: 4
problem with
      question1  April 23, 2002
Reviewer:  Vikas
      Sharma  from Delhi India
Hi Tom ,
Below are the extract of the link you refered. It help me lot in understanding
the other things also but I am facing one problem is the method described below
is
not working. I am using the plsql gateway mod_plsql of 8.1.7. I am pasting the
copy of my wdbsvr.app configration file.

/**************** from the document ************** */
Example 1:

exclusion_list=testschema.testpkg.*,sys.*,dbms_*,pkg?.*

This example protects all procedures in testschema.testpkg package, all
procedures in
the sys schema, all procedures in packages that match dbms_, and all procedures
in
packages that start with pkg and one character that can be a wildcard.
***********************extract ends ******************

I have tried with writing in both cases (upper and lower) and restarted my http
server
and listener several time. Then also i am able to use owa_util.showsource
and use dbms_utility.exec_ddl_statement.

My configration file <ORACLE_HOME>\Apache\modplsql\cfg\wdbsvr.app
_________________________________________
[WVGATEWAY]
administrators = scott
adminPath = /admin_/
;admindad =
;debugModules =
defaultDAD = TEST
;upload_as_long_raw =
;upload_as_blob =
;timingsTable =
;timingsModules =
;enablesso =
;servletroot =
;stateful =
;custom_auth =

[DAD_hrcm]
exclusion_list = SYS.*,DBMS_*,PKG?.*,OWA_UTIL*
;exclusion_list = sys.*,dbms_*,pkg?.*,owa_*
connect_string   =  orcl.del1
password   =  hr_cm
username   =  hr_cm
default_page   =
document_table   =
document_path   =
document_proc   =
upload_as_long_raw   =
;upload_as_blob   =
name_prefix   =
always_describe   =
after_proc   =
before_proc   =
reuse   =  Yes
;connmax   =
pathalias   =
pathaliasproc   =
enablesso   =  No
sncookiename   =
stateful   =  No
;custom_auth   =
;

          Followup:

Can you see if  <Note:178284.1> on metalink applies to your case?

      Rating: 4
Thanks it
      worked  April 27, 2002
Reviewer:  Vikas
       from Delhi India
Hi,
Thanks lot. I went through the article and installed the new patch now it is
working. Thanks again.

Regards,
Vikas

April 23, 2002 Martin J. -- Thanks for the question regarding
      "Migrating from 8.1.7 to 9i", version 9i

      You Asked
Hello Tom,

I have been searching for migration or upgrade guides for changing from Oracle
8.1.7 to Oracle 9i database server.  The Database Administration - Upgrade and
Migration page on MetaLink does not have an article about migrating to 9i from
8.1.7.  It lists Alerts and Known Issues, Migrating (7.x to 8.x), Upgrading (8.x
to 8.x), Migration Internals, Migrating from a Non-Oracle Database to Oracle,
but nothing about 9i.  I have searched though white papers and documentation and
have come up short.  Could you point me in the right direction on some online
documentation?  Thanks for your time!

Sincerely,
Martin

      and we said...
every release of Oracle comes with a "migration guide".

go here

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/nav/docindex.htm
and read this:

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/nav/docindex.htm#index-MIG
....
April 23, 2002 Bhavani -- Thanks for the question regarding
      "Oracle Application Server", version 8.1.7

      You Asked
Hi Tom,

We user Oracle Web Application Server as our reporting tool.  My problem is, We
want to display only 100 Records at a time on the web page.  When the user
clicks next, we need to display next 100, or if he clicks previous, we need to
display previous 100.

Is there any way, to display records like this with out sending request to
database every time user clicks the link, using some mechanism in application
server, like caching all the records at web application server level etc.etc.

Thank you Tom.

Sincere Regards
Bhavani

      and we said...
Using a servlet and a stateful connection you could (i'm not a java programmer,
I do not have an example, but any java programmer should be able to figure this
out pretty easily).

I would ask yourself however "do I really want to".  google doesn't.  yahoo
doesn't.  The big ones do not maintain a session for you on their server (i
won't maintain a state for you on my server either).  We all paginate.
Review &
      Followup

      Rating:
      4
  April 23, 2002
Reviewer:  Bhavani
       from India
No Tom, showing all the 10,000 records on a single page is taking long time.  To
reduce that time, we want to show 100 records at a time.  And it will be very
heavy for the database to send the records again and again.  So what we want is,
get all the records once, show piece by piece.  Is it possible?

          Followup:

Write a servlet.

Getting 10k records is *utterly silly* if you think about it.  At 100
records/page, that would be 100 pages.

Have you ever gone past page 5 to 10 on a search?  10k records is more data then
any human will look at.

Every time I see this request -- I think the same thing "bad idea".

Here, I limit you to 500 records and let you page through it.  I wouldn't dream
of pulling 10k records into a middle tier and keeping them there (I can support
tons more users then you can -- I don't waste resources on my server when you
are not actually ON my server).

I would put these results into a table and use standard SQL to paginate through
the result set if running the query is too "onerous".

read

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:76812348057
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:138012348065
for the two ways I do it (simple query with rownum if the query is fast enough,
saving the rowids of the result into a search table otherwise and paging through
that)

April 23, 2002 Twin -- Thanks for the question regarding "Open
      Cursor Issue", version 8.1.6

      You Asked
Tom,

Thanks for taking time to answer the questions. I have question about the
implict cursors are not closed after the Java PrepareCall statement is called.
Following is the test case to illustrate the issue (I'm modified your example
somewhat to demostrate),

create or replace package demo_pkg
as
    type refcur is ref cursor;

    procedure get_cur( x in out refcur );
end;
/

create or replace package body demo_pkg
as

procedure get_cur( x in out refcur )
is

l_user varchar2(1000);

begin

    open x for select USER from dual;

    select user
      into l_user
     from dual where rownum = 1;

    select user
      into l_user
     from dual where rownum = 1;

end;

end;
/

import java.sql.*;
import java.io.*;
import oracle.jdbc.driver.*;

class curvar
{
  public static void showOpenCnt( Connection conn, String msg)
  throws SQLException
  {
      Statement stmt = conn.createStatement();
      ResultSet rset =
          stmt.executeQuery
          ( "select a.value, b.name "+
              "from v$mystat a, v$statname b "+
             "where a.statistic# = b.statistic# "+
               "and a.statistic#= 3");
      System.out.println( "====================================" );
      System.out.println( msg );
      System.out.println( "====================================" );
      System.out.println( " " );

      while( rset.next() )
        System.out.println( rset.getString(1)+ " " +
                            rset.getString(2) );
      System.out.println( "-----------------------" );
      rset.close();
      stmt.close();

      Statement stmt1 = null;
      ResultSet rset1 = null;

      stmt1 = conn.createStatement();
      rset1 = stmt1.executeQuery("select sid, sql_text from v$open_cursor where
sid = (select a.sid "+
              "from v$mystat a, v$statname b "+
              "where a.statistic# = b.statistic# "+
              "and a.statistic#= 3 "+
              "and rownum = 1)");

      System.out.println("Open Cursors Currently");
      while (rset1.next()) {
          System.out.println("SID***" + rset1.getInt(1) + "   " +
rset1.getString(2) );
      }
      System.out.println( "-----------------------" );
      System.out.println( " " );
      rset1.close();
      stmt1.close();
  }

  public static void main (String args [])
  throws SQLException, ClassNotFoundException
  {
      String query  =
           "begin demo_pkg.get_cur( :1 ); end;";

      DriverManager.registerDriver
          (new oracle.jdbc.driver.OracleDriver());

      Connection conn=
        DriverManager.getConnection
        ("jdbc:oracle:oci8:@ora8idev",
          "scott", "tiger");

      showOpenCnt( conn, "Before Anything" );

      CallableStatement cstmt = conn.prepareCall(query);
      cstmt.registerOutParameter(1,OracleTypes.CURSOR);
      cstmt.execute();

      showOpenCnt( conn, "After prepare and execute" );

     ResultSet rset = (ResultSet)cstmt.getObject(1);
      showOpenCnt( conn,
             "After prepare and execute and getObject" );

      for(int i = 0;  rset.next(); i++ )
      {
        if ( i == 10 )
           showOpenCnt(conn,"After fetching 10 rows...");
      }

      showOpenCnt( conn, "After but before any close" );

      rset.close();
      showOpenCnt( conn, "After ResultSet closes" );

      cstmt.close();
      showOpenCnt( conn, "After CallableStatement closes" );

  }
}

Here is the results,

C:\demo>java curvar
====================================
Before Anything
====================================

1 opened cursors current
-----------------------
Open Cursors Currently
SID***52   select sid, sql_text from v$open_cursor where sid = (select
-----------------------

====================================
After prepare and execute
====================================

5 opened cursors current
-----------------------
Open Cursors Currently
SID***52   select sid, sql_text from v$open_cursor where sid = (select
SID***52   SELECT USER   FROM DUAL  WHERE ROWNUM = 1
SID***52   SELECT USER   FROM DUAL  WHERE ROWNUM = 1
SID***52   begin demo_pkg.get_cur( :1 ); end;
SID***52   SELECT USER   FROM DUAL
-----------------------

====================================
After prepare and execute and getObject
====================================

5 opened cursors current
-----------------------
Open Cursors Currently
SID***52   select sid, sql_text from v$open_cursor where sid = (select
SID***52   SELECT USER   FROM DUAL  WHERE ROWNUM = 1
SID***52   SELECT USER   FROM DUAL  WHERE ROWNUM = 1
SID***52   begin demo_pkg.get_cur( :1 ); end;
SID***52   SELECT USER   FROM DUAL
-----------------------

====================================
After but before any close
====================================

5 opened cursors current
-----------------------
Open Cursors Currently
SID***52   select sid, sql_text from v$open_cursor where sid = (select
SID***52   SELECT USER   FROM DUAL  WHERE ROWNUM = 1
SID***52   SELECT USER   FROM DUAL  WHERE ROWNUM = 1
SID***52   begin demo_pkg.get_cur( :1 ); end;
SID***52   SELECT USER   FROM DUAL
-----------------------

====================================
After ResultSet closes
====================================

4 opened cursors current
-----------------------
Open Cursors Currently
SID***52   select sid, sql_text from v$open_cursor where sid = (select
SID***52   SELECT USER   FROM DUAL  WHERE ROWNUM = 1
SID***52   SELECT USER   FROM DUAL  WHERE ROWNUM = 1
SID***52   begin demo_pkg.get_cur( :1 ); end;
-----------------------

====================================
After CallableStatement closes
====================================

3 opened cursors current
-----------------------
Open Cursors Currently
SID***52   select sid, sql_text from v$open_cursor where sid = (select
SID***52   SELECT USER   FROM DUAL  WHERE ROWNUM = 1
SID***52   SELECT USER   FROM DUAL  WHERE ROWNUM = 1
-----------------------

As you can see the two identical "SELECT USER   FROM DUAL  WHERE ROWNUM = 1" SQL
statements in the procedure are still open after the PrepareCallstatement
closed.

So, is it correct? Could you explain and help to solve the problem since if
there is loop and OPEN_CURSURS set to 500 or 1000, it is easy to get ORA-01000
error.

Thanks

Twin Dophin.

      and we said...
These are cached by PLSQL for performance.

They will remain there until you run out of slots in your OPEN CURSOR array --
at which point they are flushed if not currently being used (plsql lets them "go
away" if and when the server needs that slot)

They do not count against you, they are there for performance.  It is an
EXCELLENT reason why most java programs entire suite of SQL should consist of
nothing more then begin .... end; -- never any actual DML of its own.  More
manageable, more flexible.

You can test this out yourself by using this:

create or replace package demo_pkg
as
    type refcur is ref cursor;

    procedure get_cur( x in out refcur );
end;
/

create or replace package body demo_pkg
as

g_first_time boolean default true;

procedure get_cur( x in out refcur )
is

l_user varchar2(1000);

begin

    open x for select USER from dual THIS_IS_A_JAVA_CURSOR;

    if ( g_first_time )
    then

    select user
      into l_user
     from dual THIS_IS_PLSQL where rownum = 1;

    select user
      into l_user
     from dual THIS_TOO_IS_PLSQL where rownum = 1;
    g_first_time := false;
    end if;
end;

end;
/

that plsql only needs the cursors for a bit -- we don't need them everytime...

Now I modified the java to be:

  public static void main (String args [])
  throws SQLException, ClassNotFoundException
  {
      String query  =
           "begin demo_pkg.get_cur( :1 ); end;";

      DriverManager.registerDriver
          (new oracle.jdbc.driver.OracleDriver());

      Connection conn=
        DriverManager.getConnection
        ("jdbc:oracle:oci8:@ora817dev",
          "scott", "tiger");

      showOpenCnt( conn, "Before Anything" );

      CallableStatement cstmt = conn.prepareCall(query);
      cstmt.registerOutParameter(1,OracleTypes.CURSOR);

      for( int j = 0; j < 100; j++ )
      {
          cstmt.execute();
          showOpenCnt( conn, j + ") After prepare and execute" );

          ResultSet rset = (ResultSet)cstmt.getObject(1);

          for(int i = 0;  rset.next(); i++ );
      }

      cstmt.close();
      showOpenCnt( conn, "After CallableStatement closes" );
  }

I don't close the result sets - we just let them leak all over the place.  I
have open_cursors set to 50 and run:

> !java
java curvar
====================================
Before Anything
====================================

1 opened cursors current
-----------------------
Open Cursors Currently
SID***8   SELECT VALUE FROM NLS_INSTANCE_PARAMETERS WHERE PARAMETER ='
SID***8   select a.value, b.name from v$mystat a, v$statname b where a
SID***8   ALTER SESSION SET NLS_TERRITORY = 'AMERICA'
SID***8   ALTER SESSION SET NLS_LANGUAGE = 'AMERICAN'
SID***8   select sid, sql_text from v$open_cursor where sid = (select
SID***8   ALTER SESSION SET NLS_LANGUAGE= 'AMERICAN' NLS_TERRITORY= 'A
-----------------------

====================================
0) After prepare and execute
====================================

5 opened cursors current
-----------------------
Open Cursors Currently
SID***8   SELECT VALUE FROM NLS_INSTANCE_PARAMETERS WHERE PARAMETER ='
SID***8   select a.value, b.name from v$mystat a, v$statname b where a
SID***8   SELECT USER   FROM DUAL THIS_IS_PLSQL  WHERE ROWNUM = 1
SID***8   begin demo_pkg.get_cur( :1 ); end;
SID***8   ALTER SESSION SET NLS_TERRITORY = 'AMERICA'
SID***8   ALTER SESSION SET NLS_LANGUAGE = 'AMERICAN'
SID***8   select sid, sql_text from v$open_cursor where sid = (select
SID***8   ALTER SESSION SET NLS_LANGUAGE= 'AMERICAN' NLS_TERRITORY= 'A
SID***8   SELECT USER   FROM DUAL THIS_TOO_IS_PLSQL  WHERE ROWNUM = 1
SID***8   SELECT USER   FROM DUAL THIS_IS_A_JAVA_CURSOR
-----------------------

====================================
1) After prepare and execute
====================================

6 opened cursors current
-----------------------
Open Cursors Currently
SID***8   SELECT VALUE FROM NLS_INSTANCE_PARAMETERS WHERE PARAMETER ='
SID***8   select a.value, b.name from v$mystat a, v$statname b where a
SID***8   SELECT USER   FROM DUAL THIS_IS_PLSQL  WHERE ROWNUM = 1
SID***8   begin demo_pkg.get_cur( :1 ); end;
SID***8   ALTER SESSION SET NLS_TERRITORY = 'AMERICA'
SID***8   ALTER SESSION SET NLS_LANGUAGE = 'AMERICAN'
SID***8   select sid, sql_text from v$open_cursor where sid = (select
SID***8   ALTER SESSION SET NLS_LANGUAGE= 'AMERICAN' NLS_TERRITORY= 'A
SID***8   SELECT USER   FROM DUAL THIS_TOO_IS_PLSQL  WHERE ROWNUM = 1
SID***8   SELECT USER   FROM DUAL THIS_IS_A_JAVA_CURSOR
SID***8   SELECT USER   FROM DUAL THIS_IS_A_JAVA_CURSOR
-----------------------

note that after each iteration I got more and more "this is a java cursor".
The plsql guys stayed in there.... UNTIL:

====================================
45) After prepare and execute
====================================

50 opened cursors current
-----------------------
Open Cursors Currently
SID***8   select a.value, b.name from v$mystat a, v$statname b where a
SID***8   SELECT USER   FROM DUAL THIS_IS_PLSQL  WHERE ROWNUM = 1
SID***8   begin demo_pkg.get_cur( :1 ); end;
SID***8   select sid, sql_text from v$open_cursor where sid = (select
SID***8   ALTER SESSION SET NLS_LANGUAGE= 'AMERICAN' NLS_TERRITORY= 'A
SID***8   SELECT USER   FROM DUAL THIS_TOO_IS_PLSQL  WHERE ROWNUM = 1
SID***8   SELECT USER   FROM DUAL THIS_IS_A_JAVA_CURSOR
<lots of those chopped out>
SID***8   SELECT USER   FROM DUAL THIS_IS_A_JAVA_CURSOR
-----------------------

====================================
46) After prepare and execute
====================================

49 opened cursors current
-----------------------
Open Cursors Currently
SID***8   select a.value, b.name from v$mystat a, v$statname b where a
SID***8   begin demo_pkg.get_cur( :1 ); end;
SID***8   select sid, sql_text from v$open_cursor where sid = (select
SID***8   ALTER SESSION SET NLS_LANGUAGE= 'AMERICAN' NLS_TERRITORY= 'A
SID***8   SELECT USER   FROM DUAL THIS_IS_A_JAVA_CURSOR
<lots chopped NOTE: PLSQL cursors *gone*>
SID***8   SELECT USER   FROM DUAL THIS_IS_A_JAVA_CURSOR
-----------------------

====================================
47) After prepare and execute
====================================

50 opened cursors current
-----------------------
Open Cursors Currently
SID***8   select a.value, b.name from v$mystat a, v$statname b where a
SID***8   begin demo_pkg.get_cur( :1 ); end;
SID***8   select sid, sql_text from v$open_cursor where sid = (select
SID***8   ALTER SESSION SET NLS_LANGUAGE= 'AMERICAN' NLS_TERRITORY= 'A
SID***8   SELECT USER   FROM DUAL THIS_IS_A_JAVA_CURSOR
...
SID***8   SELECT USER   FROM DUAL THIS_IS_A_JAVA_CURSOR
-----------------------

java.sql.SQLException: ORA-01000: maximum open cursors exceeded

        at java.lang.Throwable.<init>(Compiled Code)
        at java.lang.Exception.<init>(Compiled Code)
        at java.sql.SQLException.<init>(Compiled Code)
        at oracle.jdbc.dbaccess.DBError.throwSqlException(Compiled Code)
        at oracle.jdbc.oci8.OCIDBAccess.check_error(Compiled Code)
        at oracle.jdbc.oci8.OCIDBAccess.parseExecuteDescribe(Compiled Code)
        at oracle.jdbc.driver.OracleStatement.doExecuteQuery(Compiled Code)
        at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(Compiled
Code)
        at oracle.jdbc.driver.OracleStatement.executeQuery(Compiled Code)
        at curvar.showOpenCnt(Compiled Code)
        at curvar.main(Compiled Code)

Review &
      Followup

      Rating: 5
Open Cursor
      Issue  April 23, 2002
Reviewer:
      A reader
Tom,

Great, I tested here. it works as you said.

Thanks

April 23, 2002 chaitanya -- Thanks for the question regarding
      "Row Pointer in FETCH", version 8.1.7

      You Asked
Hi Tom,

I am wondering where can i find more information regarding OPEN, FETCH and CLOSE
operations on REF CURSORS?

I have a procedure A which has a OUT Parameter (REF CURSOR).
I have another Porcedure B which also has a OUT Parameter (REF Cursor).
Procedure A calls B,  the output of Procedure B is also the output of Procedure
A.
In Procedure A, after calling Procedure B , i have to check number of rows
retured by procedure B, if zero rows i have to open another cursor and return,
other-wise if procedure B returns 1 or more than one row, i have to just return
the refcursor.  If FETCH is used then ROW POINTER is where? [ in all the cases
where of one row fetched, no rows fetched, more than one row fetched where are
the row pointers]

If Procedure A is called from JAVA program, in all the cases where will the row
pointer be ?  How to display from 1st row till last.  Can Oracle navigate to the
first record.

[ without using count(*) from the base query of REF CURSOR, is there a way to
find no. of records returned by REF CURSOR ? ]  Any built-in functions for
REFCURSORS ?

      and we said...
You cannot accomplish your goal.

If you check to see if there is a row -- you will lose the row (plsql will have
fetched it, it is gone, java will not see it).

Until you actually fetch the data -- even we do not know if the query will
return a row or not.
April 23,
      2002 Boopathi -- Thanks for the
      question regarding "Query benchmarking and create table question", version
      8.1.7

      You Asked
Tom,

1) Whenever we do query benchmarking, i.e when we want to compare 2 different
query for a given set of data, you always advice us to do the trace and compare.

   When we compare, should we be considering the fact that the first run may
have buffered the data in the memory?

2) In
   create table as (select * from a, b, c where a.created_dttm > sysdate-200 and
a.id = b.id and b.id = c.id)
    how many records are fetched at a time?
    if there is a fetch size how could that be changed? Is that setting
arraysize in SQL*Plus?

Thanks a lot,

Boopathi

      and we said...
1) you can -- or you can run the queries many times one after the other.  Take
the last timing only.

Or, you can offline/online the tablespaces before each query - that'll flush the
buffer pool -- making each "worst case"

2) It is an internal fetch -- it's not really fetched at all -- a fetch implies
a client getting the data, but here there is no client getting the data.

You cannot change anything about it (other then run it in parallel and have more
then one process/thread working on it)
April 23, 2002 Beth -- Thanks for the question regarding "8.1.7
      vs 8.1.6", version 8.1.7

      You Asked
Is 8.1.7 considered to be a rollup of 8.1.6 patches and the new functionality
listed in the Oracle8i Documentation Addendum Release 3?

If so, I seem to have only found the New Features doc listed above.

How do I find a list of fixes/patches incorporated in 8.1.7?

We are moving from 8.1.6.0.0 and I am trying to find a comprehensive list of 1)
all bug fixes, and 2) all new functionality.

Thanks

      and we said...
The doc addendum contains the new functionality.

in $ORACLE_HOME/rdbms/doc, there is a readme with new functionality and bug
fixes as well.
Review & Followup

      Rating:
      5
  April 23, 2002
Reviewer:  Beth
       from USA
Thanks.  I'll look there.

Also, how shall I decided if I want to upgrade to 8.1.7.0.0 vs 8.1.7.3.0 or
other.

It seems there is a lot of debate as to whether to apply the latest cumulative
patch, or only apply one if needed specifically.

It seems that Oracle Technical Support always wants you to be on the *latest*
patch release....

Thoughts?

          Followup:

8173 should be your only choice, you shouldn't consider any of the intermediate,
non-supported versions if you are doing an upgrade.

I cannot imagine what the debate would be... It must be something like this:

someone: We should run with a version that is not supported
someone_else: Yeah, the one with known fixed bugs, thats what we should go with
voice_of_reason: I don't know, doesn't sound like a good idea....

(joking mood this evening).  8173 should be the target for you.  It has the
patches for the patches.

      Rating: 5
Thanks for the
      clarification  April 24, 2002
Reviewer:
      Yogeeraj  from Mauritius
Hello,

A.
to your clarifications, should we add:

" + apply patches that came after patchset 8.1.7.3"?

B.
and why not move directly to 9i?

thank you

Best Regards
Yogeeraj

      Rating:
      3
  April 24, 2002
Reviewer:
      A reader

April 24, 2002 Reprakash -- Thanks for the question regarding
      "OPTIMIZER_INDEX_COST_ADJ under RBO for partitioned tables", version 8.1.7

      You Asked
Tom

Our init.ora is set to RBO. We also have selected partitioned tables in the
database.By setting the parameter OPTIMIZER_INDEX_COST_ADJ to a value, will it
affect the queries that access partitioned tables or not?

Thanks
Reprakash

      and we said...
Guess what -- you are using CBO anyway.

Partitioning (among a growing set of features) cause the CBO to be used -
regardless of any other setting.

So yes, setting an init.ora parameter that affects the behaviour of the CBO will
affect your query plans since you are using the CBO.
Review &
      Followup

      Rating:
      5
  April 23, 2002
Reviewer:  Reprakash
      Ram  from Champaign, IL
Thanks for the quick response.

      Rating:
      5
  April 23, 2002
Reviewer:
      A reader
Hi Tom,

 Can you please tell me how execution plan is decided in RULE based Optimizer
and COST base Optimizer.

 I have this senerio

We are using RULE base Optimizer.

I have one table USER_CONTACT. I have BU_ID column
and it referenced in two indexes.

S_CONTACT_U1         S_CONTACT            BU_ID
                     S_CONTACT            CONFLICT_ID
                     S_CONTACT            FST_NAME
                     S_CONTACT            LAST_NAME
                     S_CONTACT            MID_NAME
                     S_CONTACT            OWNER_PER_ID
                     S_CONTACT            PR_DEPT_OU_ID
and

S_CONTACT_U2         S_CONTACT            BU_ID
                     S_CONTACT            CONFLICT_ID
                     S_CONTACT            FST_NAME
                     S_CONTACT            LAST_NAME
                     S_CONTACT            MID_NAME
                     S_CONTACT            OWNER_PER_ID

When i query by BU_ID Column in my where clause
It always pick  S_CONTACT_U2   index .

Can you explain me why it is only selecting S_CONTACT_U2   index.

Thanks.

          Followup:

drop and recreate the indexes in opposite order and the RBO will use the other
one.

As far as RBO is concerned -- these two indexes are equivalent -- it takes the
first one it finds.

Personally -- I would DROP S_CONTACT_U2.  looks 100% redundant and useful mostly
to slow down inserts/updates and deletes.

      Rating:
      3
Clarification  April 23, 2002
Reviewer:
      Ashok Jayaraman  from Seoul
I believe that RBO ususally used the last index that was created on the any of
the columns that figure in the where condition.Is it true?

          Followup:

With terms like "usually" -- I can safely say "yes" or "no" and not be right or
wrong ;)

It is sort of like flipping a coin, if I empirically observe that the coin lands
heads up mostly -- can I say "coins usually land heads up".

We can run a test:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t as select * from scott.emp;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx1 on t(empno,ename);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx2 on t(empno,job);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where empno = 1234;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T'
   2    1     INDEX (RANGE SCAN) OF 'T_IDX2' (NON-UNIQUE)

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop index t_idx1;

Index dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop index t_idx2;

Index dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx2 on t(empno,job);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx1 on t(empno,ename);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where empno = 1234;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T'
   2    1     INDEX (RANGE SCAN) OF 'T_IDX1' (NON-UNIQUE)

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

So it would appear that that is the case -- but -- i wouldn't bank on it.

April 24, 2002 Ak
      -- Thanks for the question regarding "Index", version 8.1.5

      You Asked
Hi tom,
   I am Using RULE BASED OPTIMIZER.I have a system which has lot
and lots of reports,and most of  the reports has a select criteria
as follows.At this moment cann't change to cost based mode and use
function index,because this might slow down my other module,because
of change in optimizer plan.

The user will enter start date and end-date in front-end.Start date
can be 2 years old and end date might not be current date.
I cann't index this field, as for the perticular date,it is
possible that i will be having lots of records.And even if i index
i will have to use to_char(start_date,'dd/mm/yyyy')=to_char(end_date,
'dd/mm/yyyy'),so index will not be used.Most of the query will
look like ...

select * from a where timestamp between
to_char(start_date,'dd/mm/yyyy') and to_char(end_date,'dd/mm/yyyy')

My all this query takes lots of time,and hence report is slow.
Any idea here how to go about doing it.

Yes timestamp is also a date field...!!

Thanks for your effort.

Regards
  Ak

Yes ,start_date,end_date is a column in a table,and the datatype is
date for both.

Thanks,
regards
  Ak

      and we said...
If timestamp is a date field, why is the predicate:

between to_char() and to_char()

and not between DATE and DATE?

You can in fact use an FBI -- you would just HINT your query (that would use the
CBO) and no one else would see it.  Read

http://osi.oracle.com/~tkyte/article1/index.html

ops$tkyte@ORA815.US.ORACLE.COM> create table t ( time_stamp date, start_date
date, end_date date );

Table created.

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> insert into t select created, sysdate-100,
sysdate from all_objects;

10935 rows created.

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> create or replace function btwn( p_x in date,
p_y in date, p_z in date ) return number
  2  deterministic
  3  as
  4  begin
  5      if ( p_x between trunc(p_y) and trunc(p_z) )
  6      then
  7          return 1;
  8      else
  9          return 0;
 10      end if;
 11  end;
 12  /

Function created.

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> create index t_idx on t( btwn( time_stamp,
start_date, end_date ) );

Index created.

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> alter session set query_rewrite_enabled=true;

Session altered.

ops$tkyte@ORA815.US.ORACLE.COM> alter session set
query_rewrite_integrity=trusted;

Session altered.

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> set autotrace on explain
ops$tkyte@ORA815.US.ORACLE.COM> select /*+ INDEX( T T_IDX ) */ *
  2   from t
  3   where btwn( time_stamp, start_date, end_date ) = 1
  4  /

TIME_STAM START_DAT END_DATE
--------- --------- ---------
07-MAR-02 14-JAN-02 24-APR-02
07-MAR-02 14-JAN-02 24-APR-02
07-MAR-02 14-JAN-02 24-APR-02
07-MAR-02 14-JAN-02 24-APR-02
22-APR-02 14-JAN-02 24-APR-02
09-FEB-02 14-JAN-02 24-APR-02
11-APR-02 14-JAN-02 24-APR-02
10-APR-02 14-JAN-02 24-APR-02
10-APR-02 14-JAN-02 24-APR-02
05-APR-02 14-JAN-02 24-APR-02
09-FEB-02 14-JAN-02 24-APR-02
09-APR-02 14-JAN-02 24-APR-02
09-APR-02 14-JAN-02 24-APR-02
09-APR-02 14-JAN-02 24-APR-02
09-APR-02 14-JAN-02 24-APR-02
05-MAR-02 14-JAN-02 24-APR-02
05-MAR-02 14-JAN-02 24-APR-02
11-APR-02 14-JAN-02 24-APR-02
23-FEB-02 14-JAN-02 24-APR-02
09-APR-02 14-JAN-02 24-APR-02
28-JAN-02 14-JAN-02 24-APR-02
06-MAR-02 14-JAN-02 24-APR-02
06-MAR-02 14-JAN-02 24-APR-02
06-MAR-02 14-JAN-02 24-APR-02
06-MAR-02 14-JAN-02 24-APR-02
06-MAR-02 14-JAN-02 24-APR-02
06-MAR-02 14-JAN-02 24-APR-02
14-APR-02 14-JAN-02 24-APR-02
28-JAN-02 14-JAN-02 24-APR-02
06-MAR-02 14-JAN-02 24-APR-02
06-MAR-02 14-JAN-02 24-APR-02
23-APR-02 14-JAN-02 24-APR-02

32 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=36 Bytes=1944)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=2 Card=36 Bytes=1944)
   2    1     INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE) (Cost=1 Card=36)

ops$tkyte@ORA815.US.ORACLE.COM> set autotrace off

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

how about TIMESTAMP -- what is it?

Are start_date and end_date database columns -- is timestamp?  need more info.

April 24, 2002 -- Thanks for the question regarding "Index usage
      and Null Values", version 816

      You Asked
Hi,
 1)  If a column has non unique index but it has null values.Will a query using
this column in where clause use an Index ? More so if it retrieves only 20% of
the rows for CBO. I tried creating a non unique index on the famous emp table :
column : comm. I queried for comm = 0 and it did a full table scan. Why?Will it
ever use an Index?

2) Its always better to index foreign Keys ( I read your article on that) What
happens to those entries which are nulls as null do not have a index entry
(except for Bitmapped Index). SO a query on the foreign key "is null" will
always result in a full T S.Pls correct me if I am wrong. So the advantage of no
child table locking is far more than the Full Table Scan of the child table when
u index a foreign key.do u agree ?
Thanx

      and we said...
Your keyboard is malfunctioning.  It is not sending the Y and O keys sometimes
(not all of the time, just sometimes for some words)

1) if the index makes sense, YES, the index could be used.

the CBO does not have a hard limit at 20% -  it could be much LOWER, it could be
HIGHER (if you want the details -- I have them in my book in my extensive
chapter on indexes)

EMP, when used with CBO, is generally TOO SMALL to use ANY INDEXES.  EMP fits on
a block.  To use an index, we would have to read an index block, then a table
block.  Why do that when we can just read the table block?

2) It is *not* always better (i though i spelt out when you should -- meaning,
there are times when you don't have to)...

Yes, a query on a fkey that is indexed AND the fkey is entirely null will not
use a b*tree index (cannot use).

I do not understand:

So the advantage of no
child table locking is far more than the Full Table Scan of the child table when

u index a foreign key.

They seem to be two totally different topics, I'm having a hard time correlating
them for comparision.

April 24, 2002 -- Thanks for the question regarding "Index usage
      and nulls", version 816

      You Asked
Hi,
   I tried editing my earlier Question,it did not allow me to.
Here's Q 3) How to tune/re-write/re-design query for the condition in where
clause
is NULL or is no null as these will always result in Full Table Scan.
Suppose i have 100000 rows in a table of which indexed (non unique)column x has
10000 null values. Inthis case the query will always do a Full table scan for
condition in where clause where x is null.How to avoid this

Thanx.

      and we said...
Function based indexes are one method:

http://osi.oracle.com/~tkyte/article1/index.html

Using a concatenated index where ONE of the columns is NOT NULL is another:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table emp as select * from scott.emp;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create index emp_idx on emp(comm,empno);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace on
ops$tkyte@ORA817DEV.US.ORACLE.COM> select /*+ first_rows index( emp emp_idx ) */
empno
  2    from emp
  3   where comm is null;

     EMPNO
----------
      7369
      7566
      7698
      7782
      7788
      7839
      7876
      7900
      7902
      7934

10 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: FIRST_ROWS (Cost=1 Card=5 Bytes=130)
   1    0   INDEX (RANGE SCAN) OF 'EMP_IDX' (NON-UNIQUE) (Cost=3 Card=5
Bytes=130)

April 24, 2002 Sarah -- Thanks for the question regarding "Using
      SELECT statement in stored procedures -- returning result sets from stored
      procedures.", version 7.3

      You Asked
I am trying to create a simple stored procedure that will return a result set.
i.e.
PROCEDURE sp_products (prod_name IN VARCHAR)
IS
BEGIN
SELECT * FROM TBL_PRODUCTS
WHERE P_NAME = prod_name;
END;

but I keep getting the error PLS-00428: an INTO clause is expected in this
SELECT statement.  I am a SQL Server 7 user and dont understand what I am doing
wrong (I dont want to SELECT INTO any table, I just want to return the result
set from the TBL_PRODUCTS table). I am new to Oracle so would appreciate a
detailed explanation.

      and we said...
see

http://osi.oracle.com/~tkyte/ResultSets/index.html
for all of the details on this.

followup to comment

someone please explain how:

create or replace function sp_ListEmp return types.cursortype
as
    l_cursor    types.cursorType;
begin
    open l_cursor for select ename, empno from emp order by ename;
    return l_cursor;
end;
/

is *harder* then

create or replace function sp_ListEmp return types.cursortype
as
begin
    select ename, empno from emp order by ename;
end;
/

???
Review &
      Followup

      Rating: 5
Result
      Sets  August 16, 2001
Reviewer:  sridhar
       from NH USA
Hi TOM,
  Yeah the response was helpfull.
  But i don't know why Oracle has implemented it in this way.
Sybase database has much easier way to return Result sets.
And i also have a question about how good it is to leave cursors open?
bye

      Rating: 5
the link was
      helpful but   April 24, 2002
Reviewer:
      Schesser
/********************************************************************************
***********/
    ' /* Create a packaged procedure that accepts a department number and
returns the employees. */
    ' /*     [Note: A standalone procedure will not work, it must be a packaged
procedure!]      */
    '
/********************************************************************************
***********/
    '
    ' CREATE OR REPLACE
    ' PACKAGE DEPARTMENT AS
    '   TYPE  CURSOR_TYPE IS REF CURSOR;
    '   PROCEDURE GET_EMPS (I_DEPTNO     IN  NUMBER,
    '                       O_RESULT_SET OUT CURSOR_TYPE);
    ' END;
    ' /
    ' CREATE OR REPLACE
    ' PACKAGE BODY DEPARTMENT AS
    '   PROCEDURE GET_EMPS (I_DEPTNO     IN  NUMBER,
    '                       O_RESULT_SET OUT CURSOR_TYPE)
    '   AS
    '   BEGIN
    '     OPEN O_RESULT_SET FOR
    '     SELECT EMPNO, ENAME
    '     FROM EMP
    '     WHERE DEPTNO = I_DEPTNO;
    '   END;
    ' END;
    ' /

a.Why will a standalone procedure not work for procedures returning result sets
using ref cursors.

b.I want to execute the procedure  above and print the results on te screen
using sqlplus is that not possible , why so . That was possible using a function
, why is it not possible using a procedure like above.

          Followup:

a) they will. they do.  In fact the very first example on the referenced page is
STANDALONE. this example WISELY chose not to.  standalone procedures and
functions should be banned in real systems.  they are good for demos.

b) re-read the link.  the first example on that page is:

REM SQL*Plus commands to use a cursor variable

April 24, 2002 Ambarish -- Thanks for the question regarding
      "sql query", version 8.1.5

      You Asked
Hi Tom,

Thanks for your earlier answer.

Lets say i have a column ENAME which has leading and trailing
white spaces and a literal say THE in between.

How do i write an sql query to take care of the leading white
spaces ? If i use '%THE%' i am safe with trailing but not
leading white spaces.

By leading white spaces i mean a column value which looks like
'  John   '. How do i search for this user given that the user
name may have white spaces both before and after.

You said select * from emp where ename like '%John%' will find
it.

Unfortuantely that will find 'Wayne John' as well. I want to
have a where clause which says find John with/without leading
and trailing spaces and only spaces.

Thanks
Chitnis

      and we said...
Ok, now it is clear.

ops$tkyte@ORA8I.WORLD> create table t ( ename varchar2(20) );

Table created.

ops$tkyte@ORA8I.WORLD> insert into t values ( '    John   ' );
1 row created.

ops$tkyte@ORA8I.WORLD> insert into t values ( 'Wayne John  ' );
1 row created.

ops$tkyte@ORA8I.WORLD> select * from t where trim(ename) =
'John';

ENAME
--------------------
    John

ops$tkyte@ORA8I.WORLD>

You could even create an index on this to make it fast.  see

http://osi.oracle.com/~tkyte/article1/index.html
for more info.
Review & Followup

      Rating: 5
Trimming
      in-between spaces  April 24, 2002
Reviewer:
      Munz  from Reston, USA
Tom:

If I want to trim spaces between words in a strin how do you do that. Do you use
replace ()?

2. What is ASCII for blank?

Thank you

          Followup:

You can use multiple replaces.  I would be tempted to parse the words out and
put them back together.  See

http://osi.oracle.com/~tkyte/SkipCols/index.html
I have a package, delimited, there that does that.

select ascii( ' ' ) from dual;

      Rating: 4
How about
      SOUNDEX?  April 24, 2002
Reviewer:  Ramesh
       from U.S.A
How about using SOUNDEX to compare a string with embedded spaces?
e.g.
select name from vendor
where
soundex(name)=soundex('OracleCorp.');

April 24, 2002 Steve -- Thanks for the question regarding
      "first_value when doing joins with multiple tables", version 8.1.7

      You Asked
Hi Tom,

I am trying to better understand the first_value funtion when multiple
joins/tables are involved. The problem I'm having is making sure
I specify the right "partition by" clause. I created some test
tables and I did manage to get the result I wanted but it took
a few attempts. It's easy with test data to know when the query
is correct. It's not so obvious or easly to validate with live data.

The example I used involved 5 tables:

people:                a list of people
jobs:                  a list of jobs
job_history:           for a given person, the list of
                       jobs they held.
job_address_history:   For a given job/company, their address history
                       (i.e. the company moved a few times)
address_history:       For a given person, their address history
                       (i.e. they moved a few times).

Note: The SQL to create/populate these tables is at the
      end of this message.

Query Goal:
-----------

List all users, with their current job, current home address, and,
for their current job, the company's current address.

I think this is the query:

col address format a22
col name format a14

select distinct
   first
  ,last
  ,first_value(name)
     over (partition by job_history.person
           order by    end_date desc nulls first
          )  name
   --
  ,first_value(start_date)
     over (partition by job_history.person
           order by    end_date desc nulls first
          )  start_date
   --
  ,first_value(end_date)
     over (partition by job_history.person
           order by    end_date desc nulls first
          )  end_date
   --
  ,first_value(address_history.address)
     over (partition by people.id
           order by    address_history.move_out_date desc nulls first
          )  address
   --
  ,first_value(job_address_history.address)
     over (partition by people.id
           order by    end_date desc nulls first
          )  Company_address
from  people
     ,job_history
     ,jobs
     ,address_history
     ,job_address_history
where      people.id          = job_history.person
      and  job_history.JOB_ID = jobs.job_id
      and  jobs.job_id        = job_address_history.job_id
      and  people.id          = address_history.person;

FIRST  LAST   NAME     START_DAT END_DATE  ADDRESS
COMPANY_ADDRESS
------ ------ -------- --------- --------- ----------------------
------------------------
John   Doe    Oracle   01-APR-98           John's current house   Oracle's
current address
Mary   Smith  Oracle   01-NOV-00           Mary's current house   Oracle's
current address
Mike   Jones  Oracle   01-JAN-01           Mike's current house   Oracle's
current address

But when I first tried the query, I used the following:

select distinct
   first
  ,last
  ,first_value(name)
     over (partition by job_history.person
           order by    end_date desc nulls first
          )      name
   --
  ,first_value(start_date)
     over (partition by job_history.person
           order by    end_date desc nulls first
          )      start_date
   --
  ,first_value(end_date)
     over (partition by job_history.person
           order by    end_date desc nulls first
          )      end_date
   --
  ,first_value(address_history.address)
     over (partition by people.id
           order by    address_history.move_out_date desc nulls first
          ) address
   --
   --,first_value(job_address_history.address)
   --  over (partition by people.id
   --
  ,first_value(job_address_history.address)
     over (partition by job_address_history.job_id
           order by    end_date desc nulls first
          )      Company_address
from  people
     ,job_history
     ,jobs
     ,address_history
     ,job_address_history
where      people.id          = job_history.person
      and  job_history.JOB_ID = jobs.job_id
      and  jobs.job_id        = job_address_history.job_id
      and  people.id          = address_history.person

FIRST  LAST   NAME     START_DAT END_DATE  ADDRESS
COMPANY_ADDRESS
------ ------ -------- --------- --------- ----------------------
----------------------------------------
John   Doe    Oracle   01-APR-98           John's current house   Burger King's
current address
John   Doe    Oracle   01-APR-98           John's current house   McDonalds's
current address
John   Doe    Oracle   01-APR-98           John's current house   Oracle's
current address
Mary   Smith  Oracle   01-NOV-00           Mary's current house   Can. Tire's
first and current address
Mary   Smith  Oracle   01-NOV-00           Mary's current house   Oracle's
current address
Mike   Jones  Oracle   01-JAN-01           Mike's current house   Oracle's
current address
Mike   Jones  Oracle   01-JAN-01           Mike's current house   Wal-mart's
current address.

7 rows selected.

Which is obviously wrong.

Can you give me some guidelines or "rules" to properly
define the partitions to avoid the "guessing game" I
seem to always be going through.

Thanks Tom.

Steve

Here's the scripts to create/populate the tables in case you need them.

drop table people;
create table people (id int,first varchar2(20),last varchar2(20));
insert into people values (91,'Mary','Smith');
insert into people values (92,'John','Doe');
insert into people values (93,'Mike','Jones');

drop table jobs;
create table jobs (job_id int,name varchar2(30),address varchar2(40));
insert  into jobs values (1,'McDonalds','Maple Road');
insert  into jobs values (2,'Burger King','Main Street');
insert  into jobs values (3,'Oracle','500 Oracle Parkway Redwood Shores, CA');
insert  into jobs values (4,'Wal-mart','President Drive');
insert  into jobs values (5,'Canadian Tire','Maple Leaf drive');
insert  into jobs values (6,'Sybase','22 cul-de-sac ave');

drop table job_history;
create table job_history (person int,start_date date,end_date date, job_id int);
insert into job_history values (92,'01-jan-1985','01-jan-1989',1);
insert into job_history values (92,'01-jan-1989','01-apr-1998',2);
insert into job_history values (92,'01-apr-1998',NULL,3);
insert into job_history values (91,'01-jan-1990','01-nov-2000',5);
insert into job_history values (91,'01-nov-2000',null,3);
insert into job_history values (93,'01-jan-1988','01-jan-1990',6);
insert into job_history values (93,'01-jan-1990','01-jan-2001',4);
insert into job_history values (93,'01-jan-2001',null,3);

drop table address_history;
create table address_history(person int, move_in_date date, move_out_date date,
address varchar2(40));
insert into address_history
  values (91,'01-jan-1990','01-jan-1995','Mary''s first house');
insert into address_history
  values (91,'01-jan-1995','01-jan-1997','Mary''s second house');
insert into address_history
  values (91,'01-jan-1997','01-jan-2000','Mary''s third house');
insert into address_history
  values (91,'01-jan-2000',NULL,'Mary''s current house');
insert into address_history
  values (92,'28-apr-1966','27-jul-1991','John''s first house');
insert into address_history
  values (92,'27-jul-1991','15-sep-1994','John''s second house');
insert into address_history
  values (92,'15-sep-1994',NULL,'John''s current house');
insert into address_history
  values (93,'01-jan-1964','01-jan-1977','Mike''s first house');
insert into address_history
  values (93,'01-jan-1977',NULL,'Mike''s current house');

drop table job_address_history;
create table job_address_history(job_id int, move_in_date date,
              move_out_date date, address varchar2(40));
insert into job_address_history
  values (1,'01-jan-1979','01-jan-1992','McDonalds''s first address');
insert into job_address_history
  values (1,'01-jan-1992','01-jan-1999','McDonalds''s second address');
insert into job_address_history
  values (1,'01-jan-1999',NULL,'McDonalds''s current address');
insert into job_address_history
  values (2,'01-jan-1985','01-jan-1997','Burger King''s first address');
insert into job_address_history
  values (2,'01-jan-1997',NULL,'Burger King''s current address');
insert into job_address_history
  values (3,'01-jan-1980','01-jan-2000','Oracle''s first address');
insert into job_address_history
  values (3,'01-jan-2000','01-dec-2000','Oracle''s second address');
insert into job_address_history
  values (3,'01-dec-2000',NULL,'Oracle''s current address');
insert into job_address_history
  values (4,'01-jan-1966','01-jan-2000','Wal-mart''s first address.');
insert into job_address_history
  values (4,'01-jan-2000',NULL,'Wal-mart''s current address.');
insert into job_address_history
  values (5,'28-apr-1966',NULL,'Can. Tire''s first and current address');
insert into job_address_history
  values (5,'27-jul-1991','15-sep-1994','Sybase''s First address');
insert into job_address_history
  values (5,'15-sep-1994',NULL,'Sybase''s current address');

      and we said...
first, thanks so much for an easy to run example -- create tables, insert
statements -- everything I needed.  That was most excellent (hint hint rest of
world)

well, if you have my book -- I think I cover this topic fairly well & verbosely.
 It's hard to visualize.  Once you learn how to "read it aloud", it might help.

For example -- the only differences in your queries is the last column Company
Address.

You had in the first (correct) query:

  ,first_value(job_address_history.address)
     over (partition by people.id
           order by    end_date desc nulls first
          )  Company_address

In "english" that says -- "take all of the ROWS in the ENTIRE result set, group
it by person.id.  Within each group of person.id, sort the data desc by
END_DATE, putting NULLS first.  Then, take the FIRST_VALUE of that group and
assign it to each row in that group (every row with the same person id)"

In effect that finds the most recent (null being most most recent)
job_address_history.address for a person.id (a person)

The second one (incorrect one) ask for:

  ,first_value(job_address_history.address)
     over (partition by job_address_history.job_id
           order by    end_date desc nulls first
          )      Company_address

that says partition the entire result set into groups by
JOB_ADDRESS_HISTORY.JOB_ID.  Now, sort each of these groups (which contain rows
for possibly many different PEOPLE) by end_date desc (nulls first).  Now for
each row with the same JOB_ID, assign the address of the most recent company
address.  So, each PERSON in the database who had the same job as some other
person will get the SAME address - the address for perhaps some other people.id.

In short, you wanted "first values" by people.id -- you always wanted to create
a partition (a window if you will) that looks as people.id and sorts the data in
that window looking for the "first value" for that PERSON.

Grouping the data by JOB_ID did some operations across people -- something you
didn't want to do.

It can be hard to visualize but maybe this will help:

o the analytics happen on the ENTIRE result set (they are done last)
o they work within the partitions, the groups, you define in that result set
o if you needed something "by person", person.id would be your partition key

Review &
      Followup

      Rating: 5
I'll sleep
      easier tonight!  March 15, 2002
Reviewer:  Steve
      McKee  from Montreal, Canada
Yes it did help. I think the comment that really makes it clear was:

 o the analytics happen on the ENTIRE result set (they are done last)

So if I understand correctly, you

   o execute the query,
   o get all the possible combinations for a given person,
   o define the partition window by person id,
   o then, for each column, decide how you want to order the result set to nail
down the column you want.

The other important thing to remember is that it's the "distinct" clause that
wraps it all up and returns the single, result row.

(sigh)

Would it be safe to say that in general, for a given query, the partition key is
always the same? What might change, though, is the order by (in some cases, it
may contain a combination of columns to nail down the row value you really
want).

Thanks a lot Tom (book is on order).

          Followup:

No, it's not safe to assume that.

Suppose - you wanted a report that shows:

a person
what they make
the salary of the highest paid person in their dept
the salary of the highest paid person with their same job
the salary of the highest paid person with the same mgr.

Well, that would be:

scott@ORA817DEV.US.ORACLE.COM> select ename || ' a ' || job "A person",
  2             sal   "makes",
  3             first_value(sal) over ( partition by deptno order by sal desc )
"Highest in Dept",
  4             first_value(sal) over ( partition by job    order by sal desc )
"Highest in Job",
  5             first_value(sal) over ( partition by mgr    order by sal desc )
"Highest/same mgr"
  6    from emp
  7  /

A person                  makes Highest in Dept Highest in Job Highest/same mgr
-------------------- ---------- --------------- -------------- ----------------
KING a PRESIDENT           5000            5000           5000             5000
CLARK a MANAGER            2450            5000           2975             2975
MILLER a CLERK             1300            5000           1300             1300
SCOTT a ANALYST            3000            3000           3000             3000
FORD a ANALYST             3000            3000           3000             3000
JONES a MANAGER            2975            3000           2975             2975
ADAMS a CLERK              1100            3000           1300             1100
SMITH a CLERK               800            3000           1300              800
BLAKE a MANAGER            2850            2850           2975             2975
ALLEN a SALESMAN           1600            2850           1600             1600
TURNER a SALESMAN          1500            2850           1600             1600
WARD a SALESMAN            1250            2850           1600             1600
MARTIN a SALESMAN          1250            2850           1600             1600
JAMES a CLERK               950            2850           1300             1600

14 rows selected.

In general- the partition, order by and range can and will change.  In your
case, it did not since you were "people" focused.

      Rating: 5
Just when I
      though I had it all figured out...  April 24, 2002

Reviewer:  Steve Mckee  from Montreal,
Hi Tom,

I have yet another problem with the first_value/last_value function. Here's the
query:

select  distinct
        substr(th_th_id,1,8),
        first_value(account_after)
          over (partition by substr(th_th_id,1,8)
             order by nvl(account_after,'0')||th_th_id
--to_char(start_time,'yyyymmddhh24miss')
                 desc NULLs first)
               from    pp_calls
where   account = 522229650001

When I use the first_value, I get results like:

20020306    148.84
20020307    72.68

So far, so good. When I use the last_value
(exact same query, just use the last_value
instead of first_value) I get:

20020306    0
20020306    96.2
20020306    106.2
20020306    111.2
20020306    116.2
20020306    122.08
20020306    127.08
20020306    132.08
20020306    137.08
20020306    148.84
20020307    0
20020307    1.38
20020307    3.88
20020307    55.92
20020307    72.68

I was expecting to get different values of course,
but not more rows. I am yet again confused.

You'll  probably spot the problem right away without sample data.
But if you require sample data, I will gladly forward it to
you.

Thanks.

Steve

          Followup:

If you have my book, I go through the analytic stuff in detail.  One comment I
make in there is:

...
Jumping ahead just a little to the very next section on windowing clauses ? it
can be said that the existence of an ORDER BY in an analytic function will add a
default window clause of ?RANGE UNBOUNDED PRECEDING?.  What that means is that
the set of rows to be used in the computation is the current and all preceding
rows in the current partition.  Without the ORDER BY the default window is the
entire partition.
....

-- the computation is the current and ALL PRECEDING (but not following) rows!!

So, sometimes it helps to just dump the entire thing and see what you got:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select substr( th_th_id, 1, 8 ),
  2         account_after,
  3         first_value( account_after)
              over ( partition by substr( th_th_id, 1, 8 )
  4           order by nvl(account_after,'0') || th_th_id desc nulls first ) fv,
  5         last_value( account_after)
              over ( partition by substr( th_th_id, 1, 8 )
  6           order by nvl(account_after,'0') || th_th_id desc nulls first )
lv1,
  7         last_value( account_after)
              over ( partition by substr( th_th_id, 1, 8 )
  8           order by nvl(account_after,'0') || th_th_id desc nulls first
  9           rows between unbounded preceding and unbounded following ) lv
 10    from t
 11   where account = 522229650001
 12   order by 1, nvl(account_after,'0') || th_th_id desc nulls first
 13  /

SUBSTR(T ACCOUNT_AFTER         FV        LV1         LV
-------- ------------- ---------- ---------- ----------
20020306           9.9        9.9        9.9        1.1
20020306           8.8        9.9        8.8        1.1
20020306           7.7        9.9        7.7        1.1
20020306           6.6        9.9        6.6        1.1
20020306           5.5        9.9        5.5        1.1
20020306           4.4        9.9        4.4        1.1
20020306           3.3        9.9        3.3        1.1
20020306           2.2        9.9        2.2        1.1
20020306         15.15        9.9      15.15        1.1
20020306         14.14        9.9      14.14        1.1
20020306         13.13        9.9      13.13        1.1
20020306         12.12        9.9      12.12        1.1
20020306         11.11        9.9      11.11        1.1
20020306          10.1        9.9       10.1        1.1
20020306           1.1        9.9        1.1        1.1
20020307       115.115    115.115    115.115    101.101
20020307       114.114    115.115    114.114    101.101
20020307       113.113    115.115    113.113    101.101
20020307       112.112    115.115    112.112    101.101
20020307       111.111    115.115    111.111    101.101
20020307        110.11    115.115     110.11    101.101
20020307       109.109    115.115    109.109    101.101
20020307       108.108    115.115    108.108    101.101
20020307       107.107    115.115    107.107    101.101
20020307       106.106    115.115    106.106    101.101
20020307       105.105    115.115    105.105    101.101
20020307       104.104    115.115    104.104    101.101
20020307       103.103    115.115    103.103    101.101
20020307       102.102    115.115    102.102    101.101
20020307       101.101    115.115    101.101    101.101

30 rows selected.

That shows that while FV is "fixed" in the window (default range is unbounded
preceding) the LV1 is the "last value UPTO the current row".  We have to specify
the RANGE to get the last last value in the partition!

      Rating: 5
Tom, you kill
      me!  April 24, 2002
Reviewer:  Steve McKee
       from Montreal
You are amazing. Answers fast and with data!! Thanks!!!

This was not obvious and I didn't spot it in
the Oracle manuals. I have your book but it's
at home and I'm at customer site in Mexico.

A huge thanks.

April 25, 2002 Anthony -- Thanks for the question regarding
      "ora-4068", version 8.1.7.2.1 EE

      You Asked
HI TOM,

Tnx for this oppurtunity again, Can You explain how does the ORA-4068 occurs and
how can I go about it?

How does oracle manages the freelist (8i) on LMT's? And how can we track the
freespace, used space, and fragmentation on LMT's?

What's the difference between creating the index with parallel option and not in
parallel mode ?

TNX
NOTNA

      and we said...
That error occurrs when

- you have a package
- someone used the package
- the package maintains a state
- you recreate the package, killing their state.

Freelists on LMT's are no different then freelists on DMT's.  Freelists are
based on SEGMENTS, not tablespaces.  LMT's do not get fragemented, that is one
of their properties.  No need to track that which does not happen.

Search for free.sql on this site to get a script that'll list free space, used
space and more by tablespace.

As for the index -- isn't it obvious?  One creates the index using more then one
thread/process (in parallel).  The other -- doesn't.
Review &
      Followup

      Rating:
      3
ora-4068  April 24, 2002
Reviewer:
      Anthony Velarde  from Philippines
Actually here's what's happenning,

    We have a two schema on one DB and two separate folders for the FMX. One for
development and one for QA. When we apply a fix on a certain procedure we
development was updated then after testing on development we update the QA
schema and recompile the forms from QA folder. When we execute the FMX on the QA
folder it's running well, but when we execute the FMX using the QA folder on the
DEVELOPMENT schema (I assure you that nobody updated the DEVELOPMENT schema
after the update on QA schema) we encountered the error ora-4068, but when we
compile the FMX from the QA folder using the DEVELOPMENT environment the problem
disappears. Sometimes I change the FORMS60_PATH from the regedit and the problem
disappears again (again I assure you that nobody is compiling procedures or
forms on both environments.
 I hope you can give me an explaination on this.
TNX.
ANTHONY

          Followup:

This is fixed by simply re-executing the package.  The default mode of checking
to see if things have changed is a timestamp mode.  When you compiled the forms
against different databases, the timestamps are different.

You can also look into using remote_dependencies_mode = signature mode instead
of timestamp (database level parameter)

April 25, 2002 Peter -- Thanks for the question regarding
      "Granting Select Privilege to System Views", version 8.1.7

      You Asked
Hi Tom,

I was trying to create the view, stats, that you have in
http://asktom.oracle.com/~tkyte/runstats.html
However, I ran
into problem trying to grant select privileges to some data dictionary views.

connect sys@mapdev03

SQL> grant select on v$statname to test_user;
grant select on v$statname to test_user
                *
ERROR at line 1:
ORA-02030: can only select from fixed tables/views

What's the right way to get around this problem so I can get the view created?

thanks

- peter

      and we said...
v$statname is a synonym

v_$statname is the view.

See the second requirement in the list
Review &
      Followup

      Rating:
      4
  April 26, 2002
Reviewer:  Vijay
       from India
That was a really nice one Tom.
Thanks :)

April 25, 2002 omer -- Thanks for the question regarding "why
      would you trace an exception", version 8.1.7

      You Asked
This will definitely rank as a silly question, but I have learnt over
the last couple of years that , silly questions should be the first ones to be
gotten out of our way.

what is a API  Application Program(ming) Interface

what does it actually mean

I read it in the context of
"Oracle8i PL/SQL provides an API for tracing the execution of PL/SQL
programs on the server. You can use the trace API, implemented on the
server as the DBMS_TRACE package, to trace PL/SQL functions, procedures, and
exceptions.
"

Now coming to dbms_trace package. How can this be usefull to me.

What does it mean to trace a procedure or a function, and especially an
exception. How and why would you trace an exception.

What kind of information we get when we trace a procedure or a function. Becasue
to get the timing statistics we already have dbms_profiler. What is dbms_trace
all about, can you tell with examples.

      and we said...
An API is a collection of functions someone provides to you to program with.
JDBC is an API.  ODBC is another one.  OCI from Oracle is yet another API.....

DBMS_TRACE is an API in that it is a set of functions we wrote for you.

You would use the trace package to have the server log information in a table as
your program executes.  If you are hitting some exception in your code and you
are having a hard time figuring out WHY -- you can use this trace to see what
code you are executing and where/why the exception is being thrown exactly.

See the documentation for more info on dbms_trace and just turn it on and use it
-- thats the best I can say here.  It is very self explainatory when you start
looking at what it logs as to what it provides.
Review &
      Followup

      Rating: 5
I beg your
      pardon  September 18, 2001
Reviewer:  Jonathan
      Nguyen
I observe that you are reluctant to give an example of fixing  errors of a
procedure or a function, using dbms_trace.

Why cant you give an illustration , as to how we can use this package. We know
how to set it up. You can start from there.

This is my most humble observation and request

      Rating: 3
Interpretation
      of DBMS_TRACE  April 25, 2002
Reviewer:  Jon
       from CT, USA
I searched your archives and read documentation, but I didn't find how I should
interpret NULL or <anonymous> in the event_unit.  We are trying to see where we
are spending our time in a batch job but over 60% of the event_units fall into
this category.  I don't know if that's telling us something or not.  Can you
shed any light on this?

Here is the summary of my data:

12:27:13  ==>  select count(*) FROM SYS.PLSQL_TRACE_EVENTS WHERE RUNID = 5;

 COUNT(*)
---------
    98550

12:27:22  ==>  select  count(*) FROM SYS.PLSQL_TRACE_EVENTS WHERE RUNID = 5 and
nvl(event_unit,'<anonymous>') = '<anonymous>';

 COUNT(*)
---------
    60551

          Followup:

anonymous is the <anonymous> block.

Begin
    code
end;

that is an "anonymous block".

If you:

 execute immediate 'begin code; end;'

that is an anonymous block.  It is simple an "unnamed block"

April 25, 2002 Ken -- Thanks for the question regarding "AskTom
      Architecture", version 8.1.7

      You Asked
Hi Tom,

Thanks for this great resource! I am sure there are many others out there like
myself who spend several hours a day reading the questions and your answers.

Since so many people seem to be very curious about the architecture of your
site, have you considered adding a page of site statistics?  I would be
interested in seeing how many hits the site gets per hour or per day, the size
of the database, etc.  I know that your site must be getting more popular by the
day and like my favorite sports team, I guess I'm just interested in how you are
doing.

I'm sure you are very busy with questions, conferences, books and such and
probably don't have the time.  By the way, the Expert book is outstanding! I
recommend it to everyone I talk to.

Best of luck, and thanks again!

Ken

      and we said...
Interesting Idea.  I'll have to actually change my architecture to accomidate
that tho!

I'll need a materialized view or two to support the traffic reports (currently,
I just scan away when I'm interested but the table has a couple hundred thousand
records in it -- people clicking away would kill me).

I'll do it some weekend when its raining out ;)

Thanks for the feedback on the book, appreciate it.
Review &
      Followup

      Rating: 5
...since your
      taking requests :-)  April 25, 2002
Reviewer:
      Connor McDonald  from UK
It would be nice to limit the search by date

Cheers
Connor

      Rating:
      3
Search/Archive  April 25, 2002
Reviewer:
      Irfan  from Kuwait
Hi Tom,
Just to say that in the page once when we enter after clicking the question,
'search' and 'archive' does not work with "right click" - "open in new window" -
every other link works with. I really apologize if its a problem with my IE
browser.
Thanks a lot for your solid support.

          Followup:

It's due to the javascript -- it is very common on many websites and it is not
limited to IE.

April 25, 2002 Yolanda -- Thanks for the question regarding
      "Finding maxextents for tablespaces", version 8.1.6

      You Asked
Tom,

I have a script that checks for tables and indexes that are
within 25 of their maxextent value, but I was wondering how
to find this same information for tablespaces.

I would appreciate any help you can provide.

Thanks,

Yolanda Herron

      and we said...
Unless it is a locally managed tablespace with UNIFORM sizing --
you cannot.  In a dictionary managed tablespace -- the number of
extents a tablespace "might" have left is a function of the next
extents on the objects therein, their pctincrease and so on.  If
I have a tablespace with 100 meg free and 3 objects -- one with
a next extent of 50meg and pctincrease = 0, another with a next
extent of 45meg and pctincrease=50 and the third with a next
extent of 101meg, I have 0, 1 or 2 extents left in the
tablespace.

The same problem exists for locally managed tablespace with
autoallocate.

Throw into the mix the fact that datafiles might be
autoextensible and you have a question that has no answer.

If it is a locally managed tablespace with UNIFORM sizing AND
all of the datafiles are autoextend=no you can simply integer
divide the sum of bytes from dba_free_space for that tablespace
by the extent size for that tablespace.  Otherwise -- there is
no answer.

Review &
      Followup

      Rating: 3
How to find how
      may times a datafile has extended  April 25, 2002

Reviewer:  Pawan  from USA
Tom,
   One of the tablespaces on our system is on autoextend mode. This system is
being used for last 6 months and now I want see how many times the datafile
extended and by how much so that I get an idea of the growth rate. Is there a
way to do it. Or is there some other way I can get this information on
tablespace growth over a period if the tablespace is in AUTOEXTEND mode.

          Followup:

You would have to take a snapshot of v$datafile over time to capture this
information.  We do not keep a history of autoextends.

April 25, 2002 Saradha -- Thanks for the question regarding
      "Multi Tasking in PL/SQL", version 8.1.7

      You Asked
Is there anyway possible in PL/SQL to invoke multiple sessions and run a
procedure in the multiple sessions simultaneously.

I want to use this in a real time applications where there are 250 users logging
into the application. The users are connected to Oracle through a client tool.
(Power Builder is the Front End Tool)

For example, if an user calls an stored procedure, that stored procedure has to
be run for 10 times with different parameter values.
I dont want to run this sequentially one after another for 10 times in the same
session because it may take long time.
I am looking for a way where I can run the stored procedure in 10 different
sessions simultaneously.

I thought about placing 10 jobs using DBMS_JOB.SUBMIT but because of
the heavy job load ( 250 users * 10 = 2500 jobs may be scheduled in the Job
scheduler at the same time and so on) our DBA group is looking for some other
better way.

Thanks in advance.
Pushparaj

      and we said...
Well, dbms_job is the only way and it'll be a "good" way since YOU control the
degree of concurrency.  Your DBA's will setup upto 36 job queue processes (1,000
in 9i) and that will be the degree of concurrency (so your 250 users will NOT be
able to kill the system).

If you didn't use jobs, you would just "fork them off" (unix term) and totally
kill the system.  You need something like DBMS_JOB so you have control over the
amount of resources people will consume.
Review &
      Followup

      Rating: 3
We have similar
      problem.  April 25, 2002
Reviewer:  Dilip M /
      JPMC  from New York, NY USA
Tom,
We have similar problem. (DBMS oracle 8.1.7/Frontend PB7).
appplication calls a backend procedure which takes about 4 minutes to complete
the task. In the mean time users wait  for task to complete.

Is there anyway, a job can be submitted to oracle and user can still work in the
current session?
Does oracle session work always sequentially or we can spawn thread?
If it allows to create thread or something similar, where can I find more
information?
If not, is it because RDBMS concepts doesnot premit or because of implementation
issues?

Thank you!

          Followup:

That is a great example of when you might want to use DBMS_JOB!!!  It was
designed to that.

Yes, your PB app can submit the stored procedure to be executed.  Soon after
committing, the job will be run in *another* session.  Your end user is free to
continue working.  I would suggest using DBMS_APPLICATION_INFO as a means of
"communicating" the jobs progress to PB.

      Rating: 5
Multi tasking
      in PL/SQL  April 25, 2002
Reviewer:  Saradha
       from NJ,USA
Tom,

Thank you very much for your quick response.
I feel very confident now after I got the reply back from you. Can you please
clarify the following on this issue.

Assume that there are 100 users and had sumitted 10 jobs each at the same time
and the next date value for all the submitted 1000 jobs are SYSDATE. (almost
same time, may be differences in seconds )
The parameters are set to the following.
JOB_QUEUE_PROCESS=36
JOB_QUEUE_INTERVAL=60

1) Does it mean that at any point of time oracle can only run 36 jobs in 36
sessions simultaneously and the next set will be picked up after 60 seconds only
if the already running 36 jobs completed.

2) Can I schedule a job in another instance through a db link. ?
I have two instanances in the same server and I have database link created in
each instance.
Out of 10 jobs, 8 jobs are executing a stored procedure compiled in the other
instance. So my question
is instead of scheduling all the 10 jobs in same instance, can I schedule the 8
jobs in the remote database. So that the work load on one database will be
minimized . Is it possible ?

3) Are there any other system parameters or system resources (OS level and
Database level) should I be considering to implement this in a more efficient
way so that the overall performance can be improved.

4) What are the disadvantages of using DBMS_JOB for an online application where
the jobs will be keep submitted for every minute or so.

5) Can you suggest me some books where I can get more information on this issue.

6) Are there any other tool (like Pro*C ) or Utility can be considered at all in
a replacement for DBMS_JOB.

Thanks again for your great help Tom.
Pushparaj

          Followup:

1)  That means there will be 36 concurrent threads of execution.  As jobs
finish, others will start.  You do not wait for the 36 to complete, when one
completes -- another is able to start.

2) Yes, you would run dbms_job.submit@remote_site(....)  There is an issue where
dbms_job takes a default parameter (instance) that is defaulted to a package
variable value.  You will HAVE to supply the value for that in this case.

3) besides thinking about why a process needs 10 parallel steps to complete?
No, not really.

4) I use dbms_job lots, nothing I am aware of.

5) using dbms_job?  I have a pretty good chapter on it (and other stuff) in my
book.  Not aware of any specific resource germane to your problem.

6) AQ (advanced queues) would be something else to look at.

April 26, 2002 Hakan -- Thanks for the question regarding
      "Truncate changes next extent automatically", version 8.1.6.2

      You Asked
hi tom,
to transfer modules from development to production, here is what
i do,
i take an export, and import it into the production,
then i change all tables&indexes next extents and pctincrease
(1m and 0)
then i use 'alter index ... rebuild tablespace INDX' to place
the indexes,
and then truncate the tables...
pls mention that developers use various storage parameters...

after that, when i start to pump the real data to tables,
the tables and indexes gets their next extent back, (maybe not
their, at least they are not 1m). also, although i set
pctincrease to 0, some of them strangely expands like
pctincrease<>0

i am really confused,
am i missing something ?

thank you very much.
have a good day.
hakan.

      and we said...
Well, before we dive into this -- I think you REALLY want to use
locally managed tablespaces with uniform sizes.  In this
fashion, the storage parameters are basically ignored (initial
is obey but if you say initial 5m with a uniform size of 1m
you'll get 5-1meg extents).

As for what happens with the truncate.  From the SQL Reference
manual:

...
The table’s storage parameter NEXT is changed to be the size of
the last extent deleted from the segment in the process of
truncation.
....

So, that is where the "mystery" extent sizes are coming from.
You want to do things in THIS order:

o take an export, import into production
o TRUNCATE then tables
o and THEN change the next
o and THEN rebuild the indexes (which will be much faster since
  the tables are empty)

Don't forget -- extent sizes are requests, not mandates.  We
feel free to round them up and will round them up -- to make
things fit (to avoid leaving really tiny holes).  That'll
explain the "pctincrease" which isn't really a pctincrease at
all probably.

Again -- I think you really want to rebuild the database using
locally managed tablespaces and avoid all of this alltogether.
It is the ONLY type of tablespace I use now (except for system
which cannot be locally managed)
Review &
      Followup

      Rating: 3
Please
      clarify  April 25, 2002
Reviewer:  Rob
       from Pittsburgh, PA
The table’s storage parameter NEXT is changed to be the size of the last extent
deleted from the segment in the process of truncation.

Tom:

Does "last" extent mean the last extent deleted and then
the truncate is finished or the last extent that was
allocated to the segment. We have noticed this change
of next extent size when we truncate after a direct path load with sqlldr. I
believe that direct path load trims the
last extent so it would seem necessary to reset next extent
after the truncate. Am I missing anything here.
(I know, I know, use LMT)

          Followup:

The table?s storage parameter NEXT is changed to be the size of the last extent
deleted from the segment in the process of truncation.

The extents should be removed in reverse order.  If you direct path load into
say an empty table and do not fill up the extent -- extent trimming will kick in
on that exent and that would change the next downwards.

      Rating: 3
Addl.
      clarification  April 26, 2002
Reviewer:
      A reader  from Pittsburgh, PA
Just to be clear, "reverse order" means the last extent
allocated to the table.
For example:

Table X has 1 initial extent, a direct path load causes
extent 2 to be allocated, as the load gets further along
extents 3,4 and 5 are allocated. Extent 5 would be trimmed
at the end of the load. Would truncate cause extent 1 or
extent 5 to be deallocated first.

Is it a valid workaround to use truncate with the reuse
storage clause?

          Followup:

no, not the last extent allocated.  Consider this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t
  2  tablespace system
  3  storage ( initial 1k pctincrease 10 )
  4  as
  5  select * from all_objects;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select extent_id, bytes, blocks
  2   from user_extents
  3  where segment_name = 'T'
  4  order by extent_id;

 EXTENT_ID      BYTES     BLOCKS
---------- ---------- ----------
         0      16384          2
         1      16384          2
         2      24576          3
         3      32768          4
         4      40960          5
         5      81920         10
         6      81920         10
         7      81920         10
         8      81920         10
         9      81920         10
        10     122880         15
        11     122880         15
        12     122880         15
        13     163840         20
        14     163840         20
        15     204800         25
        16     204800         25
        17     245760         30
        18     245760         30
        19     286720         35
        20     327680         40

21 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select next_extent from user_tables where
table_name = 'T';

NEXT_EXTENT
-----------
     335872

ops$tkyte@ORA817DEV.US.ORACLE.COM> truncate table t;

Table truncated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select next_extent from user_tables where
table_name = 'T';

NEXT_EXTENT
-----------
      16384

ops$tkyte@ORA817DEV.US.ORACLE.COM>

The extents were freed in reverse order, from 20 on back.

So, the example I was talking about is when the "first second" extent (that LAST
de-allocated extent) was trimmed.  Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t
  2  tablespace system
  3  storage ( initial 1k next 5000k )
  4  as
  5  select * from all_objects where 1=0;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select next_extent from user_tables where
table_name = 'T';

NEXT_EXTENT
-----------
    5120000

ops$tkyte@ORA817DEV.US.ORACLE.COM> host sqlldr 'ops$tkyte/xxx' t.ctl direct=true
parallel=true

SQL*Loader: Release 8.1.7.3.0 - Production on Fri Apr 26 11:13:24 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Load completed - logical record count 22902.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select extent_id, bytes, blocks
  2   from user_extents
  3  where segment_name = 'T'
  4  order by extent_id;

 EXTENT_ID      BYTES     BLOCKS
---------- ---------- ----------
         0      16384          2
         1    2580480        315    <<<< some trimming going on

ops$tkyte@ORA817DEV.US.ORACLE.COM> select next_extent from user_tables where
table_name = 'T';

NEXT_EXTENT
-----------
    5120000

ops$tkyte@ORA817DEV.US.ORACLE.COM> truncate table t;

Table truncated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select next_extent from user_tables where
table_name = 'T';

NEXT_EXTENT
-----------
    2580480   <<<<====== phenomena you observe

ops$tkyte@ORA817DEV.US.ORACLE.COM>

Now, if I do it like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t
  2  tablespace system
  3  storage ( initial 1k pctincrease 50)
  4  as
  5  select * from all_objects where 1=0;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select next_extent from user_tables where
table_name = 'T';

NEXT_EXTENT
-----------
      16384

ops$tkyte@ORA817DEV.US.ORACLE.COM> host sqlldr 'ops$tkyte/megan95' t.ctl
direct=true parallel=true

SQL*Loader: Release 8.1.7.3.0 - Production on Fri Apr 26 11:18:22 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Load completed - logical record count 22902.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select extent_id, bytes, blocks
  2   from user_extents
  3  where segment_name = 'T'
  4  order by extent_id;

 EXTENT_ID      BYTES     BLOCKS
---------- ---------- ----------
         0      16384          2
         1      16384          2
         2      24576          3
         3      40960          5
         4      81920         10
         5     122880         15
         6     163840         20
         7     245760         30
         8     368640         45
         9     532480         65
        10     778240         95
        11     204800         25

12 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select next_extent from user_tables where
table_name = 'T';

NEXT_EXTENT
-----------
      16384

ops$tkyte@ORA817DEV.US.ORACLE.COM> truncate table t;

Table truncated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select next_extent from user_tables where
table_name = 'T';

NEXT_EXTENT
-----------
      24576

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select extent_id, bytes, blocks
  2   from user_extents
  3  where segment_name = 'T'
  4  order by extent_id;

 EXTENT_ID      BYTES     BLOCKS
---------- ---------- ----------
         0      16384          2

ops$tkyte@ORA817DEV.US.ORACLE.COM>

the next is not 204,800 (last trimmed extent)

April 26, 2002 Venkatesh -- Thanks for the question regarding
      "SQL Query", version 8.1.7

      You Asked
Hi Tom,

    Could you please help me in forming the SQL Query.

    I have table called "DEFECTS"

    desc DEFECTS

    BUGNUMBER                 number
    PROJECT_ID                number
    REPORTED_ON_DT                    date
    DATE_FIXED                date
    DATE_CLOSED                date

    This table holds information about the Bugs.
    We have 3 date fields in the above table.

    REPORTED_ON_DT  --> date on which When the Bug is reported.
    DATE_FIXED    --> date on which When the Bug is fixed.
    DATE_CLOSED    --> date on which When the Bug is closed.

    My inputs will be 2 dates, I need report between these 2
        dates.

    Inputs :
    From_Dt :01-Jan-2002
    End_dt  :08-Jan-2002

    My report should like this.

           Date   Reported_Counts Fixed_Counts   Closed_Counts
    01-Jan-2002      2        0        1
    02-Jan-2002       0             0               0
    03-Jan-2002       0             6               1
    04-Jan-2002      12            10               1
    05-Jan-2002       8             6               0
    06-Jan-2002       4             3              10
    07-Jan-2002       0             0               0
    08-Jan-2002       3            10               5

    On 02-Jan-2002 & 07-Jan-2002, you can see all the counts
        are zero, even if there is no counts, I need to show them
        zero.

Thank you,
Venkat

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( reported_on_dt date,
date_fixed date, date_closed date );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t
  2  select to_date('01-jan-2002')+trunc(dbms_random.value(-10,10)),
  3         to_date('01-jan-2002')+trunc(dbms_random.value(-10,10)),
  4         to_date('01-jan-2002')+trunc(dbms_random.value(-10,10))
  5   from all_users
  6  /

34 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> delete from t where to_date( '05-jan-2002' )
in ( reported_on_dt, date_fixed, date_closed );

6 rows deleted.
so, we have at least one date without any data...

ops$tkyte@ORA817DEV.US.ORACLE.COM> variable from_dt varchar2(25)
ops$tkyte@ORA817DEV.US.ORACLE.COM> variable end_dt  varchar2(25)

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec :from_dt := '01-jan-2002'; :end_dt :=
'08-jan-2002';

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

REPORTED_ON_DT       DATE_FIXED           DATE_CLOSED
-------------------- -------------------- --------------------
09-jan-2002 00:00:00 28-dec-2001 00:00:00 30-dec-2001 00:00:00
01-jan-2002 00:00:00 10-jan-2002 00:00:00 01-jan-2002 00:00:00
30-dec-2001 00:00:00 10-jan-2002 00:00:00 29-dec-2001 00:00:00
10-jan-2002 00:00:00 01-jan-2002 00:00:00 31-dec-2001 00:00:00
03-jan-2002 00:00:00 04-jan-2002 00:00:00 07-jan-2002 00:00:00
01-jan-2002 00:00:00 07-jan-2002 00:00:00 27-dec-2001 00:00:00
30-dec-2001 00:00:00 25-dec-2001 00:00:00 25-dec-2001 00:00:00
04-jan-2002 00:00:00 08-jan-2002 00:00:00 06-jan-2002 00:00:00
30-dec-2001 00:00:00 07-jan-2002 00:00:00 04-jan-2002 00:00:00
08-jan-2002 00:00:00 10-jan-2002 00:00:00 26-dec-2001 00:00:00
07-jan-2002 00:00:00 04-jan-2002 00:00:00 24-dec-2001 00:00:00
31-dec-2001 00:00:00 30-dec-2001 00:00:00 01-jan-2002 00:00:00
25-dec-2001 00:00:00 07-jan-2002 00:00:00 31-dec-2001 00:00:00
31-dec-2001 00:00:00 07-jan-2002 00:00:00 23-dec-2001 00:00:00
29-dec-2001 00:00:00 31-dec-2001 00:00:00 25-dec-2001 00:00:00
03-jan-2002 00:00:00 08-jan-2002 00:00:00 25-dec-2001 00:00:00
03-jan-2002 00:00:00 23-dec-2001 00:00:00 08-jan-2002 00:00:00
01-jan-2002 00:00:00 06-jan-2002 00:00:00 07-jan-2002 00:00:00
10-jan-2002 00:00:00 09-jan-2002 00:00:00 27-dec-2001 00:00:00
26-dec-2001 00:00:00 01-jan-2002 00:00:00 06-jan-2002 00:00:00
09-jan-2002 00:00:00 23-dec-2001 00:00:00 27-dec-2001 00:00:00
07-jan-2002 00:00:00 24-dec-2001 00:00:00 23-dec-2001 00:00:00
29-dec-2001 00:00:00 03-jan-2002 00:00:00 04-jan-2002 00:00:00
03-jan-2002 00:00:00 07-jan-2002 00:00:00 10-jan-2002 00:00:00
26-dec-2001 00:00:00 23-dec-2001 00:00:00 29-dec-2001 00:00:00
06-jan-2002 00:00:00 03-jan-2002 00:00:00 06-jan-2002 00:00:00
23-dec-2001 00:00:00 07-jan-2002 00:00:00 03-jan-2002 00:00:00
29-dec-2001 00:00:00 03-jan-2002 00:00:00 07-jan-2002 00:00:00

28 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select dt,
  2            (select count(*) from t where reported_on_dt = dt)
reported_counts,
  3            (select count(*) from t where date_fixed = dt) fixed_counts,
  4            (select count(*) from t where date_closed = dt) closed_counts
  5   from ( select to_date( :from_dt )+rownum-1 dt
  6            from all_objects
  7           where rownum <= to_date(:end_dt)-to_date(:from_dt)+1
  8            )
  9  /

DT                   REPORTED_COUNTS FIXED_COUNTS CLOSED_COUNTS
-------------------- --------------- ------------ -------------
01-jan-2002 00:00:00               3            2             2
02-jan-2002 00:00:00               0            0             0
03-jan-2002 00:00:00               4            3             1
04-jan-2002 00:00:00               1            2             2
05-jan-2002 00:00:00               0            0             0
06-jan-2002 00:00:00               1            1             3
07-jan-2002 00:00:00               2            6             3
08-jan-2002 00:00:00               1            2             1

8 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review &
      Followup

      Rating: 5
SQL
      Query  April 26, 2002
Reviewer:  Venkatesh
       from Hyderabad
Tom,

   It really helped me in solving my problem.
   Thanks a lot for your help.

Thanks,
Venkatesh

      Rating: 5
Alternative
      approach  April 26, 2002
Reviewer:  Ed
       from Cheshire, U.K.
Excellent solution!

When I saw this I immediately thought of using decodes and an outer join.  The
trouble is, of course, that we've got 3 columns with the dates in and you can't
use outer joins with OR or IN.

I did get it to work with this, but it is a bit cumbersome and I've got a
nagging feeling it should be easier, but it is Friday afternoon here:

SELECT dt,
       NVL(reported_counts,0) reported_counts,
       NVL(fixed_counts,0) fixed_counts,
       NVL(closed_counts,0) closed_counts
FROM   (SELECT dt1,
               SUM(DECODE(t.reported_on_dt,dt1,1,0)) reported_counts,
               SUM(DECODE(t.date_fixed,dt1,1,0)) fixed_counts,
               SUM(DECODE(t.date_closed,dt1,1,0)) closed_counts
        FROM   (SELECT to_date( :from_dt )+rownum-1 dt1
                FROM   all_objects
                WHERE rownum <= TO_DATE(:end_dt)-TO_DATE(:from_dt)+1),
               t
        WHERE  dt1 IN (t.reported_on_dt,t.date_fixed,t.date_closed)
        GROUP BY dt1) summ,
       (SELECT to_date( :from_dt )+rownum-1 dt
        FROM   all_objects
        WHERE rownum <= TO_DATE(:end_dt)-TO_DATE(:from_dt)+1) dates
WHERE summ.dt1(+) = dates.dt

Definitely not as neat as yours!

Ed

          Followup:

That's interesting.  That was exactly my first thought as well.  I was going to
outer join it but as you discovered -- found it is not very straight forward.

I punted with the "easy" answer ;)

I also tested yours against mine with a couple of thousand rows.  Overall
results:

o if you add three indexes, one each on the date columns, mine performs better
(index reads for the subquerys)

o if you leave the indexes off -- yours performs much better as it full scans
once (mine would full scan three times PER ROW!!!)

Just food for thought.

      Rating:
      4
Excellent!!  April 26, 2002
Reviewer:
      Sanjay Raj  from USA
As ususal simple, Elehant and Excellent

      Rating: 3
Here is my 2c
      (against Tom's 98c ;-)  April 26, 2002
Reviewer:
      Ravi  from NY, NY
select DateCriteria, SUM(Reported_Counts),  SUM(Fixed_Counts),
SUM(Closed_Counts)
from (
  select REPORTED_ON_DT AS DateCriteria, count(*) AS Reported_Counts,0 AS
Fixed_Counts,0 AS Closed_Counts
  from DEFECTS
  where REPORTED_ON_DT between '01-Jan-2002' and '08-Jan-2002'
  group by REPORTED_ON_DT

  union all

  select DATE_FIXED, 0, count(*),0
  from DEFECTS
  where DATE_FIXED between '01-Jan-2002' and '08-Jan-2002'
  group by DATE_FIXED

  union all

  select DATE_CLOSED, 0, 0, count(*)
  from DEFECTS
  where DATE_CLOSED between '01-Jan-2002' and '08-Jan-2002'
  group by DATE_CLOSED

  union all

  select to_date('01-Jan-2002', 'dd-mon-yyyy')+rownum-1,0,0,0
  from all_objects where
  to_date('01-Jan-2002', 'dd-mon-yyyy')+rownum-1 <= to_date('08-Jan-2002',
'dd-mon-yyyy')
)
group by DateCriteria
/

April 26, 2002 pete -- Thanks for the question regarding
      "commonts on your knowledge example", version 8.1.7

      You Asked
Tom,
Below is a cut out from a slide in your powerpoint presentation.
Can you elaborate more on how the de-dup was done using analytic functions?

I have a need to do a similar task.
Thanks.

Truncate was fine, the rest -- not at all
Insert should be Insert /*+ append */, direct path insert
Select count(*) -- this was in PL/SQL -- should have been sql%rowcount (or even
select num_rows after the analyze!)
Analyze should have been estimate, and no need to compute histograms as they
were (no indexes, no need for them the way they used them)
De-Dup was redone using analytic functions (no one knew of these).  De-dup went
from at least 3 hours (6 hours if it found dups) to ZERO.
Steps 1, 2 execute serially, 3,4,5,6,7,8 in parallel, 9 and 10 serial
20 minutes…

      and we said...
Sure, there was a process where they inserted a bunch of data into a table and
then:

  insert into t ( c1, c2, .... )
  select c1, c2, ....
    from t1, t2, t3, t4, ....
   where ....;

  loop
     delete from t
      where (c1,c2) in ( select c1, min(c2)
                           from t
                          group by c1
                         having count(1) > 1 );
     exit when sql%rowcount = 0;
  end loop;

C2 was a date. the purpose of this loop was to remove rows with the older C2
having the same C1. Additionally -- IF all of the rows for a given C1 have the
same C2, these rows are to be removed as well.

We turned that into simply:

  insert into t ( c1, c2, .... )
  select c1, c2, ......
    from ( select c1, c2, .... ,
                   last_value(c2) OVER ( partition by c1
                               order by c2 nulls first
                               rows between unbounded preceding
                                        and unbounded following ) max_c2,
                   count(c2) OVER ( partition by c1
                          order by c2 nulls first
                          range between 0 preceding and 0 following ) cnt
             from t1, t2, t3, t4, ....
            where .... )
   where c2 = max_c2
     and cnt = 1;

That worked by finding the MAX(c2) for a given c1 (that is the row they wanted
to keep -- all others would be removed).  We only keep the rows where c2 =
max_c2.

Additionally -- we counted the rows in the partition that had the SAME c2 value.
 Then we only keep cnt=1 as they wanted to remove any C1 values whose max C2
value was repeated.

Analytic functions *rock*

Don't know if this applies to your specific case, but if you take a look at
analytics (i have a pretty good chapter on that in the book), you might be
surprised by what you can do with them.

April 26, 2002 Ganesh -- Thanks for the question regarding "Sql
      Query", version 8.1.7

      You Asked
Hi Tom,

How to write a sql to get the table information along with their corresponding
indices information for a particular owner?!!

ie., if there are 5 tables and each of them have at least 2 indexes, the result
should list each table followed by its indexes.

I did get it using a pl/sql block as shown below. But I have the feeling that it
is somewhat clumsy and can be done with the simple sql.
----------------------------------------
declare
cursor tab_cur is
    select table_name
    from sys.dba_tables
    where owner='AR';
cursor ind_cur(ltable varchar2) is
    select index_name
    from sys.dba_indexes
    where table_name=ltable;
cursor seg_cur(lsegment    varchar2) is
    select segment_name, segment_type, initial_extent,next_extent,extents,bytes
    from sys.dba_segments
    where segment_name=lsegment
    order by bytes asc;
begin
    for i in tab_cur loop
        for k in seg_cur(i.table_name) loop
        dbms_output.put_line(k.segment_name||'  '||k.segment_type||'
'||k.initial_extent||'  '||k.next_extent||'  '||k.extents||'  '||k.bytes);
        end loop;
        for j in ind_cur(i.table_name) loop
            for k in seg_cur(j.index_name) loop
        dbms_output.put_line(k.segment_name||'  '||k.segment_type||'
'||k.initial_extent||'  '||k.next_extent||'  '||k.extents||'  '||k.bytes);
            end loop;
        end loop;
    end loop;
end;
----------------------------------------

Would appreciate your help in this..

thanks
Ganesh

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> select segment_name,
  2             segment_type,
  3             initial_extent,
  4             next_extent,
  5             extents,
  6             bytes,
  7             decode( segment_type, 'TABLE', segment_name,
  8         (select table_name from dba_indexes where owner = 'SCOTT' and
  9                         index_name = segment_name ) ) tname
 10   from dba_segments
 11   where owner = 'SCOTT'
 12     and segment_type in ( 'TABLE', 'INDEX' )
 13   order by 7, 2 desc
 14  /

SEGMENT_NAME         SEGMENT_TY INITIAL_EXTENT NEXT_EXTENT EXTENTS      BYTES
TNAME
-------------------- ---------- -------------- ----------- ------- ----------
----------
BONUS                TABLE              524288      524288       1     524288
BONUS
DEPT                 TABLE              524288      524288       1     524288
DEPT
DUMMY                TABLE              524288      524288       1     524288
DUMMY
EMP                  TABLE              524288      524288       1     524288
EMP
NEW_T                TABLE              524288      524288       1     524288
NEW_T
SALGRADE             TABLE              524288      524288       1     524288
SALGRADE
T                    TABLE              524288      524288       1     524288 T
T1                   TABLE              524288      524288       1     524288 T1
SYS_C006644          INDEX              524288      524288       1     524288 T1
T2                   TABLE              524288      524288       1     524288 T2
SYS_C006645          INDEX              524288      524288       1     524288 T2
TRACE                TABLE              524288      524288       1     524288
TRACE

12 rows selected.

it seems a tad faster as well...
Review &
      Followup

      Rating: 4
Sql
      Query  April 29, 2002
Reviewer:  Ganesh
      Sundaresan  from Cincinnati, OH
Tom,

Thanks for the feedback. Yes, it runs faster too.

-Ganesh

April 27, 2002 berend -- Thanks for the question regarding
      "ORACLE TO SQLSERVER", version 8.0.6.0.0

      You Asked
L.S.

How can I make a link to MS-SQLServer?
Or how can I link from MS-SQLServer to my Oracle database?

B.J. Otten

      and we said...
From Oracle to SQLServer is done via a transparent gateway.  It
will allow for full read/write access to SQL Server with an
automatic two phase commit (data integrity).

See

http://www.oracle.com/gateways/
for details.
Review & Followup

      Rating:
      5
  May 21, 2001
Reviewer:
      A reader

      Rating: 3
Generic
      Connectivity  April 26, 2002
Reviewer:
      sivababu  from INDIA
Hello TOM,

Generic Connectivity is not working properly after configured.

    My Oracle 9i (personal edition ) IP  is 192.168.181.21 ( Windows 98 )
    Our Sqlserver 2000 Ip is  192.168.181.4  (windows 2000)

   1. The odbc is created in System dsn. it is called hsodbc
   2.  The values from C:\oracle\ora91\hs\admin\inithsodbc.ora
    HS_FDS_CONNECT_INFO = hsodbc
    HS_FDS_TRACE_LEVEL = 0
   3. The tnsnames.ora from  C:\oracle\ora91\network\ADMIN\tnsnames.ora

    hsodbc =
      (DESCRIPTION=
        (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.181.21)(PORT=1521))  --> i tried
both IP.
        (CONNECT_DATA=(SID=hsodbc))
        (HS=OK)
      )

    siva =
      (DESCRIPTION =
        (ADDRESS_LIST =
          (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.181.21)(PORT = 1521))
        )
        (CONNECT_DATA =
          (SERVICE_NAME = siva)
        )
      )
    4. The listener.ora from C:\oracle\ora91\network\ADMIN\listener.ora

     LISTENER =
      (ADDRESS_LIST=
           (ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))
           (ADDRESS=(PROTOCOL=ipc)(KEY=PNPKEY)))

    SID_LIST_LISTENER =
    (SID_LIST=
        (SID_DESC =
          (GLOBAL_DBNAME = siva)
          (ORACLE_HOME = c:\oracle\ora91)
          (SID_NAME = siva)
        )
        (SID_DESC=
              (SID_NAME=hsodbc)
              (ORACLE_HOME=c:\oracle\ora91)
              (PROGRAM=hsodbc)
            )
      )

    5. The sqlnet.ora has this line.
        NAMES.DIRECTORY_PATH= (TNSNAMES, ONAMES, HOSTNAME)

     6.  If i will do the following steps i"m getting error.

    SQL> connect scott/tiger@siva
    Connected.
    SQL> create database link hsodbc
      2   CONNECT TO sa IDENTIFIED BY sa
      3  using 'hsodbc';

    Database link created.

    SQL> select * from abc@hsodbc;
    select * from abc@hsodbc
                      *
    ERROR at line 1:
    ORA-28509: unable to establish a connection to non-Oracle system
    ORA-02063: preceding line from HSODBC

 I checked the Error message in the documentation. But i couldn't get where the
error is occurred.

 Thanks in advance and awaiting for your reply.

siva

          Followup:

does the sqlnet.ora have anything else in it.

have you used odbctest to see if the system dsn is functioning.

      Rating:
      2
listener.ora  April 26, 2002
Reviewer:
      mohammad toaha  from Dubai,UAE
For Mr.Sivababu,

Could you please try with dnsname or IP_address of your host instead of using
'Localhost' as a word as below:
     LISTENER =
      (ADDRESS_LIST=
           (ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))

Please,let us know your result.

          Followup:

Thats it!  the localhost is 127.0.0.1  -- there isn't a listener to connect to
on the ip addresses they are trying!  good eye.

      Rating: 4
SQL
      2000?  April 26, 2002
Reviewer:  Jer Smith
       from Salt Lake City, UT
Two questions on these products:

This web page:

http://www.oracle.com/gateways/gateway_bundles/gateways_datasheet.html
implies that it only works with SQL Server 7?  Will 2000 work?

Also, is there any Third Party or other product that will allow a link in the
opposite direction?  For many environments, that's the more common scenario for
two databases (SQL Server on a front-end installation, and Oracle as
Enterprise-level database)?

          Followup:

isn't sqlserver 2k  version 7?  lost track along time ago.

For the "opposite direction" -- you goto microsoft, yes, they have the gateway
to us.

      Rating: 5
Other Third
      Party  April 26, 2002
Reviewer:
      A reader  from Dryden, MI
To Jer Smith,

I believe there is a gateway from Information Builders (IBI) ,
http://www.ibi.com,/
it is called
the EDA Gateway server.  I belive it is the same thing as Oracle Transport
Gateway.

I am not sure if Data Junction will do this.  They are normally a one way system
but they were attempting to enter the space.

Good Luck.

Tom, keep up the great work!  Oracle is the best in our space.

      Rating: 3
Still not
      Connecting to Sqlserver 2000 ( version 8 )  April 27, 2002

Reviewer:  sivababu  from INDIA
Hello TOM,
   Thanks for your immediate reply.
 Regaring your First reply,
  1. The hsodbc system dsn is  successfully connecting.
  2. The Sqlnet.ora has only one line What i mentioned.
 Regarding your second reply,
  1. I tried local host ip  127.0.0.1 and my network ip 192.168.181.21 (ie my
machine with personal oracle 9i). But it is giving the same error.

  For Listener log :

TNSLSNR for 32-bit Windows: Version 9.0.1.1.1 - Production on 27-APR-2002
11:46:06

Copyright (c) 1991, 2001, Oracle Corporation.  All rights reserved.

System parameter file is c:\oracle\ora91\network\admin\listener.ora
Log messages written to c:\oracle\ora91\network\log\listener.log
Trace information written to c:\oracle\ora91\network\trace\listener.trc
Trace level is currently 0

Started with pid=4294389477
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=sivababu)(PORT=1521)))

TIMESTAMP * CONNECT DATA [* PROTOCOL INFO] * EVENT [* SID] * RETURN CODE
27-APR-2002 11:46:06 *
(CONNECT_DATA=(CID=(PROGRAM=)(HOST=)(USER=sivababu))(COMMAND=status)(ARGUMENTS=64
)(SERVICE=LISTENER)(VERSION=150999297)) * status * 0

   My computer name is sivababu and network username also sivababu.

   I couldn't get from where this problem is coming.  I'm working around this
problem for the past few days.

 Thanks and awaiting for your reply.

regards,
sivababu

          Followup:

We need to start over and not here.  Send me via email your:

tnsnames.ora
listener.ora
sqlnet.ora
inithsodbc.ora

from the SERVER this database link is created on and I'll take a look see.

can you tnsping this entry as well?

April 27, 2002 Rajashekar -- Thanks for the question regarding
      "Standby database using RMAN", version 8.1.7

      You Asked
Hello Tom,

I thank you for all the answers for all my previous queries.

I am planning to set a standby database for the production database. # The
production database backup is done using the RMAN i.e. all the data files and
archive redo log files.
#i would like to use the same backup repository of the archived redo logs in the
RMAN and apply them on the standby database.

If possible, can we automate
#To check the new archieve log backup in the RMAN repository
#If new archieve logs are backed up, restore and apply the same to standby
database

Regards
Raj

      and we said...
That is not the way standby works.  Standby and RMAN are different things, for
different purposes.  The ARCH process gets the archives to standby -- RMAN does
not.

The managed standby database automates everything you want to do already.
Please see

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76995/toc.htm
The software is already designed and built to do what you need. You just need to
configure it.
April 27, 2002
      mohammad -- Thanks for the question
      regarding "TKPROF", version 8.1.6

      You Asked
Please,explain different ways of TKPROF and finally the most appropriate answer
for the following question:

how do you make it so the developers can use TKPROF efficiently?

      and we said...
To get the real details -- I'll recommend a book -- mine.  I spend alot of pages
on this very topic.

I show how to enable tracing in a wide variety of ways.
How to make the trace files "publicly" accessible (so developers can read them).
and so on.

I'm not really sure what exactly you mean by "efficiently" -- I assume by that
you are having some problems but you don't say what they are?

If it is inability to read the trace files, see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1184635450789

April 27, 2002 deepa -- Thanks for the question regarding "Error
      in executing external procedure", version 8.1.7

      You Asked
I have created a dll,
Created a library also.

When i call the function  it gives the following error

:error mapping function
:cannot load dll

i have checked the tnsnames andlistener.ora files also.
I have placed the dll in the correct folder also.
I do know where i am going wrong.

      and we said...
and without an example -- neither do i.

do you have my book?  I spend over 70 pages on this topic alone.

Without a full example (.c file, how you compiled it, the .sql file, I cannot
help)

if you go here:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1200435685237
you'll see a short excerpt from my book which shows how to get the demo going --
to test your environment.

I'll make TWO guesses here tho.

1) _declspec (dllexport)  you didn't export the function
2) you are using C++ and the function name is getting mangled as per C++
mangling rules.
April 27, 2002
      Jeevan -- Thanks for the question regarding
      "BLOB Updation", version 8.1.7

      You Asked
I have one problem regarding BLOB updation. Right now I'm storing a XML file as
BLOB object in one table. Now if I want to update BLOB column, what I need to do
is read BLOB object, write the same in some physical file (file I/O), then after
that update the file and again update the object in table.

I don't want to do like this i.e. I want to avoid 'writing the object in
physical file'. Is it possible to directly update the BLOB object ? Idea in my
mind is something like, I will read the BLOB object then display the same in
some text area, change the text and when I say update, it will update the object
in DB. Can we do this way ? Please advice.

      and we said...
Yes you can, think about it.  If you could have:

1) read the blob out
2) written to file
3) updated file
4) read file
5) updated blob in table

you can easily replace steps 2, 3, 4 with "edit in text field".  Steps 1 and 5
would not change.

You would use dbms_lob.write to update the blob (or in java a stream, or in proc
exec sql lob...., or in oci the appropriate oci function call etc)

read:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/index.htm
there are developer guides there for LOBs.  You'll find them useful.

Review &
      Followup

      Rating: 5
A Question
      Regarding BLOB...  August 21, 2003
Reviewer:
      Riaz Shahid  from PRAL, Lahore,Pakistan
Dear Tom !

Here's the problem i am facing:

I am currently storing (well, just trying to store) the bussiness letters,
office orders etc in the database and i am using blob datatype for that. I want
user can:

(1) Type the letter in the MS Word (call word from the Form)
(2) Save it
(3) And that letter should be automatically saved in the database.
(4) If any user want to to see that letter later, he should click on some button
(or sort of like that) and the letter should be opened in MS Word (With same
formattings as it was at the time of save,in MS Word).

Can you help me regarding that ???

Note: The Letter No and the name of document would be the same. e.g; if letter
no is abc-2003-15 then the letter document name would be abc-2003015.doc.

Regards

April 27, 2002 Brad -- Thanks for the question regarding "Is
      there a performace penalty for very large rollback segments?", version
      8.1.7

      You Asked
Tom - We have had a few problems with the "ORA-01555 Snapshot too old" issue.
Part of it stems from application code that fetches across commits (while
updating the table it is fetching from).  In other cases, I can only asssume it
is delayed block clean-out because it is an CREATE INDEX that is failing and no
updates to the table are occurring during the create.

So, I intend to increase the amount of permanently allocated space of each RBS
-- but I want to understand any negative side-effects of doing so.

Our environment is mostly batch updates with rarely more than 10-20 concurrent
users or processes.  We currently have 7 rollback segments in a 2GB locally
managed tablespace, and can add space if needed. Each RBS was sized at only 4MB
(via OPTIMAL) when the database was created. I am thinking of increasing the
size of each RBS to 200MB.

Q1) Is there a performance penalty for increasing the permanently allocated size
of each RBS?

Q2) When a data block is written to a RBS, does it always write past the current
high water mark before it wraps around?  Or, does each newly assigned
transaction overwrite committed blocks where ever it finds them?

Q3) In 9i, with system managed undo, would this problem take care of itself or
is there an initialization parameter that I'd need to tweak?

Thanks for your amazing service to the Oracle community.  I've not found a
better source for the kind of information you provide.

      and we said...
There are none that I am aware of.  I am a huge fan of right sized rollback --
you are going down the correct path.

In 9i, you'll simply set the retention period on the undo tablespace and not
worry about it at all.

q1) none that I'm aware of

q2) it is a pure circular buffer, not a heap.

q3) the retention period, you have to tell us how long to retain undo.

You want to use lots of extents, say 200, 1meg extents -- for your RBS.  The
jump from 4m to 200m is large. You could monitor how fast you wrap rbs and
figure out about how long you need to to NOT wrap and size them a little
smaller...
April 27, 2002 Gunjeet -- Thanks for the question regarding
      "insert bulk data from java program", version 8.1.6

      You Asked
Hi Tom,

We are planning to read a large amount of data from a flat file
in a java application service and insert it into the database.
How can I do bulk inserts through JDBC ? I know that in PL/SQL,
one would use bind variables to do bulk inserts, but I'm not
sure if it can be achieved when data is to be passed from a java
program. Will the performance be an issue ? If you can please
give me an example of doing bulk inserts through JDBC, it will
be very helpful.
We can then use REF CURSORS to return the data from PL/SQL
procedures
back to java.

Thanks in advance,

Gunjeet

      and we said...
See

http://technet.oracle.com/doc/oracle8i_816/java.816/a81354/oraperf1.htm#1056233
there are two ways to do it.  the way I do it is to set the
execute batch to N (where N > 1).  Then I use a prepared
statement.  Oracle will automatically batch the inserts and do
an array insert every N rows, when you commit or when you close
the statement.
Review & Followup

      Rating: 4
insert bulk
      data from java program  March 12, 2001
Reviewer:
      Gunjeet Singh  from CA, USA
Thanks for pointing me to the relevant docs.

      Rating:
      4
  April 27, 2002
Reviewer:  Readwood
       from USA
That is good for java.
How to do it in pro*c

          Followup:

We do that with array inserts.

See

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76942/pc_08arr.htm#390

April 28, 2002 mohan -- Thanks for the question regarding "Is it
      required any patches ", version 8.1.6

      You Asked
Hai tom

 1)Is it required any patches  migrating from oracle 7.3.2 to 8/8i/9i on
windows/linux/solaries platform?

2)Is it any way to find out the transaction size?

3)i craeted ddl trigger ,then i am able to dropping tables ,but while altering
table it returns the below error
ex:
sql>alter table x4 add name varchar2(10)
error at line 1:
ora-00604:error occured at recursive sql level 1
ora-01401:inserted value too large for column
ora-06512:at line 13\
Can explain the cause

Thanks in advance and waiting for ur reply
MOHAN

      and we said...
1) contact support or see the admin installation guide.  The neccessary
versions/patch levels of the operating system are there for each operating
system.

2) I assume you mean "amount of redo generated".  Yes,

  1  select a.name, b.value
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4  and lower(a.name) like  '%redo%'
  5* and b.value > 0
ops$tkyte@ORA817DEV.US.ORACLE.COM> /

NAME                                VALUE
------------------------------ ----------
redo entries                           15
redo size                            3980

ops$tkyte@ORA817DEV.US.ORACLE.COM> update emp set ename=ename;

14 rows updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> delete from emp;

14 rows deleted.

ops$tkyte@ORA817DEV.US.ORACLE.COM> @afiedt.buf

NAME                                VALUE
------------------------------ ----------
redo entries                           57
redo size                           14260

If I subtract 3980 from 14260 -- i get the amount of redo generated for those
two statements.

3) Same exact thing as here:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3109217869403
you have a bug in your trigger on line 13.
Review &
      Followup

      Rating:
      3
  April 28, 2002
Reviewer:  mohan

Hai tom
Thanks for your immediate reply,and your explaination is pretty well and helped
me a lot.Onece again thanking you .

I have a question

When user submit a transaction ,that sequentely using rollback segments.How to
find out which rollback segment is used and how much time it taken to complete
the transaction and  size of the transaction?.
I know you  can explictly assign rollback segment by using set transaction
Please clarify this
Thanks in advance and waiting your reply
Mohan

          Followup:

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:561022954613
for how to find the rbs for a transaction.  USED_UBLK in v$transaction tells you
the size.  START_TIME tells you when it started.

To read about my view of "picking a rollback segment" i suggest you pick up a
copy of my book.  I'm not a fan of this practice.  It means quite simply you
haven't sized your RBS to do your workload.  In 9i -- with undo segments -- it
is thankfully "not even meaningful" any more.

April
      28, 2002 Sheshagiri -- Thanks for the
      question regarding "Security Issues", version 8.1.6

      You Asked
Can you highlight the important security features which are not
available in other databases and provide us a third-party site
which could give us an independant comparison?

Regards,

Sheshu

      and we said...
Wow, there are alot of security features would could go through.
 A good overview would be:

http://www.oracle.com/database/documents/security_in_o8i_twp.pdf
I am not personally aware of any third part site to get an
"independent" comparision.

Here are a couple of features that are pretty much unique to
Oracle that come to mind right now.  What would help however --
to answer a question like this -- is to understand your needs.
There are so many features, we could spend a day talking about
them and still not cover the ones relevant to you.  A big list
of features is fine, but if it cannot do what you require, a
big list is not very useful.  Understanding what you need would
be really important in answering a question like this.

Virtual Private Database

http://osi.oracle.com/~tkyte/article2/index.html
for more info on that feature.  One of my favorites.

Advanced Security Option

http://www.oracle.com/database/options/security/
for more info on that.  In a nutshell, its an option that offers
things like:
o encryption and data integrity on the wire
o user authentication options like biometrics, kerberos and so
on
o single sign on
o DCE integration

Encryption of Stored Data

http://technet.oracle.com/doc/oracle8i_816/server.816/a76936/dbms_obf.htm#6518
for more info.  Supplied packgages to encrypt/decrypt stored
data.

Middle Tier Authentication

http://osi.oracle.com/~tkyte/Misc/OCI-N-Tier.html
for more info.  Basically, the ability to have proxy accounts
set up on behalf of users to allow a middle tier to perform
operations on their behalf -- a way to not lose the identity of
the user in the database when using an application server.

Invokers Rights/Creators Rights procedures

http://technet.oracle.com/doc/oracle8i_816/server.816/a76962/ch2.htm#104095
for more information.  Allows procedures to run as "caller" or
as "owner" providing some unique opportunities to secure data.

Review &
      Followup

      Rating: 3
Link page not
      found  August 07, 2001
Reviewer:  Senthil
       from Qatar
Security in 8i twp.pdf - Page was not found

      Rating:
      4
  April 27, 2002
Reviewer:  Sean
       from Atlanta
How do you define the securities on?
1) Application level
2) Database level

Thanks

          Followup:

What is the difference?  Define your terms. To me, the data is either secured or
not...

      Rating: 1
link not
      correct  April 28, 2002
Reviewer:  ashraf
       from kuwait

          Followup:

Yes, things move over time.  instead of the two www.oracle.com links use:

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/network.901/a90148/toc.htm
for a general overview and ASO (advanced security option) description.

The other links all work fine.

April 28, 2002 Isaac -- Thanks for the question regarding
      "Improve calls of before/after STATMENT trigger with Bulk binds use",
      version 8.1.6

      You Asked
Hello,

I see that using bulk binds improve performance.
My question is if it also can improve use of calling before/after statement
triggers?

I have a table with: after each row update trigger and after statement update
trigger.

A batch process update every time an unique row of this table (only one column )
by the primary key.

The problem is that for every update, the after statement update trigger is
performed.

I want to perform the update, so only after all the updates, the <after update
statement trigger> will be performed.

I'll think to use two arrays: the first with the the primary key (only one
column) and the second with the new values and using loop collection treatment
with bulk bind to solve the problem but I receive the same problem: for every
row, the after update statement trigger is performed.

CREATE TABLE it_1
    (a                              VARCHAR2(10))
/
CREATE OR REPLACE TRIGGER it_1_bus_tr
AFTER UPDATE
ON it_1
REFERENCING NEW AS NEW OLD AS OLD
begin
  dbms_output.put_line( 'after update statement' );
end;
/
CREATE OR REPLACE TRIGGER it_1_bu_tr
AFTER UPDATE
ON it_1
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
begin
  dbms_output.put_line( 'after update each-row' );
end;
/

declare
  type t_a is table of varchar2(2);
  pa t_a := t_a();
begin
  pa.extend(10);
  for i in 1..10 loop
    pa(i) := i;
    insert into it_1 values( i );
   end loop;
  commit;
  forall i in pa.first..pa.last
    update it_1 set a = pa(i)  where a = pa(i);
end;

      and we said...
It'll fire the update statement trigger N times.

This will run faster then before -- but only because updating in bulk is faster
then updating a row at a time.  It will not cause the trigger to fire less
frequently (there are still N updates -- you are not doing a single update, it
is still an update in a loop)

If you want the trigger to fire once, you will:

ops$tkyte@ORA817DEV.US.ORACLE.COM> CREATE TABLE it_1
  2      (a                              VARCHAR2(10))
  3  /

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> CREATE OR REPLACE TRIGGER it_1_bus_tr
  2  AFTER UPDATE
  3  ON it_1
  4  REFERENCING NEW AS NEW OLD AS OLD
  5  begin
  6    dbms_output.put_line( 'after update statement' );
  7  end;
  8  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create global temporary table temp ( a
varchar2(10) primary key )
  2  on commit delete rows;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into it_1 select rownum from
all_objects where rownum <= 10;

10 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2    for i in 1..10
  3    loop
  4          insert into temp values ( i );
  5    end loop;
  6    dbms_output.put_line( 'updating.....' );
  7
  8    update ( select it_1.a it_1_a, temp.a temp_a
  9                           from it_1, temp
 10                          where it_1.a = temp.a )
 11       set it_1_a = temp_a;
 12  end;
 13  /
updating.....
after update statement

PL/SQL procedure successfully completed.

update a join.

Review &
      Followup

      Rating:
      4
  April 28, 2002
Reviewer:  Isaac
      Chocron  from Israel
Why don't you use pl/sql object table instead of temporary table?

          Followup:

You need a primary key on the temp table in order to do the update on the join
view.  PLSQL objects don't have primary keys.

April 28, 2002 --
      Thanks for the question regarding "Centralized Security", version
8.1.7.2.1

      You Asked
Hello Tom-

I have been asked to evaluate an easier to manage centralized security model.
Our current environment exisits of 20+ oracle servers with a minimium of 10
instances per server with same data models with multiple data centers. Somedays,
it seems the DBAs are just trying to keep up with security role and user
changes.

We are using a web application to dbs, if that helps with the visualization of
the environment.

I have been speaking to our Oracle Tech Reps, and they say that Oracle Internet
Directory Server (LDAP) would help out in this problem because you can use
chained OID servers.  How? Is there a better way?

I haven't found too much information on this type of security model besides lots
of marketing fluff.

Your opinion, would be greatly appreciated.

      and we said...
10 instances/server!  Oh my.  That is a bit extreme.  Looking at your companies
web site -- it really sounds like you need to use:

http://asktom.oracle.com/~tkyte/article2/index.html
and virtualize your offering so you can run ONE instance/server.  You'll be able
to get alot more out of each server that way -- you might find you only need 10
servers.  I can get you from 200 instances to 10 or less.

This is how Oracle runs its hosted software -- using VPD.  It is not cost
effective to put up an instance/customer when you can handle thousands of users
in a single instance.  Your management costs go up and up and up.  Your "user
changes" problem would virtually vanish.

Even so, you can use "enterprise" or global users.

See

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/network.901/a90148/toc.htm
for a fairly comprehensive overview.  that is a 9i doc but it is 80% or more
relevant to 8i as well.
April 28,
      2002 -- Thanks for the question
      regarding "Internationalization", version 901

      You Asked
Hi Tom
Somehow  there is very little practical and implementation specific knowledge on
this topic available. I was wondering if you have touched upon this subject in
your expert book.

Please help me in unraveling some doubts on the following.
Varchar2 vs Nvarchar

1)When I create a table with a nvarchar2(20) column, this creates an
NVarchar2(60) datatype.
Why does oracle do this? Does this mean that oracle specifies this column to be
a fixed width 3 byte character.

2)In oracle 9i we can specify the following
create table test(name varchar2( 20 char));

This means that now we can add 20 characters whether single or multibyte(2,3,4).

So does this mean that now we dont need to use Nchar or nvarchar2 datatype. What
is the essential difference between varchar & nvarchar.

3) Can I store unicode characters in a varchar column. Suppose I have a
varchar2(60) column . I am sure this can store a spanish character as well as
english. So the only possible loss would be character truncation . But if I
design this column to take the max of spanish characters {eg
(varchar2(200))}that can be stored in this column, then I dont have to use
nvarchar?Is this correct.

Thanks for the help

      and we said...
No, my book doesn't really cover it as it is something I rarely touch.

1) Well, it doesn't, you would have to tell me your characterset in order to
comment.

ops$tkyte@ORA9I.WORLD> create table t ( x varchar2(20 char),
  2                                   y varchar2(20),
  3                                   z nvarchar2(20)
  4                             )
  5  /

Table created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> desc t
 Name                          Null?    Type
 ----------------------------- -------- --------------------
 X                                      VARCHAR2(20 CHAR)
 Y                                      VARCHAR2(20)
 Z                                      NVARCHAR2(20)

2) Nvarchar types in 9i are purely Unicode datatypes (changed from 8i).

See

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/server.901/a90120/ch4_dep.htm#9789
that also has a pointer to the Oracle9i Globalization Support Guide, which is
the book you are looking for.

varchar2(20 char) means you can store 20 characters -- whereas varchar2(20)
means you can store 20 bytes.  the varchar2(20 char) might take 20, 40, 60, 80
or more bytes to hold a string.  The maximum length of a varchar2 is still 4000
bytes so varchar2(4000 char) is "misleading" in that the field will max out at
4000 bytes -- not 4000 characters.

This is meaningful in multi-byte encoding schemes.

3) Yes, if your database uses such a characterset (eg: utf8)

You would use varchar2(60 char), not varchar2(200) -- that would be the better
practice.

April 28, 2002 -- Thanks for the question regarding "Ref Cursors
      and Parsing", version 8.1.7

      You Asked
We have an OCI based OLTP application (C++) that I am trying to tune.  The
application fetches data from Oracle by executing several packaged procedures
(PLSQL) that simply return a ref cursor to the application using OPEN FOR wit
static SQL.  Each time a procedure is executed, the application fetches rows
using the returned cursor then closes the ref cursor.

Since ref cursors cannot be cached by PLSQL there is going to be a soft parse
each time one of these procedures is executed.  Now I know that by using
session_cached_cursors that we can make it the softest of soft parses.  But I
need this application to be the fastest of the fast.  So I am wondering if there
is any way to eliminate the soft parsing altogether?

I have researched it, but so far have found no way other than moving all of the
SELECT statements from the PLSQL packages into the application code itself and
having the application execute the SQL directly.  For maintenance and security
reasons I would prefer to keep the SQL in a PLSQL package.  Do you know of any
solution?

      and we said...
The session_cached_cursors is very good.

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:904605517791
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1391892424274
(that shows the latching goes wayyyy down and that is the
performance/scalability killer)

Using ref cursors - there is no way, there cannot be.  You see, you have a
procedure P that returns a ref cursor.  You can call this procedure, get a ref
cursor back.  NOW before you even fetch a row, you can call P again and get
another, and another -- P is not "re-entrant" with respect to the ref cursor!
Each cursor returned from P must be a unique, new cursor since you can open N
number of cursors with it simultaneously.

If you were to move the code from plsql into oci, you might achieve a better
softparse BUT overall you might also see a large decrease in performance over
the network as you now become "chattier".  I would personally stick with the ref
cursors myself.
April 28, 2002
      Munzer -- Thanks for the question regarding
      "Indexes and Cursors", version 8.1.7

      You Asked
Tom:

I always wondered about these two questions:

1.  When you create a cursor in oracle

Cursor emplist is select * from emp

and then you open the cursor, let us say that emp has 10,000,000 records.  Does
oracle allocate server memory to whatver the size of the  ten million records
(10 mil x record size).

Can it crash if it can not find enough memory.

2.  When you do a query on a lastname for example on a table without using an
index, does oracle do a full table scan meaning that it goes through ALL columns
of each record in the table.

If you create an index, does oracle start at the record that starts with first
letter of query. For example, if you are searching for 'Smith', does it start in
the index where there is 'S' and then stop when it hits 'T'.  So it scans all
records that start with 'S'.

Is that how indexes work and make things work.

THank you,

      and we said...
1) see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:839027059026
we do not pre-answer the question.  we fetch the data from the tables as needed.
 If we do need to materialize a result set, we would use sort_area_size bytes in
ram and "swap" to temp otherwise.

No, it'll not crash, although you could run out of temp space.

2) if lastname is the LAST column physically in the table -- yes.  Else no,
it'll just piece together enough data to get the answer.  We can see this with a
small test:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t
  2  ( lastname_1 varchar2(20),
  3    a char(2000), b char(2000), c char(2000), d char(2000),
  4    lastname_2 varchar2(20) );

Table created.

here i have a table that in an 8k or less block size database will have
chained rows if a, b, c, d are filled in.  lastname_1 will be on a different
block from lastname_2

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select username, 'a', 'b', 'c',
'd', username from all_users;

34 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;
Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4  and a.name = 'table fetch continued row'
  5  /

NAME                                VALUE
------------------------------ ----------
table fetch continued row               0

that shows that in this session, we haven't hit any chained rows yet (we've
created 34 of them tho!)

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where lastname_1 = 'x';
no rows selected

ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4  and a.name = 'table fetch continued row'
  5  /

NAME                                VALUE
------------------------------ ----------
table fetch continued row               0

that shows, this query didn't do any chained rows processing -- it did not
piece the rows together.  lastname_1 is at the front of the row, hence it did
not need to.  Now:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where lastname_2 = 'x';

no rows selected

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4  and a.name = 'table fetch continued row'
  5  /

NAME                                VALUE
------------------------------ ----------
table fetch continued row              34

ops$tkyte@ORA817DEV.US.ORACLE.COM>

shows that in order to get to lastname_2 we had to piece together lastname_1,
a, b, c, d and then lastname_2.  So, it depends on where lastname is physically
in the table

Indexes are NOT done a letter at a time.  They are done by key entries -- full
key entries.  It would not scan all S's.

April 28, 2002 Randall -- Thanks for the question regarding "CBO
      and views", version 8.1.7

      You Asked
I have a problem with queries doing full table scans on
tables when selecting through a view.  My system is Oracle 8.1.7.3

Here is my view:

CREATE VIEW CMR.V_EMPLOYEE AS
   SELECT PKEY,
          LOGINNAME,
          FIRSTNAME,
          LASTNAME,
          EMAIL,
          LASTNAME || ', ' || FIRSTNAME FULLNAME,
          SAPEMPLOYEEID,
          ACTIVE,
          EMPLOYEERESTRICT
     FROM EWS.EMPLOYEE
/

Here is a few simple queries.  I need to do
an outer join on the view as the EMP_ID columns
may be null in my changerequest table.

SQL>
SQL> select e.lastname||', '||e.firstname fullname
  2  from cmr.changerequest cr,
  3   cmr.v_employee e
  4  where cr.owner_id = e.pkey(+)
  5  and cr.cr_id = 32177;

FULLNAME
----------------------------------------------------
SMITH, GREGG

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1 Bytes=51)
   1    0   NESTED LOOPS (OUTER) (Cost=3 Card=1 Bytes=51)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'CHANGEREQUEST' (Cost=2 Card=1
Bytes=8)

   3    2       INDEX (UNIQUE SCAN) OF 'PK_CHANGEREQUEST' (UNIQUE) (Cost=1
Card=2)

   4    1     TABLE ACCESS (BY INDEX ROWID) OF 'EMPLOYEE' (Cost=1 Card=23435
Bytes=1007705)

   5    4       INDEX (UNIQUE SCAN) OF 'PK_EMPLOYEE' (UNIQUE)

This one works fine.  used the PK_EMPLOYEE index to join.

SQL>
SQL> select e.fullname
  2  from cmr.changerequest cr,
  3   cmr.v_employee e
  4  where cr.owner_id = e.pkey(+)
  5  and cr.cr_id = 32177;

FULLNAME
----------------------------------------------------
SMITH, GREGG

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=44 Card=1 Bytes=49)
   1    0   NESTED LOOPS (OUTER) (Cost=44 Card=1 Bytes=49)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'CHANGEREQUEST' (Cost=2Card=1
Bytes=8)

   3    2       INDEX (UNIQUE SCAN) OF 'PK_CHANGEREQUEST' (UNIQUE) (Cost=1
Card=2)

   4    1     VIEW OF 'V_EMPLOYEE'
   5    4       TABLE ACCESS (FULL) OF 'EMPLOYEE' (Cost=42 Card=23435
Bytes=1007705)

But, if I select the FULLNAME column which is defined in the view,
CBO does a full table scan on employee.

SQL>
SQL> select e.fullname
  2  from cmr.changerequest cr,
  3   cmr.v_employee e
  4  where cr.owner_id = e.pkey
  5  and cr.cr_id = 32177;

FULLNAME
----------------------------------------------------
SMITH, GREGG

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1 Bytes=51)
   1    0   NESTED LOOPS (Cost=3 Card=1 Bytes=51)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'CHANGEREQUEST' (Cost=2 Card=1
Bytes=8)

   3    2       INDEX (UNIQUE SCAN) OF 'PK_CHANGEREQUEST' (UNIQUE) (Cost=1
Card=2)

   4    1     TABLE ACCESS (BY INDEX ROWID) OF 'EMPLOYEE' (Cost=1 Card=23435
Bytes=1007705)

   5    4       INDEX (UNIQUE SCAN) OF 'PK_EMPLOYEE' (UNIQUE)

And if I remove the Outer Join, CBO will use index even if I select the FULLNAME

column from the view.

The problem is magnified in my real query as I have four EMP_ID columns that I
join to v_employee so I get four full table scans.
What makes CBO skip the index and do a full table scan?

Thanks for any input.

      and we said...
This is more complex then it appears.

The two queries you have are *different*, will return different results.  This
query:

SQL> select e.lastname||', '||e.firstname fullname
  2  from cmr.changerequest cr,
  3   cmr.v_employee e
  4  where cr.owner_id = e.pkey(+)
  5  and cr.cr_id = 32177;

returns "," when the row in E is "made up".  The other query:

SQL> select e.fullname
  2  from cmr.changerequest cr,
  3   cmr.v_employee e
  4  where cr.owner_id = e.pkey(+)
  5  and cr.cr_id = 32177;

does not -- it returns NULL.  This fact will help me explain the complexity
involved here (we have optimizations added in 9i that fix this and a possible
solution for you in 8i -- in fact 804 and above).

Here is the complexity -- the column fullname, when outjoined to, must be
materialized BEFORE the outer join.  It cannot be done "after" since the
differences will appear (eg: one query gives ",", the other query gives null).
The optimization they added is to turn queries against non-simple columns in the
view being outer joined to from simply:

    LASTNAME || ', ' || FIRSTNAME

to the equivalent of:

   decode( OUTER_JOINED_COLUMN, NULL, NULL, LASTNAME || ', ' || FIRSTNAME )

so, in your case, the query would be rewritten (internally) as:

SQL> select decode( e.pkey, null, null, e.fullname ) fullname
  2  from cmr.changerequest cr,
  3   cmr.v_employee e
  4  where cr.owner_id = e.pkey(+)
  5  and cr.cr_id = 32177;

and that removes that ambiguity, allowing the view to be merged.

In 8.0.4 - 8.0.6 -- you would set "complex_view_merging" = true (init.ora or
session level) parameter to get this behavior (I setup a small test case to see
this).  In 8i, this parameter is obsoleted but _COMPLEX_VIEW_MERGING exists (and
is set for all Oracle application instances so it is widely used.  Please
contact support for any issues you might need to be aware of when using this
parameter as it is undocumented).

Here is an example:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t2
  2  as
  3  select *
  4    from all_objects
  5   where 1=0;
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t2_idx on t2(object_id);
Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view big_table_v
  2  as
  3  select OWNER, OBJECT_NAME, SUBOBJECT_NAME, OBJECT_ID,
  4         DATA_OBJECT_ID, OBJECT_TYPE, CREATED, LAST_DDL_TIME,
  5         TIMESTAMP, STATUS, TEMPORARY, GENERATED,
  6         owner || '.' || object_name fullname
  7    from t2
  8  /
View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int, y int );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx on t(x);
Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 55, 1 );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace on explain

ops$tkyte@ORA817DEV.US.ORACLE.COM> select owner || '.' || object_name
  2    from t, big_table_v
  3   where t.y = big_table_v.object_id (+)
  4     and t.x = 55
  5  /

OWNER||'.'||OBJECT_NAME
-------------------------------------------------------------
.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   NESTED LOOPS (OUTER)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'T'
   3    2       INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE)
   4    1     TABLE ACCESS (BY INDEX ROWID) OF 'T2'
   5    4       INDEX (RANGE SCAN) OF 'T2_IDX' (NON-UNIQUE)

ops$tkyte@ORA817DEV.US.ORACLE.COM> select fullname
  2    from t, big_table_v
  3   where t.y = big_table_v.object_id (+)
  4     and t.x = 55
  5  /

FULLNAME
-------------------------------------------------------------

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   MERGE JOIN (OUTER)
   2    1     SORT (JOIN)
   3    2       TABLE ACCESS (BY INDEX ROWID) OF 'T'
   4    3         INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE)
   5    1     SORT (JOIN)
   6    5       VIEW OF 'BIG_TABLE_V'
   7    6         TABLE ACCESS (FULL) OF 'T2'

that shows two things,

1) the index wasn't used with the second query
2) the results are different.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set
"_COMPLEX_VIEW_MERGING"=true;

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select fullname
  2    from t, big_table_v
  3   where t.y = big_table_v.object_id (+)
  4     and t.x = 55
  5  /

FULLNAME
-------------------------------------------------------------

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   NESTED LOOPS (OUTER)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'T'
   3    2       INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE)
   4    1     TABLE ACCESS (BY INDEX ROWID) OF 'T2'
   5    4       INDEX (RANGE SCAN) OF 'T2_IDX' (NON-UNIQUE)

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

that shows the complex view merging "corrected" this.  In 9i, this is not
necessary:

ops$tkyte@ORA9I.WORLD> set autotrace on explain
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> select owner || '.' || object_name
  2    from t, big_table_v
  3   where t.y = big_table_v.object_id (+)
  4     and t.x = 55
  5  /

OWNER||'.'||OBJECT_NAME
-------------------------------------------------------------
.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   NESTED LOOPS (OUTER)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'T'
   3    2       INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE)
   4    1     TABLE ACCESS (BY INDEX ROWID) OF 'T2'
   5    4       INDEX (RANGE SCAN) OF 'T2_IDX' (NON-UNIQUE)

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> select fullname
  2    from t, big_table_v
  3   where t.y = big_table_v.object_id (+)
  4     and t.x = 55
  5  /

FULLNAME
-------------------------------------------------------------

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   NESTED LOOPS (OUTER)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'T'
   3    2       INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE)
   4    1     TABLE ACCESS (BY INDEX ROWID) OF 'T2'
   5    4       INDEX (RANGE SCAN) OF 'T2_IDX' (NON-UNIQUE)

complex view merging "just happens" in that release

April 28, 2002 Vikas -- Thanks for the question regarding "File
      system upload,FGAC", version 8.1.7

      You Asked
Hi Tom,
 After a long time I got chance to ask question. I have two questions.

Q1. How can I upload a file from client machine to server file system (not
database) using mod_plsql dad configuration of oracle 8.1.7.0. I searched
through your site but could not found I got the example for uploading to
database but not to file system.

Q.2 Your book says (ch 21 pg 917 ) FGAC can be effectively use for web
application developed for application service providers (ASP) who host the data
of many clients. When using mod_plsql dad configuration of oracle 8.1.7.0 for a
PSP application. My application has a login screen which validate user (not
oracle user) from a table which stores the user account information. Once the
login is successful ,it creates a session cookies by user name and I use that in
my application for validations and authorizations.

Now the sys_context(userenv session_user) value will be same for each request.
Or the other userenv parameter will be same for all request made using mod_plsql
dad configuration of oracle 8.1.7.0.
 My question is that If I create a after logon trigger to set the value of
application context or create a procedure like set_app_role (ref. Page 927) and
call it in after logon trigger. It will not benefit me. Am I right ?  I think in
9ias where the user are the oracle user it works. Kindly let me  know how can I
use it using mod_plsql dad configuration of oracle 8.1.7.0 .0 database.

One thing more your book is very good, the way you have explain the things is
great..
The java stored procedure running OS Command  example is very helpful I have
used it at many places in my application. Great…   I shall be desperately
waiting for Oracle 9i database  version of it.

Regards,

Vikas Sharma

      and we said...
q1) file system upload is not supported via mod_plsql.

You'll need to either:

a) write something in the middle tier, say in java, that accepts the file and
writes it out

b) use a java stored procedure in the database to write the blob out after it's
uploaded to the database.

q2) Here is how it would work for you.

All of your users login to the database using the SAME userid/password (say that
name is "YOUR_APP").  You have done the authentication yourself. Your
AUTHENTICATION procedure is executable by exactly one user in the database --
YOUR_APP (so its 'safe', no one else is going to run that).

Once that authentication procedure is satisfied the user on the other end is who
they say they are -- you generate a cookie.  When you generate that cookie --
you will ALSO set the application context (your OWN context).

On subsequent requests you *must* have some routine in that authentication
package that validates the cookie -- here too you will set that application
context to the appropriate values for that user.

Now, your fined grained access control policy will NOT look at session user, it
will however look at YOUR context to get the appropriate values (hence the name
"application context" -- your application sets up the context for the current
environment -- your policy checks out that context and does the right thing)

Thanks for the feedback on the book!
Review &
      Followup

      Rating: 4
Any Effect for
      Session State  April 09, 2002
Reviewer:  Vikas
      Sharma  from INDIA
Hi,
i would like to know that, will it depend on the session state. I mean It works
for stateful and stateless session of mod_plsql.

i tried it with both it is same for both. Kindly let me know your review on
weather i should use stateful or stateless session in this case and in general.

          Followup:

See the answer to q2 above. I address that there.

Me, I use only stateless connections in all of my applications.  There is no way
I want people consuming resources of any sort on my server whilst they are not
actually using the server.  Any state I need to store for them goes into
database tables and you get a session id (thats how asktom works)

      Rating: 4
A
      clarification  April 28, 2002
Reviewer:  Vikas
      Sharma  from Delhi India
I am sorry for asking a delayed clarification. Actually i implemented it little
late. The clarification required is:
I have create a procedure create_app_context which creates a context say orgid =
123. I have also created a policy function which return orgid as predicate  from
context. Policy function work for all DML and select statements.This procedure
is called in all the pages who has DML or a select. The context takes value from
the current cookie. procedure is
CREATE OR REPLACE PROCEDURE create_app_context IS
v_cookie owa_cookie.cookie;
v_username VARCHAR2(32) ;
v_orgid NUMBER;
BEGIN
 v_cookie := owa_cookie.get('uname_orgid');
 IF (v_cookie.num_vals > 0) THEN
   v_username := SUBSTR(v_cookie.vals(1),0,(INSTR(v_cookie.vals(1),'_')-1));
   v_orgid := SUBSTR(v_cookie.vals(1),(INSTR(v_cookie.vals(1),'_')+1));
   dbms_session.set_context('hr_cm_app','username',v_username);
   dbms_session.set_context('hr_cm_app','orgid',v_orgid);
  ELSE
  dbms_session.set_context('hr_cm_app','username',null);
  dbms_session.set_context('hr_cm_app','orgid',null);
END IF;
END create_app_context;
/
The test procedure which calls a select statement.

CREATE OR REPLACE PROCEDURE  testpolicy is
begin
create_app_context;
for i in (select user_id,name,organisation_id from hr_cm_users) loop
htp.prn(i.user_id||'--'||i.name||'--'||i.organisation_id||'<p>');
end loop;
exception
when others then
htp.prn(sqlerrm);
end;
/
This all works fine. I get the result of the query as per orgid. But I have
doubt ie. suppose there are hundred of user who are using my application some
one are executing a procedure which updates a table some are selecting some are
inserting. At any point of time the value of the orgid in context is likely to
conflict i mean result may not be as desired.  For example if two user a and b
from different who has different orgid execute two procedure user a does a
update one row(can update only the rows which belongs to his orgid.) And user b
execute a procedure which select some rows from the same table (can select only
the row which belongs to his orgid.). both executes procedure at the same time.
Then are there some chances of getting not the desired result. Because one
policy function will be for update and select which uses a
sys_context('hr_cm_app','orgid') in both cases. policy function is :

CREATE OR REPLACE PACKAGE BODY hr_cm_security_policyftn AS
 gv_appctx VARCHAR2(30) default 'HR_CM_APP' ;
 gv_insupddel_pred VARCHAR2(2000) default NULL;
FUNCTION policy_hr_cm_users(p_schema IN VARCHAR2, p_object   IN VARCHAR2) RETURN
VARCHAR2 IS
BEGIN
IF sys_context('HR_CM_APP','orgid') = 0 OR sys_context  ('HR_CM_APP','orgid') IS
NULL THEN
gv_insupddel_pred := '1 = 1 ' ;
ELSE
gv_insupddel_pred := ' organisation_id =
sys_context('''||gv_appctx||''',''orgid'')';
END IF;
RETURN gv_insupddel_pred;
END policy_hr_cm_users;
.The table cloumns are:
hr_cm@ORCL.DEL1>select name,user_id,organisation_id from hr_cm_users;

NAME             USER_ID        ORGANISATION_ID
-------------------------------- ---------- ---------------
vsharma             1000                   5000
asharma             1001                  5001
administrator         0                     0
hello1                 1002                5000
hello2                 1003                5001

          Followup:

Every session will have its OWN context as you have it defined.  The context is
local to a SESSION.

Application context's and FGAC wouldn't work otherwise.

You will get the right answer...

      Rating: 3
PSP
      Logoff  August 29, 2003
Reviewer:  ht
       from California
Tom,
Can you point me in the right direction regarding writing a PSP logoff page?
I'm testing user privileges in my app but it seems that I must relaunch my
browser to login as a different user.
Thanks,
ht

      Rating: 3
Found the
      answer.  August 29, 2003
Reviewer:  ht
       from California
I should have rtfm:

Another method of deauthentication is to add /logmeoff after the DAD in the URL,
for example:

http://myhost:2000/pls/myDAD/logmeoff
Thanks and have a good weekend.
ht

April 28, 2002 Mike -- Thanks for the question regarding "Ways
      to simulate high OLTP", version 8.1.7.2.0

      You Asked
Hey Tom,

I was wondering if you could provide any insight as to how one might simulate
high OLTP processing for testing purposes.  What we're trying to test is the
response time of an application with about 100 concurrent users, some of which
will be running sql (reports, etc.) and others who will be updating and
inserting into tables (about 12 tables involved).  If you ever done this and
could provide some advice that would be great.

thanks,
Mike

      and we said...
We use tools like as Mecury's Loadrunner.

If you are serious about testing and tuning at the high end, you will buy such a
tool:

http://www-svca.mercuryinteractive.com/products/loadrunner/
...

Yes, it costs money.

Yes, if you roll your own you won't have to give them your money but you will
spend more money on your salaries rolling your own, maintaining it and working
about its limited capabilities.  (just anticipating the "but it costs money"
question that seems to follow this type of question).

Review &
      Followup

      Rating: 4
Tom's correct.
        April 29, 2002
Reviewer:  Chris  from
      Ireland.

Loadrunner is v. v. costly. However it's easy to use and very scalable and does
exactly what it says on the tin.

April 28, 2002 Liang -- Thanks for the question regarding
      "Select uploaded file problem", version 8.1.6

      You Asked
Dear Tom,

   I am trying to upload a text file which is bigger than 32760 charaters by
using pl/sql. It is fine, because by default the pl/sql will upload the content
of the text file to a long raw column which can store up to 2147483647 bytes.
   Now, I am facing a problem to retrieve the content of text file which is
longer than 32760 bytes.
   From some other forums, I know that I cannot retrieve value of long raw which
is longer than 32760 bytes, what I can do is using 3GL such as C/C++ or pro*c to
retrieve the content, but I am not familiar with them.
   So, I would like to know that is there any other way to transfer the long raw
data to blob or clob so that I can retrieve the entire content of the file by
using pl/sql only.
   If yes, please let me how to do it Else please suggest.

Thank in advance

Best Regards,
Chin

      and we said...
How are you doing the upload?  iAS, webdb, etc support uploading directly INTO a
blob.

Short of that, you can:

create global temporary table tmp ( x blob ) on commit delete rows;

and in your code:

insert into tmp select to_lob(long_raw_column) from t where .....;

to convert the long raw into a blob which you are able to piecewise read with
dbms_lob.
Review & Followup

      Rating: 4
TO_LOB function
      problem  April 28, 2002
Reviewer:  Chin
      Liang  from Malaysia
I am using the iAS to upload the text file from the HTML page and by default the
content of the file will insert into a long raw column.

From your answer it seems to solve my problem already, but this time I am facing
another problem that is I can't embed TO_LOB functiont in my pl/sql code.

According to the oracle8i Application Developer's Guide - Large Objects (LOBs),
it mention that the TO_LOB function cannot deploy inside any PL/SQL block. Is
that true?

If true then what can I do in order to convert the long raw to blob by using the
PL/SQL or is there any other way to do so.

Thank in advanced

          Followup:

execute immediate 'insert into temp select to_lob(x) from t where x = :x' using
some_variable;

April 29, 2002 Yifu -- Thanks for the question regarding "which
      is the best way to export file from a bulk table", version 8.1.7

      You Asked
Hello.
A bulk table T, more than 10,000,000 records, should be exported some txt files.
The following is my way:

1. select * from T;
2. fetch 1000 results from resultsets through JDBC;
3. write to a file;
4. if %found goto 2;

It's a time consuming job.
Which resource does this select statement consume most?
Snapshot of the select query is the wholes records in T, is not is?
If some update operations are done in the period exporting files, will the
snapshot of select query be expired?

which is the best way?

      and we said...
Have you timed a java program writing 10,000,000 records.  That in itself can be
time consuming.

Me, I would use SQLPlus or C.

http://asktom.oracle.com/~tkyte/flat/index.html
(set arraysize bigger/smaller as needed)

or

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:459020243348

Also - keep in mind the age old adage "too much of a good thing....".   An array
size of 1,000 is probably 10 times bigger then it should be.  You could be
nailing the garbage collector in java and hitting the network really hard with
large bursts.  I've found 100 to be sort of a sweet spot in general for batch
fetching.
Review & Followup

      Rating: 3
Could you give
      me more?  April 28, 2002
Reviewer:  YIfu Sun
       from China
A bulk table T, more than 10,000,000 records, when a select query like select *
from T is done,
is the snapshot the whole records in T or a piece records of table?

if the snapshot is the whole datas, I think it consumes too much. however, if
the snapshot is only a piece, read consistency will be lost. is not it?

          Followup:

snapshot?  what snapshot?

No idea what you are talking about.  If you want to know how multi-versioning
works, how queries are processed, I can suggest two books:

o the Oracle Server Concepts Manual (it is excellent and it is free)
o my book

April 29, 2002 Boopathi -- Thanks for the question regarding
      "Compare SQLCOPY, CREATE TABLE, INSERT, BULK COLLECT and BULK INSERT.",
      version 8.1.7

      You Asked
Tom,

Can you please tell me which one would be the fastest and why?

The data is selected using the following query:
select
  chb.id  dnz_chr_id,
  chb.CONTRACT_NUMBER        contractNum,
  msib.segment1              itemName,
  ib.quantity
From
   mtl_system_items_b     msib,
       hz_cust_site_uses_all  csu_in,
       hz_cust_acct_sites_all cas_in,
       csi_item_instances     ib,
       okc_k_items            ci,
       okc_k_lines_b          clb_product,
       okc_k_lines_b          clb_service,
       okc_k_headers_b        chb
where  chb.scs_code  = 'SERVICE'
and     chb.sts_code IN ('ACTIVE', 'OVERDUE', 'QA_HOLD', 'SIGNED')
and    clb_service.chr_id = chb.id
and    clb_service.lse_id = 1
and    clb_service.sts_code IN ('ACTIVE', 'OVERDUE', 'QA_HOLD', 'SIGNED')
and    clb_product.cle_id = clb_service.id
and    clb_product.lse_id = 9
and    clb_product.sts_code IN ('ACTIVE', 'OVERDUE', 'QA_HOLD', 'SIGNED')
and    ci.cle_id            = clb_product.id
and    ci.jtot_object1_code = 'OKX_CUSTPROD'
and    ib.instance_id = to_number(ci.object1_id1)
and    cas_in.party_site_id(+)      = ib.install_location_id
and    csu_in.cust_acct_site_id(+) = nvl(cas_in.cust_acct_site_id, -999)
and    nvl(csu_in.site_use_code,'SHIP_TO') = 'SHIP_TO'
and    msib.inventory_item_id = ib.inventory_item_id
and    msib.organization_id   = ib.inv_master_organization_id
)

These are huge 11i tables.

1) SQLCOPY ... INSERT dest_table USING ....
with following
SET COPYCOMMIT 4;
SET ARRAYSIZE 5000;
SET LONG 200

2) INSERT /*APPEND parallel(dest_table,10) */ into dest_table ...

3) CREATE TABLE AS ...

4) Using PLSQL:
    BULK COLLECT
    FORALL.. BULK INSERT
    COMMIT for every 25000 records.

Can you please also explain if the COMMIT size matters in this. Can commit size
be set for #3 without modifying the select statement.

Thanks a lot,

Boopathi

      and we said...
I would believe the parallel direct path insert OR a parallel create table as
select will beat the others.

#4 -- that would be the SLOWEST -- don't even consider it.  committing doesn't
save "resources", it causes you to WAIT more often for log file sync's.  It
generates MORE redo.  It generates MORE undo.  You introduce procedural code
into something non-procedural.  That'll only slow it down.

#1 -- while very efficient -- would work best on a heavily indexed table (not
the case here, since you are doing the copy into a new table -- no indexes).  It
is "C" based (fast).  I wouldn't use nearly that arraysize (100 rows is a good
"sweet spot" and I wouldn't use copy commit.  I would size my rollback segments
properly.  #1 can be very slow if the SQLPLUS session *is not* on the server in
question as the data is pulled TO the client and sent back to the SERVER.  Do
that on a single machine if you can.

I would go with a create table NOLOGGING + parallel direct path insert
personally.  It'll be the same as a create table as select.  Then, do
unrecoverable index builds in parallel on this table.

You DO NOT want to set a commit site.  A CTAS and direct path insert into
unindexed tables generate VERY VERY little undo (they load above the high water
mark for the table, they do not record row by row the changes).  committing
frequently is *bad*, *slow*, *bad*.

Review &
      Followup

      Rating:
      5
  April 28, 2002
Reviewer:
      A reader

      Rating: 3
Append
      hint  April 29, 2002
Reviewer:
      A reader
Tom,
Regarding 2..
a) When Insert is parallelised using parallel hint, Isn't append mode used by
default?
b) For using hints, and especially for comparing the performance between various
options and that two for big tables, the hint syntax is so important (missing +
in this case), as oracle just assumes hint as a comment, and one could end up in
wrong conclusion.
c) What about using parallel DML?

          Followup:

a) no, read:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c21dlins.htm#2507
b) yes, if you give us garbage in, we'll give garbage out.

c) what about it?

April 29, 2002 --
      Thanks for the question regarding "Java Connection In Oracle.", version
816

      You Asked
Previously, we developed a client/server program in java, which connect to
Oracle 8i Directly.
Now, Due to expansion (from LAN to WAN). We are having headache on what kind of
connection to be use. If using WAN, Firewall and Connection will cause some
problem to us. So, we cannot use the direct connection. Because of this, we are
thinking of changing connection to web connection in the Client/Server Java
program. Well, for us, we are thinking it is still not the best way to do it.
Does anyone have any idea on what other kind connection can we use?

Besides, can i know how to do web connection in java client/server programming?

Furthermore, Is there anyway that i can run a client program in pl/sql?

      and we said...
Given your above constraints, I would suggest this (if you must do this in java
that is)

o jsp's for the user interface, give me HTML on the client browser.  Just html

o servlets for the app logic in the middle tier

o heavy use of stored procedures for database logic (be nice to see NO
insert/update/delete/select's in the java code at all)

That is sort of "industry standard" (well, people will argue that last point).

PLSQL can only run things on the server, not the client.
April 29, 2002 Venkatesh -- Thanks for the question regarding
      "Archive table data ...", version 8.1.6

      You Asked
Hello Tom ,
         Here is a requirement that has really taken away my weekend .
This database is for a SURVEY FORM application thats filled out annually . The
requirement is that I store the copy of data for a particular year for a period
of 5 yrs after which the same can be deleted permanently . There are about 26
tables in all . At a minimum ,the last years data needs to be within the
database as there are certain references to that information in the SURVEY form
. I have to maintain copies of all 5 yrs of data .The Syrvey_id and Year form
the Primary key .

Eg

Survey_id Year Category

1          98    Manufacturer
1          99    Manufacturer
1          00    Manufacturer (lets say this is the current year)
2          01    Contracter
....

and so on  .
Here for the survey id 1 , I have decided to store the current year (00) and the
previous year data (99) . Storing all the 5 yrs data in the same table would
affect performance . In this case what is the best way out to archive table data
. If I create History tables then it would imply i create about 26 more history
tables as the main ones . Please suggest .

Thanx and Regards
Venkatesh

      and we said...
This screams out for partitioning!

I don't know why you feel having the history in there would affect performance.
If you do indexed reads (sounds like you will be) it will not matter whether you
have 1 row, 1000 rows, 1000000000 rows in there.

Partitions will allow you to easily archive data.

I would suggest a SINGLE table, partitioned by year, with locally partitioned
indexes.
April 29, 2002 Min-Koo -- Thanks for the question regarding "I
      forgot passwrod in oracle 9i.", version 9i

      You Asked
Hello Tom.

I forgot passwords of sys, system. Until 8i, I could change
password of sys and system using internal account.

But, there is no internal account in 9i.

I think that I can change my password file in oracle 9i using ORAPWD.
But, How can I find 'current password file'?

I want to do

1. change my sys and system password file.
2. know the current oracle password file which is currently being used.

      and we said...
there is "connect / as sysdba" which is the same as internal.  That *is*
internal in 9i (and 8i, and 8.0, and .... so on )

The password file is always in a known location ($ORACLE_HOME/dbs on unix,
$ORACLE_HOME/database on NT) and it is named after the $ORACLE_SID.

Review &
      Followup

      Rating: 3
I tried connect
      / as sysdba only to fail.  April 29, 2002

Reviewer:  Min-Koo Seo  from Seoul, Republic of Korea

SQL> connect /
ERROR:
ORA-01004: default username feature not supported; logon denied

SQL> connect / as sysdba
ERROR:
ORA-01031: insufficient privileges

SQL>

My initSID.ora file has entity like below.
#remote_login_passwordfile=EXCLUSIVE

I logged on as administrator.

Is there any reason which makes it impossible to log in
using 'connect / as sysdba?'

          Followup:

You are not in the proper group (meaning in 8i, connect internal would have also
failed).

The OS account you are logged into, one whatever OS you are on, is not the
proper account.

April 29, 2002 Susan -- Thanks for the question regarding "COUNT
      FUNCTION WITH JOINS", version 8.1.7

      You Asked
Hi Tom,

Forgive me for asking two questions at once.

First, I know this SQL is wrong, but, I'm not sure why.  I'm trying to list
master_ids per form type (form_id) and get a count of master_id per form type.
When I perform the individual counts it brings back different results. Is it the
joins? Could I use rollup to format the count after the listing of master_ids
per type - instead of count showing up each row?

SELECT
      A.master_id AS BASE,
      B.master_id AS AS03,
      C.master_id AS AS06,
      COUNT(A.master_id) OVER() BASE_COUNT,
      COUNT(B.master_id) OVER() AS03_COUNT,
      COUNT(C.master_id) OVER() AS06_COUNT
FROM
      (SELECT master_id FROM formstatus WHERE form_id = 'BASE') A,
      (SELECT master_id FROM formstatus WHERE form_id = 'AS03') B,
      (SELECT master_id FROM formstatus WHERE form_id = 'AS06') C
WHERE
      A.master_id = B.master_id (+) AND
      A.master_id = C.master_id (+)

Second, I'm trying to add a check constraint on a table that already exits. I
want to restrict a columns value to 0 or 1 (the default is already set to 0)
here's my sql -

ALTER TABLE adeptdev.FTRACK_TYPE
ADD CONSTRAINT FTRACK_DETAIL_CC CHECK (DETAIL = 0 OR DETAIL = 1)

the table was altered but is there a way to have the condition reference the
column once i.e. (DETAIL = 0 or 1)

Thank you for your help.

      and we said...
If I understand you -- this might work:

select master_id,
       count(decode(form_id,'BASE',1,null)) cnt_base,
       count(decode(form_id,'AS03',1,null)) cnt_as03,
count(decode(form_id,'AS06',1,null)) cnt_as06
  from formstatus
 group by master_id

That'll get a list of all master_ids in the table formstatus.  You will get a
count of each form type.

check( detail IN ( 0, 1 ) )

would do the other thing.

Review &
      Followup

      Rating: 3
not quite what
      I wanted   April 26, 2002
Reviewer:  Susan
       from Boston, MA
this is what i need the query to return, ideally, I'd like it to break on the
count rows so I don't have that count column showing up in each row

base   as06      as03  base_count as06_count aso3_count
1                           3        5          3
2       2                   3        5          3
3                  3        3        5          3
        4                   3        5          3
        5                   3        5          3
        6                   3        5          3
                   7        3        5          3
                   8        3        5          3

          Followup:

that output makes no sense to me given the original question.  You'll really
need to be a little more detailed.

      Rating: 3
more detail
        April 29, 2002
Reviewer:  Susan  from
      Boston, MA
here's my table

susan@DEVL> desc palz.formstatus
 Name                            Null?    Type
 ------------------------------- -------- ----
 FORMSTAT_ID                     NOT NULL NUMBER(6)
 MASTER_ID                       NOT NULL VARCHAR2(12)
 EVENTTYPE_ID                    NOT NULL CHAR(4)
 VER_ID                                   CHAR(1)
 FORM_ID                         NOT NULL CHAR(4)
 STATUS                                   CHAR(1)
 PRSTATUS                                 CHAR(1)
 ADD_DATE                                 DATE
 ADD_BY                                   VARCHAR2(30)
 CLEAN_DATE                               DATE
 DETAIL                                   VARCHAR2(50)
 PERCODE                                  CHAR(4)
 FLDCOUNT                                 NUMBER(5)
 TOTADDTIME                               NUMBER(5)
 IDLEADDTIME                              NUMBER(5)
 TOTEDITTIME                              NUMBER(5)
 IDLEEDITTIME                             NUMBER(5)
 EDITCOUNT                                NUMBER(4)
 COMP_BY                                  VARCHAR2(25)
 COMP_D                                   DATE

i need a listing of master_ids, per form_id (see original query) and a count of
all the master_ids per form type - maybe it can't be done in one query ?

          Followup:

I understand "list of master id by form_id".

What is "form type" though?

      Rating:
      3
sorry  April 29, 2002
Reviewer:
      A reader
form type is form_id

          Followup:

Guess I'm not going to be able to help you cause I still believe:

select master_id,
       count(decode(form_id,'BASE',1,null)) cnt_base,
       count(decode(form_id,'AS03',1,null)) cnt_as03,
count(decode(form_id,'AS06',1,null)) cnt_as06
  from formstatus
 group by master_id

answers the question -- sorry.

      Rating: 1
Example
      ???  April 29, 2002
Reviewer:  A reader
       from Espa?a

      Rating: 1
si hay que
      romper por form_id, quizas valga esto  April 29, 2002

Reviewer:  A reader  from Espa?a
select master_id, form_id
       count(decode(form_id,'BASE',1,null)) cnt_base,
       count(decode(form_id,'AS03',1,null)) cnt_as03,
count(decode(form_id,'AS06',1,null)) cnt_as06
  from formstatus
 group by master_id, form_id

      Rating: 4
This should
      works if I have understood it properly  April 29, 2002

Reviewer:  Raj  from Cerritos, CA USA
select distinct
       a.masterid,
       b.masterid,
       c.masterid,
       e.basecount,
       e.as03count,
       e.as06count
  from formstatus a,
       formstatus b,
       formstatus c,
       formstatus d,
       (select sum(decode(formid,'BASE',1,0)) basecount,
               sum(decode(formid,'AS03',1,0)) as03count,
               sum(decode(formid,'AS06',1,0)) as06count
          from formstatus) e
 where d.masterid = a.masterid(+)
   and a.formid(+) = 'BASE'
   and d.masterid = b.masterid(+)
   and b.formid(+) = 'AS03'
   and d.masterid = c.masterid(+)
   and c.formid(+) = 'AS06'
   and d.formid in ('BASE', 'AS03', 'AS06')

April 29, 2002 Monish -- Thanks for the question regarding "How
      to convert a .fmb file into text file.", version 6

      You Asked
Hi Tom,

We are looking at ways to document the code in our .fmb files.

Is there a way we can get a single text file with all the code of a .fmb file.

Thanks.

      and we said...
No not really, you would be looking at a tool like Designer 2000 (a "case tool")
which can reverse engineer a ton of fmb's and the database upon which they work
and generate as much documentation as you like.

Even if you could get all of the code from an fmb into a file, it would be
meaningless.  It would just be a collection of unrelated blocks of code with no
flow information, no dependencies, no nothing.

Review &
      Followup

      Rating: 5
how should I do
      this  April 29, 2002
Reviewer:
      A reader
Tom, I have an fmx file with me , but I dont have forms builder or forms runtime
installed on my client. But I want the user to click on the fmx file on there
desk top and the form should come up.

How can I make this possible, I'm not allowed to have the forms builder  on my
client. What are the different ways this can be made possible.

          Followup:

Thats like saying "have, I have a .exe file on this floppy, I want to run it on
solaris but I cannot install any DOS or windows emulation software"....

Well, you can use the forms server to host the .fmx file on a MIDDLE tier and
let the client just run java through the browser.

      Rating: 2
forms
      api  May 02, 2002
Reviewer:  Steve Rainbird
       from England
You could use the forms api routines and create a c program to list out the
forms info.

April 29, 2002 Rajesh -- Thanks for the question regarding "List
      of values in for loop", version 8.1.7

      You Asked
HI Tom,

I have simple question about for loops.
I have a set of values for which, I need to do some oparations for each value in
the set.

begin
 for i in ( 'city', 'country', 'state','zip');
loop
 dbms_output.put_line(i);
end loop;
end;

If I use the set in for loop, it errored out with following errormessage.
ORA-06550: line 3, column 1:
PLS-00103: Encountered the symbol "LOOP" when expecting one of the following:

   * & - + / at mod rem .. <an exponent (**)> || year DAY_

Could you please let me know, is it possible to directly use this set values in
for loop.

Thanks in advance.

Thanks
Rajesh

      and we said...
In a stored procedure you can code:

is
   type array is table of varchar2(25);
   l_data array := array( 'city', 'country', 'state', 'zip' );
begin
   for i in 1 .. l_data.count
   loop
      dbms_output.put_line( l_data(i) );
   end loop;
end;
/

That is as close as you will get.
April 29, 2002 Shivaswamy -- Thanks for the question regarding
      "RAID", version 817

      You Asked
Hi TOM,

With block level stripping implimented in RAID5, if I use it, does it call for,
also to impliment datafile stripping? I mean, is it necessary or effective to
distribute the multiple datafiles of a given tablespace? And will it have any
impact to place the datafiles belonging to 'DATA' & INDEX on different dirves?
(I plan to have a strip width of 4.)

Shivaswamy

      and we said...
see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:359617936136
for some general raid guidelines.

As for striping -- if the raid array is doing it, you need not do it at the
datafile level.

We just had a FASCINATING disscussion on the usenet newsgroups about this myth
that separating table and index data is "best practice".  it can actually be
counter productive:

http://groups.google.com/groups?dq=&hl=en&safe=off&threadm=3cbf2607%240%2415182%24626a54ce%40news.free.fr&rnum=1&prev=/groups%3Fq%3Dg:thl1506710792d%26dq%3D%26hl%3Den%26safe%3Doff%26selm%3D3cbf2607%25240%252415182%2524626a54ce%2540news.free.fr
check it out -- read the postings by Jonathan Lewis, Howard Rodgers and Me in
particular (read all 84 if you have time ;)

Review &
      Followup

      Rating:
      4
  April 29, 2002
Reviewer:
      Shivaswamy  from Dallas, TX
Tom,
Thanks for your interesting pointer on usenet(I had referred to all of your RAID
notes)..I will read all of them - I am in the process of finalizing my high end
Solaris server with a fantastic storage..and I need all the info. Should you
have anyhting else to make me enlightened on io, disk through-put etc., pl. do
let me know.
Thanks and regards,
Shivaswamy


Subject | Article | Last Updated (East coast time)
passing records and objects to procedures when they are invoked dynamically | article only | Apr 30, 2002 07:37 am
Loading data which contains Long datatype by using SQLLoad | article only | Apr 30, 2002 11:35 am
Huge Insert! | article only | Apr 30, 2002 11:40 am
Creation of already created Tables with their Constraints on other machine | article only | Apr 30, 2002 02:17 pm
Few questions--RBS & Partitions | article only | Apr 30, 2002 08:09 pm
Parsing the data | article only | Apr 30, 2002 08:11 pm
Connect by prior procedure bug. | article only | May 01, 2002 09:41 am
data parsing and querying | article only | May 01, 2002 10:46 am
Usage of Stored Procedures | article only | May 01, 2002 10:53 am
Filtering rows | article only | May 01, 2002 09:10 pm
upgrading v8.0.6 database to v8.1.6 | article only | May 01, 2002 09:19 pm
Which tables to put into which BUFFER_POOL | article only | May 02, 2002 07:28 am
Security: Restricting Ad-hoc applications to have read-only access | article only | May 02, 2002 09:14 am
Viewing Blob Data or Size / Retrieving Blob from database | article only | May 02, 2002 09:22 am
Generic XML reporting package | article only | May 03, 2002 09:32 am
Manage user access in database application development | article only | May 03, 2002 11:16 am
Database Capitalization | article only | May 03, 2002 01:57 pm
A Question on SQL Query | article only | May 03, 2002 02:03 pm
Sizing DB Block in relations to XMLType & CLOBs | article only | May 03, 2002 07:33 pm
member merge | article only | May 03, 2002 07:45 pm
Type change | article only | May 04, 2002 01:07 pm
Syntax checking without execution of query | article only | May 04, 2002 01:08 pm
JAVA in Oracle -- when and how long does the JVM get started /stay around | article only | May 04, 2002 01:14 pm
How to use Sequence number with multi threaded program execution? | article only | May 04, 2002 01:23 pm
dbms_profiler Error | article only | May 04, 2002 01:39 pm
Internet Programming | article only | May 04, 2002 01:44 pm
_trace_files_public | article only | May 04, 2002 01:50 pm
Testing for DML result | article only | May 04, 2002 07:33 pm
Ordering Non Numeric values | article only | May 05, 2002 09:27 am
Size of a File (of anytype from the current OS) | article only | May 05, 2002 06:13 pm
How do you keep up! | article only | May 05, 2002 06:17 pm
Table partitioning and load performance | article only | May 05, 2002 07:29 pm
IP Address or Other Unique Identifier in a Web Form | article only | May 06, 2002 07:12 am
are indexes reused? | article only | May 06, 2002 07:15 am
Archiving, Replication, Stand By | article only | May 06, 2002 07:20 am
Matrix Report : Group by Dates in a Month as Rows | article only | May 06, 2002 07:22 am
Locks and Latches. | article only | May 06, 2002 08:47 am
Alert Log ORA- emails | article only | May 06, 2002 09:52 am
dynamic set up of dbms_sql.bind_variable/bind_array | article only | May 06, 2002 10:07 am
database cluster | article only | May 06, 2002 08:48 pm
Number(2) and Number(38) | article only | May 06, 2002 08:57 pm
row(s) 1 - 41 of 41
April 30, 2002 Kelly -- Thanks for the question regarding "passing records and objects to procedures when they are invoked dynamically", version
You Asked
Hi Tom,

I don't believe there is a way to do
this according to the documentation but
I want to dynamically call a
stored procedure and as an input
parm pass a record%rowtype.

It goes something like this.

create cursor get_chg_data as
Select insert_dttm, table_name, column_name
from get_chg_data
order by insert_dttm.

cursor_rec get_chg_data%rowtype;

open get_chg_data
loop
fetch get_chg_data into cursor_rec;
exit when get_chg_data%notfound

myProc:='exec my_pkg.' || cursor_rec.table_name || '_sp(:m1)';
myCur:=dbms_sql.open_cursor;
dbms_sql.parse(myCur, myProc, dbms_sql.native);
dbms_sql.bind_variable(myCur, ':m1', cursor_rec);

Would I need to define every field in the table
and bind each one?

cursor get_chg_data is
Select *
from get_chg_data
order by insert_dttm.

open get_chg_data
loop
fetch get_chg_data into MY_GLOBAL_PACKAGE.get_chg_data_record;
exit when get_chg_data%notfound

myProc:='exec my_pkg.' || cursor_rec.table_name || '_sp(
MY_GLOBAL_PACKAGE.get_chg_data_record )';
myCur:=dbms_sql.open_cursor;
dbms_sql.parse(myCur, myProc, dbms_sql.native);
dbms_sql.bind_variable(myCur, ':m1', cursor_rec);
and we said...
Here is what I do:

create package MY_GLOBAL_PACKAGE
as
   get_chg_data_record   get_chg_data%rowtype;
end;
/

and then in the routine you are trying to write you would code:

   myProc := 'begin mypkg.' || cursor_rec.table_name ||
              '( my_global_package.get_chg_data_record ); end;'

So here we are passing a global variable to each of the routines.  I do this all
of the time to make life easier -- instead of "binding" each and every variable
in a dynamic call -- just put them in global variables and pass those around.
It is still "shared sql" friendly since the plsql variable is automatically a
bind variable as well.  I do this when I need to dynamically invoke something
that takes a plsql table type as well -- since you cannot bind those either (or
object types, etc,etc, etc)....
Review & Followup
Rating: 1
Missing code extract? April 30, 2002
Reviewer: A reader
Tom,
is the code extract missing from this example?
I read:
"you would code:...So here we are passing a global variable" with nothing in
between
Regards
Sarah

Followup:
Look at that, wonder where it went.  Well, I put it back.

April 30, 2002 Sharon -- Thanks for the question regarding "Loading data which contains Long datatype by using SQLLoad", version Oracle 8.1.5
You Asked
I have an exported data file from Lotus Notes, one column
contains comments information which user can enter as many as
they want, when I look at export file, the comments column data
runs mutiple lines since it contains Enter Key(user press enter
key), so, how do I give correct control file in order to loading
data by using SQLLoad. The table in Oracle contains one column
as Long datatype to store comments. This column is last column.

If you need more info, please let me know!

Thank you very much in advance!

Sharon
and we said...
Loading data with embedded Newlines -- that is something that
has been problematic for SQLLDR historically – how to load free
form data that may include a newline in it.  The newline
character is the default “end of line” character to SQLLDR and
the ways around this did not offer much flexibility in the past.
 Fortunately, in Oracle8i 8.1.6 and up, we have some new
options.  I'll list them out with some examples and tell you
which work in which versions.  I see you have version 8.1.5 so
the "easiest" option (the STR option below) is not available.

The options for loading data with embedded newlines are now as
follows:

o Load the data with some other character in the data that
represents a newline (e.g. put the string \n in the text where a
newline should appear) and use a SQL function to replace that
text with a chr(10) during load time.  This works for string
data of 4000 bytes or less only!  This works in all releases
(although in 7.x, the limit is 2000 bytes)

o Use the FIX attribute on the infile directive and load a fixed
length flat filed.  This works in all releases.

o Use the VAR attribute on the infile directive and load a
varying width file that uses a format such that the first few
bytes of each line are the LENGTH of the line to follow.  This
also works in all releases.

o Use the STR attribute on the infile directive to load a
varying width file with some sequence of characters that
represent the end of line – as opposed to just the newline
character representing that.  This is new in Oracle8i release
8.1.6 (it appears to work in 8.1.5 but it is not documented nor
supported and you get funny results in the log file about the
number of records actually loaded and processed.  In 8.1.6 -- it
works as expected and is documented/supported)

We will demonstrate each in turn.

Use some character other then a newline:

This is an easy method if you have control over how the input
data is produced.  If it is easy enough to convert the data when
creating the data file – this will work fine.  The idea is to
apply a SQL function to the data on the way into the database,
replacing some string of characters with a newline.  An example
control file with inline data could be:

load data
infile *
into table T
TRUNCATE
fields terminated by ',' optionally enclosed by '"'
(
TEXT "replace(:text,'\\n',chr(10))"
)
begindata
How Now\nBrown Cow

Notice how in the call to replace we had to use  “\\n” not just
“\n”.  This is because \n is recognized by SQLLDR as a newline
and it would have converted it into a newline – not a 2
character string.  In SQLLDR -- \\n is how to get the character
string constant \n into the control file.  When we execute
SQLLDR with the above control file, the table T is loaded with:

ops$tkyte@DEV816> select * from t;

TEXT
----------
How Now
Brown Cow

Use the FIX attribute:

The FIX attribute is another method available to us.  If you use
this – the input data must appear in fixed length records.  Each
record will be exactly the same number of bytes as any other
record in the input data set.  When using POSITIONAL data – this
is especially valid, those files are typically fixed length
input files to begin with.  When using “free form” data, this is
less flexible as those files are generally varying length files
to begin with.

When using the FIX attribute, you will code a control file
similar to this:

load data
infile fix.dat "fix 25"
into table T
TRUNCATE
fields terminated by ',' optionally enclosed by '"'
(
TEXT
)

That specifies an input data file that will have records that
are 25 bytes each.  This includes the trailing newline that may
or may not be there – in this case, the newline is nothing
special in the input datafile.  It is just another character to
be loaded or not.  So, if you take a datafile such as:

$ cat fix.dat
123456789012345678901234
how now
brown cow
this
is
another
line

Where each and every line is exactly 25 bytes long and load it
using the above control file, you will get:

ops$tkyte@DEV816> select '"' || text || '"' text, dump(text,16)
dumptext from t
  2  /

TEXT                           DUMPTEXT
------------------------------
----------------------------------------
"123456789012345678901234      Typ=1 Len=25:
"
31,32,33,34,35,36,37,38,39,30,31,32,33,3
                               4,35,36,37,38,39,30,31,32,33,34,a

"how now                       Typ=1 Len=25:
brown cow
68,6f,77,20,6e,6f,77,a,62,72,6f,77,6e,20
"                              ,63,6f,77,20,20,20,20,20,20,20,a

"this                          Typ=1 Len=25:
is
74,68,69,73,a,69,73,a,61,6e,6f,74,68,65,
another                        72,a,6c,69,6e,65,20,20,20,20,a
line
“

Notice how each row loaded ends with a newline – that is because
my input data always had a newline in the 25’th byte.  If this
is not desired, you should enclose your data in quotes (and
account for that additional space on each line in the FIX
attribute).  In that fashion, the trailing newline will not be
loaded as part of the input data.

Using the VAR attribute:

Another method of loading data with embedded newline characters
is to use the VAR attribute.  When using this format, each
record will begin with some fixed number of bytes that represent
the total length of the incoming record.  Using this format – I
can load varying length records that contain embedded newlines –
but only if I have a record length field at the beginning of
each and every record.  So, if I use a control file such as:

load data
infile var.dat "var 6"
into table T
TRUNCATE
fields terminated by ',' optionally enclosed by '"'
(
TEXT
)

The “VAR 6” says that the first 6 bytes of each input record
will be the length of that input record.  If I take a datafile
such as:

$ cat var.dat
000028"1234567890123456789012345"
000020"How now
brown cow"
000023"this
is
another
line"

I can load it using that control file.  In my input datafile – I
have 3 lines of data.  The first line starts with 000028 –
meaning that the next 28 bytes represent the first input record.
 As you can see I have 25 characters of text (the number 123…345
plus 2 characters for the quotes plus 1 character for the
newline).  The next line starts with 000020 – it has 20 bytes of
text and so on.  Using this format datafile, we can easily load
our data with embedded newlines.

Using the STR attribute:

This is perhaps the most flexible method of loading data with
embedded newlines.  Using the STR attribute – I can specify a
new end of line character (or sequence of characters).  This
allows you to create an input datafile that has some special
character at the end of each line – the newline is no longer
“special”.

I prefer to use a sequence of characters – typically some
special “marker” and then a newline.  This makes it easy to see
the end of line character when viewing the input data in a text
editor or some utility as each record still has a newline at the
end of it.  To use this, we might have a control file like:

load data
infile str.dat "str X'7c0a'"
into table T
TRUNCATE
fields terminated by ',' optionally enclosed by '"'
(
TEXT
)

The above usings the “STR X’7c0a’” to specify that each input
record will end with a | (pipe character) and a newline.  To
construct that string – made of the hex characters X’7c0a’ – I
used SQLPlus.  For example to see that a PIPE is 7c, and a
newline 0a, I simply:

ops$tkyte@DEV816> select to_char( ascii( '|' ), 'xx' ) from dual
  2  /

TO_
---
 7c

ops$tkyte@DEV816> select to_char( ascii( '
  2  ' ), 'xx' ) from dual;

TO_
---
  a

So, if your input data looks like:

$ cat str.dat
123456789012345678901234|
how now
brown cow|
this
is
another
line|

The above control file will load it correctly.
Review & Followup
Rating: 4
how to use STR infile directive with command-line 'DATA=' parameter ? April 30, 2002
Reviewer: Paul Ryan from London
Thanks for this very useful summary of the options available for using
SQL*Loader when there are newlines in the data to be loaded.

Of course using the STR attribute on the infile directive  is the best way -
but, how do you use this technique if you are specifying the datafile name on
the command line (and so there is no INFILE directive in the control file) ?

Followup:
data= on the command line will always override the infile= in the ctl file, so
just

load data
infile to_be_named.later "str X'7c0a'"
into table T
TRUNCATE

and use data= on the command line

April 30, 2002 -- Thanks for the question regarding "Huge Insert!", version 817
You Asked
Hi Tom
I am executing a query that fetches from a cursor first then does an insert
based on the number of matching records as follows

CREATE OR REPLACE PROCEDURE test AS
Dt     date;
DelID        Integer;
LocID        Integer;
CURSOR c1 IS select po_id, po_dt, to_number(name)
          from Order, Location
          Where Order.id = Location.id;
BEGIN
    OPEN c1;
    LOOP
        FETCH c1 Into DelID, Dt, LocID;
        EXIT WHEN C1%NOTFOUND;

        Insert into Results
        select DelID, NVal, prod_id, NULL, qty,
               decode(qty, 0, 0, cost/qty), 0,
               NULL, 1
        from subs, Inv
        Where subs.id = LocID
          And v_dt = Dt
          And Subs.id = Inv.inv_id;

        COMMIT;

    END LOOP;
    CLOSE C1;
END;
/

The subs table contains 128 million records
Inv table contain 40000 records .
Cursor c1 returns 1.3 million records.
This insert has taken 3 days to insert just 4 million records and there are way
more than 4 million records.
Im thinking of using direct insert :Insert /* + Append */. Is this going to help
me?
I have another question on the placement of commit, I think in hte above query
it is getting committed at every one insert , is this right thing to do?
Also should I disable the Index on the Results tables and rebuild it later?
Is there any other way to optimize the above operation?

Thanks
Steve
and we said...
The fastest would be to disable the indexes (mark them unusable) and do this in
a SINGLE insert:

insert /*+ append */ into results
select delId, nval, prod_id, null, qtr, decode(qtr,0,0,cost/qty), 0, null, 1
  from subs, inv, ( select po_id DelID, po_dt dt, to_number(name) LocID
                      from order, location
                     where order.id = location.id ) c1
  where subs.id = c1.locId
    and v_dt = c1.dt
    and subs.id = inv.inv_id;

commit;

and rebuild the indexes using UNRECOVERABLE (and maybe even parallel).

I hate committing in a cursor for loop -- wastes resources, takes longer,
generally inefficient.

I really don't like doing procedural code where a single statement will do the
work for me.
Review & Followup
Rating: 4
September 17, 2001
Reviewer: A reader
Hi Tom
I have altered the table to be in NOLOGGING mode , would this single stmt insert
without being in a loop fail because of not being committed
ORA-01555 "snapshot too old

How do I commit after every 10000 records?

Thanks

Followup:
The insert might fail with a 1555 because the query:

select delId, nval, prod_id, null, qtr, decode(qtr,0,0,cost/qty), 0, null, 1
  from subs, inv, ( select po_id DelID, po_dt dt, to_number(name) LocID
                      from order, location
                     where order.id = location.id ) c1
  where subs.id = c1.locId
    and v_dt = c1.dt
    and subs.id = inv.inv_id;

could not get a consistent read.  This would be caused by OTHER sessions
modifying subs, inv, order, and location however.

You committing inside of a loop would not prevent the 1555 (in fact, the #1
cause of a 1555 is committing inside of a loop with an open cursor).

Your loop will fail with an ora1555 just as easily as the single insert would.
It would indicate that your rollback segments wrap faster then the longest
running query you have.  The solution for that is to size your rollback segments
bigger, you size RBS based not only on your transaction size but your QUERIES as
well.

Rating: 5
but.. September 18, 2001
Reviewer: Nag from USA
If we commit every 10,000 records or so, there is chance that we might still get
snapshot too old error. This is because when yu open your cursor, it gets the
records as of that point of time. In between when you are working with y our ,
cursor , if some else changes your records, You will lose your data integrity in
the cursor, as a commit, will clear the blocks you had for your records in the
rollback segment.

Tom, your comments, and what is the solution.

Followup:
See above.

You will NOT lose data integrity.  You get a consistent read from Oracle.  The
result set is fixed in stone at the point of time the statement begins..

Rating: 5
What is unrecoverable option September 18, 2001
Reviewer: Nag from USA
Tom

Kindly explain the 3 phrases you have used in the given context.

wastes resources,
takes longer,
generally inefficient.

Followup:
Unrecoverable/Nologging allows some operations to take place without generating
redo information.

The direct path insert using the /*+ APPEND */ hint is one of those.  A create
table as select can be another.  An index build yet another.

I say committing in a loop wastes resources because people generally do it in
the belief that it SAVES resources.  It has the opposite effect.  You'll
generate MORE REDO this way, you'll WAIT longer and more frequently on LGWR, and
you'll generally use more system resources.

It takes longer -- well, thats self explainatory.  It takes LONGER.

Here is an exmaple that sums it up nicely.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view stats
  2  as select 'STAT...' || a.name name, b.value
  3        from v$statname a, v$mystat b
  4       where a.statistic# = b.statistic#
  5      union all
  6      select 'LATCH.' || name,  gets
  7        from v$latch;

View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t1;
Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t1 ( x int, y char(2000) );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_x     number := 0;
  3      l_start number;
  4  begin
  5      insert into run_stats select 'before', stats.* from stats;
  6
  7      l_start := dbms_utility.get_time;
  8      for i in 1 .. 1000
  9      loop
 10                  insert into t1 values ( 1, 'x' );
 11                  commit;
 12      end loop;
 13      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 14
 15      insert into run_stats select 'after 1', stats.* from stats;
 16
 17      l_start := dbms_utility.get_time;
 18      for i in 1 .. 1000
 19      loop
 20                  insert into t1 values ( 1, 'x' );
 21      end loop;
 22      commit;
 23      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 24
 25      insert into run_stats select 'after 2', stats.* from stats;
 26  end;
 27  /
190 hsecs
27 hsecs

PL/SQL procedure successfully completed.

Ok, so it is CLEAR that it is faster to commit once instead of committing
lots... Look at the resources now

ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value-a.value run1,
c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11     and a.name like '%redo%'
 12   order by abs( (c.value-b.value)-(b.value-a.value))
 13  /

NAME                                           RUN1       RUN2       DIFF
---------------------------------------- ---------- ---------- ----------
STAT...redo buffer allocation retries             1          0         -1
STAT...redo log space requests                    2          0         -2
STAT...redo log space wait time                 137          0       -137
LATCH.redo writing                             1031          3      -1028
STAT...redo entries                            3578       2124      -1454
LATCH.redo allocation                          3592       2132      -1460
STAT...redo size                            2728780    2345248    -383532

7 rows selected.

The commit once logic used less of everything, time, latches (locks), redo space
-- everything.

Rating: 5
Iam saying exactly what you are saying September 18, 2001
Reviewer: Follow up from USA
Tom

"You will NOT lose data integrity.  You get a consistent read from Oracle.  The
result set is fixed in stone at the point of time the statement begins.."

Yes, you do get result set which is fixed in stone. But when you loop, and do
some operations, and commit, for every 10,000 records, aint the blocks in the
rollback segment for this cursor, get cleared. i.e. if someone else has changed
the data selected by your cursor , and committed.

Rating: 5
I had enough of snapshot too old -- lets beat it to death September 18, 2001
Reviewer: A reader from USA
Expand on

"It would indicate that your rollback segments wrap faster then the longest
running query you have."

and

For some reasons, the dba's are reluctant to assgin a separate rollback segment
for each indivudual process, is there any other solution for this.

What can be the dba's concerns.

I have tried to enquire, but either they dont answer, or the issue gets into
bureaucracy, from one desk to other.

Here is one frustrated developer, what do I need to do.  Most of my processses
do what had been discussed here.

Rating: 4
September 18, 2001
Reviewer: A reader
Hi Tom
The single insert you suggested does not execute and hangs from the sqlplus ,if
I assume it is processing then it means it is taking way longer than the insert
using the cursor and looping through it.

Also I have noticed that even though we are using the APPEND hint the
transaction still generates redo , as I monitored the Redo Size and Redo Entries
for the session executing the Insert.
What I am doing in the cursor is a simple join and then the Insert with the
Append hint.

CURSOR c1 IS select po_id, po_dt, to_number(name)
          from Order, Location
          Where Order.id = Location.id;

I also altered the Results table in Nologging mode but it still generates Redo.

Thanks
Steve

Followup:
The query is processing, you can use v$session_longops to monitor its progress.

Since you just got this advice yesterday and you said:

This insert has taken 3 days to insert just 4 million records and there are
way more than 4 million records.....

I don't see how you could have determined that its taking longer.  I still have
2 days to go don't I?  :)

You can take steps to TUNE the query itself -- I just merged your query into it,
write it differently after analyzing its execution plan.

The redo being generated will be from the INDEXES on the table.  They generate
redo.  Suggestion was to disable them, bulk load, rebuild them unrecoverable.

Rating: 4
Huge Inserts September 19, 2001
Reviewer: Raj from India
Hi Mr.Tom,
can i use Commit like this?. Is cursor for loop itself has implicit
commit?.Please let me know

BEGIN
for record1 in cursor1
LOOP
huge insert statements;
END LOOP;
BEGIN
for record2 in cursor2
LOOP
huge insert statements;
END LOOP;
COMMIT;
END;

Followup:
there are no implicit commits in PLSQL.  Some SQL statements perform a commit
(all DDL) but normally -- there are no implicit commits.

As I've said many many times -- if you can avoid the loop and just do an insert
into t select * from something_else -- you'll be better off.

Yes, you can do the above if you want.

Rating: 4
Alternatively, use some NDS September 19, 2001
Reviewer: Keith Miller from UK
I have a "bulk insert" template I use which I've adapted for your query (see
below). Be interested to know what you think of this solution Tom.

You could always hash partition the the results table into x partitions, create
x seperate bits of code that will select x seperate lots of data and insert into
seperate the partitions of the results table. You could then kick off all those
inserts at once.

DECLARE
   CURSOR cursor1 IS
   SELECT /*+ FIRST_ROWS */
          delId,
          nval,
          prod_id,
          null,
          qtr,
          DECODE(qtr,0,0,cost/qty),
          0,
          null,
          1
   FROM   subs, inv, (SELECT po_id DelID,
                             po_dt dt,
                             TO_NUMBER(name) LocID
                      FROM   order, location
                     WHERE   order.id = location.id ) c1
   WHERE  subs.id = c1.locId
   AND    v_dt = c1.dt
   AND    subs.id = inv.inv_id;

   field1_array   DBMS_SQL.VARCHAR2_TABLE; -- or DBMS_SQL.NUMBER_TABLE or
DBMS_SQL.DATE_TABLE
   field2_array   DBMS_SQL.VARCHAR2_TABLE; -- or DBMS_SQL.NUMBER_TABLE or
DBMS_SQL.DATE_TABLE
   field3_array   DBMS_SQL.VARCHAR2_TABLE; -- or DBMS_SQL.NUMBER_TABLE or
DBMS_SQL.DATE_TABLE
   field4_array   DBMS_SQL.VARCHAR2_TABLE; -- or DBMS_SQL.NUMBER_TABLE or
DBMS_SQL.DATE_TABLE
   field5_array   DBMS_SQL.VARCHAR2_TABLE; -- or DBMS_SQL.NUMBER_TABLE or
DBMS_SQL.DATE_TABLE
   field6_array   DBMS_SQL.VARCHAR2_TABLE; -- or DBMS_SQL.NUMBER_TABLE or
DBMS_SQL.DATE_TABLE
   field7_array   DBMS_SQL.VARCHAR2_TABLE; -- or DBMS_SQL.NUMBER_TABLE or
DBMS_SQL.DATE_TABLE
   field8_array   DBMS_SQL.VARCHAR2_TABLE; -- or DBMS_SQL.NUMBER_TABLE or
DBMS_SQL.DATE_TABLE
   field9_array   DBMS_SQL.VARCHAR2_TABLE; -- or DBMS_SQL.NUMBER_TABLE or
DBMS_SQL.DATE_TABLE

   v_rowcount            PLS_INTEGER;
   v_bulk_collect_limit  PLS_INTEGER := 50000;

BEGIN
   v_rowcount := 0;

   OPEN cursor1;

   LOOP
      FETCH cursor1 BULK COLLECT
      INTO  field1_array,
            field2_array,
            field3_array,
            field4_array,
            field5_array,
            field6_array,
            field7_array,
            field8_array,
            field9_array
      LIMIT v_bulk_collect_limit;

      IF cursor1%NOTFOUND AND v_rowcount = cursor1%ROWCOUNT
      THEN
         EXIT;
      ELSE
         v_rowcount := cursor1%ROWCOUNT;
      END IF;

      FORALL indx IN field1_array.FIRST..field1_array.LAST
      INSERT /*+ APPEND */ INTO results
      VALUES
      (field1_array(indx),
       field2_array(indx),
       field3_array(indx),
       field4_array(indx),
       field5_array(indx),
       field6_array(indx),
       field7_array(indx),
       field8_array(indx),
       field9_array(indx));

      COMMIT;
   END LOOP;
   CLOSE cursor1;
END;
/

Followup:
Two things -- it doesn't use NDS (assume that means native dynamic sql?) and the
/*+ append */ hint is ignored unless it is an "INSERT ... SELECT", "INSERT ...
VALUES" can never use a direct path insert.

It is better then row at a time -- not as good as just doing it in one
statement.  50,000 rows could tend to eat gobs of RAM as well -- it's adjustable
I see....

Rating: 4
How about compromise - say, COMMIT every few hundred-thousand rows? September 19, 2001
Reviewer: MDz from CA, USA
Knowing the distribution of data, it would be possible to run this insert in
batches.  Something along these lines:

BEGIN
  FOR b IN 1..CEIL(:maxPOId/100000) LOOP
    insert /*+ append */ into results
    select delId, nval, prod_id, null, qtr,
           decode  (qtr,0,0,cost/qty), 0, null, 1
      from subs, inv, ( select po_id DelID, po_dt dt,
                               to_number(name) LocID
                          from order, location
                         where order.id = location.id
                           AND PO_ID BETWEEN (b-1)*100000
                                     AND B*100000-1) c1
    where subs.id = c1.locId
      and v_dt = c1.dt
      and subs.id = inv.inv_id;
    commit;
  end loop;
end;

With a little more work on this script, it could be modified to re-run after a
failure, and continue from where it left off.  The only downside I see in doing
this is that the SELECT for insert may take something like a full table
scan/hash join in every iteration, which would then slow down the process...

Rating: 5
Thanks for the feedback Tom September 20, 2001
Reviewer: Keith Miller
I dind't mean NDS, meant new bulk processing features, but it was in the middle
of a very busy day (that's my excuse and I'm sticking to it!)
I didn't realise about the APPEND hint not working there, so that's useful.

Rating: 3
September 20, 2001
Reviewer: Steve
Seems to me this insert is going to take forever to load this data.
I am thinking if I could use Sql loader instead of this insert to load my
results table. But Im not too proficient in it.
Im thinking before running the loader to do the following steps

Ill put the result of this cursor into a tmp table

Instead of :
CURSOR c1 IS select po_id, po_dt, to_number(name)
          from Order, Location
          Where Order.id = Location.id;
Ill use:
Insert into tmp(po_id,po_dt,ord_id)
select po_id, po_dt, to_number(name)
          from Order, Location
          Where Order.id = Location.id;

I have the huge (128 million rows) SUBS table's data in .dat files. I can
formulate the ctl file ,of
course with your help as follows. I dont know right syntax for the following:

load data
infile 'x.dat'
APPEND
into table results
fields terminated by ','
(DelID, (this is the po_id from the tmp table?)
 NVal, (this is from a sequence)
 prod_id, (this is a column in the .dat file for the subs table)
 null (this would be default),
 qty, (this is a column in the .dat file for the subs table)
 decode(qty, 0, 0, cost/qty), (cost is also a column in the .dat file for the
subs table. How to do this?)
 0 (this would be default),
 NULL (this would be default),
 1 (this would be default))

How can I ensure in the ctl file the following:
Where subs.id = tmp.ord_id
          And v_dt = tmp.po_dt
So that it loads from the .dat file only matching records between subs data &
tmp table rows into the results table
I dont know how to do the above , I would greatly appreciate your help.

Rating: 5
A long time, well yes of course it is September 24, 2001
Reviewer: Keith Miller
Steve,

Of course your load is going to take a while. You are looking at 128 million
rows. However you write it even the most efficient code in the world is going to
take time to process all this data.

If you are going to use sql*loader, the only way to make that quick is to use a
direct path load, but even then you are going to find that it will take time.

Look at hash partitioning your results table and then running several processes
in parallel. And before dismissing any of the suggestions out of hand I suggest
you try a few.

But remember, this is not a small amount of data so don't expect to find
anything that will run in ten minutes!

Rating: 5
And furthermore September 24, 2001
Reviewer: Keith Miller
Just re-read your SQL*Loader query and there is no way you can join the data in
the flat file to a table within SQL*Loader. It just doesn't work that way.
SQL*Loader can only get data from the file you supply it, not external sources.

You could write the combined results to a flat file so you effectively have the
whole results table in flat file format. Then you could use direct path load to
load this in. You are not able to apply functions to fields when using direct
path so you will have to have the data in the format you wish it to be inserted.

I just can't see though how this could possibly be faster than running an update
in a single process.

Face the facts - you have a large load to do, it's going to take time. If that
is not acceptable to your superiors then they obviously have no understanding
about how long things take with very large volumes of data.

Rating: 4
Your Suggestion Required April 30, 2002
Reviewer: pawan from USA
Tom,
   I have gone through various answers on your site and think that I have
understood why we get ORA 01555 error. I am running into ORA 0155 while running
the following query. Can you please suggest an alternative or ways to avoid the
error. Also how do I figure out how much rollback will be needed if I know that
the SELECT part of the query will result in 210 million records. The SOURCE
TABLES ( SOURCE_TB_A and SOURCE_TB_B ) have primary keys on them I have disabled
other Indexes. An example to find the RBS required would be great help. Suppose
I log on to SQL*plus create similar source tables A & B with only 1000 records
and run tghis Insert statement and query the USED_UBLK can I approximate the RBS
requirement.
Here is my query

INSERT INTO TARGET_TABLE
SELECT /*+ USE_NL(X, S) INDEX(X) INDEX(S) */
 X.XPONENT_CAT,
 X.RX_PAYMENT_TP,
 X.YR,
 X.MTH,
 X.DAY,
 X.WEEK,
 X.TRI,
 X.QTR,
 X.MKT_ID,
 X.IMS_PRESCRIBER_NUM,
 X.PRD_GRP_ID,
 X.IMS_PAYER_ID,
 X.IMS_PLAN_ID,
 X.AS_OF_DT,
 X.IMS_MTH_NUM,
 X.CMTY_NRX_CNT_PTRK,
 X.CMTY_TRX_CNT_PTRK,
 X.CMTY_NRX_DOLL_PTRK,
 X.CMTY_TRX_DOLL_PTRK,
 X.CMTY_NRX_VOL_PTRK,
 X.CMTY_TRX_VOL_PTRK,
 X.CMTY_NRX_THPY_PTRK,
 X.CMTY_TRX_THPY_PTRK,
 S.TERR_ID
FROM
 SOURCE_TB_B S, SOURCE_TB_A X
WHERE
 S.IMS_PRESCRIBER_ID=X.IMS_PRESCRIBER_NUM
AND X.MKT_ID =$$MKT_ID
 AND EXISTS
 (SELECT 'X'
  FROM SLS_FORCE_MKT_TB SF
  WHERE SF.MKT_ID = X.MKT_ID
  AND   SF.SLS_FORCE_TP_ID = S.SLS_FORCE_TP_ID)

Thanks for your help

Followup:
You need enough rollback permanently configured so your RBS do not wrap in the
time it takes to run that query.

If you estimate it'll take 1 hour (or whatever) to run that query, you need to
make sure you don't wrap rollback in 1 hour.  In 9i, you would use an undo
tablespace with a 1 hour, 15 minute retention period.  In 8i and before, you
need to monitor v$rollstat to see how often you are currently wrapping and size
your RBS upwards to make it so you wrap every 1 hour 15 minutes instead.

April 30, 2002 vivek -- Thanks for the question regarding "Creation of already created Tables with their Constraints on other machine", version 8.0
You Asked
1)i have more than 200 tables in my database.  The same have been build
over a period of time.  My problem is I do not have the scripts which
where used for creating the tables alongwith their constraints.  Is it possible
to create a script file for recreating the tables alongwith their constraints,
if i want to create the same tables at some other office.

2)Out of these 200 tables 40 are master tables. I want to know the dependent
tables having foreign key.  Can i find out through a scipt file the dependent
tables, in ascending order , as per their creation date.
and we said...
1) exp userid=u/p rows=N owner=u
   imp userid=u/p full=y indexfile=create.sql

create.sql will have your DDL

2) Yes, you would use user_constraints:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select parent_table, child_table, last_change
  2    from (select table_name parent_table,
  3                             constraint_name parent_key
  4                    from user_constraints
  5                   where constraint_type = 'P' ) p,
  6             (select table_name child_table,
  7                             r_constraint_name referenced_key,
  8                             last_change
  9                    from user_constraints
 10                   where constraint_type = 'R'
 11                     and r_owner = user ) c
 12   where p.parent_key = c.referenced_key
 13     order by parent_table, last_change
 14  /

PARENT_TABLE                   CHILD_TABLE                    LAST_CHAN
------------------------------ ------------------------------ ---------
T1                             T2                             28-APR-02
T1                             T3                             28-APR-02
TEST_TABLE                     TEST_TABLE                     22-APR-02

You might join the child table to user_tables to pick up the "real" creation
date but for most purposes -- last_change should do.  See the REFERENCE manual
for a description of this view and what it means.
Review & Followup
Rating: 4
Reader April 29, 2002
Reviewer: A reader
Is the condition

r_owner = user

redundant

Followup:
no, you could be referencing objects in other schemas.  I did this only for
tables contained within your schema.  You could rewrite to go against ALL_ or
DBA_ tables to get cross schema references.

Rating: 4
Creation of already created Tables with their Constraints on other machine April 30, 2002
Reviewer: Vivek from mumbai
Thanks Tom,

I tried export and import and i could get the scripts for creating the tables.
it worked fine.  But i also wanted the scripts for the constraints along with
the tables. Is it possible anyway.

The other query for finding out child tables was very good.
thank you again.

Followup:
Yes, they are there?

a@ORA817DEV.US.ORACLE.COM> create table t
  2  ( x int primary key,
  3    y int check ( y between 1 and 5 ),
  4    z int not null
  5  )
  6  /

Table created.

a@ORA817DEV.US.ORACLE.COM>
a@ORA817DEV.US.ORACLE.COM> host exp userid=a/a tables=t

Export: Release 8.1.7.3.0 - Production on Tue Apr 30 14:13:11 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.7.3.0 - Production
With the Partitioning option
JServer Release 8.1.7.3.0 - Production
Export done in WE8ISO8859P1 character set and WE8ISO8859P1 NCHAR character set

About to export specified tables via Conventional Path ...
. . exporting table                              T          0 rows exported
Export terminated successfully without warnings.

a@ORA817DEV.US.ORACLE.COM> host imp userid=a/a full=y indexfile=t.sql

Import: Release 8.1.7.3.0 - Production on Tue Apr 30 14:13:12 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.7.3.0 - Production
With the Partitioning option
JServer Release 8.1.7.3.0 - Production

Export file created by EXPORT:V08.01.07 via conventional path
import done in WE8ISO8859P1 character set and WE8ISO8859P1 NCHAR character set
. . skipping table "T"

Import terminated successfully without warnings.

a@ORA817DEV.US.ORACLE.COM> host cat t.sql

REM  CREATE TABLE "A"."T" ("X" NUMBER(*,0), "Y" NUMBER(*,0), "Z"
REM  NUMBER(*,0) NOT NULL ENABLE) PCTFREE 10 PCTUSED 40 INITRANS 1
REM  MAXTRANS 255 LOGGING STORAGE(INITIAL 16384 NEXT 16384 MINEXTENTS 1
REM  MAXEXTENTS 2147483645 PCTINCREASE 10 FREELISTS 1 FREELIST GROUPS 1
REM  BUFFER_POOL DEFAULT) TABLESPACE "SYSTEM" ;
REM  ... 0 rows
REM  ALTER TABLE "A"."T" ADD CHECK ( y between 1 and 5 ) ENABLE ;
REM  ALTER TABLE "A"."T" ADD PRIMARY KEY ("X") USING INDEX PCTFREE 10
REM  INITRANS 2 MAXTRANS 255 STORAGE(INITIAL 16384 NEXT 16384 MINEXTENTS 1
REM  MAXEXTENTS 2147483645 PCTINCREASE 10 FREELISTS 1 FREELIST GROUPS 1
REM  BUFFER_POOL DEFAULT) TABLESPACE "SYSTEM" ENABLE ;

Ok, wait -- I see the issue, it is 8.0.  It does not print out the constraints:

$ cat t.sql

REM  CREATE TABLE "A"."T" ("X" NUMBER(*,0), "Y" NUMBER(*,0), "Z"
REM  NUMBER(*,0) NOT NULL ENABLE) PCTFREE 10 PCTUSED 40 INITRANS 1
REM  MAXTRANS 255 LOGGING STORAGE(INITIAL 16384 NEXT 16384 MINEXTENTS 1
REM  MAXEXTENTS 505 PCTINCREASE 50 FREELISTS 1 FREELIST GROUPS 1
REM  BUFFER_POOL DEFAULT) TABLESPACE "SYSTEM" ;
REM  ... 0 rows

you could use strings on unix:

$ strings expdat.dmp
EXPORT:V08.00.06
RTABLES
1024
4000
                                                  Tue Apr 30 14:14:17 2002d
#G##
TABLE "T"
CREATE TABLE "T" ("X" NUMBER(*,0), "Y" NUMBER(*,0), "Z" NUMBER(*,0) NOT NULL
ENABLE)  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 LOGGING STORAGE(INITIAL
16384 NEXT 16384 MINEXTENTS 1 MAXEXTENTS 505 PCTINCREASE 50 FREELISTS 1 FREELIST
GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "SYSTEM"
INSERT INTO "T" ("X", "Y", "Z") VALUES (:1, :2, :3)
ALTER TABLE "T" ADD  PRIMARY KEY ("X") USING INDEX PCTFREE 10 INITRANS 2
MAXTRANS 255 STORAGE (INITIAL 16384 NEXT 16384 MINEXTENTS 1 MAXEXTENTS 505
PCTINCREASE 50 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE
"SYSTEM" ENABLE
ALTER TABLE "T" ADD  CHECK ( y between 1 and 5 ) ENABLE
EXIT

to get them, or try:

$ imp userid=a/a full=y show=y

Import: Release 8.0.6.0.0 - Production on Tue Apr 30 14:16:14 2002

(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Connected to: Oracle8 Enterprise Edition Release 8.0.6.0.0 - Production
With the Partitioning option
PL/SQL Release 8.0.6.0.0 - Production

Export file created by EXPORT:V08.00.06 via conventional path
. importing A's objects into A
 "CREATE TABLE "T" ("X" NUMBER(*,0), "Y" NUMBER(*,0), "Z" NUMBER(*,0) NOT NUL"
 "L ENABLE)  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 LOGGING STORAGE(IN"
 "ITIAL 16384 NEXT 16384 MINEXTENTS 1 MAXEXTENTS 505 PCTINCREASE 50 FREELISTS"
 " 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "SYSTEM""
. . skipping table "T"

 "ALTER TABLE "T" ADD  PRIMARY KEY ("X") USING INDEX PCTFREE 10 INITRANS 2 MA"
 "XTRANS 255 STORAGE (INITIAL 16384 NEXT 16384 MINEXTENTS 1 MAXEXTENTS 505 PC"
 "TINCREASE 50 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABLESPACE "
 ""SYSTEM" ENABLE"
 "ALTER TABLE "T" ADD  CHECK ( y between 1 and 5 ) ENABLE"
Import terminated successfully without warnings.

but you'll have to clean up the output a bit.

Rating: 5
Tables with Dependent Tables (Foreign keys to Parent) July 16, 2003
Reviewer: Ryan Docena from Asian Development Bank - Manila, Philiippines
Slight improvement on Tom's SQL, I turned it into a recursive function.

-- Pr_Tbl_Dpndnt.sql FK Dependent Tables (SQL for cursor adapted from
asktom.oracle.com) --
CREATE    OR REPLACE PROCEDURE pr_table_dependents
(
i_table        IN VARCHAR2,
i_level        IN INTEGER DEFAULT 1,
i_recurse    IN BOOLEAN DEFAULT TRUE
)
IS
    kt_tabs    CONSTANT VARCHAR2(10) := CHR(009);
    TYPE    typ_fk_dpndnt_rec IS RECORD(
        parent_table    VARCHAR2(30),
        child_table    VARCHAR2(30),
        last_change    DATE
    );
    csr_rec    typ_fk_dpndnt_rec;
    CURSOR    csr_fk_dpndnt
    IS
    SELECT    parent_table, child_table, last_change
    FROM    (SELECT    table_name parent_table,
            constraint_name parent_key
        FROM    user_constraints
        WHERE    constraint_type = 'P' ) p,
        (SELECT    table_name child_table,
            r_constraint_name referenced_key,
            last_change
        FROM    user_constraints
        WHERE    constraint_type = 'R'
            AND r_owner = user ) c
    WHERE    p.parent_key = c.referenced_key
        AND parent_table = i_table
    ORDER    BY parent_table, last_change;
BEGIN
    OPEN    csr_fk_dpndnt;
    LOOP
        FETCH    csr_fk_dpndnt
        INTO    csr_rec;
        EXIT    WHEN csr_fk_dpndnt%NOTFOUND;

        pkg_utilities.pr_put_lines(fn_repeat(kt_tabs, i_level-1) ||
        csr_rec.parent_table || ' - ' ||
        csr_rec.child_table || ' - ' ||
        csr_rec.last_change);

        -- Recursive Call to get next dependent tables --
        IF    (i_recurse AND csr_rec.parent_table != csr_rec.child_table)
        THEN    pr_table_dependents(csr_rec.child_table, i_level+1);
        END IF;
    END LOOP;
    CLOSE    csr_fk_dpndnt;
END    pr_table_dependents;
/
-- pkg_utilities.pr_put_lines() replaces the standard DBMS_OUTPUT.PUT_LINE
because of the limitation in displaying characters but i think you can still use
DBMS_OUT.PUT_LINE in this case

CREATE    OR REPLACE FUNCTION fn_repeat(
    i_text IN VARCHAR2,
    i_times IN INTEGER)
RETURN    VARCHAR2
AS
    v_text    VARCHAR2(4000) := '';
BEGIN
    FOR i IN 1..i_times LOOP
        v_text := v_text || i_text;
    END LOOP;

    RETURN    TRIM(v_text);
END    fn_repeat;
/

begin
     pr_table_dependents('REF_SECTORS');
end;

April 30, 2002 -- Thanks for the question regarding "Few questions--RBS & Partitions", version 817
You Asked
Hi Tom
I have a table with 70 million records. No partitions. When I try to do a select
count(*) from this table it gives me this error.

ORA-01555: snapshot too old: rollback segment number 6 with name "RBS5" too
small

I created a large Rollback Tablespace as :
CREATE TABLESPACE RBS_LARGE_TBS01
    DATAFILE 'D:\ORACLE\ORADATA\STAPLES\RBS_LARGE02.DBF' SIZE 512000K AUTOEXTEND
OFF
    LOGGING
    MINIMUM EXTENT 8192
    DEFAULT STORAGE(INITIAL 100M
                    NEXT 100M
                    MINEXTENTS 2
                    MAXEXTENTS 4
                    PCTINCREASE 0)
    ONLINE
    PERMANENT

Then I created a Rolback segement & associated it with this tblspc:

REATE ROLLBACK SEGMENT RBS_LARGE_06
    TABLESPACE RBS_LARGE_TBS01
    STORAGE(INITIAL 100M
            NEXT 100M
            MINEXTENTS 2
            MAXEXTENTS 4
            OPTIMAL 400M)
/
ALTER ROLLBACK SEGMENT RBS_LARGE_06 ONLINE

Then I do a
Commit;
set transaction  use  rollback segment RBS_LARGE_06;
select count(*) from sales;
It gives me an error:
ERROR at line 1:
ORA-01555: snapshot too old: rollback segment number 6 with name "RBS5" too
small

I dont understand why it is taking the wrong rollback segment "RBS5" which is a
small one whereas the largest is "RBS_LARGE_06 "?
When noone is using the datbase, and then if I execute this procedure it works.
Q1) What does this imply?

Q2)I havent made the change in the init.ora of the database. Should I?

Q3)Another question would be should I partition this table? I am on NT with only
one disk drive.

Q4)Do I have to create a Locally managed tablespace for using partitions on my
tables? Is this mandatory?
I have all dictionary managed tablespaces right now.

Q5)Also if I want to create partitions based on divisions(1,2,3) what kind of
partitioning should I use?
Can I use range or is there some kind of equal specification available, like
"Values equal to=1" ?

Thanks
and we said...
the probability of an ORA-01555 is directly proportional to the size of your
SMALLEST -- not largest (and definitely not the one you are using).

Very briefly: the 1555 happens when you are processing a read query.  We need to
redo some rollback from ANY one of the many rollback segments you have
configured (a query might need EVERY rollback segment as some point).  We
discover the information we need to read has been overwritten and raise the
1555.

Search for

ORA-01555

on my site for various articles on this.  Also, if you are interested, I cover
this topic somewhat to death in my book with lots of examples

q1) it implies your rollback segments are sized for your UPDATE transactions but
not your reads.  This is very common -- people size for the updates but forget
all about the reads.  You need to use equi-sized rollback segments (all of the
SAME size) and each should be big enough so that they do not wrap during the
time it takes to run your longest running query.

q2) nope

q3) it won't help here, partitioning won't solve this issue.

q4) no, LMTs are not neccesary HOWEVER they are the best thing to happen to the
database since sliced bread.  You should use them anyway.

q5) in 8i and before, you would use range partitions (less than 2 will get
division 1, less than 3 will get 2, less than 4 will get 4)

In 9i, we do have LIST partitions so you could use an IN LIST to partition the
data more clearly.

btw: are you using the CBO (cost based optimizer). If so, it'll use the primary
key index (you do have a primary key) if one exists.
Review & Followup
Rating: 5
(q3) August 27, 2001
Reviewer: Connor from UK
Partitioning won't help the problem, but it will probably help with a 70m row
table in all sorts of other nice ways.

Rating: 4
RBS&Partitions August 28, 2001
Reviewer: kamal.P from Singapore
Pl let me know any limitation for max size ,while using locally managed
tablespace ..
regards
kamal.P

Followup:
There are none, none that are any different from dictionary managed tablespaces
anyway.

The max extent size is still 2gig.

The size of a tablespace is limited only by the number and max size of files you
can have on your platform.

Rating: 4
More Explanation for a newbie April 30, 2002
Reviewer: Newbie from USA
Tom,
   You say
"Very briefly: the 1555 happens when you are processing a read query.  We need
to
redo some rollback from ANY one of the many rollback segments you have
configured (a query might need EVERY rollback segment as some point).  We
discover the information we need to read has been overwritten and raise the
1555."

Suppose I have two equi sized rollback segs each 15 GB and I am selecting
records from two different tables and inserting the resulset (200 million
records) into a third table, and I get ORA 01555. I am sure that the transaction
was using only rollback seg R01 and NOT R02. Do you mean that Oracle may need
both R01 and R02 for data consistency? I am using Informatica to load which
internally uses a cursor to select and Insert records and commits every 50,000
records. My rollback optimal parameter is 200m. IS this correct? How should one
size the Optimal parameter? I have tried even to change the commit to happen
every 1 million records. I know it is bad to commit in a open cursor and will
increates the commit to 200 million records (total resultset). NO ONE IS USING
THE DATABASE when this LOAD is on. Teh load runs between 4 to 8 hours ( i have
tried 5 times now) and then fails with ORA 01555.

Thanks for your help.

Followup:
Yes, Oracle may need both r01 and r02 to provide a read consisitent view of the
two tables you are querying.

Committing inside of a cursor for loop is the best, most sure fire way to get an
ora-1555.

I do not use optimal personally (hence my advice would be ... the right size is
no size at all).

The 1555 can happen even with no one else on.

see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:895410916429
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:360536564061
for some examples.

You need to size the RBS to be big enough so as to NOT wrap during your longest
running query.

April 30, 2002 Pichaimani -- Thanks for the question regarding
      "Parsing the data", version 8.0.6

      You Asked
Hi Tom,
   Right now we are storing some data in long columns and parsing it thru PL/SQL
for applying it to our application tables. The problem we now have is before
parsing thru PL/Sql we should have appropriate procedures as the pattern will
differ. We are now thinking of pulling out the parsing from PL/SQL and we can
have a user configurable parsing instructions. The parsing can be done by XML
and store it.
Can we do that with Oracle 8i and Oracle XML parser? Which one will be faster
PL/SQL or Oracle XML parser, what steps we should follow for implenting XML in
PL/SQL.
   Could u suggest any initial threads to start with XML in Oracle.

Thanks

      and we said...
1) don't use long
2) don't use long
3) use a clob
4) yes, XML would be a much better choice and if you are upgrading, goto 9i,
don't goto already obsolete software.  9i has excellent XML support.  8i has
very good XML support.

visit:

http://otn.oracle.com/tech/content.html
for more info.  XML is most likely the way to go.
Review &
      Followup

      Rating: 4
why CLOB, why
      not LONG  April 30, 2002
Reviewer:  rajiv

TOM,

  why should we only use CLOB? why not LONG?

          Followup:

Longs have been a depricated datatype since version 8.0.

supported but not encouraged.
provided for backwards compatibility.

a clob is about 1,000,000% more functional then a long.

May 01, 2002 Christoph -- Thanks for the question regarding
      "Connect by prior procedure bug.", version 8.1.7.1.1

      You Asked
I have encoutered the following situation in a procedure using CONNECT BY PRIOR:

rem I have created a procedure that populates a table from a cursor loop using a
connect by prior clause.
rem The procedure does not function properly in the following scenario:
rem   1. Procedure executes properly and inserts rows correctly.
rem   2. Select statement shows correct table entries in correct order (KING at
the top)
rem   3. delete emp2;
rem   4. commit;
rem   5. Execute procedure again.
rem   6. Select shows table entries out of order (KING at the bottom)
rem
rem  This only happens when i issue a DELETE <TABLE> command followed by a
commit;
rem  If i were to issue a select statement on the table between steps 4 and 5
and
rem  then continue with step 5, step 6 would show the results correctly.
rem  Also if I were to issue ROLLBACK instead of steps 3 and 4, the procedure
would work correctly.
rem
rem Somehow the delete command caused the procedure to fail.
rem
rem I included the complete code and demonstration.
rem
connect scott/tiger

col curr_lvl format 99
col org_chart format a30
col empno format 9999
col mgr format 9999
col job format a10
col deptno format 99

drop table emp2;

create table emp2 (
  curr_lvl int,
  org_chart varchar2(50),
  empno int,
  mgr int,
  job varchar2(20),
  deptno int);

create or replace procedure emptest
is
  cursor c1 is
    select
      level,lpad(' ',level*2,' ') || ename org_chart,
      e.empno, e.mgr, e.job, e.deptno
      from  emp e
     connect by prior empno = mgr
       start with mgr is null;
begin
  for r1 in c1 loop
    insert into emp2 values(r1.level,
      r1.org_chart,r1.empno,r1.mgr,r1.job,r1.deptno);
  end loop;
end;
/

exec emptest;

REM Everything is in order.
select * from emp2;

delete emp2;

commit;

exec emptest;

REM Notice how KING who should be first, is now last.
select * from emp2;

rollback;

exec emptest;

REM Everything is in order again.
select * from emp2;

      and we said...
You have made the 100% erroneous assumation that DATA COMES OUT in the same
order it went in!

If you need data in a certain order, there is ONLY one way to get it - an ORDER
BY.  Period.

Without an ORDER BY, you can only assume the data is coming out in some random,
arbitrary order.
May 01, 2002
      rajiv -- Thanks for the question regarding
      "data parsing and querying", version 8.1.6

      You Asked
tom,

  i get set of ids from table as comma seperated

  table : id_table
  column : id_column

  id_column
  -----------
  1,2,3,4
  8,9,4,6
  11,19,17,1

like the above the column stores

now the main table has the followin rows

table : main_table
columns : id
        : Name

id     Name
----   ----
1      1a
2      2b
3      3c
4      4d
6      6e
8      8f
9      9g
11     11h
17     17k
19     19l

now using the id_table i need to get the names present in main_table

i.e. like

Name
-----
for the first row of id_table
1a
2b
3c
4d
for second row
8f
9g
4d
6e
11h
19l
17k
1a

without using dynamic query can we do it in a pl/sql procedure

i think, we have to write these details into another table and then query based
on the values of this table

is there any other better way you can suggest?

thanks
rajiv

      and we said...
Well, you can do it:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select *
  2    from t1, t2
  3   where instr( t1.id_column||',' , t2.id || ','     ) > 0
  4   order by t1.id_column, t2.id
  5  /

ID_COLUMN                    ID NAME
-------------------- ---------- ------------------------------
1,2,3,4                       1 1a
1,2,3,4                       2 2b
1,2,3,4                       3 3c
1,2,3,4                       4 4d
11,19,17,1                    1 1a
11,19,17,1                    9 9g
11,19,17,1                   11 11h
11,19,17,1                   17 17k
11,19,17,1                   19 19l
8,9,4,6                       4 4d
8,9,4,6                       6 6e
8,9,4,6                       8 8f
8,9,4,6                       9 9g

13 rows selected.

that does not imply any sort of "performance".. This will be slow as sin on a
table with more then a trivial amount of rows.  Keep that in mind

Review &
      Followup

      Rating:
      4
Oops  April 29, 2002
Reviewer:
      frank  from NL
Your query made up a 9 in the second id_column !

Maybe better:

where instr( ','||t1.id_column||',' , ','||t2.id || ','     ) > 0

          Followup:

DOH!  I knew that.  Thanks for correcting it -- typed it a bit too quickly.

      Rating:
      4
Oops  April 29, 2002
Reviewer:
      frank  from NL
Your query made up a 9 in the second id_column !

Maybe better:

where instr( ','||t1.id_column||',' , ','||t2.id || ','     ) > 0 ?

      Rating: 4
but the
      performance  April 30, 2002
Reviewer:  rajiv

tom,

 that is working fine, but i am little worried about the performance(as you also
mentioned in your answer). as the tables involved are huge,

i need further info can i place the ID values based on ID_COLUMN in a PL/SQL
TABLE and then query like

select id,name from t2 where t2.id in <pl/sql table values>

          Followup:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:110612348061

      Rating: 5
selecting one
      more column with pl/sql table  April 30, 2002

Reviewer:  rajiv
how do i do it in this fashion, i.e. i need to select one more column along with
the pl/sql table row and want to convert a local pl/sql table to the subquery?
is the following possible?

declare
  l_data myTableType := myTabletype();
  x ref cursor;
  myrefcur x;
begin
  for i in mycursor
  loop
    l_data.extend;
    l_data(l_data.count) := i.value;
  end loop;
  open myrefcur select * from all_users where (col1,user_id) in (select
col1,<the clause that
converts the l_data pl/sql local table to mytable)

end;

thanks

          Followup:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace type mytableType as table
of number
  2  /

Type created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> variable x refcursor
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_data myTableType := myTableType( 1, 2, 3, 4, 5 );
  3  begin
  4      open :x
  5       for 'select *
  6             from all_users
  7            where (username, user_id) in ( select all_users.username,
column_value
  8                                             from TABLE ( cast (:l_data as
myTableType) )
  9                                          )' using l_data;
 10  end;
 11  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> print x

USERNAME                          USER_ID CREATED
------------------------------ ---------- ---------
SYSTEM                                  5 28-AUG-01

      Rating: 5
direct with out
      binding?  May 01, 2002
Reviewer:  rajiv

tom,

 that is cool answer, helped me a lot.

 can't we give l_data directely like below with out binding?
is that an issue?(why?)

open :x for select *
              from all_users
           where (username, user_id) in ( select all_users.username,
column_value
                                           from TABLE ( cast (l_data as
myTableType) )
                                          )

          Followup:

try it.  for me, it did not work -- plsql was not happy with the construct in
817 so I used dynamic sql to work around it.

Note that this will be HORRIBLY inefficient as you are now using a correlated
subquery -- the l_data structure will be returned ONCE per row in all_users.
This probably is one of those things that "looks really cool but isn't 100%
practical in the real world"

May 01, 2002 Glenn -- Thanks for the question regarding "Usage
      of Stored Procedures", version 9.0.1

      You Asked
What is the best way to tell if stored procedures are actually being used?  We
can check DBA_SOURCE to see if other procedures might call it. Is there
something else I could use to try to get more info?

Thank-you

      and we said...
You could enable auditing and audit the executions.

You could look in the shared pool (v$sql) and see statements that include this
procedure and see their execution count.

You can look in all_dependencies to see what other procedures use this one as
well.  You need to do this because 1 and 2 above will only show direct
invocations, not usage by other procedures.
May 01, 2002 Glenn -- Thanks for the question regarding
      "Filtering rows", version 9.0.1

      You Asked
A query returns a result set in a format as follows:

ID         LINK   CNT
40032   32        1
40033   43        1
40034   16        6
40034    22       2
40034    28       2
40035    12       1
40035    14       1
40036    18       1
...

There are several hundred thousand rows in the result set. The desired
result:  for each id, return the MAX(CNT) associated with the id, along with a
related LINK.  If an ID has two LINKs with the same MAX(CNT) (ex 40034), it does
not matter which LINK is used for the final result.

So for this example, an acceptable result for the query would be:

ID      LINK   CNT
40032   32        1
40033   43        1
40034   16        6
40035   12        1
40036   18        1
...

Returning a LINK along with the MAX(CNT) is causing me some grief.  I am nesting
sub-selects.  I feel I should be able to do an ORDER BY and filter somehow with
ROWNUM < 2.  I guess I could return a table from a stored procedure.  I thought
I have seen examples like this before, but cannot find any.  Could you offer a
suggestion?

Thank-you

      and we said...
select id,
       to_number( substr( data, 1, 9 ) ) cnt,
       to_number( substr( data, 10 ) ) link
  from (
select id, max( to_char(cnt,'fm000000009') || link ) data
  from t                       123456789
 group by id
       )

will get you that.....

Review &
      Followup

      Rating: 4
what about this
      ?  May 01, 2002
Reviewer:  A reader

Hi tom,

 I just have, I think similer, I don't know yet
issue

I have a table

stmt1:
SQL> select * from t1;

 ID EMAIL
--- --------------------------
  1 test@hotmail.com
  2 test1@hotmail.com
  3 test2@hotmail.com
  4 test@hotmail.com
  5 test1@hotmail.com
  6 test2@hotmail.com
  7 test@hotmail.com
  8 test1@hotmail.com
 10 test10@hotmail.com
  9 test2@hotmail.com

10 rows selected.

stmt 2:
SQL>select distinct email from t1;

EMAIL
------------------------------
test10@hotmail.com
test1@hotmail.com
test2@hotmail.com
test@hotmail.com

stmt: 3
SQL>SELECT DISTINCT id,email from t1;

 ID EMAIL
--- ------------------------------
  1 test@hotmail.com
  2 test1@hotmail.com
  3 test2@hotmail.com
  4 test@hotmail.com
  5 test1@hotmail.com
  6 test2@hotmail.com
  7 test@hotmail.com
  8 test1@hotmail.com
  9 test2@hotmail.com
 10 test10@hotmail.com

10 rows selected.

 my question is how can i write query which will yield me distint ids( min or
max does not matter) for distinct email address ? (will the soulution be
scalable ? --operate on any kind of data )

 can we do this...

Thanks for all your help..
from thi

          Followup:

show me the output you want.

"yield me distinct ids for distinct email address"

does not make sense to me.  I don't know what you want really.

      Rating:
      4
  May 01, 2002
Reviewer:
      A reader
sorry for stmt: 3 above

stmt 4:
SQL>SELECT DISTINCT email,id from t1;

EMAIL                           ID
------------------------------ ---
test10@hotmail.com              10
test1@hotmail.com                2
test1@hotmail.com                5
test1@hotmail.com                8
test2@hotmail.com                3
test2@hotmail.com                6
test2@hotmail.com                9
test@hotmail.com                 1
test@hotmail.com                 4
test@hotmail.com                 7

10 rows selected.

      Rating:
      3
  May 01, 2002
Reviewer:
      A reader
sorry tom,

  I was unable to explain clearly..

 here I need the ids for distinct email address only.

stmt1:
SQL> select * from t1;

 ID EMAIL
--- --------------------------
  1 test@hotmail.com
  2 test1@hotmail.com
  3 test2@hotmail.com
  4 test@hotmail.com
  5 test1@hotmail.com
  6 test2@hotmail.com
  7 test@hotmail.com
  8 test1@hotmail.com
 10 test10@hotmail.com
  9 test2@hotmail.com

10 rows selected.

stmt 2:
SQL>select distinct email from t1;

EMAIL
------------------------------
test10@hotmail.com
test1@hotmail.com
test2@hotmail.com
test@hotmail.com

stmt 4:
SQL>SELECT DISTINCT email,id from t1;

EMAIL                           ID
------------------------------ ---
test10@hotmail.com              10
test1@hotmail.com                2
test1@hotmail.com                5
test1@hotmail.com                8
test2@hotmail.com                3
test2@hotmail.com                6
test2@hotmail.com                9
test@hotmail.com                 1
test@hotmail.com                 4
test@hotmail.com                 7

10 rows selected.

so what I need is out put like

EMAIL                           id
------------------------------  ---
test10@hotmail.com               10
test1@hotmail.com                 8
test2@hotmail.com                 9
test@hotmail.com                  7

Thanks,

          Followup:

That would be:

select email, max(id) from t group by email;

      Rating: 4
Stop wasting
      his valuable time!  May 01, 2002
Reviewer:  a
      real reader  from work
You shouldn't be classified as a reader asking this question.

RTFM and lookup having

you need having max(count)

      Rating: 3
Thanks,
      tom  May 02, 2002
Reviewer:  A reader

May 01, 2002 Nilanjan -- Thanks for the question regarding
      "upgrading v8.0.6 database to v8.1.6", version 8.0.6,8.1.6

      You Asked
Hi Tom,

I have oracle 8.1.6 software intalled. And I have a v8.0.6
database. I want to upgrade it to v8.1.6. Is u0800060.sql the
only the script I have to execute ? Apparently, this script is
calling i0800050.sql,utlip.sql,c0800050.sql and  a0800050.sql.
Do I need to run some other scripts ?

Thanks,
Nilanjan

      and we said...
The proper procedure to update is always documented in the
server Migration guide.  This is the definitive way to get this
information.

If possible, you should use the supplied GUI tools to upgrade as
it will take care of running the necessary scripts.  In order to
do a manual upgrade, please see:

http://technet.oracle.com/doc/oracle8i_816/server.816/a76957/migupgra.htm#6967
and specifically:

http://technet.oracle.com/doc/oracle8i_816/server.816/a76957/migupgra.htm#24532
which discusses the 8.0.6 to 8.1.6 migration steps - either
through the GUI or manually.

Review &
      Followup

      Rating: 2
Upgrading to
      8.1.7 from 8.1.5  May 01, 2002
Reviewer:
      Ravi  from India
Hi Tom,
I want to upgrade my Database from 8.1.5 to 8.1.7.
I need following information.
- What are the things, we need to take care of ?
- Detailed steps and explaination ?
- How can I revert back in case of problem ?

Please let me know above things.
It would be great help.

          Followup:

fortunately for every release we produce a handy guide called the Oracle
Migration guide that covers all such details:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a86632/toc.htm

Surprisingly, that was not clear in the answer already?  With every release --
look for that guide!

May 02, 2002 Phil
      -- Thanks for the question regarding "Which tables to put into which
      BUFFER_POOL", version 8.1.7

      You Asked
Tom ,

I have read metalink article [NOTE:135223.1] Oracle Multiple Buffer Pools
Feature. It is very good. However I wondered if there are any scripts which you
might have which could be run periodically to check which pools tables should be
in. Also is there a way of telling if you have made a mistake and put an object
in the wrong pool?

Thanks

Phil

      and we said...
There is no way a script could tell you this.

Experience and knowledge of how the table is in fact used is the only way to
know whether you want an object

o in the keep pool
o the recycle pool
o the default pool

YOU are the only one with the knowledge of where an object might best be placed.
 Looking into the existing buffer pool won't tell you where it should go -- you
cannot infer from there how it it used at all.

The way to tell if you made a mistake... that can be done.

Take statspack reports now and for a couple of days.  Make your changes.  Keep
taking the statspack.  Then, compare the results from before and after -- if you
have improved the statspack report - you've done good.  If not, you made a
mistake.
Review
      & Followup

      Rating:
      3
  May 02, 2002
Reviewer:
      A reader
Thanks

      Rating: 5
Possible
      suggestion...  May 02, 2002
Reviewer:
      Connor  from UK
Not that its by any means definitive, but the touch count at least gives a
measure of what activity is occurring within the cache.  Armed with that (and a
healthy knowledge of the app) can assist with buffer pool selection.

Cheers
Connor

May 02, 2002 Matt -- Thanks for the question regarding
      "Security: Restricting Ad-hoc applications to have read-only access",
      version 7.3.4

      You Asked
Tom,

I am working on a bespoke client server based system that uses an Oracle 7.3 DB
(an upgrade to 9i is planned and in progress).

Currently the system implements it's access security using Oracle roles and
privileges, alongside an application owned table that defines usernames,
passwords and password expiry information. These passwords are stored as ASCII
text.

I need to restrict the access of third party products (excel, toad, pl/sql
developer etc) to be read-only. The issue, I guess is that the read only user
would be able to read the ASCII passwords and then gain access this way.

Is there some Oracle functionality in 7.3 that might help??

I have been reading about some mechanisms to encrypt the passwords on this site
and have also read some doco on Fine Grained Security Access.

Is there anything that can be done easily on 7.3, or would you consider a better
approach to be to wait for the 9i upgrade and use something like fine grained
security access and something like an ON_LOGON trigger??

Any help or advise received with thanks.

Mat.

      and we said...
There isn't too much you can do in 7.3 (besides using password protected roles
and obscuring the passwords in some fashion).

In 9i -- we can easily accomplish 100% of your goals -- easily.

The problem is -- before 9i -- there is no way to identify your application --
NO WAY.  You cannot tell the difference between toad, excel and your
application.  You cannot look at the program column in V$session (i'll just copy
toad.exe to your_app.exe and I'll appear to be your app).

In 9i - we have a very strong,  very secure mechanism for identifying an
application using N-Tier proxy authentication.
Review &
      Followup

      Rating: 3
Examples of the
      use of Proxy Authentication  May 03, 2002

Reviewer:  Matt  from Australia
Tom,

Can you provide any reference to example implementations of this technology?
I've dug into some of the security documentation and the application developers
guide, but what I could really do with is some examples of how to configure the
proxy server and how I restrict access to particular applications.

Cheers.

May 02, 2002 justin -- Thanks for the question regarding
      "Viewing Blob Data or Size / Retrieving Blob from database", version 8.0.6

      You Asked
I amin the process of retrieving a Blob from a table in an
Oracle 8.0.6 database which looks like the following:

Name                            Null?    Type
------------------------------- -------- ----
ID_INTERNAL                     NOT NULL NUMBER(13)
DOC_ID                          NOT NULL NUMBER(13)
ID_ACCT                                  VARCHAR2(12)
PDF                                      BLOB
SUBJECT                                  VARCHAR2(70)
TEXT_MSG_ID                              NUMBER(3)
DATE_SENT                                DATE
IMAGE_NAME                               VARCHAR2(20)

I think I have successfully inserted a Blob (pdf file) into the
table where doc_id is 5,  but i keep getting a null pointer
exception in my java code.  I initialized the record with the
empty_blob() function and I suspect that nothing was ever
written to the record when the Blob was inserted.

My first question is how can i see if my Blob actually contains
anything since sqlplus does not support viewing of Blob
datatypes?

My second question is how should I retrieve a Blob from the data
base? I am using the following java code:

....establish a connection to the database

String qry="select pdf from mailbox where doc_id=5;
stmt=dbcon.createStatement();
stmt.executeQuery(qry);

while(rs.next()){
    myBlob=rs.getBlob("PDF");
}

java.io.InputStream = myBlob.getBinaryStream();

As a last note,  your site by far is the best Oracle resource
out there. Our entire project has used you services in manner or
another.  Thanks for all the help.

      and we said...
For the first question...  To see the length, you can always

select dbms_lob.getlength( pdf ) from T where doc_id = 5;

To see some of the RAW data in sqlplus -- see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:549422761760
it describes how to use utl_raw and dbms_lob.substr to view
pieces of the blob in SQLPlus.

For #2, you are on the right track - getBinaryStream.  If you
goto

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:238014476571
you'll see an example of my reading a long raw and writing it to
a blob.  The reading of a long raw is much the same as the
reading of a blob.

Looking at your code above -- the java.io.InputStream =
statement should be inside of the loop.

Are you running into an issue with BLOBS in Java?  If you are
asking "am I doing it the right way" -- the answer is yes.
There are other ways but that is the accepted way to do it....

Thanks for the feedback.

Review &
      Followup

      Rating:
      3
  May 02, 2002
Reviewer:  Thomas
       from Germany
Hello,
I get after this line: "select dbms_lob.getlength( pdf ) from T where doc_id =
5;"

This Error from ORACLE:
"ORA-06509: PL/SQL: ICD-Vector lacks in this Paket
ORA-06512: in "QUILS.DBMS_LOB", line 393
ORA-06512: in line 6"

          Followup:

why did you install the dbms_lob code in your QUILS schema?

You did this to yourself.  drop the dbms_lob package and ONLY run the scripts in
$ORACLE_HOME/rdbms/admin when connected as:

o sys
o internal
o "as sysdba"

May
      03, 2002 Tom -- Thanks for the question
      regarding "Generic XML reporting package", version 8.1.7

      You Asked
Hi Tom,

First off, great book.....you didn't shirk on the length did you!!

My question is this.  I am trying to write an XML-based reporting package which
will take an SQL query, the start row and the end row and return a paged HTML
table [such as you get with Amazon etc].  My current strategy is the following
[pseudo-code in places]

create or replace procedure report
(
sqlquery varchar2,start integer,end integer
)
as
cu integer;
descTab dbms_sql.descTab;
cnt integer;
recordCount INTEGER;
resultSet CLOB;
styleSheet CLOB;
begin

--First find out how many rows we have in the result
execute immediate 'select count(*) from (' || sqlquery || ')' into recordCount;

--Then find the format of our columns
cu := dbms_sql.open_cursor();
dbms_sql.parse(cu,sqlquery,dbms_sql.native);
dbms_sql.describe_columns(cu,cnt,descTab);
dbms_sql.close_cursor(cu);

--Generate a stylesheet based on the column description
--for example align numeric columns to the right, varchar to the left
styleSheet := generateStylesheet(descTab);

--Get the XML as a CLOB passing the start and end rows and a --stylesheet
resultSet := xmlgen.getXML(sqlquery,start,end,stylesheet);

--Output the result to the web page

end report;

At the moment, any procedure that uses the functionality of this reporting
package, generates an sql string from the values passed to it and passes that to
the reporting package.  Obviously this is terrible for the shared pool [each
statement is potentially unique] so I was wondering if you could suggest a way
of making better use of the shared pool whilst still allowing this package to
return results for any given SQL statement it is passed?

Thanks for any help on this one..

Tom

      and we said...
First -- DON'T COUNT THE RECORDS.  That count can and will be *wrong* and just
wastes time and resource (what happens when someone modifies the table and
commits after you count, before you select???)  Use this technique:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:14612348049
to get the rows you want.

basically, the query YOU will parse will be:

select *
  from ( select rownum r, a.*
            from ( original_query )
          where rownum <= END_POINT )
wnere r >= START_POINT

Second -- add "alter session set cursor_sharing=force" to the code.  Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure p ( p_query in
varchar2 )
  2  as
  3     l_cursor number := dbms_sql.open_cursor;
  4  begin
  5          execute immediate 'alter session set cursor_sharing=force';
  6
  7          dbms_sql.parse( l_cursor, p_query, dbms_sql.native );
  8
  9          -- ... rest of code here, just do this before you return:
 10
 11          execute immediate 'alter session set cursor_sharing=exact';
 12  end;
 13  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec p( 'select * from dual TEST_QUERY where
dummy = ''X'' ' )

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select sql_text from v$open_cursor where
user_name = user and sql_text like '%TEST_QUERY%';

SQL_TEXT
------------------------------------------------------------
select * from dual TEST_QUERY where dummy = :"SYS_B_0"

See how the system will automagically rewrite the query using bind variables for
us (if you have my book -- see chapter 10, i go into depth on this topic)
Review &
      Followup

      Rating: 5
How to work out
      number of records to page if I don't count  May 02, 2002

Reviewer:  Tom
Tom,

Thanks for the response.  Excellent idea to use cursor sharing force....I hadn't
thought of that.  You say not to count the rows as this can be incorrect when in
a multi-user environment, but if I do not do this then how can I work out how
many rows to display in the paging section of the report.  Basically I want to
display the data in chunks of 10 [for example] with numbers at the bottom to let
me page [rather than simply next and previous buttons].  To do this I need to
know the row count....is there any way of doing this that will work?

          Followup:

I would suggest forgoing that "pagination" idea and just go with previous or
next.

Look at google for example.  They make a REASONABLE assumption that people will
never go beyond 10 pages.

When you search, you can goto pages 1, 2, 3, .... 10 and prev/next.

If you goto page 10, they'll let you goto page 11 if one exists (the fact that
page 10 was full means there is a page 11).  But, you cannot get to page 11
without going to page 10.

I would just "assume" there will be 10 pages with 10 rows each.  Let the user
pick ANY of them.  If you discover that page "5" doesn't have any data (you get
zero rows), you then goto page 4 (and so on, until you get data) and fix the
"page scale" at the bottom.  Just like the big search engines do.

Unless you want to make your interface painfully painfully slow -- drop the
count(*).

      Rating: 5
Not sure this
      is correct  May 02, 2002
Reviewer:  Tom

Tom,

I see your point about count slowing thigs down, however this is implemented in
the big search engines somehow.  For example, in Google.com if you search for
"conjugate gradient linear" [including the quotes to give a very specific search
term] then you get given a page with 6 numbers at the bottom as well as the
forward / back paging buttons.

This means they must know how many results are present [althlough I suppose
their results are less likely to change].  Will this be accomplished by a
count(*) and if not how will it be done.....I always assumed that the count
overhead will not be that large assuming that we count on an indexed column.  I
would have thought that providing buttons for pages that do not exist would
confuse user navigation.

Is it possible that they emply a hybrid approach, by using

select count(*) from
( SQL QUERY)
where rownum <= (10*rows per page)

to find out whether they have the first 10 pages to display, and then extend
this as people page, so that the count for the first rows is very fast [limited
by rownum] and slows down as you page further.  Is this a viable approach to the
problem?

          Followup:

Google does it like asktom does it.

if you search for asktom on google, it says:

Results 1 - 10 of about 1,480.

If you do the same on asktom.oracle.com:

{asktom}
Approximately 781 records found.

Using text searches -- it is quite different, very very different.  We are able
to approximate the number of results due to the way text indexes are organized.
Note: it is an APPROXIMATION.  You will find sometimes that last page (or pages)
do not REALLY exist!

So, like I said -- assume 10 pages exist (if you find out otherwise, adjust).

If you want my opinion -- here it is:

DO NOT COUNT.
Assume there are 10 pages -- when you find otherwise, adjust (just like google,
just like asktom)

the count overhead will be HUGE for an arbitrary query.

the rownum trick will *sometimes* make it faster, sometimes NOT.

      Rating:
      3
  May 02, 2002
Reviewer:
      A reader
What about selecting into a collection, then using count and return the
collection and the total number.

how does otn.oracle.com do this with their search tool? It tells you the total
number found and returns pages.

          Followup:

TEXT indexes are totally different then "counting the hits from an arbitrary
relational query".

selecting into a collection -- that would be "bad".  The routine is designed to
get rows N through M.  You would have to fetch out the entire result set.

For relational, typical queries -- the number of rows is NOT KNOWN until after
the very last row is fetched.  You would have to fetch the entire result set
(obviating the performance benefit of just getting the first 10)

May
      03, 2002 YM -- Thanks for the question
      regarding "Manage user access in database application development",
      version 9.0.1

      You Asked
Hi Tom,

I would like to seek your opinion on managing user access rights in database
applications.

I have seen 2 different approaches:
First, create all users in a database table, with all the user rights and
controls stored, and write packages to handle access control with applications.
Typically, the application program uses single database user to access the
database.

Second approach, create all users as database users, and set relevant access
rights. But it seems that some access controls still have to be implemented in
application level as it may not be covered by database itself.

What is your opinion over the aprroaches? What is your preferred way of
controlling access to your database applications?

Thank you!

      and we said...
what relevant access controls are not in the database that would have to be
implemented in the application itself?

I use the database.

I don't have to create a table of users.
I don't have to write password aging/3 strikes your out/account locking
I don't have to write code to manage and verify roles
I don't have to do lots of things

If you tell me what you find lacking -- I'll tell you how that is done in the
database -- there are tons of things in the database to do this (eg: fine
grained access control for example)

Review &
      Followup

      Rating: 4
Why do a lot of
      users use tables to store passwords then  May 02, 2002

Reviewer:  Srimal  from Trenton, USA
Hello Mr Tom,

I use the database completly for managing users, but over the last one week, I
have noticed in this site that a good number of users are using tables to manage
users (including passwords) for various reasons.

That had me thinking about any limitation on Oracle's end (except clear text
password, I could not think of any). The solutions that you presented for using
secure passwords have also been excellent...

FGAC is a boon to people like us!

      Rating: 2
Application
      access control  May 02, 2002
Reviewer:  YM
       from Malaysia
Hi,

I have such puzzle because many programmers claim that by using table(s) and
coding themselves, they have the freedom to control up to very detail level --
access control to some operations in the form & report level, i.e.
update/query/delete of certain fields in the Form, etc. And they also want to
allow application administrator to manipulate users without knowing how Oracle
manages users.

I would be pleased to hear more about the Fine Grained Control Access and its
examples...

Thank you!

          Followup:

If they can do it with their own tables and coding, they can do it with the data
dictionary too -- can't they (yes, rhetorical question, I do it -- i know you
can do it AND i don't have to code 100% of it, just the little teeny tiny bit I
need to when I need to)

As for:

And they also want to
allow application administrator to manipulate users without knowing how Oracle
manages users.

How does doing it in the database defeat that?  Lets see -- to add a user to
their application:
    insert into table ....

to add a user to my application:
    grant create session to user ....

their ADMIN screen has to issue a sql statement in both cases, the admin user
doesn't do the insert do they?  (pretty sad app if they do)

To give a role to a user their way:
   insert into table ...

the database way:
    grant role ....

In either case -- THEIR app that the ADMIN USES executes the SQL right?  What
care does the admin use have whether it does an insert/delete or grant/create?

See

http://osi.oracle.com/~tkyte/article2/index.html
or my book for FGAC info.

      Rating: 3
There are a
      table in 11i ----FND_USER  May 02, 2002
Reviewer:
      Alex  from Singapore
Hi, Tom
I'm also think about changing to use database user in my application. But I
found in Oracle 11i, there is a table about application users, following is the
description of this table i found on metalink.
"
FND_USER stores information about application users. Each row includes the
user's username (what a user types in at the sign-on screen), password, and
information on when the user should change the password. Each row also contains
information on when the user last signed on, start and end dates for when a
username is valid and a description of the user. Oracle Application Object
Library does not use the USER_PASSWORD and LOCAL_PRINTER columns. You need one
row for each application user at your site. Oracle Application
Object Library uses this information to allow users to sign on to an
application.
"
It's seems 11i also manages user in a table. Thus can system use oracle
"Profile" to limit resources used by each application user?

      Rating: 5
User Access
      Control through database is the best  May 03, 2002

Reviewer:  Riaz Shahid  from PRAL,Islamabad,Paksitan

Hay Friend!

I have been Oracle DBA since 1997....and i never encountered any kind of problem
in managing user through database instead of using my own tables.

Yes, Oracle also do manage users access through tables but the method(s) applied
for that are well-tested and debugged as well as much more efficient than that
of managing users through our own tables. If some utility has been designed to
facilitate you, then it seems quite awakward to look for own...thus waste
resources and time in managing that. The rule is "If milk is available from shop
then it is foolish to have a cow".

If someone has any problems regarding managing users through database, then do
tell me. I will try to solve that problem.

Regards

May 03, 2002 Jason -- Thanks for the question regarding
      "Database Capitalization", version 8.1.5

      You Asked
Tom,
  We are having a minor debate here about the capitalization of
data in the database.  One of the requirements is that when the
information is re-displayed to the user, it needs to be pretty.
So, if I type in my name as jASON, it should come back to me as
Jason.  This sounds like a case for Initcap, but what about
names like Amundson-McGysel?
  Three questions:
    1) How should the data be stored? (Upper, Mixed, or Lower)
    2) When should the function that makes the transformation be
applied?  This may be answered in question 1.
    3) How should the data in the tables be updated?

Keep in mind that the function may have bugs, or exceptions that
pop up when people don't like the way we capitialized their
name.

Thanks,
Jason.

      and we said...
I've always believed that the data should be stored as it was
entered myself.  There are too many exceptions to do proper
capitalization using a function.

I would store the data "as entered" and if I needed to do a case
insensitive search -- I would use a function based index on the
UPPER() of the data (see

http://osi.oracle.com/~tkyte/article1/index.html
for info/examples).  If I had to have this special "initcap"
functionality -- I suppose I would write a function in PLSQL
that performs the transformation and "SELECT" it out when I
needed it -- eg:

select transform( name ) from T where id = 5;

That way, the data is stored as entered, can be displayed as you
like and can be searched in a case insensitive fashion.
Review &
      Followup

      Rating: 4
For the names
      of Inventory Products- we have similar problems  May 03,
      2002
Reviewer:  Bill  from Toronto, ON, Canada

Hi Tom:

Thank you for taking the times and efforts to establish such a useful site.

In our production environment, we populate our Oracle tables from Mainframe.
Since all product names are in uppercase in Mainframe, we have a stored
Procedure just to INITCAP of all the product names in the table. The problem
occurs is that some product has, say, "MIKE'S PRODUCT A" or "O'NEIL'S PRODUCT
B". After applying the INITCAP function, they become "Mike'S Product A" and
"O'Neil'S Product B". Both 'S is converted to S which is not desirable.

My question is if there is any simple workaround to selectively apply the
INITCAP or any ponders/directions ? Thanks in advance.

          Followup:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select x, rtrim(replace( initcap( x )|| ' ',
'''S ', '''s ' )) y
  2    from t
  3  /

X                         Y
------------------------- -------------------------
MIKE'S PRODUCT A          Mike's Product A
O'NEIL'S PRODUCT A        O'Neil's Product A
PRODUCT A DE O'NEIL'S     Product A De O'Neil's

would probably work.....

May 03, 2002 Anji
      -- Thanks for the question regarding "A Question on SQL Query", version
      8.1.6

      You Asked
Hi Tom
I have a question on SQL Query.

The scenario is like this:--
I have two tables doc and docelements.
doc contains id and name of the document and
docelements contain id of the doc , keyword  and frequency (no of occurances of
that keyword in that doc).
Now i want to write a query that selects all document names, keywords and their
frequencies such that all keywords are present in those documents.

For Ex:--
I have created tables and data like this

create table doc ( id NUMBER, docname VARCHAR(20))
create table docelements ( id NUMBER, keyword VARCHAR(20),frequency NUMBER)
 primary key for doc is id.
 primary key for docelements is (id, keyword)

INSERT INTO doc VALUES (1, 'test1');
INSERT INTO doc VALUES (2, 'test2');
INSERT INTO doc VALUES (3, 'test3');

INSERT INTO docelements VALUES (1, 'anji', 2);
INSERT INTO docelements VALUES (1, 'peter', 3);
INSERT INTO docelements VALUES (1, 'chandra', 3);
INSERT INTO docelements VALUES (2, 'anji', 2);
INSERT INTO docelements VALUES (3, 'anji', 2);
INSERT INTO docelements VALUES (3, 'chandra', 2);
INSERT INTO docelements VALUES (3, 'david', 2);

Now if i give keywords anji,peter,chandra, the query should return id as
id  docname  keyword
--  -------  -------
1    test1    anji
1    test1    chandra
1    test1    peter

I have come up with the query as follows. I think the following way is not
better way. Can you suggest me any better way of doing this other than the
following one.

SELECT a.id, a.docname, c.keyword
FROM   doc a,
(
SELECT id
FROM   docelements
WHERE  keyword IN ('anji', 'peter', 'chandra')
GROUP  BY id
HAVING COUNT(*) = 3) b,
       docelements c
WHERE  a.id = b.id AND
       b.id = c.id AND
       c.keyword IN ('anji', 'peter', 'chandra');

Thanks
--Anji

      and we said...
Did you consider that interMedia text, part of the database -- something you
already have -- does this and does it very fast?

That would be the better way, your query would become:

select * from doc where contains( text, '{anji} & {peter} & {chandra}' );

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/inter.817/index.htm

select *
  from doc
 where 3 = ( select count(*)
               from docelements
              where docelements.id = doc.id
                and keyword in ( 'anji', 'peter', 'chandra' )
           )

would be my approach, you only care about getting the doc row -- not the
docelements row (you know the keywords already)

Review &
      Followup

      Rating:
      5
  April 26, 2002
Reviewer:  Anji
       from CA, USA
Tom
Is this query is more efficient than the one i mentioned in the question?
Can i know how it is efficient?
Thanks
--Anji

          Followup:

See

http://asktom.oracle.com/~tkyte/runstats.html
and benchmark it on a sizeable dataset.  post the results!  it is what I would
do.

      Rating:
      3
  May 02, 2002
Reviewer:
      A reader  from Ca, USA
Hi Tom,

It doesn't seem like your query performs?!

-- -----------------------------------------------------------
-- create sample data...
-- -----------------------------------------------------------
BEGIN
   DELETE FROM doc;
   DELETE FROM docelements;

   FOR i IN 1..5000 LOOP
      INSERT INTO doc VALUES (i, 'Test' || i);

      IF (MOD(i, 3) = 0) THEN
         INSERT INTO docelements VALUES (i, 'Anji', 3);
      END IF;

      IF (MOD(i, 2) = 0) THEN
         INSERT INTO docelements VALUES (i, 'Peter', 3);
      END IF;

      IF (MOD(i, 6) = 0) THEN
         INSERT INTO docelements VALUES (i, 'Chandra', 3);
      END IF;

      IF (MOD(i, 12) = 0) THEN
         INSERT INTO docelements VALUES (i, 'David', 3);
      END IF;
   END LOOP;

   commit;
END;
/

-- -----------------------------------------------------------
-- Results of the queries...
-- -----------------------------------------------------------
SQL> set timing on
SQL> SELECT COUNT(*)
  2  FROM   doc a,
  3  (
  4  SELECT id
  5  FROM   docelements
  6  WHERE  keyword IN ('Anji', 'Peter', 'Chandra')
  7  GROUP  BY id
  8  HAVING COUNT(*) = 3) b
  9  WHERE  a.id = b.id;

 COUNT(*)
---------
      833

Elapsed: 00:00:00.90

SQL> select COUNT(*)
  2    from doc
  3   where 3 = ( select count(*)
  4                 from docelements
  5                where docelements.id = doc.id
  6                  and keyword in ( 'Anji', 'Peter', 'Chandra' )
  7             )
  8  /

 COUNT(*)
---------
      833

Elapsed: 00:00:43.33
SQL>

-- -----------------------------------------------------------
-- runstat
-- -----------------------------------------------------------
declare
    type    VarCharTab is table of varchar2(255);
    type    NumberTab is table of NUMBER;

    l_start number;
    l_x     NumberTab;
    l_y     VarCharTab;

begin
    delete from run_stats;
    commit;

    -- start by getting a snapshot of the v$ tables
    insert into run_stats
    select 'before', stat.*
    from  (
    select 'STAT...' || a.name name, b.value
    from   v$statname a, v$mystat b
    where  a.statistic# = b.statistic#
    union  all
    select 'LATCH.' || name,  gets
    from   v$latch) stat;

    -- and start timing...
    l_start := dbms_utility.get_time;

    SELECT a.id, docname BULK COLLECT
    INTO   l_x, l_y
    FROM   doc a,
          (SELECT id
           FROM   docelements
           WHERE  keyword IN ('Anji', 'Peter', 'Chandra')
           GROUP  BY id
           HAVING COUNT(*) = 3) b
    WHERE  a.id = b.id;

    dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );

    -- get another snapshot and start timing again...
    insert into run_stats
    select 'after 1', stat.*
    from  (
    select 'STAT...' || a.name name, b.value
    from   v$statname a, v$mystat b
    where  a.statistic# = b.statistic#
    union  all
    select 'LATCH.' || name,  gets
    from   v$latch) stat;

    l_start := dbms_utility.get_time;

    SELECT id, docname BULK COLLECT
    INTO   l_x, l_y
    FROM   doc
    WHERE  3 = ( select count(*)
                 from   docelements
                 where  docelements.id = doc.id and
                        keyword in ( 'Anji', 'Peter', 'Chandra' )
               );

    dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
    insert into run_stats
    select 'after 2', stat.*
    from  (
    select 'STAT...' || a.name name, b.value
    from   v$statname a, v$mystat b
    where  a.statistic# = b.statistic#
    union  all
    select 'LATCH.' || name,  gets
    from   v$latch) stat;
end;
/

-- -----------------------------------------------------------
-- runstat result
-- -----------------------------------------------------------
SQL> select a.name, b.value-a.value run1, c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11   order by abs( (c.value-b.value)-(b.value-a.value))
 12  /

NAME                                          RUN1      RUN2      DIFF
---------------------------------------- --------- --------- ---------
STAT...calls to get snapshot scn: kcmgss         3         2        -1
STAT...db block changes                         15        16         1
STAT...enqueue requests                          1         0        -1
STAT...deferred (CURRENT) block cleanout         3         2        -1
 applications

STAT...messages sent                             1         0        -1
STAT...redo entries                              7         8         1
STAT...sorts (memory)                            5         4        -1
STAT...parse time cpu                            1         0        -1
STAT...free buffer requested                     1         0        -1
LATCH.kwqit: protect wakeup time                 0         4         4
LATCH.active checkpoint queue latch              0        31        31
LATCH.cache buffer handles                       0       112       112
STAT...redo size                             20104     20328       224
LATCH.JOX SGA heap latch                         0       271       271
LATCH.mostly latch-free SCN                      1       539       538
LATCH.cache buffers lru chain                    0       596       596
LATCH.session idle bit                           0       712       712
LATCH.session allocation                         0       713       713
LATCH.redo writing                               3       771       768

NAME                                          RUN1      RUN2      DIFF
---------------------------------------- --------- --------- ---------
LATCH.list of block allocation                   0       776       776
STAT...no buffer to keep pinned count          804         0      -804
LATCH.messages                                   4       835       831
STAT...rows fetched via callback               833         0      -833
STAT...table fetch by rowid                    833         0      -833
LATCH.checkpoint queue latch                     2       857       855
LATCH.transaction allocation                     0       991       991
LATCH.row cache objects                          0      1102      1102
LATCH.undo global data                           1      1214      1213
LATCH.dml lock allocation                        0      1387      1387
LATCH.shared pool                               21      1480      1459
LATCH.redo allocation                            9      1629      1620
LATCH.enqueue hash chains                        0      2235      2235
LATCH.enqueues                                   0      2310      2310
STAT...recursive cpu usage                       8      4323      4315
STAT...sorts (rows)                           7290      2291     -4999
STAT...table scans (short tables)                1      5001      5000
LATCH.library cache                             85      7527      7442
STAT...db block gets                            15     20016     20001
STAT...consistent gets                        2525    105021    102496
STAT...buffer is not pinned count             1715    105016    103301

NAME                                          RUN1      RUN2      DIFF
---------------------------------------- --------- --------- ---------
STAT...no work - consistent read gets           49    105016    104967
STAT...table scan blocks gotten                 21    105016    104995
STAT...session logical reads                  2540    125037    122497
LATCH.cache buffers chains                    2620    266819    264199
STAT...table scan rows gotten                 5415  27080000  27074585

45 rows selected.

          Followup:

Sigh, no schema -- totally nonrepresentative data.

I guess I have to assume you did not index docelements -- based on the table
scan rows gotten.

try a real schema:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table doc ( id number primary key,
text varchar2(2000) );

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table docelements ( id number, keyword
varchar2(25), some_number number,
  2                             constraint doc_ele_pk primary key(id,keyword) );

In any case, the original query will work best when MOST docelement sets will
not have 3, mine will work best when MOST do.

      Rating:
      3
  May 03, 2002
Reviewer:
      A reader  from Ca, USA
Here's the schema that was used to produce the above result. Indexes did exist.

create table doc ( id NUMBER, docname VARCHAR(20));
create table docelements ( id NUMBER, keyword VARCHAR(20),frequency NUMBER);

alter table doc add constraint doc_pk primary key (id);
alter table docelements add constraint docelements_pk primary key (id, keyword);

          Followup:

me too -- i see no such large difference.

use tkprof, rerun and analyze your results.  I cannot reproduce.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table doc ( id NUMBER, docname
VARCHAR(20));

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table docelements ( id NUMBER, keyword
VARCHAR(20),frequency NUMBER);

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table doc add constraint doc_pk primary
key (id);

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table docelements add constraint
docelements_pk primary key (id, keyword);

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> BEGIN
  2     DELETE FROM doc;
  3     DELETE FROM docelements;
  4
  5     FOR i IN 1..5000 LOOP
  6        INSERT INTO doc VALUES (i, 'Test' || i);
  7
  8        IF (MOD(i, 3) = 0) THEN
  9           INSERT INTO docelements VALUES (i, 'Anji', 3);
 10        END IF;
 11
 12        IF (MOD(i, 2) = 0) THEN
 13           INSERT INTO docelements VALUES (i, 'Peter', 3);
 14        END IF;
 15
 16        IF (MOD(i, 6) = 0) THEN
 17           INSERT INTO docelements VALUES (i, 'Chandra', 3);
 18        END IF;
 19
 20        IF (MOD(i, 12) = 0) THEN
 21           INSERT INTO docelements VALUES (i, 'David', 3);
 22        END IF;
 23     END LOOP;
 24
 25     commit;
 26  END;
 27  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set timing on
ops$tkyte@ORA817DEV.US.ORACLE.COM> SELECT COUNT(*)
  2  FROM   doc a,
  3  (
  4  SELECT id
  5  FROM   docelements
  6  WHERE  keyword IN ('Anji', 'Peter', 'Chandra')
  7  GROUP  BY id
  8  HAVING COUNT(*) = 3) b
  9  WHERE  a.id = b.id;

  COUNT(*)
----------
       833

Elapsed: 00:00:00.06
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select COUNT(*)
  2    from doc
  3   where 3 = ( select count(*)
  4                 from docelements
  5                where docelements.id = doc.id
  6                  and keyword in ( 'Anji', 'Peter', 'Chandra' )
  7             )
  8  /

  COUNT(*)
----------
       833

Elapsed: 00:00:00.51
ops$tkyte@ORA817DEV.US.ORACLE.COM>

both are subsecond.

May 03, 2002 Yves
      -- Thanks for the question regarding "Sizing DB Block in relations to
      XMLType & CLOBs", version 9.0.1

      You Asked
Hi Tom

The requirement for my database (9.0.1) is that it be able to store XML
fragments as well as relational data derived from XML data as well.  I am
planning to
use the XMLType for my XML fragments.  Here is my strategy:  I have various
entity types in my database; for each entity type there is an associated XML
fragment as well as relational data for that entity.  I have created a table of
what I call templates (XML Fragments) and every entity that needs to store XML
fragments stores it in my entity table.  Of course I have a foreign key from
each entity table to the template table.

My main concern is about economy of storage and database block size.  Some of
the XML fragments will have 5 lines while others will have 100 or 1000 lines.
My understanding from reading the literature is that regardless of the size of
the XML, Oracle must use a minimum of space equal to the database block size.
For me even if I size my database block down to 1k, I'd still be wasting a lot
of space.  On the other hand, some of the relational tables in my database can
have
a row size of 8k.  Using the 1k example an 8k record will be spread over 8
database block and querying will be much slower given this.

Any thoughts, advise, corrections, or suggestion to my quandary?

Thanks,

Yves

      and we said...
Using CLOBS (in the xmltype) -- they will be stored INLINE upto 4000 bytes.  It
is only when they are stored out of line that they will each take a CHUNKSIZE
set of storage (blocksize being the smallest chunk).

Hence -- I do not see any problems here.  The clob for the XML will be stored
inline when it is small and out of line when it is big.

Review &
      Followup

      Rating:
      3
  May 03, 2002
Reviewer:  Yves

What do you mean by "inline up to 4000 bytes"?  Is 4k the upper limit for
XMLType?  I thought XMLTType was really a metatype for CLOBs.  If the latter is
true, the smallest chunk size for a clob is a DB block.  Please clarify your
response.

          Followup:

No, I mean CLOBS are stored (by default) "inline" -- in the table itself -- not
in the clob segment -- when they are 4000 or less characters.

They are moved "out of line" when they exceed that (from 4000 bytes to 4
gigabytes).

XMLType is built on a clob.
A clob will be "in line" -- stored in the table, not the clob segment -- upto
4000 bytes.
A clob will move out of line taking up one or more chunksize when it exceeds
that.

So, your clobs that are small -- they will NOT consume a chunk.  The will just
be in the table (like a varchar2 would).

This is totally transparent to you.

May 03, 2002 Daniel -- Thanks for the question regarding
      "member merge", version 8.1.6

      You Asked
Hi Tom,

I have a table like

member_id  maintain_dt  colx    coly
1          2002-04-25   'A'     <null>
1          2001-01-13   'B'     'C'
2          2002-05-01   <null>  'D'
2          2001-01-14   'E'     'F'
3          2002-05-02   'G'     <null>
3          2002-05-01   'H'     <null>
3          2002-01-30   'I'     'J'
4          2002-02-27   'K'     <null>
4          2002-01-30   'L'     'M'
4          1999-01-28   'N'     'O'

I want to get the record for each member with the latest maintain_dt, for colx,
I want to keep the value coming from the latest record, but for coly, if the
value from latest record is not null, I want to keep it, otherwise, I want to
get the not null value from the second latest record.

So, the result should be
member_id  maintain_dt  colx    coly
1          2002-04-25   'A'     'C'
2          2002-05-01   <null>  'D'
3          2002-05-02   'G'     'J'
4          2002-02-27   'K'     'M'

How can I do this in one sql statement like analytical function?

Thanks,
Daniel

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> select member_id,
  2   max(maintain_dt),
  3   substr( max( to_char(maintain_dt,'yyyymmddhh24miss') || colx ), 15 ) colx,
  4   substr( max(
           decode(coly,NULL,null,
                    to_char(maintain_dt,'yyyymmddhh24miss')) || coly ), 15 )
coly
  5    from t
  6  group by member_id
  7  /

 MEMBER_ID MAX(MAINTA C C
---------- ---------- - -
         1 2002-04-25 A C
         2 2002-05-01   D
         3 2002-05-02 G J
         4 2002-02-27 K M

The first trick there is to get the colx that comes with the MAX(maintain_dt)
for a given member id.

substr( max( to_char(maintain_dt,'yyyymmddhh24miss') || colx ), 15 ) colx,

does that.  It does that by getting the max maintain_dt again (using a nice
ascii sort sequence on the date -- making it sortable) and pulling the colx
along with it (bear in mind, if you have two records with the same maintain_dt
for a member_id, you'll get the MAX colx for that!)

Next, you want the coly for the max maintain_dt for that member id, given that
coly is NOT NULL.  This:

substr( max( decode(coly,NULL,null,to_char(maintain_dt,'yyyymmddhh24miss')) ||
coly ), 15 ) coly

does that by nulling out the maintain_dt when coly is NULL and keeping it
otherwise.  So, we find the max maintain_dt for a given member_id such that coly
is not null and keep that one.

There you go.....

May 04, 2002 Dusan -- Thanks for the question regarding "Type
      change ", version 8.1.6

      You Asked
Hi Tom,

We have tables with columns based on type t_type1 VARRAY(100).
We need to alter (enlarge) this type to VARRAY(300), but the
tables are not empty, so we cannot drop and recreate them.
How to do this in the easiest way ?

thanks,

Dusan

      and we said...
There is currently no chance of doing schema migration with
object types - once we have a persistent object created that
uses the type -- the ability to change the structure of that
object type is gone.

How big is this table?  What I would suggest is something like
the following:

ops$tkyte@DEV816> create type t_type as varray(100) of number
  2  /
Type created.

ops$tkyte@DEV816> create table t ( x int primary key, y t_type
);
Table created.

ops$tkyte@DEV816> insert into t values
  2  ( 1, t_type( 1, 2, 3, 4, 5 ) );
1 row created.

ops$tkyte@DEV816> insert into t values
  2  ( 2, t_type( 11, 12, 13, 14, 15 ) );
1 row created.

ops$tkyte@DEV816> create table temp
  2  ( x int, y int );
Table created.

ops$tkyte@DEV816> insert /*+ APPEND */ into temp
  2    select t.x, t2.*
  3      from t t, table(t.y) t2
  4  /

10 rows created.

ops$tkyte@DEV816> create index temp_idx on temp(x) unrecoverable
  2  /
Index created.

ops$tkyte@DEV816> alter table t drop column y;
Table altered.

ops$tkyte@DEV816> create or replace type t_type as varray(300)
of number
  2  /

Type created.

ops$tkyte@DEV816> alter table t add y t_type
  2  /
Table altered.

ops$tkyte@DEV816> update t
  2    set y = cast
  3              ( multiset(
  4                    select y
  5                      from temp
  6                     where temp.x = t.x )
  7                 as t_type )
  8  /

2 rows updated.

ops$tkyte@DEV816> select * from t;

         X
----------
Y
-----------------------------------------------------------------
-----------------------------------
         1
T_TYPE(1, 2, 3, 4, 5)

         2
T_TYPE(11, 12, 13, 14, 15)
Review & Followup

      Rating:
      4
Reader  May 03, 2002
Reviewer:
      A reader
Tom,

I am trying to select multiple rows from a table.

I wrote the code:
declare
type t_emp is a table of emp%rowtype index by binary integer;
l_emp t_emp;
begin
select * into l_emp from emp where deptno = 10;
end;
/
I get PLS-103.
Can I store multiple rows into a table type this way or
is there an alternative approach

Thanks

          Followup:

what do you want to do with them?

return them to a client?  see

http://asktom.oracle.com/~tkyte/ResultSets/index.html
process them procedurally?

begin
  for x in ( select * from emp where deptno = 10 )
  loop
      ....
  end loop;
end;

is all you need.

      Rating:
      4
Reader  May 03, 2002
Reviewer:
      A reader
I want to process them procedurally.

declare
type t_emp is a table of emp%rowtype index by binary integer;
l_emp t_emp;

begin
for x in (select * from <global emporary table>)
loop
   select * into l_emp where deptno =  x.deptno;
   dbms_output.put_line ....l_emp the rows returned
end loop;
end;

I want to have the ability to get returned set formatted in
,csv format, spool it and read the spooled file
into  excel spead sheet

Thanks

          Followup:

then it is quite simply:

begin
   for x in ( select * from <gtt> )
   loop
       for l_emp in ( select * from emp where deptno = x.deptno )
       loop
             ........
       end loop;
   end loop;
end;

Actually, just:

begin
    for x in ( select * from emp where deptno in (select deptno from gtt ) )
    loop
       ....

would be better.  And if you are really looking for a shortcut, search for
dump_csv on this site and your code will be:

begin
    dump_csv( 'select * from emp where deptno in ( select deptno from gtt )',
              '/tmp',
              'emp.csv' );
end;

and if you want formatting for excell, search for owa_sylk on this site.

      Rating:
      5
Reader  May 07, 2002
Reviewer:
      A reader
Tom

Thanks very much for the prompt response with
the impressive terse code.

I was creating report in the form
<table1><table2>table3><table4><table5> all concatenated.
I reviewed the dump_csv script and owa_sylk and used
the techniques in them to build my script successfully

Thanks

May 04, 2002 Ramasubbu -- Thanks for the question regarding
      "Syntax checking without execution of query", version Oracle8i Enterprise
      Edition Release 8.1.6.3

      You Asked
Hi Tom,
     I have a front end screen where user can form a select statement.Once user
will submit the syntax checking I should verify whether the query syntatically
correct or not?.How should I achieve  above by using SQL/PLSQL?.

For ex,
      User will form a query like

select account_no,balance from acct_master where customer_id=1000123243

For the above the query I want to know whether syntatically correct or not.I
don't want execution part.

Awaiting your reply.
Thank you.

      and we said...
declare
   l_cursor number := dbms_sql.open_cursor;
begin
    execute immediate 'alter session set cursor_sharing=force';
    dbms_sql.parse( l_cursor, p_query, dbms_sql.native );
    execute immediate 'alter session set cursor_sharing=exact';
exception
    when others then
        execute immediate 'alter session set cursor_sharing=exact';
        dbms_sql.close_cursor( l_cursor );
        RAISE;
end;
/

will do that.  if they are going to be putting literals in like that MAKE SURE
TO USE CURSOR_SHARING, so as to not totally trash your shared pool.
Review &
      Followup

      Rating: 4
What if it were
      DDL?  May 03, 2002
Reviewer:  Scott
       from Chicago, IL, USA
Say the contents of p_query was a DDL statement.  As I understand the current
implementation of dbms_sql it executes DDL a part of the parse() call.

   DROP TABLE terribly_important CASCADE CONSTRAINTS;
   CREATE OR REPLACE PROCEDURE malicious_intent AS ...
   GRANT EXECUTE ON malicious_intent TO wimpy_everyday_user;
   etc.

Hence, if the application was one where the app user is logged onto the database
as a rather privileged db user (an unfortunately common enough occurance), then
this opens up quite a security hole.

cheers,
Scott

          Followup:

Correct -- good point.  Should probably add:

if substr(upper(ltrim(p_query)),1,6) in ( 'SELECT', 'INSERT', 'UPDATE', 'DELETE'
)
the
   parse it.

      Rating: 5
syntax checking
      for stored procedure without creation...   May 04, 2002

Reviewer:  Ramasubbu  from Tokyo
I understood that dbms_sql.parse does parse as well as execution for ddl
statements.But I am  interested to asking is there any way to syntax checking
for stored procedure without creation.
Thank you
muthu

          Followup:

Nope, you either create it or not.

Or, maybe you can do this

declare

procedure that_you_want_to_create
is
...
begin
  ....
end;

begin
   null;
end;

and parse that block -- that'll do it.

May 04, 2002 Reji
      -- Thanks for the question regarding "JAVA in Oracle -- when and how long
      does the JVM get started /stay around", version 8.1.6

      You Asked
Tom:

    I have a trigger in one of the table, when I insert a record
    this trigger get fired and that will execute a stored
    procedure which in turn invoke a method from a java class -
This method open a socket and sends a msg to an external
process.

    When the stored procedure invoke the method from the java
class, does oracle creates a new VM?  if it is what is the life
span of that VM?  If I have so many insert into this table, does
that mean I will have so many of these VM up and running at the
same time?  What is the overhead of doing this way? or Is there
any other way I can accomplish the same thing?

   In my case, after I finish sending the msg to the external
    process, do I have to close the socket explicitly?

TIA
Reji

      and we said...
The VM is on a session by session basis.

Once you have started your mini-jvm, it'll be there until you
log out.

The first java call has some measurable overhead.  Subsequent
calls are instantaneous.

Ultimately you should close the socket yourself -- yes.  Do you
have to do it right away -- no, you can have a "cleanup" call
later on to close the socket instead of opening/closing it each
and every time.

If you just need sockets, you can do it in plsql with UTL_TCP in
8.1.6.
Review
      & Followup

      Rating: 5
Another
      one  May 03, 2002
Reviewer:  Kumar
       from USA
so if 100 user connected to oracle ,100 jvm instance will be there ,is that
right?

          Followup:

there is one real JVM, each session will get their own VJVM (a virtual java
virtual machine).  The all share the same "big" jvm.  They each have their own
"session" however (I cannot see your globals/static data and such)

      Rating: 5
How many no
      VJVM?  May 03, 2002
Reviewer:  Kumar
       from USA
How will i know how many no VJVM is running?

          Followup:

I don't know what you mean.

      Rating:
      3
JVM  May 04, 2002
Reviewer:  Vijays
       from India
How do we know which version of JVM running ?

          Followup:

create or replace and compile java source named "props" as
public class props {
  public static void show_props() {
    System.getProperties().list(System.out);
  }
}
/
show errors
create or replace procedure java_props
as language java name 'props.show_props()';
/
show errors
exec dbms_java.grant_permission( user,
'java.util.PropertyPermission','*','read,write' );
set serveroutput on size 10000
exec dbms_java.set_output(10000)
exec java_props

will do it:

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec java_props
-- listing properties --
java.specification.name=Java Platform API Specification
java.version=1.2.1
oracle.aurora.mts.INIT=oracle.aurora.mts.rdbms.INIT
user.timezone=EST

May 04, 2002 Kate
      -- Thanks for the question regarding "How to use Sequence number with
      multi threaded program execution?", version 8.1.7

      You Asked
Hi Tom,
This is a design question that I need your help with.
We need to assign a sequential number to a very large volumn transaction table
when a certain condition is met. The daily batch jobs and online transaction
that updates the record are all need
to run in parallel and most of all the sequence number
assigned to the records should not have a gap.

I was inclined to use Oracle Sequence and database trigger on the
table. But I have a issue with using sequence.
When the program fails after issuing multiple updates
and does roll back the trigger had already updated sequence number
and there is no way to reset the sequence number.

The way I was thinking of using the sequence number is
something like the following:

CREATE OR REPLACE TRIGGER  TRG_ASSIGN_SEQ_NBR
BEFORE UPDATE ON BIG_TRANS_TBL
FOR EACH ROW WHEN (new.STATUS = 'COMPLETE' and old.SEQ_ID = 0)
BEGIN
   SELECT SEQ_NBR1.NextVal into :new.SEQ_ID from dual;
END;

Any suggestion or alternative solutions will be greatly appreciated.

Thanks,

Kate.

      and we said...
This requirement:

...that updates the record are all need
to run in parallel and most of all the sequence number
assigned to the records should not have a gap....

cannot be met.

a) you cannot in parallel
b) generate a gap free number.

It is IMPOSSIBLE.  You can only:

a) in SERIAL
b) generate a gap free number (very very slowly)

Rethink your requirement (i say this over and over for this).

as it stands you cannot use a SEQUENCE (a single rollback will generate a
"gap").  You cannot use a home grown sequential number generator either IN
PARALLEL (same rollback/failure issue).  You will be stuck generating them ONE
AT A TIME -- doing ONE TRANSACTION AT A TIME.

Parallel and "gap free sequences" is an OXYMORON.

Serial, slow and gap free is possible....

In the end, it is up to you but this requirement should be re-thought and met in
perhaps a DIFFERENT way somehow.

Review &
      Followup

      Rating: 3
Unfortunately,
      this is a real requirement...  May 07, 2002

Reviewer:  Kate  from San Francisco, USA
Thanks for the timely response. Unfortunately I can not relax the requirement.
It comes from more than a decade old accounting practice in Europe. At least now
I am sure that the system generated sequence can not be used for this purpose. I
am looking into alternatives.
Thanks again.

May 04, 2002 Yogesh -- Thanks for the question regarding
      "dbms_profiler Error ", version 8.1.7

      You Asked
Hi, I installed the dbms_profiler package, But when I started profiler by using
following

Scott@orcl>  exec dbms_profiler. START_PROFILER;
BEGIN dbms_profiler. START_PROFILER; END;

*
ERROR at line 1:
ORA-06528: Error executing PL/SQL profiler
ORA-06512: at "SYS.DBMS_PROFILER", line 123
ORA-06512: at "SYS.DBMS_PROFILER", line 132
ORA-06512: at line 1

I got above error. why this is so ? Other procs / functions are executing, viz

Scott@orcl> CREATE OR REPLACE PROCEDURE pro_test
  2
  3  AS
  4      Var1              BINARY_INTEGER;
  5      Var2               BINARY_INTEGER;
  6  BEGIN
  7      DBMS_PROFILER.GET_VERSION(Var1,var2);
  8          dbms_output.put_line('Var1 '||Var1);
  9              dbms_output.put_line('Var2 '||Var2);
 10  END;
 11  /

Scott@orcl> exec pro_test;
Var1 2
Var2 0

Where I'm going wrong ?

      and we said...
Run the PROFTAB.SQL script for each user that needs to do profiling. (or take
the existing ones and make sure that they are publically accessible -- i suggest
each developer has their OWN copy so as to not stomp on eachother)
May 04, 2002 Evara -- Thanks for the question regarding
      "Internet Programming", version 8.1.7

      You Asked
hii. tom.
your book is very is cool.
do you have plan to write expert one-on-one for Oracle 9i ?

Tom, how can I create Internet application for upload file from browser using
Mod-PLSQL ?

do you have tools to stress test for oracle database ?
and where the new Web Alchemy ?

thanks tom

      and we said...
I'm considering a performance book (much smaller, about 600 pages) that would
cover up through 9i.

I'm waiting a while for a "9i expert one-on-one".  9iR2 is coming out very soon,
90% of the questions I get (or more) are still on 8i (and before, lots of 8.0,
7.3 and even some 7.1 out there).  Before I go into a "second edition" on that
one, I'll wait until the demand for it is much larger.

http://technet.oracle.com/docs/products/ias/doc_library/90200doc_otn/web.902/a90855/feature.htm#1005870
describes the upload/download process with mod plsql.

Internally and for benchmarks we many times use Loadrunner (mercury loadrunner)
to stress test.  Otherwise, it is just sqlplus scripts or a simple URL
"flinger".

I'm not aware of any new Web Alchemy versions.
May 04, 2002 doug
      -- Thanks for the question regarding "_trace_files_public", version 9.0.1

      You Asked
I tried to add this parameter into my spfile with "alter system set
_trace_files_public scope=both."  It flags an error on the leading underline.
How do I get it in there?  Thanks.

      and we said...
quoted identifiers:

ops$tkyte@ORA9I.WORLD> alter system set "_TRACE_FILES_PUBLIC" = true scope =
both;
alter system set "_TRACE_FILES_PUBLIC" = true scope = both
                 *
ERROR at line 1:
ORA-02095: specified initialization parameter cannot be modified

ops$tkyte@ORA9I.WORLD> c/both/spfile
  1* alter system set "_TRACE_FILES_PUBLIC" = true scope = spfile
ops$tkyte@ORA9I.WORLD> /

System altered.

cannot be dynamically changed.
May
      04, 2002 Munzer -- Thanks for the
      question regarding "Testing for DML result", version 8.1.7

      You Asked
Tom:

1.  Is there any other way in PL/SQL to test for the result of running (INSERT,
UPDATE or DELETE) other than checking for SQL%ROWCOUNT if larger than 0 or not.

2.  Would the SQL%ROWCOUNT give you also the number of records that were
inserted, updated or deleted.

3.  Does 8i support the use of CASE statement.

4.  Would 9i web toolkit be better than 8i?  can it work with an 8i database.

Thank you,

      and we said...
1) and you would need another way WHY?  No, sql%rowcount -- as cumbersome as it
is (attempt at humor) is the way.

You know -- updating zero rows is not an error, I believe you might be thinking
that it should be?

2) yes.

3) in SQL, yes -- with 8i release 2 and up.  In 9i, in sql and plsql natively.

4) I do not believe it was significantly updated at all -- functionality would
be the same.  As the toolkit is shipped WITH the database, you should use the
version already installed in the database (never put 9i code into 8i and vice
versa)
May 05, 2002 rajesh -- Thanks for the question regarding
      "Ordering Non Numeric values", version 817

      You Asked
hi tom

I have to order the following characters

231
22
5678
12/02-03
2/02-03
10/02-03
as
22
231
5678
2/02-03
10/02-03
12/02-03

is that is possible. please help me
sincerely
rajesh

      and we said...
sure, anything is possible.

I cannot provide you a 100% answer because I don't know your data.  I made the
assumption that:

a) if the string doesn't have a / in it, its a number that I can to_number
b) 2/02-03 is a date in dd/mm format, not sure what the -03 is.  I ignored it.

So, if those assumptions are not correct -- you should be able to take the
method below and CHANGE it yourself into a method that works.  What is important
is you see the technique, not that you get the exact answer:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t
  2  order by decode( sign(instr(x,'/')), 1, 2, 1 ),
  3                   decode( sign(instr(x,'/')), 1, 0, to_number(x) ),
  4                   decode( sign(instr(x,'/')), 1,
to_date(substr(x,1,instr(x,'-')-1),'dd/mm'), to_date(null) )
  5  /

X
--------------------
22
231
5678
2/02-03
10/02-03
12/02-03

6 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

So, that order by is really:

order by
    <if the string has a slash in it, we want those "2cnd" so return 2, else
     return 1>,

    <if the string has a slash in it, return 0, else to_number the string so
     Oracle will sort the string as a number.  Remember, all strings with
     slashes will have a 2 as the first order by and a 0 as the second now>

    <if the string has a slash in it, return to_date on the first part, else
     return null>

You will probably have to work on that last part given I don't know your rules
for sorting that.
Review & Followup

      Rating: 4
thank you
      tom  May 05, 2002
Reviewer:  A reader

hi tom

thank u for your wonderful answer.

(b)
[2/02-03 is a date in dd/mm format, not sure what the -03 is.  I ignored it.]

as you had assumed the number before the / is not indicating the month. and it
can be up to 9999.
ie 9999/02-03.
in such situation what can i do

sincerely

rajesh

          Followup:

then just parse differently.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select x,
  2             decode( sign(instr(x,'/')), 1, 2, 1 ) c1,
  3         decode( sign(instr(x,'/')), 1, 0, to_number(x) ) c2,
  4         decode( sign(instr(x,'/')), 1, to_number(substr(x,1,instr(x,'/')-1))
) c3,
  5         decode( sign(instr(x,'/')), 1, to_number(substr(x, instr(x,'/')+1,
instr(x,'-')-instr(x,'/')-1 )) ) c4,
  6             decode( sign(instr(x,'-')), 1,
to_number(substr(x,instr(x,'-')+1)) ) c5
  7    from t
  8  /

X                  C1         C2         C3         C4         C5
---------- ---------- ---------- ---------- ---------- ----------
231                 1        231
22                  1         22
5678                1       5678
12/02-03            2          0         12          2          3
2/02-03             2          0          2          2          3
10/02-03            2          0         10          2          3

6 rows selected.

and order by whatever you need.

May
      05, 2002 nasir -- Thanks for the
      question regarding "Size of a File (of anytype from the current OS)",
      version 6i

      You Asked
hello tom
Is there anyway to get the size of file(in bytes) by passing the complete path
of the file as paramenter ...?

thanx in advance
nasir

      and we said...
IF you have an Oracle directory object created to that path -- yes, you can open
the file as a bfile and use dbms_lob.getlength to discover the size.

Otherwise, you'll be writing a java stored procedure to do this.
Review &
      Followup

      Rating:
      4
thanks  May 06, 2002
Reviewer:
      nasir  from UAE
thanks a lot

May 05, 2002 Patrick -- Thanks for the question regarding "How
      do you keep up!", version 9.0.1

      You Asked
Tom,

I was just curious on your “time-management” habits. How in God Green Earth do
you keep up with ALL the changes that occur with the Oracle
Database/application/etc. The formidable task of “keep-up” is wearing me down! I
know it is your/our job. But...whewww! 9R2 right around the corner and I just
got my hands on 9.0.1 "a little". Anyway, any tips are helpful and great site!!

Patrick

ps: and i don't drink JOLT or mtn-dew! ha!

      and we said...
I don't drink JOLT or Mountain Dew either...

I have the luxury of getting to do pretty much one thing -- Oracle the database.
 I have enough knownledge about the surrounding technologies to know when/when
not (thats more important sometimes -- when NOT to use them) to use them -- and
then get someone else to pick it up from there.

I still learn something new about the database almost every day.  Reading the
concepts guide with each major release helps as well.
Review &
      Followup

      Rating: 5
how do you get
      time to reply.....  May 05, 2002
Reviewer:  a
      reader  from LA, USA
tom,
How and where do you get time to reply to all questions?
And if I not mistaken you are VP right?
So how do you manage to find time to answer our questions?
Very big puzzle for me!

Thank you tom for alllll the help...

      Rating:
      3
  May 06, 2002
Reviewer:
      A reader
Thanks Tom. I need to make my world "just" one thing..but fat chance so far!
Again thanks
patrick

      Rating: 5
You MUST be
      leading a VERY VERY HAPPY FAMILY LIFE  May 06, 2002

Reviewer:  Sam
For being able to do all this so successfully.

Thanks and a huge chunk of credit to your Wife and Kids.

May 05, 2002 Yves -- Thanks for the question regarding "Table
      partitioning and load performance", version 9.0.1

      You Asked
Hi Tom.

Will partitioning improve speed of "inserts" in an OLTP environment, where at
peak times I have hundreds of users inserting data in my database online?

Thanks.

      and we said...
It can yes -- by decreasing the contention on the set of freelists on the table.

By having a table partitioned -- it is like having N number of tables, instead
of just one.  You'll have that many more freelists, reduced contention.

If you are using automatic segment space management -- much of this will be moot
as that is sort of self tuning in that regards.  In that case, partitions will
be more of an "admin" thing -- making certain admin functions easier/faster.
May 06, 2002 Janice -- Thanks for the question regarding "IP
      Address or Other Unique Identifier in a Web Form", version 6.0.8.13

      You Asked
Hi

I am using Form Builder 6.0.8.13 and IAS 1.0.2.2 with a 7.3.3.3.0 database.

What I am trying to do is use Form Builder to kick off SQL*Loader to load a
table.  The table has a USERID column which I need to populate. In my web form
environment, I am having trouble populating this value uniquely so I can group
the loaded data sets together.  All users are running as READONLY.  The
SQL*Loader command is initiated from a Form Trigger via a HOST command, so the
SQL*Loader session runs as a different global.sessionid from the Web Form.  The
web form needs to be able to identify which data its SQL*Loader command created.

Ideally, the solution would be to use the IP Address of the user's local PC (not
the web server IP Address).  I cannot seem to get this value with the features
available with 7.3.3.3.0.  Is there some way to find this?  I've also tried
using DBMS_PIPES to pass unique ids between the form and an ON-INSERT trigger on
the table being loaded by SQL*Loader.  But, multiple users can run the form and
load the same table at the same time, so I am afraid that this will not
guarantee that the right user will read the right piped info.  Any thoughts?

The user is responsible for the contents of the .ctl and .dat file so I cannot
require or trust them to specify the USERID in their file.  I can override some
of the SQL*Loader commands by modifying the HOSTed command line SQL*Loader
command.

Any ideas or thoughts?

Thanks for your time

      and we said...
Ok, suppose you wanted to pass this unique id from one session to another.  One
method would be to use v$session (your DBA will need to grant the owner of this
package I propose SELECT on V_$SESSION when connected as SYS or INTERNAL).

Suppose you have a package like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package sqlldr_state_pkg
  2  as
  3      g_unique_id   varchar2(48);
  4
  5      procedure set_unique_id( p_unique_id in varchar2 );
  6      procedure unset_unique_id;
  7  end;
  8  /

Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package body
sqlldr_state_pkg
  2  as
  3
  4  g_client_info varchar2(25) default 'sqlldr unique id';
  5  g_date_fmt    varchar2(25) default 'yyyymmddhh24miss';
  6
  7  procedure set_unique_id( p_unique_id in varchar2 )
  8  is
  9  begin
 10      dbms_application_info.set_client_info( g_client_info );
 11      dbms_application_info.set_module( p_unique_id,
to_char(sysdate,g_date_fmt) );
 12  end;
 13
 14  procedure unset_unique_id
 15  is
 16  begin
 17      dbms_application_info.set_client_info( null );
 18      dbms_application_info.set_module( null, null );
 19  end;
 20
 21
 22  begin
 23      select module into g_unique_id
 24        from v$session
 25       where username = USER
 26         and client_info = g_client_info
 27         and abs( sysdate-to_date( action, g_date_fmt )) < 3/24/60;
 28  exception
 29          when no_data_found then
 30                  g_unique_id := null;
 31  end;
 32  /

Package body created.

In the forms application, you will call sqlldr_state_pkg.set_unique_id right
before invoking SQLLDR.  SQLLDR will reference sqlldr_state_pkg.g_unique_id via
a trigger on the table you are loading.  This package variable will be populated
automagically based on that query starting on line 23.  It'll find the v$session
row for this user, that has the client_info set to a special value and the
action column was filled in less then three minutes ago (3 minutes is rather
arbitrary).  Right after the sqlldr command returns -- your forms application
will call sqlldr_state_pkg.unset_unique_id to remove it (and any confusion it
might cause).

So, your trigger might be:

begin
   sqlldr_state_pkg.set_unique_id( userenv('sessionid') );
   host( 'sqlldr .....' );
   sqlldr_state_pkg.unset_unique_id;
end;

now your rows will be identifed by the sessionid of the form.  A trigger on the
table would silently supply the unique id:

create trigger t_trigger before insert on t for each row
begin
   :new.id_column := sqlldr_state_pkg.g_unique_id;
end;
/

Review &
      Followup

      Rating: 5
IP Address or
      Other Unique Identifier in a Web Form  January 14, 2002

Reviewer:  Janice  from FL
Thanks, Tom!  This has enabled me to put the form's global.sessionid as the
identifier on the records loaded with SQL*Loader.  I had not used the
dbms_application_info package (found Metalink Note 61722.1 for addition info)
with v$session and your solution is working well.  I am still testing with
multiple, concurrent users but so far so good!

Thanks again for your timely response.

      Rating: 5
Another
      question about time  May 05, 2002
Reviewer:
      Janice  from FL
Sorry--I have another question on this...regarding the arbitrary 3 minutes):
If it is possible that the user's SQL*Load may run more than 3 minutes (possibly
lots of data or slow system?), should I be concerned that 3 minutes may not be
enough?  I don't think I want to use anything much more than 3 minutes since
there can be multiple, concurrent users

          Followup:

It matters not that the sqlload takes more then 3 minutes.  The lookup is done
only once, upon startup of sqlldr.

      Rating: 4
Package
      Initialization Part  May 31, 2002
Reviewer:
      Janice  from FL
DUH!  Now, I understand.  The package initialization part is called only *one*
time per *session*, the first time the package is referenced.  And, even though
we null our v$session values when we are done with a SQL*Load, the g_unique_id
value is still known by the package within the session and subsequent loads
within the session will work as expected.

Thanks for your patience!

May 06, 2002 -- Thanks for the question regarding "are indexes
      reused?", version 8.1.7

      You Asked
Hi Tom

Index block "free" space are reused as data block "free" spaces after deletes?
Or it will keep on spliting leaf?

For example if I delete 10 rows of range 10 tp 100 then I insert again I
understand that the space freed (ok not actually freed but reusable) in the data
blocks can be reused for future inserts, does this happen with index blocks too?

      and we said...
Yes, yes it does.

To see this is true, do this:

create table t ( x int );

create index t_idx on t(x);

insert into t values ( 1 );
insert into t values ( 999999999999999999999999 );
commit;

begin
   for i in 2 .. 9999999999
   loop
      insert into t values ( i );
      commit;
      delete from t where x = i;
      commit;
   end loop;
end;

if the index space was not reused -- that index would just grow and grow -- only
it doesn't.  It'll be one block forever.
Review &
      Followup

      Rating:
      3
  May 05, 2002
Reviewer:  Ash
       from Australia
If I rememeber correctly, all entries in an index block have to be deleted
before it can be reused.

          Followup:

NO -- the test above proves that is 100% patently FALSE.

Think of the index like a data structure.  A block is placed into a very very
specific location within that data structure.  That block will STAY in that
place until it is 100% emptied.  Then that block can be moved to another
location within that data structure.

While the block is fixed in its location -- any and all data that can / should
be put onto it will.  The deleted entries on that index block ARE reused for
subsequent insert/updates.  The space is constantly and continously reused on
that block -- for any and all data that needs to reside where the index block is
physically located.

May 06, 2002 Mahmood -- Thanks for the question regarding
      "Archiving, Replication, Stand By", version 8.1.7

      You Asked
Hello Tom
Database running in Archive mode, Archiving files are in destination system.
My Question is > If the database server is crash due to hardware failure or
other problems, and i m install the new database on new system, how I could
apply the archive files to update my new installed database?

Question 2 > Write the step by step configuration for Stand By database server
(from installation to data availability) on 8.1.7.0 ?
Also i wanna configure the Real Application Cluster of 9i.

Question 3 > How I can perform Replication from Differnt sites through a
Telephone on win2k AS in real time ?

Thanx

      and we said...
1) you can archive over the network as well to another system (standby does that
for example).

If you can get access to the disks with the archives, you can easily apply them.
You should be trying to get the archives OFF of the system as fast as possible
and archiving to more then one destination to avoid losing any.

2) read:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76995/toc.htm
from cover to cover (and then you'll realize it is slightly more then a bulleted
list).

Then read the books:

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/nav/docindex.htm#index-REA
and after you upgrade to 9i (when RAC is available -- in 8i, it was OPS), you'll
know exactly what to do.

3) Talk really fast?  I assume you mean via dialup.  To us -- it is no different
then when you are on a T3 -- just slooowwwwwer.  It is all tcp/ip.  Over a 56kbs
dialup -- you are probably "dreaming".  It'll be too slow to be feasible.

Review &
      Followup

      Rating: 3
Applying
      archive logs to another database?  May 06, 2002

Reviewer:  Ashok
Hi
The first question was whether he can recover by applying the archive logs to a
newly created database. It is not possible.

          Followup:

I believe by "newly created database" he meant "i just installed the Oracle
software and restored my backups".

I make this assumption based on "being in archive log mode", "have a crash",
"new system".

You are correct, you cannot apply archives to a "brand new, just created using
CREATE DATABASE, database".  You can only apply them to the same database image
which you would backup and restore to a new instance.

May 06, 2002 Pascal -- Thanks for the question regarding
      "Matrix Report : Group by Dates in a Month as Rows ", version 8.1.7

      You Asked
Hi Tom

Thanks for a chance to ask my Question.

Suppose i have the Following table :

CREATE  TABLE   my_bills  (
  CUSTOMER  VARCHAR2 (100),
  TZ        VARCHAR2 (100),
  PRICE     NUMBER (20,4),
  TOTAL     NUMBER,
  AMOUNT    NUMBER (20,4),
  SERVICE   VARCHAR2 (100),
  TIMESTAMP      DATE ) ;

I use this table as summary Table  and that means i put Totals for Customer , TZ
and SERVICE   for a given  Timestamp in a given MONTH.

Now i  would  like to Get a  Matrix Report  wherein THE COLUMNS would be all
AVAILABLE  DISTINCT DATES  from column TIMESTAMP  and these would appear as
COLUMNS.

Below  each corresponding TIMESTAMP , i would like to SUM up TOTAL for CUSTOMER
, TZ and SERVICE.

For example , if  i was to run this Report Today, i would get something like
what is shown below  for field CUSTOMER .

                   01.05.2002       02.05.2002      03.05.2002

CUSTOMER1          sum(total)       sum(total)      sum(total)
CUSTOMER2          sum(total)       sum(total)      sum(total)

I got stuck with a QUERY which would do something similar  to :

   SUM(TOTAl) for records in my_bills
   GROUP  by
  ( select distinct TIMESTAMP    from   my_bills )   column-wise ,

  GROUP BY
  CUSTOMER/SERVICE/TZ   Row-wise

I hope the query is clear.

Thanks i advance

pascal

      and we said...
select customer,
       sum( decode( to_char(timestamp,'dd'), '01', total, 0 ) "Day 1",
       sum( decode( to_char(timestamp,'dd'), '02', total, 0 ) "Day 2",
       ..
       sum( decode( to_char(timestamp,'dd'), '31', total, 0 ) "Day 31"
  from t
 group by customer
/

will do that.  Then, just use LAST_DAY in your app to figure out "what columns
to ignore" at the end of the select list...

Review &
      Followup

      Rating: 5
Thanks very
      much Tom...but   May 06, 2002
Reviewer:
      pascaaal  from HH,Germany
Hi Tom
Thanks very much for your response..

In fact my  real problem  was to how to make the Query get only the Available
Dates from the TABLE itself...the query that you have just given me is more like
'Fixed Days as Columns' Approach...

But how do i get the Available dates in a Month Dynamically ...i know i have to
use Last_Day ...but i can't figure out a way of incorporating that into the
Query above..

Thanks in advance

pascal

          Followup:

A query has a fixed number of columns.

You would have to dynamically generate such a query -- hence my advice to use a
FIXED number of columns and ignore those which you don't want.

May
      06, 2002 Rahul -- Thanks for the
      question regarding "Locks and Latches.", version 8.1.6

      You Asked
Hi Tom

Can you please explain me the differences among Locks, Latches, Contention and
Serialization.
Pls provide me a scenario with example to get it more clear if
possible.

Thanks.

      and we said...
I'll recommend to you a book -- mine.  I go into this in depth along with lots
and lots of examples and things other then
locks/latches/contention/serialization.  You won't be sorry.

A latch is a lock.  A lock is something we use to prevent concurrent access to
objects.  A latch is just a lock we use to protect our in memory data
structures.

Contention is what happens when lots of people try to get the same lock at the
same time.  Then content with eachother for this resource.

Serialization with respect to locking means "one at a time, get in a line and
wait for the resource, process SERIALLY"

Review &
      Followup

      Rating:
      4
  May 06, 2002
Reviewer:  Vijay
       from India
Are you refferring to your Oracle Expert one on one book.
I am also interested to know what's your next book.

          Followup:

Yes, that is the book

Thinking about a performance book currently.  Haven't started writing yet..

      Rating:
      3
Good  May 07, 2002
Reviewer:  Rahul
      Sharma  from India
Thanks Tom.

May 06, 2002 Krishna -- Thanks for the question regarding
      "Alert Log ORA- emails", version 8.1.6

      You Asked
Hi Tom,
Thank you for quick responses for all my questions. Here is one more.
I need to set up an ORCLE DBMS JOB which grabs the ORA- errors from alert log
that have not been read and email to me. How do I do that?.
Thanks
Krishna

      and we said...
see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1352202934074
it shows how to load the alert into a global temporary table that you can
process, looking for things you want to look for and emailing you when you deem
it appropriate.  You'll have to add some things here -- like how to remember
what's already been processed.  For that I would suggest setting up a table:

create table remember_where_in_alert( offset int );
insert into remember_where_in_alert values ( 0 );

When you process the alert, you will READ that row and set my variable "l_last"
= offset+1 (in my sample provided).  You will also update this single row to be
dbms_lob.getlength(l_bfile) so as to remember where you processed upto.

May 06, 2002 Eugen -- Thanks for the question regarding
      "dynamic set up of dbms_sql.bind_variable/bind_array", version 8.1.7 &
      9i

      You Asked
Hi Tom,

Is it possible to set up the dbms_sql.bind_array dynamical?
I'd like to do the following processing

procedure xyz(p_owner varchar2, p_tab_name varchar2,
              p_coll Mycollection)
begin
  ..
  ..
  query the all_tab_columns for the column_names
  -- set up the insert statement dynamical, so that it looks like
  v_msg := 'insert into '||p_owner||'.'||p_tab_name||'('||..
           'values (:x1, x2, x3....);
  dbms_sql.parse(v_crs, v_msg, dbms_sql.native);
  --
  -- BIND the Collection
  --dbms_sql.bind_array(v_crs, ':x1', p_coll.myArray, start, end);
  --                                  ^^^^^^^^^^^^^^
  -- instead of statement before use loop below
  v_cnt := 1;
  for crs in (select column_name from all_tab_columns
              where table_name = p_tab_name and owner = p_onwer)
  loop
    v_msg := 'p_coll.'||v_column_name;
    dbms_sql.bind_array(v_crs, ':x'||v_cnt, v_msg, start, end);
    v_cnt := v_cnt + 1;
  end loop;
  --
  v_ret := dbms_sql.execute(v_crs);
  ..
  ..
end;

What I'd like to do is pass a string instead of the array into
dbms_sql.bind_array, Is it a NOOP or do you know a solution.

TIA,
Eugen

      and we said...
You can pass a string which represents the name of a PACKAGE GLOBAL variable (a
variable defined in a package spec).

The code would just be:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace type myCollType as table of
number
  2  /

Type created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int, y myCollType ) nested
table y store as y_tab;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package globals
  2  as
  3     g_data myCollType := myCollType( 1, 2, 3, 4, 5 );
  4  end;
  5  /

Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure dyn_insert(
p_owner in varchar2,
  2                                          p_tname in varchar2,
  3
    p_var   in varchar2 )
  4  as
  5  begin
  6          execute immediate
  7          'begin
  8                   insert into ' || p_owner || '.' || p_tname || '
  9                   ( x, y ) values ( 1, ' || p_var || ' );
 10           end;';
 11  end;
 12  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dyn_insert( user, 't', 'globals.g_data'
);

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

         X
----------
Y
---------------------------------------------------------------------------------
--------------------------------------------------
         1
MYCOLLTYPE(1, 2, 3, 4, 5)

May 06, 2002 -- Thanks for the question regarding "database
      cluster", version 8i

      You Asked
Can you tell me i here about oracle 9I cluster what that new mean since 8i has a
cluster ?

i have ask previus question about table with table(t1 varchr2(4000),id number)

my question i need to search for some text in table.t1 through portal and then
it retrun for me with back ground color for example red

your answer for me is when i have external document but the case here is it
return direct the content of the table can you pls give me example for that

thanks
best reg

      and we said...
There are two ways to use clusters in Oracle -- two very different ways.

There is a cluster database object -- a storage mechanism.  It clusters rows
with the same cluster key together physically.

There is a cluster of computers, a bunch of computers working together in a
cluster on a single database instance.

In Oracle8i and before, we had "OPS" Oracle Parallel Server.  In 9i, this is now
called RAC -- Real Application Clusters.  See

http://download-west.oracle.com/otndoc/oracle9i/901_doc/rac.901/a89867/whatsnew.htm#983402
for whats new there.

I do not understand the second part of this question at all.  Not enough context
to give an answer.
Review & Followup

      Rating: 3
second
      question  October 03, 2001
Reviewer:
      A reader
I have one table like that

table1 (t1 varchar2 (40000),id nmber)

when i make report in portal i make search for a text in table1.t1 for this text
like :
select id,t1 from table1 where t1 like :d1
then the output well prsent the search contain :d1
and browse it with back ground red

in previus example i saw in your archive it was just search
and put the seacrch in a table with mark back ground

      Rating:
      5
  May 02, 2002
Reviewer:
      A reader
Tom,

 Can you explain RAC(Real apllication cluster) architecture
with some diagram?

I heard a lot about it but never saw it.

Thanks,

          Followup:

http://download-west.oracle.com/otndoc/oracle9i/901_doc/rac.901/a89867/pssvarch.htm#9070
...

      Rating: 4
Hardware for
      RAC  May 03, 2002
Reviewer:  A reader

I would like to get some hands on experiance with setting up 9i RAC.  Can you
recomend a cheap hardware setup (bare minimum) to do this.

          Followup:

Linux boxes, that would be the "cheapest", smallest.

      Rating:
      3
  May 03, 2002
Reviewer:  Tianhua Wu
       from Atlanta, GA
I know for OPS, you can set it up in a single node linux intel box.  For RAC,
you might be able to do the same thing. Check metalink.

      Rating: 5
is it possible
      to use 2 different OS in clustering?  May 03, 2002

Reviewer:  A reader  from LA USA
Tom,
Is it possible to use different OS boxes in clustering.
Say like 1 NT and 1 solaris and 1 linux.

          Followup:

no, homogenous solutions only

      Rating:
      4
  May 06, 2002
Reviewer:
      A reader
I have two P4 boxes running linux (one only linux and one linux and windows
partitioned). How can I set up a cluster with this. Do I have to have an
external disk array? Can you pls guide me to some linux sites which explains how
to go about clustering? Thanks.

          Followup:

http://otn.oracle.com/products/oracle9i/content.html
has a link to an IBM redbook   --

http://www.redbooks.ibm.com/redpapers/pdfs/redp0410.pdf
\
--  that tells you how to install RAC
on IBM Netfinity with SuSE.  I haven't read it/done it myself..

      Rating: 3
Need a
      lead  August 26, 2003
Reviewer:  H.S.Anand
       from New Delhi, India
Hi tom,

can you please give any link where I can find some info on how to install oracle
9i Rel2 on Sun Solaris 8 (2 boxes) with veritas cluster installed [9i RAC is not
being used in this scenario].

thanks in advance

May 06, 2002 Bob -- Thanks for the question regarding
      "Number(2) and Number(38)", version 8.1.7

      You Asked
Are there any issues in joins where the join fields are defined as different
number precisions? In a select like this:
Select * from a,b
where a.number2_field = b.number38_field
The table "a" number field is Number(2) and table "b" number field is number
(38). Both contain only 2 digit numbers.

vs.
 making the 2 number fields equal precision(both number(2)?

Also, how about joins making these fields CHAR(2) instead? Any benifits on join
or query execution times in general to be expected from CHAR vs NUMBER in this
case?

      and we said...
Nope, number(n,m) should simply be consider an "edit" -- the data is made to fit
the scale/precision upon input into the system.  After that a number is a number
is a number is a number.

Never never never put a number into a string  -- it is a number, someone will
want to sum it, average it, do something with it and by putting it into a string
-- you will introduce "garbage" at some point (someone will put "AA" in there)
making it impossible to use the column as a number.

Use the best datatype for the column possible -- the most defining.  In this
case, NUMBER is the right choice.
Review &
      Followup

      Rating: 5
number
      datatype  May 06, 2002
Reviewer:
      A reader
Hi Tom

I almost always define number datatype as

create table x (x number);

I almost never define the precision, is there a side effect of my method? I do
it because it always works :D

          Followup:

As I said:

 number(n,m) should simply be consider an "edit"

the n and m are like constraints -- similar to "NOT NULL".  The constrain the
values contained therein is all.

If your numbers should be in some threshold, you should use n,m, otherwise
NUMBER is just fine.

      Rating: 3
Specify scale
      for NUMBERs  May 06, 2002
Reviewer:
      A reader
Reader,

> I almost always define number datatype as
> create table x (x number);

there is an Steve Adams article about number scales at:
http://www.ixora.com.au/tips/number_scale.htm

          Followup:

Interesting, never thought of it that way.  As always -- Steve Adams makes good
sense once again.

Problem is -- what is the right scale for a floating point number -- eg: the
numbers are in fact floats in this case (using log, 1/3, etc).  What exactly is
the right scale then?  Something to think about tho.


Subject | Article | Last Updated (East coast time)
DBMS_SQL package and returning clause of INSERT statement | article only | May 07, 2002 08:19 am
Cursor_Sharing | article only | May 07, 2002 08:26 am
Row Locking | article only | May 07, 2002 08:38 am
fold_a in sqlplus | article only | May 07, 2002 10:48 am
sqlplus &1 will parse off first word only, how do you QUOTE it? | article only | May 07, 2002 10:56 am
Index and Not Equal Operator | article only | May 07, 2002 02:03 pm
WebDB and existing PL/SQL stored procedures | article only | May 07, 2002 07:48 pm
Generate java code in SQL/PLUS | article only | May 08, 2002 07:17 am
inserting into a table with defaults | article only | May 08, 2002 07:28 am
Cross Matrix | article only | May 08, 2002 08:38 am
Where to look first for DB tuning and which tool? | article only | May 08, 2002 09:04 am
How can we ensure that Oracle cursors are closed after a java program accesses the db? | article only | May 08, 2002 09:07 am
Loading java.util.regex as SYS | article only | May 08, 2002 11:15 am
Calling Xsu procedure from inside a Xsu procedure | article only | May 08, 2002 12:59 pm
SQL Loader | article only | May 08, 2002 09:10 pm
SQL Tuning help again | article only | May 09, 2002 06:57 am
Making APPEND default behavior | article only | May 09, 2002 06:59 am
ora-01652 & ora-01630 | article only | May 09, 2002 07:12 am
check PGA usage at OS level (UNIX/LINUX) | article only | May 09, 2002 07:21 am
To Index or Not To Index | article only | May 09, 2002 07:25 am
Create INDEX | article only | May 09, 2002 07:31 am
ODBC in PL/SQL | article only | May 09, 2002 07:20 pm
How to ignore the unwanted data, loading from XML to a table using DBMS_XML Package. | article only | May 09, 2002 07:22 pm
How do I retrive vallues from LONG RAW's in 7.3.4 when there is no pakage! | article only | May 09, 2002 07:23 pm
CURSOR | article only | May 09, 2002 07:28 pm
UTL_FILE don't write all text | article only | May 09, 2002 07:31 pm
executing findstr command through SQLplus Host | article only | May 09, 2002 07:42 pm
How to get a date including millisecond in PL/QL | article only | May 10, 2002 04:35 pm
plsql stuff & backup | article only | May 12, 2002 09:03 am
restrict user more than specefied | article only | May 12, 2002 09:39 am
Compress Primary Key | article only | May 13, 2002 07:13 am
BLOBS/CLOBS | article only | May 13, 2002 10:20 am
SQL Query Help | article only | May 13, 2002 06:58 pm
Same table but different record sets per user | article only | May 13, 2002 07:24 pm
How did the optimizer arrive at the following plan. | article only | May 13, 2002 08:09 pm
Top-N query FOR UPDATE | article only | May 13, 2002 08:17 pm
row(s) 1 - 36 of 36
May 07, 2002 Saverio -- Thanks for the question regarding " DBMS_SQL package and returning clause of INSERT statement", version 8.1.5
You Asked
Hy Thomas,

I'm using the following piece of code to load a file in a blob field (img)  of
the table XL2.

...

l_blob    BLOB;
l_bfile   BFILE;
begin
    INSERT INTO xl2(key,img)
           VALUES ('1', EMPTY_BLOB ())
           RETURNING img  INTO l_blob;

           l_bfile := BFILENAME ('EXCEL_DIR', 'xl1.xls');
          DBMS_LOB.fileopen (l_bfile);
          DBMS_LOB.loadfromfile (l_blob, l_bfile, DBMS_LOB.getlength (l_bfile));
          DBMS_LOB.fileclose (l_bfile);
      COMMIT;
end;

In order to use the same code to load different tables (having the same
structure) I would like to use the dynamic SQL (DBMS_SQL package);
but I'm not able to do it.
I do not know how to implement the "returning img into l_blob" clause of the
insert statement;
can you please help me with an example?

I need to use the DBMS_SQL package because i'm using Oracle 8.1.5 on NT.

Thank you again

Saverio
and we said...
You do not need or want to dbms_sql in this case.  You are on 8i -- that is
sufficient for native dyanmic sql.

It'll look like this:

scott@ORA815.US.ORACLE.COM> create table t ( x int, y blob );

Table created.

scott@ORA815.US.ORACLE.COM>
scott@ORA815.US.ORACLE.COM> declare
  2          l_blob blob;
  3          l_x    number default 1;
  4  begin
  5
  6          execute immediate
  7          'begin
  8                  insert into ' || 't' || ' values ( :x, empty_blob() )
  9                  returning y into :y;
 10           end;'
 11          USING in l_x, out l_blob;
 12
 13          dbms_lob.writeAppend( l_blob, 1, '00' );
 14  end;
 15  /

PL/SQL procedure successfully completed.

scott@ORA815.US.ORACLE.COM> select dbms_lob.getlength(y) from t;

DBMS_LOB.GETLENGTH(Y)
---------------------
                    1
May 07, 2002 Twin -- Thanks for the question regarding "Cursor_Sharing", version 8.1.7
You Asked
Tom,

We have an interesting problem,

When we set cursor_sharing to 'force', doing following SQL having space trailing
the return value 'Y'.

SQL> select 'Y' from dual;
SQL> 'Y'
SQL> --------------------------------
SQL> Y

However, after changing cursor_sharing to 'exact, the same sql will return no
space trailing the return value 'Y'.

SQL> select 'Y' from dual;
'
-
Y

Could you explain why?

Thanks

Twin
and we said...
Sure -- if you have my book I go into this in some detail in chapter 10 (along
with other cursor_sharing=force caveats).

Your query "select 'Y' from dual" is being turned into "select :SYS_BV0 from
dual"

It is no longer selecting a fixed width constant.  It is selecting a varying
width constant now.  We no longer "know" that will return a char(1), it can
return upto a varchar2(N).

Here is a small example I use in the book to show the differences:

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_theCursor     integer default dbms_sql.open_cursor;
  3      l_descTbl       dbms_sql.desc_tab;
  4      l_colCnt        number;
  5  begin
  6      execute immediate 'alter session set cursor_sharing=exact';
  7      dbms_output.put_line( 'Without Cursor Sharing:' );
  8      for i in 1 .. 2
  9      loop
 10          dbms_sql.parse(  l_theCursor,
 11                          'select substr( object_name, 1, 5 ) c1,
 12                                  55 c2,
 13                                 ''Hello'' c3
 14                            from all_objects t'||i,
 15                          dbms_sql.native );
 16
 17          dbms_sql.describe_columns( l_theCursor,
 18                                         l_colCnt, l_descTbl );
 19
 20          for i in 1 .. l_colCnt loop
 21              dbms_output.put_line( 'Column ' ||
 22                                    l_descTbl(i).col_name ||
 23                                    ' has a length of ' ||
 24                                    l_descTbl(i).col_max_len ) ;
 25          end loop;
 26          execute immediate 'alter session set cursor_sharing=force';
 27          dbms_output.put_line( 'With Cursor Sharing:' );
 28      end loop;
 29
 30      dbms_sql.close_cursor(l_theCursor);
 31      execute immediate 'alter session set cursor_sharing=exact';
 32  end;
 33  /
Without Cursor Sharing:
Column C1 has a length of 5
Column C2 has a length of 2
Column C3 has a length of 5
With Cursor Sharing:
Column C1 has a length of 30
Column C2 has a length of 22
Column C3 has a length of 32
With Cursor Sharing:

PL/SQL procedure successfully completed.

The substr( object_name, 1, 5 ) gets turned into substr( object_name, :bv1, :bv2
) -- we no longer KNOW it is 5 characters - we've set it to 30 (30 being larger
then 5 and all queries that use substr( object_name, 1, 5 ), "1, 6 )", "1, 7 )",
..... "1, 29 )", "1, 30 )" will all goto 30 so they can share the same cursor.
If we did a substr( object_name||object_name, 1, 31 ) -- it'll goto 60
characters, to increase the likelyhood of sharing a cursor.

The constant 55 is replaced with :SYS_BVn.  We no longer know it is 55, it is
some number.  Instead of a number with a max size of 2 bytes -- it is a full
number(38) with 22 bytes max possible.

And so on.  There are other cursor sharing side effects -- if you have my book,
check out chapter 10.
May 07, 2002 Anil -- Thanks for the question regarding "Row Locking", version 8.1.7
You Asked
Hello sir,

I have a problem in Java Jdbc API

For ex:
I have a table emp( ename varchar2(20), salary number(10));

Data is
           Anil 10000
           Sunil 20000

Now if one session  update the salary of emp Anil and does not commit
yet.
Now if another session tries to update the same row then it will wait.
I want, it should not wait rather it should give the message that
the "resource is busy Try Later"

Anil
and we said...
read
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1207435931386
Not only do you NOT want them to wait -- you do not want them to ever update
that row until they re-query it!!! else you have what is known as a LOST UPDATE.
May 07, 2002 J -- Thanks for the question regarding "fold_a in sqlplus", version 8.1.7
You Asked
Hi
I wanto define fold_after option on a column which is defined as
break column on sqlplus report.However this causes extra blank lines
to be introduced in the report.

 select region,ename,salary from emp
 order by region;

REGION               ENAME                    SALARY
------------------- -------------------- ----------
REG01                Joseph                      100
REG01                john                        150
REG01                RAVI                      12222
REG02                JAI                         100
REG02                HAI                     3331200
REG02                BYE                        1500
REG029               DADA                      19000
REG03                CHING                      1200

break on region
column region fold_after

 select region,ename,salary from emp
 order by region;
REGION
--------------------
ENAME                    SALARY
-------------------- ----------
REG01
Joseph                      100

mathew                     1200

john                        150

REGION
--------------------
ENAME                    SALARY
-------------------- ----------
REG01
RAVI                      12222

REG02
JAI                         100

HAI                     3331200

REGION
--------------------
ENAME                    SALARY
-------------------- ----------
REG02
BYE                        1500

REG029
DADA                      19000

REG03
CHING                      1200

REGION
--------------------
ENAME                    SALARY
-------------------- ----------
REG03

Here you can see that extra blank line is introduced between detail
lines under regions . ex between records for mathew and john

How can we avoid this

I need to get
REGION
--------------------
ENAME                    SALARY
-------------------- ----------
REG01
Joseph                      100
mathew                     1200
john                        150

thanks
and we said...
Best I can do is remote ONE of the blank lines with:

set recsep off

The "blank" line is coming from the supressed region column in the break.  It is
"there", you just don't see it.  There is no way for sqlplus to know "not to
print" it at all.
Review & Followup
Rating: 5
May 07, 2002
Reviewer: A reader
hi Tom.

On sql prompt if you type DOC,it will change sqlprompt.

eg.
SQL> doc
DOC>  <---------------

Can you explain?
Thanks.

Followup:
It is an "old" sqlplus command:

ops$tkyte@ORA817DEV.US.ORACLE.COM> document
DOC>hello
DOC>there
DOC>#
ops$tkyte@ORA817DEV.US.ORACLE.COM>

you go into "documentation" mode until you enter a #.

The command was supported, but REMARK has superceded it.

<quote>
DOCUMENT
Purpose

Begins a block of documentation in a command file.
Syntax

DOC[UMENT]

Usage Notes

For information on the current method of inserting comments in a command file,
refer to the section "Placing Comments in Command Files"under "Saving Commands
for Later Use" in Chapter 3 and to the REMARK command in the "Command Reference"
in Chapter 8.

After you type DOCUMENT and enter [Return], SQL*Plus displays the prompt DOC> in
place of SQL> until you end the documentation. The "pound" character (#) on a
line by itself ends the documentation.

If you have set DOCUMENT to OFF, SQL*Plus suppresses the display of the block of
documentation created by the DOCUMENT command. (See "SET DOCUMENT" later in this
appendix.)
</quote>

May 07, 2002 Kirk -- Thanks for the question regarding "sqlplus &1 will parse off first word only, how do you QUOTE it?", version 8.1.7
You Asked
Tom,
  Have implemented emailing inside Oracle.  Now, I have
a batch file that FTPs down a file, and processes it.

  The problem is that if any FTP problems arise, I have
to EMAIL a list of people (names stored in a settings table)
that the error occurred.

  I setup a special procedure USP_SendLoadStatus(nFail, sSubj, sMsg);
to handle this.

  Now, when I try to call it via a batch file calling pl/sql,
I get weird parsing.

  I searched your archives, and found someone else mentioned
this, but your answer skipped over this issue as you got an
environment variable to do some of the work.

Nonetheless: (SendErrorMsg.inp)
begin
  usp_SendLoadStatus(1, '&1', '&2');
end;
/
exit

And the (SendErrorMsg.bat):
REM %1 is the Subj, %2 is the message
sqlplus user/pwd@orcl @SendErrorMsg.inp %1 %2

I even tried calling it myself from the cmd prompt:
sqlplus user/pwd@orcl @SendErrorMsg.inp "Hello World" "Body Here"
and
sqlplus user/pwd@orcl @SendErrorMsg.inp "'Hello World'" "'Body Here'"

********
I debugged it using:
Select '&1' from dual;

And calling it manually as above!

And it would always parse "Hello World" to Hello

So, is there a way to handle this?

My "Hack Around" is to CREATE the INP file on the fly using
the >> Operator to concatenate and create the lines...  But that
is a HACK to me.

Thanks,

Kirk Out!
and we said...
c:\> sqlplus tkyte/tkyte @test "\"hello world\" \"goodbye\""

with test.sql as:

prompt &1
prompt &2
exit

produces this output:

SQL*Plus: Release 8.1.6.0.0 - Production on Tue May 7 08:17:49 2002

(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Connected to:
Oracle8i Enterprise Edition Release 8.1.6.0.0 - Production
With the Partitioning option
JServer Release 8.1.6.0.0 - Production

hello world
goodbye
Disconnected from Oracle8i Enterprise Edition Release 8.1.6.0.0 - Production
With the Partitioning option
JServer Release 8.1.6.0.0 - Production
Review & Followup
Rating: 5
Amazing... Where in the world do you find this stuff? May 07, 2002
Reviewer: Kirk from USA
Tom,
  Thanks...  You make it all seem easy.

  Anyways, I read your site constantly,
and love your links to other sources of
information...

  Is there a chance you could include
WHERE in HECK you looked to get the answer?
I think it would take me from feeding me a fish
each day, to helping me fish for myself...

  Also, there is a BUG on your site if you
search for something that is in a HYPERLINK,
it colorizes it RED INSIDE the link, and that
breaks the link from working.

Kirk Out!

Followup:
For this one --  I just "knew it".  It isn't even an Oracle question really but
a "DOS" or "CMD" processor question.

I didn't read anything -- I just know how to escape things at the 'dos' command
prompt (and it would be different on unixland)....

I know about the hyperlink issue.  My choices are

o turn off highlighting
o live with the side effect

If you use the 'bookmark' link on the bottom of the page -- it'll goto a page
with no highlighting, rendering the links functional again.

Rating: 5
Since a Feedback Thread is Going on... May 08, 2002
Reviewer: A reader
Tom,

When i search your site the engine does not search inside the comments. To be
very specific let us say i know a particular person wrote a comment and u
answered to it and i am trying to search for that person's name the pages dont
show up in the results. Only direct questions show up.

Can you add this functionality pls.

Regards,
Ganesh R

May 07, 2002 Peter -- Thanks for the question regarding "Index and Not Equal Operator", version 8.1.7
You Asked
Hi Tom,

In our application, there is a requirement to generate dynamic SQL based on some
user input. One of the operators that was allowed is <>. I understand that when
<> appears in the predicate, the index defined for that particular column cannot
be used.  What's the best way to handle this situation so that we can provide
the flexibility of making <> available without comprimising the performance of
the system.

Thanks
and we said...
Well, you get what you ask for don't you?

I mean, if you run the query:  "select * from t where x <> :x" and that is going
to return 99% of the rows -- it'll take the amount of time it takes to get 99%
of the rows.

We cannot make it "go faster" then the time it takes to get 99% of the rows and
if T is big, that'll take a while.

As an aside remember these facts:

a) INDEXES do not mean "fast"
b) INDEXES can mean "slow, really really slow"
c) INDEXES do not mean "fast"
d) A full table scan is not a bad thing
e) INDEXES do not mean "fast"

(and we can use indexes even in queries with <> given certain cases)
Review & Followup
Rating: 3
May 07, 2002
Reviewer: A reader from Ca, USA
Can you elaborate on your last comment

"...we can use indexes even in queries with <> given certain cases."

Can you give an example.

Thanks

Followup:
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace on explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from big_table where
object_id <> 5;

  COUNT(*)
----------
   1099007

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=67 Card=1 Bytes=5)
   1    0   SORT (AGGREGATE)
   2    1     INDEX (FAST FULL SCAN) OF 'BIG_TABLE_PK' (UNIQUE) (Cost=67
Card=1099007 Bytes=5495035)

Rating: 3
fast inequality May 07, 2002
Reviewer: Mikito Harakiri
If there is a frequency hisogram on the column x, and if you rewrite the
predicate "x <> 1" as "x > 1 or x < 1", and also if 1 happens to be the most
frequent value, then the query will use concatenation and index on x in both
branches of the plan. Note that using literal is critical in this case. With
bind variable peeking you might somewhat approximate the case with literals.

May 07, 2002 Kelli -- Thanks for the question regarding "WebDB and existing PL/SQL stored procedures", version Oracle 8.0.5/WebDB2.1
You Asked
Can I migrate some of my existing PL/SQL procedures to WebDB?
I'd like to host some of my stored procedures via the web, and
I'm
thinking this is an easy way to do it. But how? I'm not sure
what
to do. thanks for your help.
and we said...
There are 2 cases to consider.  Case 1 is if the stored
procedures are existing "web" stored procedures that use the
'HTP' packages to generate HTML.  Case 2 is if the stored
procedures are just 'normal' stored procedures to process
transactions.

The answer in both cases is simply yes.

In the first case, that they are "web" stored procedures that
use the HTP package to generate HTML, we can call these directly
from the WebDB listener as we did with the PLSQL cartridge under
OAS.  The WebDB implements the PLSQL cartridge in much the same
manner as the OAS PLSQL cartridge does.  Most every stored
procedure that you have today that utilizes the HTP packages
will function unchanged under WebDB.  File upload is done a
little differently under WebDB then under OAS so those routines
will have to change but in general, your existing applications
can run under WebDB as they did under OAS.

In the second case, that they are 'normal' stored procedures
that just take some inputs -- perform a transaction -- and then
return some output, then answer is again yes.  WebDB allows you
to hook in calls to existing or new stored procedures in many
places in a report, form, or website.  For example, when
creating a report -- I can put in calls and pass the report
inputs to existing procedures before the page is generated,
after the page is generated and at various points during the
generation of the page.
Review & Followup
Rating: 5
Happy Anniversary!!! May 07, 2002
Reviewer: J from ma
I think this is #1.
Been a couple of years this week by my estimates and I just wanted to say thanks
for the great site and all the help and tips I've picked up here. You are truly
all knowing.

Followup:
thanks!

Well, officially it has been since Jan 2000 -- that is when this really started.
 I got it onto the web in May 2000 (as the email solution they originally setup
for me didn't scale very well - in particular I couldn't TURN IT OFF!!!)

Rating: 5
2 years old but has ages of information ;) May 08, 2002
Reviewer: Yogeeraj from Mauritius
Hello,

hope that you don't mind me joining our friend here in greeting this site:
 o    o    oo    ooooo   ooooo    o   o
 o    o   o  o   o    o  o    o    o o
 oooooo  o    o  o    o  o    o     o
 o    o  oooooo  ooooo   ooooo      o
 o    o  o    o  o       o          o
 o    o  o    o  o       o          o

A   N   N   I   V   E   R   S   A   R   Y

best regards
Yogeeraj

This site help me in doing my job more efficiently. I learn everyday with each
reply to a question.

Rating: 5
Happy Anniversary May 08, 2002
Reviewer: AK from India
Well Tom,
This feedback is not for the answer to question asked above but for the site on
the whole and to your efforts to help the Oracle developer community in
particular.

Its is a job very well done. Continue the good work.

Rating: 5
Great site!!! May 08, 2002
Reviewer: pascal from HH,Germany
Hi Tom + Mark,

o    o    oo    ooooo   ooooo    o   o
 o    o   o  o   o    o  o    o    o o
 oooooo  o    o  o    o  o    o     o
 o    o  oooooo  ooooo   ooooo      o
 o    o  o    o  o       o          o
 o    o  o    o  o       o          o

A   N   N   I   V   E   R   S   A   R   Y

Rating: 4
just a wish May 08, 2002
Reviewer: Vikas Sharma from Delhi India
Hi tom,
This is great site for oracle resources we learn lot every day. And at same time
i must thanks you for your book it is great..  I wish you will continue to help
us in future also.

Best Wishes,
Vikas

May 08, 2002 -- Thanks for the question regarding "Generate java code in SQL/PLUS", version 8.1.6
You Asked
Hi, Tom,

How are you.

I have below source code:

create or replace and compile java source named "Something" as
import oracle.sql.*;

public class Something {
   ...............
}

When i wrote above java source file under SQLPLUS,
I got the following errors:

ERROR at line 1:
ORA-29536: badly formed source: Encountered "<EOF>" at line 1, column 20.

Was expecting:

";" ...

It complains my ";" after "import oracle.sql.*", so do i need grant
some priveleges or others?

Thanks
============================
Hi Tom,

My database is Oracle 8i, the message under SQLPLUS is:

SQL*Plus: Release 8.0.6.0.0 - Production on Fri Jun 22 10:12:30 2001

(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Connected to:
Oracle8i Enterprise Edition Release 8.1.6.1.0 - Production
With the Partitioning option
JServer Release 8.1.6.0.0 - Production

But when i executed the following, i got:

dummy@someserver> create or replace and compile java source named
  2  "Somthing"
  3  as
  4  import oracle.sql.*;
"Somthing"
*
ERROR at line 2:
ORA-29536: badly formed source: Encountered "<EOF>" at line 1, column 20.

Was expecting:

";" ...

How should i do?

Thanks
and we said...
You have to use 8i or above sqlplus

the 8.0 plus doesn't understand the "create or replace and compile ..." syntax
and is prematurely submitting the CREATE statement to the database.
Review & Followup
Rating: 4
Very helpfull - It doesn't work with 8.1.7 svrmgr also January 18, 2002
Reviewer: Depreter Frederic from Belgium
You have to use sqlplus.

The svrmgr program delivered with 8.1.7 is not able
to compile java sources.
Not mentionned in Oracle documentation (I think)
=> very useful

Rating: 3
Generate Java code in SQLPLUS May 07, 2002
Reviewer: Lynn from usa
version 8.1.7 on Windows 2000

I tried to create/compile your javamail source code and got the following error:

create or replace and compile java source named "mail"
                                                *
ERROR at line 1:
ORA-29536: badly formed source: oracle.aurora.sqljdecl.TokenMgrError: Lexical
error at line 34, column 33.  Encountered: "@" (64), after : ""

Please help! Thanks, Lynn

Followup:
what is on your line 34?  Do an "list" in the script right after the slash or
run it with SET ECHO ON

Rating: 4
May 07, 2002
Reviewer: Lynn from usa
Thanks for following up on this.

Please see line 34 below:

33          props.put("mail host", SMTPHost);
34          Message msg = new MimeMessage(Session.getDefaultInstance(props,
null));
35          msg.setFrom(new InternetAddress(from));
36          if (to != null && to.length() > 0)
37              msg.setRecipients(Message.RecipientType.TO,
38                             InternetAddress.parse(to, false));

When compile, it expects to enter variables like to, cc, bcc, and subject.  For
example,
it prompts:  Enter value for to:  email@address.com
Then, I got this error if entered without <>:
ORA-29536: badly formed source: Encountered "to" at line 34, column 24.
Was expecting one of:
"instanceof" ...
")" ...
">" ...
"<" ...

If I entered values with < >, for example,
<email@address.com> when it prompts

I'll get ora-29536: badly formed source: oracle.aurora.sqljdecl.tokenmgrerror:
lexical error.

Thanks in advance.
Lynn

Followup:
Lets see the entire thing from start to finish I guess.  Looks like you might
have lost a quote or something

Rating: 4
May 07, 2002
Reviewer: lynn from usa
Thanks for your quick response.

Here it is:

Connected to:
Oracle8i Enterprise Edition Release 8.1.7.3.0 - Production
With the Partitioning option
JServer Release 8.1.7.3.0 - Production

SQL> create or replace and compile java source named "mail"
  2  as
  3      import java.io.*;
  4      import java.sql.*;
  5      import java.util.Properties;
  6      import java.util.Date;
  7      import javax.activation.*;
  8      import javax.mail.*;
  9      import javax.mail.internet.*;
 10      import oracle.jdbc.driver.*;
 11      import oracle.sql.*;
 12
 13      public class mail
 14      {
 15          static String dftMime = "application/octet-stream";
 16   static String dftName = "filename.dat";
 17       public static oracle.sql.NUMBER
 18                  send(String from,
 19                       String to,
 20                       String cc,
 21         String bcc,
 22         String subject,
 23         String body,
 24         String SMTPHost,
 25         oracle.sql.BLOB attachmentData,
 26         String attachmentType,
 27         String attachmentFileName)
 28             {
 29      int rc = 0;
 30      try
 31          {
 32                 Properties props = System.getProperties();
 33          props.put("mail1server", SMTPHost);
 34          Message msg = new MimeMessage(Session.getDefaultInstance(props,
null));
 35          msg.setFrom(new InternetAddress(from));
 36          if (to != null && to.length() > 0)
 37              msg.setRecipients(Message.RecipientType.TO,
 38                             InternetAddress.parse(to, false));
 39          if (cc != null && cc.length() > 0)
 40             msg.setRecipients(Message.RecipientType.CC,
 41                             InternetAddress.parse(cc, false));
 42          if (bcc != null && bcc.length() > 0)
 43              msg.setRecipients(Message.RecipientType.BCC,
 44                             InternetAddress.parse(bcc, false));
 45          if ( subject != null && subject.length() > 0 )
 46              msg.setSubject(subject);
 47          else msg.setSubject("(no subject)");
 48               msg.setSentDate(new Date());
 49          if (attachmentData != null)
 50          {
 51             MimeBodyPart mbp1 = new MimeBodyPart();
 52             mbp1.setText((body != null ? body : ""));
 53       mbp1.setDisposition(Part.INLINE);
 54             MimeBodyPart mbp2 = new MimeBodyPart();
 55             String type = (attachmentType != null ? attachmentType :
dftMime);
 56             String fileName = (attachmentFileName != null ?
attachmentFileName : dftName);
 57                    mbp2.setDisposition(Part.ATTACHMENT);
 58             mbp2.setFileName(fileName);
 59             mbp2.setDataHandler(new DataHandler(new
BLOBDataSource(attachmentData, type)));
 60                    MimeMultipart mp = new MimeMultipart();
 61                  mp.addBodyPart(mbp1);
 62           mp.addBodyPart(mbp2);
 63                  msg.setContent(mp);
 64                 }
 65          else
 66          {
 67             msg.setText((body != null ? body : ""));
 68                 }
 69             Transport.send(msg);
 70                    rc = 1;
 71                 } catch (Exception e)
 72                 {
 73                   e.printStackTrace();
 74            rc = 0;
 75                 } finally
 76                 {
 77                 return new oracle.sql.NUMBER(rc);
 78                 }
 79              }
 80      static class BLOBDataSource implements DataSource
 81      {
 82         private BLOB   data;
 83         private String type;
 84         BLOBDataSource(BLOB data, String type)
 85         {
 86             this.type = type;
 87             this.data = data;
 88         }
 89         public InputStream getInputStream() throws IOException
 90         {
 91         try
 92            {
 93            if (data == null)
 94               throw new IOException("No data.");
 95               return data.getBinaryStream();
 96               } catch(SQLException e)
 97               {
 98                 throw new
 99                 IOException("Cannot get binary input stream from BLOB.");
100               }
101            }
102            public OutputStream getOutputStream() throws IOException
103            {
104            throw new IOException("Cannot do this.");
105                }
106            public String getContentType()
107            {
108            return type;
109            }
110       public String getName()
111       {
112       return "BLOBDataSource";
113       }
114     }
115   }
116  /
Enter value for to: email@address.com
old  36:         if (to != null && to.length() > 0)
new  36:         if (to != null email@address.comlength() > 0)
Enter value for cc: email@address.com
old  39:         if (cc != null && cc.length() > 0)
new  39:         if (cc != null email@address.comlength() > 0)
Enter value for bcc: email@address.com
old  42:         if (bcc != null && bcc.length() > 0)
new  42:         if (bcc != null email@address.comlength() > 0)
Enter value for subject: test
old  45:         if ( subject != null && subject.length() > 0 )
new  45:         if ( subject != null testlength() > 0 )
create or replace and compile java source named "mail"
                                                *
ERROR at line 1:
ORA-29536: badly formed source: Encountered "email" at line 34, column 24.

Was expecting one of:
"instanceof" ...
")" ...
">" ...
"<" ...
"?" ...
"==" ...
"<=" ...
">=" ...
"!=" ...
"||" ...
"&&" ...
"++" ...
"--" ...
"+" ...
"-" ...
"*" ...
"/" ...
"&" ...
"|" ...
"^" ...
"%" ...
"<<" ...
">>" ...
">>>" ...
"." ...
"[" ...
"(" ...

and if <> was entered with the variables, it gave me this:

Enter value for to: <email@address.com>
old  36:         if (to != null && to.length() > 0)
new  36:         if (to != null <email@address.com>length() > 0)
Enter value for cc: <email@address.com>
old  39:         if (cc != null && cc.length() > 0)
new  39:         if (cc != null <email@address.com>length() > 0)
Enter value for bcc: <email@address.com>
old  42:         if (bcc != null && bcc.length() > 0)
new  42:         if (bcc != null <email@address.com>length() > 0)
Enter value for subject: test
old  45:         if ( subject != null && subject.length() > 0 )
new  45:         if ( subject != null testlength() > 0 )
create or replace and compile java source named "mail"
                                                *
ERROR at line 1:
ORA-29536: badly formed source: oracle.aurora.sqljdecl.TokenMgrError: Lexical
error at line 34, column 30.  Encountered: "@" (64), after : ""

Followup:
set define OFF

before running that.  That'll stop the substitution variable processing sqlplus
is doing.

Rating: 3
oops ----------> && May 08, 2002
Reviewer: Yogeeraj from Mauritius
hi,

is it not the "&&" causing the problems here? ;)

set define off;

regards
Yogeeraj

Followup:
exactly, very clear when the entire thing is pasted in...

Rating: 5
September 23, 2003
Reviewer: A reader

May 08, 2002 Vinayak -- Thanks for the question regarding "inserting into a table with defaults", version
You Asked
hi,
I have got a table testtab with two columns numcol1 and numcol2
numcol1 has a default value 3

when i insert data into this i need to specify the column name
also

like
insert into testtab(numcol2) values(12);
This works

but if i give
insert into testtab values(12);
this fails

i can understand that ORACLE first matches the number of columns

and the number of values in the values clause.

Is there a way i can insert data into the table without
specifying the columnnames so that the default_value is entered
into the table for the column numcol1 and the value that i
specify into numcol2

This may appear silly but please answer
and we said...
Lets say you had a table:

create table T ( numcol1 number default 3, numcol2 number
default 3 );

what should "insert into t values ( 1 );" do?  Why?

What if the table was:

create table t ( numcol1 number default 3, numcol2 number,
numcol3 number );

insert into t values ( 1, 2 );

should 1 go into numcol2 or numcol3 -- why (or why not)?

I believe the reason is one of consistency and clarity.  You
either must supply ALL values for a row -- or you must explicity
name the columns you are inserting.  Another solution would be
to:

create view t_view as select numcol2 from t;

Now you can "insert into t_view values ( 12 )" without having to
name the column and without having any of the ambiguity the
other syntax would leave us with.

remember in a relational world -- the order of columns is not
relevant (there is no such as "order of columns").
Review & Followup
Rating: 3
insert a row with all values default May 08, 2002
Reviewer: prashant from INDIA
Hi Tom,
This question has been bothering me since long. How one could put a row in a
table with all values default, had the table been defined like that. The same
problem is with a procedure where if I provide default values to all the
parameters and if i want to call it with those values. You have mentioned the
possibility in answer to this query but have not in clear terms rejected the
possibility. So please answer.

Followup:
In 9i, you could:

insert into t (c1) values ( DEFAULT );

In 8i and before, you cannot (to what end would a row with all default values be
useful anyway??)

I do not understand the second part:

The same
problem is with a procedure where if I provide default values to all the
parameters and if i want to call it with those values.

Are you asking "how do i pass the defaults?"  If so, the answer is "just don't
pass ANYTHING and the defaults are passed"

May 08, 2002 Mohammed -- Thanks for the question regarding "Cross Matrix", version 8.1.7
You Asked
Hi Tom,
       I was trying to do matrix report for these result of data
PHONE_NUMBER                     START_DAT END_DATE      SIM_ID
-------------------------------- --------- --------- ----------
71766147                         03-SEP-01 23-SEP-01 1001925400
71766147                         23-SEP-01 05-FEB-02 1000581032
71766147                         05-FEB-02 05-FEB-02 1004067313
71766147                         05-FEB-02           1004067123

these are the transaction of changing SIM_ID how can i do Cross matrix  like

PHONE_NUMBER       New IMSI      OLD_IMSI1  OLD_IMSI2  OLD_IMSI3 ....
-------------------------------- ---------- ---------- ----------
71766147         1004067123     1001925400  1000581032 1004067313 ...

these data from one table I was trying by doing this sql

select phone_number,
       max(decode(END_DATE,Null,Sim_id) ) Old_IMSI1,
       max(decode(END_DATE,Null,Sim_id) ) Old_IMSI2,
       max(decode(END_DATE, Null,Sim_id) ) Old_IMSI3
from (Select distinct
             phone_number,id,end_date,Sim_id
      from  subscriber_channels
      where phone_number ='71766147')
group by phone_number

but it does not realy work properly I was trying to Put Null NOT  insted of
NULL but it does not work .

Could you please help me to do this Report ,I am still  beginer in doing Matrix
Report in SQL LEVEL .

Thank you very much

Not : if END_DATE IS NULL then this is the NEW IMSI
################################################################
Good Morning :
I realy sorry if I sent you this updated agina because my computer was
shoutdowned when it was Updating . ;)

Well , Yes  there  is no  UPPER BOUND to the number of old "imsi's"
phone number can have at most N  old sim_id's)

As I explained  to that If i use This SQL :
SQL > select Phone_number,start_date,End_date from subscriber_channels
     where phone_number ='71766147 '
I will get this Informations;
PHONE_NUMBER                     START_DAT END_DATE      SIM_ID
-------------------------------- --------- --------- ----------
71766147                         03-SEP-01 23-SEP-01 1001925400
71766147                         23-SEP-01 05-FEB-02 1000581032
71766147                         05-FEB-02 05-FEB-02 1004067313
71766147                         05-FEB-02           1004067123

where some times phone_number can only have one Active SIM_ID
and may be many of old SIM_ID .
how can I get the Information as Cross Matrix  Report.
like :

PHONE_NUMBER       New IMSI      OLD_IMSI1  OLD_IMSI2  OLD_IMSI3 ....
-------------------------------- ---------- ---------- ----------
71766147         1004067123     1001925400  1000581032 1004067313 ...

With my best Regards .

####################################################################
and we said...
Either of:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select phone_number,
  2         max( decode( rn, 1, sim_id, null ) ) "New IMSI",
  3         max( decode( rn, 2, sim_id, null ) ) Old_imsi1,
  4         max( decode( rn, 3, sim_id, null ) ) Old_imsi2,
  5         max( decode( rn, 4, sim_id, null ) ) Old_imsi3
  6    from ( select phone_number,
  7                  sim_id,
  8                  row_number() over ( order by end_date NULLS FIRST ) rn
  9             from t
 10            where phone_number = 71766147
 11         )
 12   group by phone_number
 13  /

PHONE_NUMBER   New IMSI  OLD_IMSI1  OLD_IMSI2  OLD_IMSI3
------------ ---------- ---------- ---------- ----------
    71766147 1004067123 1001925400 1000581032 1004067313

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select phone_number,
  2         max( decode( rn, 1, sim_id, null ) ) "New IMSI",
  3         max( decode( rn, 2, sim_id, null ) ) Old_imsi1,
  4         max( decode( rn, 3, sim_id, null ) ) Old_imsi2,
  5         max( decode( rn, 4, sim_id, null ) ) Old_imsi3
  6    from ( select phone_number,
  7                  sim_id,
  8                  rownum rn
  9             from ( select phone_number, sim_id
 10                      from t
 11                     where phone_number = 71766147
 12                     order by end_date NULLS FIRST )
 13         )
 14   group by phone_number
 15  /

PHONE_NUMBER   New IMSI  OLD_IMSI1  OLD_IMSI2  OLD_IMSI3
------------ ---------- ---------- ---------- ----------
    71766147 1004067123 1001925400 1000581032 1004067313

should do it.  Here I assumed there was at most 4 sim_id's / phone.  If there
are more, add more decodes.
Review & Followup
Rating: 4
Cross Matrix May 24, 2002
Reviewer: Krishnan from India
This was very useful in getting the idea, though I was looking for a similar SQL
for Sybase.

May 08, 2002 Hamid -- Thanks for the question regarding "Where to look first for DB tuning and which tool?", version 8.1.7
You Asked
Hi Tom,
I wonder if you could tell me in a nut shell the areas of DB you would look for,
for tuning porpuses and the kind of tool you start with (UTLBSTAT, ...).
Many thanks.
and we said...
If you have my book -- i document this in chapter 10.

If there was a single way, software would do it.

The problem with tuning is that each case seems to be uniquely different from
the last.  You have to take the sum of your experiences and apply that to this
new problem.

In that chapter, I relate a couple of "tuning stories" and how there could be no
cook-book to solve them.

In general tho, it might go like this:

o tune the app in single user
o tune the app in multi-user
o tune the database
o tune the OS

do not skip a step.  do them in that order.  No sense in tuning the database
before you tune the app.

The tools I use are very very simple:

o tkprof+sql_trace+timed_statistics (and "runstats"
http://osi.oracle.com/~tkyte/runstats.html
o statspack

o a qualified OS guy to finish the rest.
Review & Followup
Rating: 3
May 13, 2002
Reviewer: A reader

May 08, 2002 Gopi Krishna -- Thanks for the question regarding "How can we ensure that Oracle cursors are closed after a java program accesses the db?", version 8.1.7
You Asked
Hi Tom,
      We are using J2EE with JRun as our application server.We have a problem
wherein the connection made by JRun to the oracle database opens up a lot of
cursors in Oracle. This increases the number of open cursors in within a short
span of time , we get the exception MAXIMUM_OPEN_CURSORS. We have figured out
that this happens whenever JRun makes a connection to the database. Is there
anything that can be done on the database side to ensure that cursors once
opened are closed after a specific time interval.

Thanks for your assistance,

Gopi Krishna,
and we said...
No -- that would be terrible wouldn't it.  The database "closes your resources"
and you are not done with them!!!  That would be like the OS closing your files
-- and you are only 1/2 way through reading them!

The only solution to this is to fix the bug in the developed Java code.  Your
developers are leaking cursors (they could as easily be leaking files).  They
need to use proper coding techniques to not leak them.
May 08, 2002 Pascal -- Thanks for the question regarding " Loading java.util.regex as SYS", version 8.1.7
You Asked
Hi Tom

I wanted to use some Java Classes found in a Java Package :

java.util.regex       (Since Version 1.4 JDK)

I was thinking  that may be it could have  been  available in Oracle 9i  , but
i have checked and it's not there !

Now , my question is , when  is Oracle going to  make this Package available in
the Oracle Database .

And is it possible and allowed to zip and load this package as SYS into Oracle(
or will there be Version and Copy Rights issues here )

Thanks in advance

Pascal
and we said...
Hi Pascal,

I work for Tom and he asked me to look at your questions. First, I do
not know when JDK 1.4 will be supported in the database. 9i release 2
has support for JDK 1.3.1 but since 1.4 is fairly new, expect a bit of
time before it can be integrated. 1.4 has substantial changes so it
won't get there in 9i release 2.

You can load the regexp code into the database as SYS.  However, do you
plan on extracting it from JDK 1.4 or using a third party regular
expression package like the one from the Apache organization? I'd
suggest checking out the following links.
http://jakarta.apache.org/regexp/index.html
http://jakarta.apache.org/oro/index.html
There is an example on Tom's web site that uses the code from the second
URL above. It performs Perl 5.003 pattern matching:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2200894550208

With that said, since they fall under a different package structure than
the Sun or Oracle classes, it should be okay to load them as SYS without
overwriting any core classes. For the same reason, you should be okay
when/if Oracle puts JDK 1.4 support into the database.

As for copyright issues, I believe there is documentation about software
licensing somewhere on the Apache organization web site. I think it's
pretty much free but I'm a programmer not a lawyer. :-)

Mark Piermarini
Oracle Corporation
Reston, VA

May 08, 2002 lior -- Thanks for the question regarding
      "Calling Xsu procedure from inside a Xsu procedure", version 8.1.7

      You Asked
Hi,

I'm trying the following scenario:

1) Calling a Xsu procedure that insert a xml (that is in a clob) into a certain
view.

The code to do that look like this:

insCtx := DBMS_XMLSave.newContext(table_name);
rows := DBMS_XMLSave.insertXML(insCtx,l_clob);

2) This call fires an instead of trigger that maps the xml to some tables.

3) Inside the trigger another call to Xsu insert procedure is being made, with
new clob and another view (this another view represent different tables from the
first view).

4) This should again fire an instead of trigger but instead the following
exception is being thrown:

ORA-29532: Java call terminated by uncaught Java exception:
oracle.xml.sql.OracleXMLSQLException: Exception
'orcale.jdbc.driver.OracleSQLException:ORA-29532: Java call terminated by
uncaught Java exception: orcale.xml.sql.OracleXMLSQLException: ORA-04092: cannot
SET SAVEPOINT in a trigger .

Hope that you can help,
Thanks,
Lior.

      and we said...
Well, the XSU stuff is apparently trying to set a savepoint and that is not
permitted inside of a trigger.

What you are doing sounds like a very convuluted "magic", tricky approach.  I
would use and recommend a more straight forward approach.  Instead of using tons
of layers of indirection (triggers firing triggers and so on -- lots of stuff
happening by "magic") -- I would use a stored procedure to do the right thing.

In short, you won't be able to accomplish your goal with that code as that code
uses a savepoint and setting a savepoint in a trigger is not allowed.
Review &
      Followup

      Rating: 3
Thank's for
      your answer  May 08, 2002
Reviewer:  Lior
      Weintraub  from Israel
The answer was helpful, but I was wondering if I want to use xsu that insert the
xml inside a view what do you think I should do in order to avoid triggers (the
view is a complicated view with multiple joins ).

          Followup:

Like I said -- I would use a stored procedure that processes the data IN FRONT
of the tables.  Using the triggers, while "cool", is

o hard to understand
o hard to code
o hard to maintain.

      Rating: 4
But
      Example  May 08, 2002
Reviewer:
      A reader
Hi,

I agree with you, but how to implement it in DB.
Would you mind give an example please?

Thanks

          Followup:

implement what exactly?

a stored procedure?

May 08, 2002 Rajashekar -- Thanks for the question regarding
      "SQL Loader", version 8.1.7.3

      You Asked
Hi Tom,

I am trying to load the data from the delimited text file to a staging table
using SQL Loader. If the loading of the data from the staging table is
successful with out any errors such as no discarded data/bad data/errors the i
would like to the data from the stage table to the main table.

The database is on solaris 64 bit.I am using the exit codes of sql loader to
check whether the load is successful or not but the exit code is always 0. Can
you suggest  me some way to do the above.

My shell script to check the exit code is as follows

#!/bin/sh
sqlldr scott/tiger@dev control=emp_t.ctl data=emp.dat errors=0 discardmax=0
log=emp.log
retcode=$?
echo $retcode
case $retcode in
0) echo "SQL*Loader execution successful" ;;
1) echo "SQL*Loader execution exited with EX_FAIL, see logfile" ;;
2) echo "SQL*Loader execution exited with EX_WARN, see logfile" ;;
3) echo "SQL*Loader execution encountered a fatal error" ;;
*) echo "unknown return code";;
esac

Thanks

      and we said...
you could easily just look for the existence of a .dis and .bad file.  That
would tell you want you need to know (just rm emp_t.{dis,bad} and then look for
them (-e) after the load.
Review & Followup

      Rating: 4
Can I send the
      e-mail   May 08, 2002
Reviewer:  Raj
       from Inida
Thanks,
This was very useful information.
One more thing can I send an e-mail attaching the log,bad,discard files if the
bad or discard in the directories on solaris 8.

          Followup:

sure, unix is very flexible that way.

Just do "man mail" to see how to send email from the command line.

May 09, 2002 zhu
      -- Thanks for the question regarding "SQL Tuning help again", version
8.1.7

      You Asked
hi, tom:
  Thanks for your help, i have another question about sql tuning:
1.look at the following example:
 select count(*)  into :b0
from
 st_smstrace where
(((:b1 is  not null  and rtrim(st_mpnumber)=:b1) or :b1  is null )
   and ((:b4<>0 and st_status=:b4)
   or (:b4=0 and st_status<>136)
   )
  )
  and ((:b7 is  not null  and st_submittime>=to_date(:b7,'yyyy-mm-dd'))  or  :b7
is null ))
  and ((:b10 is  not null  and st_submittime<=to_date(:b10,'yyyy-mm-dd')) or
:b10 is null ))

Developers are coding in this way, and it is making full table scan and this
table is now currently 20M in size and is continuely growing.
I wondered what shall i do with this kind of SQL.Without this or :b1  is null, i
can build in FBI index and in test it works fine.But now, in this case, FBI
won't be used.
How shall i ask the developers to rewrite this kind of SQL?Use if-then-else or
something else?
2. As we know, in most cases, indexes help us doing dml faster.But there is also
many cases that too many indexes are inefficient.Now i manage an oracle database
with too much(maybe) indexes.I wonder whether all of them are really useful.To
add an index is relatively easy but to drop one is not, right? If you face such
a database, what will you do? It is a really HARD job to explain plan all the
sql and procedures one by one, to find which index is used and which is not
used. Can you give me some instructions?
Thanks.

      and we said...
1) I would be sorely tempted to use dynamic sql here.  I would put this logic
into the database:

create or replace package my_pkg
as
    type rc is ref cursor;

    function get_st_smstrace_count( p_b1 in varchar2 default NULL,
                                    p_b4 in varchar2 default NULL,
                                    p_b7 in varchar2 default NULL,
                                    p_b10 in varchar2 default NULL)
    return number;
end;
/

create or replace context my_pkg_ctx using my_pkg
/

create or replace package body my_pkg
as
function get_st_smstrace_count( p_b1 in varchar2 default NULL,
                                p_b4 in varchar2 default NULL,
                                p_b7 in varchar2 default NULL,
                                p_b10 in varchar2 default NULL)
return number
as
    l_query  long default 'select count(*) from st_smstrace where 1=1 ';
    l_result number;
begin

    if ( p_b1 is not null )
    then
        l_query := l_query || ' and st_mpnumber = sys_context( ''my_pkg_ctx'',
''b1'' ) ';
        dbms_session.set_context( 'my_pkg_ctx', 'b1', p_b1 );
    end if;

    if ( p_b4 <> 0 )
    then
        l_query := l_query || ' and st_status = TO_NUMBER(sys_context(
''my_pkg_ctx'', ''b4'' )) ';
        dbms_session.set_context( 'my_pkg_ctx', 'b4', p_b4 );
    elsif ( p_b4 = 0 )
    then
        l_query := l_query || ' and st_status <> 136 ';
    end if;

    if ( p_b7 is not null )
    then
        l_query := l_query || ' and st_submittime >=
TO_DATE(sys_context(''my_pkg_ctx'',''b7''),''yyyy-mm-dd'') ';
        dbms_session.set_context( 'my_pkg_ctx', 'b7', p_b7 );
    end if;

    if ( p_b10 is not null )
    then
        l_query := l_query || ' and st_submittime <=
TO_DATE(sys_context(''my_pkg_ctx'',''b10''),''yyyy-mm-dd'') ';
        dbms_session.set_context( 'my_pkg_ctx', 'b10', p_b10 );
    end if;

    execute immediate l_query into l_result;

    return l_result;
end;

end;
/

and have them use that.  In that way we:

o generate a finite set of queries
o that are specific to the question being asked
o that can make best use of available indexes

Further, if the developers just need to KNOW if a single row exists -- I would
turn that into:

 l_query :=  'select count(*) from dual WHERE EXISTS ( ' || l_query || ')';

Further -- I removed the rtrim, believing it is probably not necessary.  It
would preclude an index on that column unless you use an FBI but it is most
likely not needed at all.

2) You need a tool, like OEM's tuning pack, to help you automate this.  You need
to know all of the queries, what indexes they are actually using in order to see
what might not be needed.
Review & Followup

      Rating: 4
what is the
      difference between write this logic in pro*c and stored
      procedure?  May 08, 2002
Reviewer:  zhu chao
       from china shanghai.
Thanks tom, you give me a brand new way of writing this kind of sql.
two more questions:
1.what is the difference between write this logic in pro*c and stored procedure?
2.will there be performance issue about this dynamic SQL? Seems execute
immediate will parse and execute the sql everytime it is called,right?

          Followup:

1) if it is in a stored procedure, you can easily

   a) tune it
   b) secure it
   c) audit it
   d) change it
   e) redeploy it
   f) the list goes on and on and on

put it in pro*c and you have to

   a) find the C code in order to tune it (and find a c programmer and use the
      precompiler and C compiler)
   b) GRANT SELECT to the pro*c connection (typically every user that uses
      the program) meaning they can query that table using any tool they want to
   c) Until 9i, there is no fine grained auditing on selects, with a stored
      procedure, you could add custom auditing
   d) to change it, you have to get the C code, change the C code, precompile
it,
      compile it, redeploy it.  PLSQL- create or replace package .... and it's
      done.
   e) deployment is a bear.
   f) and the list goes on and on and on

I'm a big fan of putting logic like this (database logic) in the database where
database savvy people can maintain, tune and tweak it easily.

2) Yes it will and YES in this case -- we want that.  We WANT (desire, need) a
different plan for different combos of the parameters (you have that full table
scan now, this is an attempt to use different sets of indexes when appropriate!)

Search for session_cached_cursors on this site.  That setting will alleviate
most of the issues with regards to that.

      Rating: 4
what is the
      difference between write this logic in pro*c and stored
      procedure?  May 08, 2002
Reviewer:  zhu chao
       from china shanghai.
Thanks tom, you give me a brand new way of writing this kind of sql.
two more questions:
1.what is the difference between write this logic in pro*c and stored procedure?
2.will there be performance issue about this dynamic SQL? Seems execute
immediate will parse and execute the sql everytime it is called,right?

May 09, 2002 Kapil -- Thanks for the question regarding
      "Making APPEND default behavior", version 8.1.7

      You Asked
Hi Tom,
Is there any parameter in the init.ora by which the default behavior of the
INSERTs be set to APPEND so that the HINT need not be used everytime?

Kapil

Hi Tom.
Yes, I thought append works for the values clause as well.
You say that the append operation occurs in the no logging mode.
Isnt that dependant on whether the table has been set to logging or no logging?
Also, could you give me a link to the ORACLE doc which mentions that append
works for INSERT as SELECT only. It would help me to prove it to my DBA.

Kapil

      and we said...
append would work in nologging for noarchivelog databases always, for nologging
tables in archivelog.

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c21dlins.htm#7917
(that is a document you should read from cover to cover!)

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

NO, it would be a terribly *bad* thing.

INSERT /*+ append */ as SELECT

only works for INSERT as SELECT (hopefully you are not thinking it works for all
inserts with values clauses as well).  It is a non-logged operation.  In a real
system, a backup of that tablespace you just inserted into would be called for
immediately after that operation is performed.  Hence, the DBA needs to know
when this occurrs so they can do the right thing (else you will not be able to
recover some data after a while).

Tell me -- why do you THINK you want this "feature".
Review &
      Followup

      Rating:
      4
  March 13, 2002
Reviewer:
      A reader

      Rating: 4
archivelog
      databases  May 06, 2002
Reviewer:  Kapil
       from INDIA
Hi Tom
Thus for archivelog databases the attribute of the table would come into
picture.
Coming back to the original question wouldnt a parameter which would make the
default behavior of INSERT ... AS SELECT to "APPEND" be useful for archivelog
databases as full redo log would be generated?

Kapil

          Followup:

Yes, for archivelog mode databases -- the nologging attribute would affect the
amount of redo generated.

No, I do not think in any way shape or form would APPEND as a default be useful.
 Not at all.

      Rating:
      4
  May 08, 2002
Reviewer:  Kapil
       from INDIA
Could you mention the reasons for your disagreement?

          Followup:

 please read my original answer.  I have a list of reasons right there.

May 09, 2002 STANLEY -- Thanks for the question regarding
      "ora-01652 & ora-01630", version 8.0.5

      You Asked
Hi Tom,
I had encountered few "non Oracle Exceptions" when
creating snapshots.

-------------------------------------------------------
MYBOWNER@PROJA SQL> get dly

CREATE SNAPSHOT DAILY_SL_SM
       STORAGE (
              INITIAL 30000K
              NEXT 8000K
              MINEXTENTS 1
              PCTINCREASE 0
       )
TABLESPACE myb_tab
USING INDEX TABLESPACE myb_idx
PCTFREE 10
STORAGE(INITIAL 256K NEXT 256K PCTINCREASE 0 )
REFRESH COMPLETE WITH ROWID
    START WITH SYSDATE + 1/24
    NEXT SYSDATE + 7
AS
    SELECT * FROM RDDMIX.DAILY_SL_SM@RDDMIX
        WHERE SYSDATE - TRADE_DT < 21;

MYBOWNER@PROJA SQL> @dly

    SELECT * FROM RDDMIX.DAILY_SL_SM@RDDMIX
        WHERE SYSDATE - TRADE_DT < 21

        *
ERROR at line 16:
ORA-01630: max # extents (100) reached in temp segment
           in tablespace MYB_IDX

MYBOWNER@PROJA SQL> spool off

i encountered this error and executed the following command:

SYSTEM@PROJA SQL> ALTER TABLESPACE myb_idx DEFAULT
STORAGE(MAXEXTENTS unlimited);

Which solved the problem but threw up the following error:

ORA-1652: unable to extend temp segment by 5000 in tablespace
MYB_TAB

then i solved it by reducing the initial extent (30000 K) of
DAILY_SL_SM.
-----------------------------------------------------------

My question is:

1. What are the errors ORA-1630 & ORA-1652, i could not find
proper oracle
   documentation for these errors ?

2. What is the concept of temp segment in a tablespace which is
going to store
   tables and indexes alone? Should it not be TEMP tablespace
which store temp
   segments?

3. Why is Oracle creating Temp segments in a tablespace when
creating a snapshot?
   (i have not used any SORT operation in the SELECT stmt to
create the snapshot
    OR is it because i am restricting using WHERE clause in
SELECT which makes
    Oracle create temporary segments in myb_tab Tablespace acc.
to the condition
    and then allocate "real" space for it?)

Thanks in advance.

      and we said...
1) both are documented in the Server Messages guide

http://technet.oracle.com/doc/server.804/a58312/newch225.htm#17574
The unix command line utility oerr would tell you:

$ oerr ora 1630
01630, 00000, "max # extents (%s) reached in temp segment in
tablespace %s"

// *Cause:  A temp segment tried to extend past max extents.
// *Action: If maxextents for the tablespace is less than the
            the system
//          maximum, you can raise that. Otherwise, raise
            pctincrease for the
//          tablespace

$ oerr ora 1652
01652, 00000, "unable to extend temp segment by %s in tablespace
%s"
// *Cause:  Failed to allocate an extent for temp segment in
            tablespace.
// *Action: Use ALTER TABLESPACE ADD DATAFILE statement to add
            one or more
//          files to the tablespace indicated.

2) during the creation of those objects -- temporary extents
were allocated to "create them".  After their creation -- these
temporary extents would be converted into real extents and given
to the table.  During the "create phase", they are temporary --
when the entire thing is built -- they will become permanent.

3) see above.  The temp extents will become the real extents.
Review &
      Followup

      Rating:
      5
  May 07, 2002
Reviewer:
      A reader
Hi Tom

Today we got oracle error ora-01652 and other error was unable to allocate next
extent in TEMP table space.

I am a developer here but i konw lots of dba stuff(your site is really bigest
help so far.) and i have access to V$SYSSTAT and some other v$ views.

Our DBA killed sessions that was doing high sort activity.

After killing the session he told ORACLE is not realesing temp spece in
TEMPERORY SEGMENT.So he rebooted oracle instance.

My question to you is i never heard about oracle NOT releasing temp space in
TEMP segment.is this true?

I know there is three parameter in v$SYSSTAT that tell us sort activity

sorts (memory)
sorts (disk)
sorts (rows)

Can you explain about this.
are values is total sum of all sessions?.

When session are done with sorts,does values for this parameter goes down?

eg.
When i start query Sort(disk) values was 100 ,after query is done
values goes down to 0.(It is true)

Sorry for bad english.

          Followup:

Tell your DBA to look at temp again after rebooting and after things were
running for a while.  Ask him "why does the free space in the temp tablespace
either stay the same or go down -- it never goes up".

It is normal, it is expected, it is the way that it works.  Tell them to look at
v$sort_usage / v$sort_segment to see whats allocated and whats actually being
used.

sort(disk) will never go down until the server is rebooted.  v$sysstat is a
cumulative counter table.

      Rating:
      5
  May 07, 2002
Reviewer:
      A reader
Hi Tom,

When user is doing sort operation.oracle first do sort in memory(sort_area_size)
and if the space is not enough it go to temporary tablespace(if temp tablespace
is TEMP) and create TEMP SEGMENT for sorting.Once process is done with sorting
oracle deallocate extents.

Am i right?

As far as i konw we only create TEMP tablespace ,
How Sort segment are created?.

and how many sort segment(max) can be in a database.

2) If many session are performing sort operation,will it user only one sort
segment or each sort session will have their own sort segment.

3) I have maked entry in V$SORT_USAGE is not deleted after sort is done. why?

4) What is the unit of measurment(i.e bytes or block etc..)
of the following V$SYSSTAT parameter.
sorts (memory)
sorts (disk)
sorts (rows)

5) How we konw sort segment is releasing Space or not.

I am really confused here.Please help me

Thanks

          Followup:

You were right upto the "deallocate extents".

The extents are managed internally -- once we allocate them (like RBS) we don't
give them back (there is no "shrink" for temp).  You will find the temp space
only "stays the same size or grows", it does not shrink.  The expense of
allocating the extent is removed in this fashion.

You use v$sort_usage and v$sort_segment to see what is really there and what is
used/free.

You can have as many TEMP tablespaces as you want -- one is typical, dozens can
be made -- they would each have a temp segment.  We also create temp segments
when doing some operations like CREATE TABLE AS SELECT -- these temp segments
are destined to become permanent ones when the operation is done.

2) one sort segment, many extents -- each extent used by a session.

3) sure it is.  give me an example.  Here is an example of mine -- shows that
the sort space is "freed" -- UNLESS you still need it (which is probably the
case in your situation, you weren't done with it yet!)

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set sort_area_size=32765;
Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from v$sort_usage;
no rows selected

Nothing being used, single user system...

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly statistics;
ops$tkyte@ORA817DEV.US.ORACLE.COM> select *
  2    from all_objects
  3   order by object_name, owner, object_type, last_ddl_time, created,
object_id;

22925 rows selected.

Statistics
----------------------------------------------------------
          0  recursive calls
         14  db block gets
      74628  consistent gets
        342  physical reads
          0  redo size
    2862933  bytes sent via SQL*Net to client
     170033  bytes received via SQL*Net from client
       1530  SQL*Net roundtrips to/from client
          0  sorts (memory)
          1  sorts (disk)
      22925  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from v$sort_usage;
no rows selected

Now, that query used sort space (in another session I ran select * from
v$sort_usage whilst this was sorting to verify that).

When I'm exhausted the result set -- it released the extent I was using....

Now:

ops$tkyte@ORA817DEV.US.ORACLE.COM> variable x refcursor
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2     l_rec all_objects%rowtype;
  3  begin
  4     open :x for
  5         select *
  6           from all_objects
  7          order by object_name, owner, object_type, last_ddl_time, created,
object_id;
  8     fetch :x into l_rec;
  9  end;
 10  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from v$sort_usage;

USER                           SESSION_ SESSION_NUM SQLADDR     SQLHASH
TABLESPACE                      CONTENTS  SEGTYPE
------------------------------ -------- ----------- -------- ----------
------------------------------- --------- ---------
  SEGFILE#    SEGBLK#    EXTENTS     BLOCKS   SEGRFNO#
---------- ---------- ---------- ---------- ----------
OPS$TKYTE                      80131A38         822 00                0
TEMPORARY                       TEMPORARY SORT
       202       1481          6        384          1

Ahh -- there is the sort space.  We have only fetched the first record, the
remaining 22,000 records are sitting there in temp waiting to be fetched in this
case!  We can see that by fetching them:

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly
ops$tkyte@ORA817DEV.US.ORACLE.COM> print :x

22924 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from v$sort_usage;

no rows selected

ops$tkyte@ORA817DEV.US.ORACLE.COM>

and see that the temp extent disappears.  I believe this is what you may have
observed

4) none of the above -- it is not bytes or blocks -- it is a cardinality
representing the number of times you sorted or the number of rows.  If you look
at my example above, I have ONE sort to disk -- we sorted ONCE (v$sort_usage
shows I used 6 temp extents of some 384 blocks)

5) use v$sort_usage as I demonstrated....

      Rating: 4
still problem
      with v$sort_segment  May 08, 2002
Reviewer:
      Kathrin Spiller  from germany
Thanks for the explanation of v$sort_usage and v$sort_segment.

I've learned that looking at v$sort_segment shows what is allocated.
I've got one TEMP-Tbsp, consisting of 3 datafiles with 601 MB and one of 101 MB.

09:42:49 SYS@RP1G>select tablespace_name, file_id, blocks_used from
v_$temp_space_header;

TABLESPACE_NAME                   FILE_ID BLOCKS_USED
------------------------------ ---------- -----------
TEMP                                    1       76928
TEMP                                    2       76928
TEMP                                    3       76928
TEMP                                    4         128

This shows datafile 1-3 fully used, 4 empty, except for the
locally-management-bitmap. So I expected to find a total
of 3*600 MB sort segments in use.

09:48:12 SYS@RP1G>select tablespace_name, total_blocks, max_blocks from
v$sort_segment;

TABLESPACE_NAME                 TOTAL_BLOCKS MAX_BLOCKS
------------------------------- ------------ ----------
TEMP                                   76800      76800

This are only 600 MB. Who allocated the rest?

thanks in advance

          Followup:

well, file 4 is not showing you the bitmap for the LMT -- that would be 64k (8
blocks), not 128 blocks (1m).  That looks more like an extent -- is your LMT
uniform size 1m-64k perhaps?

Anyway, v$temp_space_header is persistent across restarts.  V$sort_segment is
not.

v$temp_space_header shows you the usage of the temp files forever (as long as
they've been around).  As SOME POINT in the past, you used that many blocks from
each file (those blocks were touched).  You shutdown and restarted -- clearing
out v$sort_usage (but not v$temp_space_header as that one is based on the
tempfiles themselves)

Here is an example that might make it clearer:

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop tablespace test_temp;
Tablespace dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create temporary tablespace test_temp
  2  tempfile '/tmp/test_temp01' size 1m reuse,
  3           '/tmp/test_temp02' size 1m reuse,
  4           '/tmp/test_temp03' size 1m reuse,
  5           '/tmp/test_temp04' size 1m reuse
  6  extent management local
  7  uniform size 16k
  8  /
Tablespace created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter user ops$tkyte temporary tablespace
test_temp;
User altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec print_table( 'select * from
v$temp_space_header where tablespace_name = ''TEST_TEMP''' )
TABLESPACE_NAME               : TEST_TEMP
FILE_ID                       : 1
BYTES_USED                    : 65536
BLOCKS_USED                   : 8
BYTES_FREE                    : 983040
BLOCKS_FREE                   : 120
RELATIVE_FNO                  : 1
-----------------
TABLESPACE_NAME               : TEST_TEMP
FILE_ID                       : 3
BYTES_USED                    : 65536
BLOCKS_USED                   : 8
BYTES_FREE                    : 983040
BLOCKS_FREE                   : 120
RELATIVE_FNO                  : 2
-----------------
TABLESPACE_NAME               : TEST_TEMP
FILE_ID                       : 4
BYTES_USED                    : 65536
BLOCKS_USED                   : 8
BYTES_FREE                    : 983040
BLOCKS_FREE                   : 120
RELATIVE_FNO                  : 3
-----------------
TABLESPACE_NAME               : TEST_TEMP
FILE_ID                       : 5
BYTES_USED                    : 65536
BLOCKS_USED                   : 8
BYTES_FREE                    : 983040
BLOCKS_FREE                   : 120
RELATIVE_FNO                  : 4
-----------------

PL/SQL procedure successfully completed.

So, when we start - each file has 8 blocks used (8*8k = 64k = LMT bitmap)

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec print_table( 'select * from
v$sort_segment where tablespace_name = ''TEST_TEMP''' )

PL/SQL procedure successfully completed.

and v$sort_usages says "nothing here", we haven't used any of it yet.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set sort_area_size = 32765;
Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly statistics;
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from all_objects order by
object_name, object_type, owner, last_ddl_time, created, object_id;

22926 rows selected.

Statistics
----------------------------------------------------------
         12  recursive calls
        370  db block gets
      74632  consistent gets
        347  physical reads
          0  redo size
    2861499  bytes sent via SQL*Net to client
     170033  bytes received via SQL*Net from client
       1530  SQL*Net roundtrips to/from client
          1  sorts (memory)
          1  sorts (disk)
      22926  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec print_table( 'select * from
v$temp_space_header where tablespace_name = ''TEST_TEMP''' )
TABLESPACE_NAME               : TEST_TEMP
FILE_ID                       : 1
BYTES_USED                    : 786432
BLOCKS_USED                   : 96
BYTES_FREE                    : 262144
BLOCKS_FREE                   : 32
RELATIVE_FNO                  : 1
-----------------
TABLESPACE_NAME               : TEST_TEMP
FILE_ID                       : 3
BYTES_USED                    : 786432
BLOCKS_USED                   : 96
BYTES_FREE                    : 262144
BLOCKS_FREE                   : 32
RELATIVE_FNO                  : 2
-----------------
TABLESPACE_NAME               : TEST_TEMP
FILE_ID                       : 4
BYTES_USED                    : 770048
BLOCKS_USED                   : 94
BYTES_FREE                    : 278528
BLOCKS_FREE                   : 34
RELATIVE_FNO                  : 3
-----------------
TABLESPACE_NAME               : TEST_TEMP
FILE_ID                       : 5
BYTES_USED                    : 770048
BLOCKS_USED                   : 94
BYTES_FREE                    : 278528
BLOCKS_FREE                   : 34
RELATIVE_FNO                  : 4
-----------------

PL/SQL procedure successfully completed.

Now, that shows we used about 3/4 of a meg from each file for that sort we
did.    We now have 96+96+94+94 = 380 blocks "used" in the tempfiles.  That
means USED at some point in time...

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec print_table( 'select * from
v$sort_segment where tablespace_name = ''TEST_TEMP''' )
TABLESPACE_NAME               : TEST_TEMP
SEGMENT_FILE                  : 0
SEGMENT_BLOCK                 : 0
EXTENT_SIZE                   : 2
CURRENT_USERS                 : 0
TOTAL_EXTENTS                 : 174
TOTAL_BLOCKS                  : 348
USED_EXTENTS                  : 0
USED_BLOCKS                   : 0
FREE_EXTENTS                  : 174
FREE_BLOCKS                   : 348
ADDED_EXTENTS                 : 174
EXTENT_HITS                   : 0
FREED_EXTENTS                 : 0
FREE_REQUESTS                 : 0
MAX_SIZE                      : 174
MAX_BLOCKS                    : 348
MAX_USED_SIZE                 : 174
MAX_USED_BLOCKS               : 348
MAX_SORT_SIZE                 : 174
MAX_SORT_BLOCKS               : 348
RELATIVE_FNO                  : 0
-----------------

PL/SQL procedure successfully completed.

that shows that temp segment has 348 blocks in use (348+4*8 = 380 = total
blocks used in the tempfiles.  4*8 = LMT bitmap, 348 = sort blocks)...

Now, lets do a restart of the database:

ops$tkyte@ORA817DEV.US.ORACLE.COM> connect / as sysdba
Connected.
ops$tkyte@ORA817DEV.US.ORACLE.COM> shutdown
Database closed.
Database dismounted.
ORACLE instance shut down.
ops$tkyte@ORA817DEV.US.ORACLE.COM> startup
ORACLE instance started.

Total System Global Area   72876192 bytes
Fixed Size                    73888 bytes
Variable Size              64430080 bytes
Database Buffers            8192000 bytes
Redo Buffers                 180224 bytes
Database mounted.
Database opened.

ops$tkyte@ORA817DEV.US.ORACLE.COM> connect /
Connected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set serveroutput on

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec print_table( 'select * from
v$temp_space_header where tablespace_name = ''TEST_TEMP''' )
TABLESPACE_NAME               : TEST_TEMP
FILE_ID                       : 1
BYTES_USED                    : 786432
BLOCKS_USED                   : 96
BYTES_FREE                    : 262144
BLOCKS_FREE                   : 32
RELATIVE_FNO                  : 1
-----------------
TABLESPACE_NAME               : TEST_TEMP
FILE_ID                       : 3
BYTES_USED                    : 786432
BLOCKS_USED                   : 96
BYTES_FREE                    : 262144
BLOCKS_FREE                   : 32
RELATIVE_FNO                  : 2
-----------------
TABLESPACE_NAME               : TEST_TEMP
FILE_ID                       : 4
BYTES_USED                    : 770048
BLOCKS_USED                   : 94
BYTES_FREE                    : 278528
BLOCKS_FREE                   : 34
RELATIVE_FNO                  : 3
-----------------
TABLESPACE_NAME               : TEST_TEMP
FILE_ID                       : 5
BYTES_USED                    : 770048
BLOCKS_USED                   : 94
BYTES_FREE                    : 278528
BLOCKS_FREE                   : 34
RELATIVE_FNO                  : 4
-----------------

PL/SQL procedure successfully completed.

Now, v$temp_space_header "remembers" that at some point, we used 380 blocks,
there have been 380 blocks used over time.  The files are that big, that
"initialized" if you will...

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec print_table( 'select * from
v$sort_segment where tablespace_name = ''TEST_TEMP''' )

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from v$sort_segment
  2  /

no rows selected

but look at that!  v$sort_segment is EMPTY..  It doesn't remember its
"extents" from last time.  It needs to reallocate them again.

So, in short, v$temp_space_header and v$sort_segment are not supposed to
reconcile with eachother.  The blocks in temp_space_header are the total number
of "initialized" blocks in the tempfiles.  The blocks in sort_segment are the
total number of allocated and usable blocks.

You cannot really compare these views as you are trying.

So, to answer the "who allocated the rest" -- the answer is "no one, you have
not allocated them as yet!!!!"

      Rating:
      5
  May 08, 2002
Reviewer:
      A reader
Tom,

Your answer cleard my all doughts.

Next question.

Do there any relationship between SORT_AREA_SIZE parameter values and TEMPORARY
TABLESPACE extent size?

E.g (if we have 1 Meg of sort_area_size then INITIL and NEXT extend should be
also 1 Meg.)

Please cleary this.

Thanks

          Followup:

Yes there is (a relationship)
Yes you should -- your extent size in temp should be a multiple of the
sort_area_sizes you use.

      Rating:
      5
  May 08, 2002
Reviewer:
      A reader
Tom,

Thanks for the quick response.
We are using oracle 8.1.6 in solaris env.

We have around 200 users and TEMP TABLESPACE size is 1 GB.
We are getting ora-01652

We are having this issue in production enviornment.

We are using SIEBEL CRM.

Our dba is telling us that oracle is not releasing TEMP SPACE after doing
sort.(???)

and SIEBEL is saying there is bug for this in release 8.1.6
fot the sort stuff..

According to your response oracle is reusing space (i.e temp segment will stay
SAME or it will GROW and if we reboot instance SMON will deallocate space from
temp segment)

Please respond quickly.

Thanks.

Please reply quickly

          Followup:

This is just a general comment to all

I am not support.  Support is support and they are the ones to contact for
"urgent issues".  I travel frequently, I spends days away from the office.  When
I'm doing that -- I am obviously not here.  Don't expect or depend or rely on
this forum for things that must be answered urgently.

Now as to the comments here:

1) if Siebel is aware of a specific bug with version816 (which is not a
currently supported release of Oracle, it quite old in fact, and you would be
have hitting this issue for a long long time I would suspect) -- they should
have the bug# then?  If so, supply it -- better yet, use METALINK to look it up
yourself.  Confirm you have the same symptons, see if there is a patch.

2) you can verify if in fact sort space is being freed or not AFTER THE CURSOR
THAT NEEDED IT IS CLOSE.  I just taught you how to do that in a very detailed
fashion.  You have all of the tools you need.

3) it would take less then 4-5meg per user to EASILY EXCEED your temp space --
EASILY.  I used 3+ meg of temp space just to SORT ALL_OBJECTS.  Take alook at
some of those queries being run -- betcha you'll find some of them use QUITE A
BIT of sort space.

1 gig for 200 users seems small. For example, I've got 1.5gig+ in an autoextent
temp tablespace and my sessions highwater is 76 currently (web site, only
connected when generating a page, thousands of users -- 76 max degree of
concurrent access).  It only take ONE BIG SORT to get in the way.  Get one user
using 1/2 of the space and all of a sudden the amount of sort space / user goes
waaayyyy down.

I've given you all of the tools you need to identify

a) if you have a problem
b) how to resolve the problem.  (frankly, if Siebel says "there is a bug" and
then doesn't tell you the patch or bug number -- hmmmmmm  why wouldn't they?
Apparently if there is a specific bug, they have hit it and would now all about
it right?)

Use v$sort_usage to see what is really going on.  Again 1gig = pretty darn small
for 200 users with a CRM app.  I can imagine ONE user could be using all of it
easily.

      Rating:
      5
  May 08, 2002
Reviewer:
      A reader
Tom,

Sorry for the this.

But your comment help us to solve our problem.

Again, THANKS YOU VERY VERY MUCH.

      Rating:
      3
Sagi  May 09, 2002
Reviewer:  sagi
       from india
Hi Tom.

when is say:

SQL> set autotrace traceonly statistics;
SP2-0618: Cannot find the Session Identifier.  Check PLUSTRACE role is enabled
SP2-0611: Error enabling STATISTICS report
SQL>

What does this mean and where do i find help on SP2 errors?

REgards,

          Followup:

see

http://asktom.oracle.com/~tkyte/article1/autotrace.html
for howto setup autotrace.

see

http://otn.oracle.com/docs/products/oracle9i/doc_library/901_doc/server.901/a88827/apa.htm
for messages/codes.

      Rating:
      4
Reader  May 09, 2002
Reviewer:
      A reader
From the Concept Manual 8.1.7

"
You can also change a tablespace from PERMANENT to TEMPORARY or vice versa
by using the ALTER TABLESPACE statement for any temporary tablespace (either
locally managed or dictionary-managed).
"

However the tablespace created as "Create temporary tablespace .."
can not be changed to permanent. Is this because of the
special characteristics of tempfile

Thanks

May 09, 2002 -- Thanks for the question regarding "check PGA
      usage at OS level (UNIX/LINUX)", version 8.1.7

      You Asked
Hi Tom

I am currently trying to figure our PGA usage from some users, I can obtain from
v$sesstat views but it changes all the time. In Solaris I can use pmap to track
real memory usage, do you know any other tool for linux and hp-ux?

cheers

      and we said...
No I do not -- you would have to ask linux and hp-ux admins that one.  I don't
use those OS's too much.

v$sesstat would be MY view of choice in this case!  It changes as fast as the OS
level views would!!

I'll publish this - I'm sure someone out there reading it will post their tool
of choice on linux/hp
Review & Followup

      Rating: 5
Hope not too
      late   June 20, 2003
Reviewer:  Wayne Zhu
       from Philadelphia, PA
For UP-UX, try glance (if installed)
[which may not be as handy as pmap on Solaris or svmon (or 'ps vg') on AIX,
especially if one needs to know the whole memory of Oracle processes]

For Linux, view the file /proc/<pid>/status

Thanks Tom!

May 09, 2002 Bo -- Thanks for the question regarding "To Index
      or Not To Index", version 8.1.6

      You Asked
Hi, Tom.
I have a question about about whether using index or not.
I have several tables that containing more than 20 million records each.  Some
of them have more than 100 columns.  In addition, we need to update these tables
each month.  The process includes delete partial records (vary from 400,000 to 7
million each month), and load updated data into these tables from text file with
SQL*Loader.

I have created index for each of these table on the key column.  For the
updating process, I delete the old data with using index, and then drop index,
and load data with SQL*Loader.  After that, I have to recreate index for
performance consideration.  The process (especially the index recreation) is
very very time consuming.

Could you please suggest some time saving solution?

Thanks.

      and we said...
o do you do the index creation in parallel (parallel N)
o do you adjust the sort_area_size up in the session doing the index creation
o do you create the index in an unrecoverable fashion

All of these will help you in most cases.
May 09, 2002 Branka -- Thanks for the question regarding
      "Create INDEX", version 8.1.7

      You Asked
What is best solution for indexes if I have lots of query like:

1)
select * from t where a1=:x and a2=:y;
select * from t where a1=:x;
select * from t where a2=:y;

To make one index T(a1,a2)
Or to make two indexes T1(a1) and T2(a2)

2)
select a1,a2 from t;
select a1 from t;
select a2 from t;

To make one index T(a1,a2)
Or to make two indexes T1(a1) and T2(a2)

      and we said...
1) see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1409668892797
In 9i, it could be that either:

t(a1,a2)

OR

t(a2,a1)

would be all you need to satisfy all three queries using an index skip scan.

In 8i, if it was appropriate to use an index for all three queries (a1 and a2
are selective enough) you would typically have:

t(a1,a2) and t(a2) indexed OR
t(a2,a1) and t(a1) indexed

The concatenated index is good for "where a1=:x and a2=:y" and "where a1=:x",
the single is used for "where a2=:y".

So -- the answer is "none of the above" in this case.  I would use one or two
indexes but not two single column indices (although I suppose we could do an
index join in the two single ones...)

2) One index on t(a1,a2) IF AND ONLY IF either of a1/a2 is NOT NULL and table T
is very very "wide".

If both a1/a2 are nullable -- the index cannot be fast full scanned to answer
these queries.

If T is not really wide, having the index to fast full scan isn't buying you
enough to warrant its existence.
May 09, 2002 Irina
      -- Thanks for the question regarding "ODBC in PL/SQL", version 8.1.6

      You Asked
Dear Tom,
Thank you for your previous answer.
My new qustions are:
1. About ODBC in PL/SQL. Can you advise to use Heterogeneous
Services for PL/SQL access
   to Sybase database? Are the packages (DBMS_HS,
DBMS_HS_PASSTHROUGH) etc. included
   in Oracle8i Enterprise Edition Release 8.1.6.1.0 or
Heterogeneous Services
   must be installed separately?
2. "CURRENT_USER", contination. Please, show example of program,
in which
   sys_context('USERENV','CURRENT_USER') and
sys_context('USERENV','SESSION_USER')
   will return different results.
3. CASE. Expression CASE works very good in SQL, but it is not
compiled in PL/SQL,
   (it works only in execute immediate), is it my error, or the
fact?

      and we said...
1) the heterogenous services are core routines used by our
gateways.

http://www.oracle.com/gateways
you still need a gateway to sybase in order to make use of them.
 They will make Sybase appear to be "Oracle" and PLSQL will talk
to sybase using a database link, just as if it were another
Oracle instance.

2) Here is an example.  I'll show the difference you get from an
Invokers right and definers rights routine.  The IR routine, the
first time through, shows the session and current user to be the
same.  When we call the DR routine, the session user becomes the
owner of the procedure.  When the DR routine calls the IR
routine, the session user remains the definer of the DR routine
since the privs are those of the definer (calling a DR routine
from an IR routine switches users)

ops$tkyte@ORA8I.WORLD> create or replace procedure ir_routine(
p_call_dr in boolean default false )
  2  authid current_user
  3  as
  4      l_cuser varchar2(50);
  5      l_suser varchar2(50);
  6  begin
  7      select sys_context( 'userenv', 'current_user' ),
  8             sys_context( 'userenv', 'session_user' )
  9        into l_cuser, l_suser
 10        from dual;
 11
 12      dbms_output.put_line( 'IR routine' );
 13      dbms_output.put_line( 'Current User = ' || l_cuser );
 14      dbms_output.put_line( 'Session User = ' || l_suser );
 15
 16      if( p_call_dr)
 17      then
 18          execute immediate 'begin ops$tkyte.dr_routine;
end;';
 19      end if;
 20  end;
 21  /

Procedure created.

ops$tkyte@ORA8I.WORLD> show err
No errors.
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> create or replace procedure dr_routine
  2  authid definer
  3  as
  4      l_cuser varchar2(50);
  5      l_suser varchar2(50);
  6  begin
  7      select sys_context( 'userenv', 'current_user' ),
  8             sys_context( 'userenv', 'session_user' )
  9        into l_cuser, l_suser
 10        from dual;
 11
 12      dbms_output.put_line( 'DR routine' );
 13      dbms_output.put_line( 'Current User = ' || l_cuser );
 14      dbms_output.put_line( 'Session User = ' || l_suser );
 15
 16      ir_routine;
 17  end;
 18  /

Procedure created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> grant execute on ir_routine to scott;

Grant succeeded.

ops$tkyte@ORA8I.WORLD> grant execute on dr_routine to scott;

Grant succeeded.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> @connect scott/tiger
ops$tkyte@ORA8I.WORLD> connect &1
Connected.
ops$tkyte@ORA8I.WORLD> @login
ops$tkyte@ORA8I.WORLD> set echo off
scott@ORA8I.WORLD>
scott@ORA8I.WORLD> exec ops$tkyte.ir_routine(true);
IR routine
Current User = SCOTT
Session User = SCOTT
DR routine
Current User = OPS$TKYTE
Session User = SCOTT
IR routine
Current User = OPS$TKYTE
Session User = SCOTT

PL/SQL procedure successfully completed.

3) Yes, there are SQL features that are not available in PLSQL
-- there are a couple of cases like that:

o select ( select count(*) from dual ) from dual;
o select * from ( select dummy from dual order by dummy );
o select * from dual group by rollup( dummy );
o select * from dual group by cube( dummy );
o select row_number() over ( order by dummy ) from dual;

in addition to case to name a few.  You must either use dynamic
sql or hide this functionality in a view.

In Oracle9i, PLSQL will be using the same SQL parser as the
kernal so new SQL functionality will be immediately avaiable in
PLSQL.

Review &
      Followup

      Rating: 5
question for
      plsql  March 20, 2001
Reviewer:  dipsinh
      r.parmar  from valsad

      Rating: 5
3.question -
      CASE  March 20, 2001
Reviewer:  Helena
      Markova  from Bratislava, Slovakia

      Rating: 3
Learning by
      accident?  October 23, 2001
Reviewer:  Marc
      Blum  from Aachen, Germany
hey, I searched all answers concerning CUBE and ROLLUP, and stumbled over the
possibility to access an ODBC-datasource from PL/SQL. Not that I'm a fan of
ODBC, but, hey that's real life...

      Rating:
      3
DBMS_HS  May 09, 2002
Reviewer:
      Pushparaj Arulappan  from New Jersey, USA
Tom,

We are currently having Oracle8i Enterprise Edition Release 8.1.7.3.0 and Oracle
Procedural Gateway for APPC
Version 8.0.6.1.0 for HP-UX. So we have been using Procedural Gateway for
Sending and Receiving data from DB2
database.

Can Oracle8i HS (using DBMS_HS and other related HS packages) be used to connect
to DB2 database and query
against DB2 tables ?
can this service work as a transparent gateway so that we
can replace the current Procedural Gateway architecture.

I read the documentation about the Oracle8i Heterogeneous Service but I am still
not clear if this can work as a transparent Gateway to communicate with DB2.
I have checked in my database and we do installed all this HS related packages.

We are ready change our current architecture if this HS can help to communicate
with Mainframe ?

Please advice me?
Thanks
Pushparaj

          Followup:

The PG4APPC is very specialized software.  You ahve APPC connectivity (not
tcp/ip) between your unix box and the mainframe.  You are interacting with CICS
transactions -- not SQL.

If you have an ODBC driver to DB2 on the mainframe, yes you could use the
generic ODBC HS services HOWEVER -- the PG4APPC is designed for high volume,
FAST, efficient access to optimized CICS transactions.  ODBC to DB2 on a
mainframe -- it has none of those qualities.

May 09, 2002 Penchal -- Thanks for the question regarding "How
      to ignore the unwanted data, loading from XML to a table using DBMS_XML
      Package.", version 8.1.7.3.0

      You Asked
Hi,
The following XML file is in CLOB of xml_table.
<?xml version='1.0'?>
<ROWSET>
  <ROW num="1">
    <EMPNO>1001</EMPNO>
    <ENAME>TOM</ENAME>
    <DEPTNO>10</DEPTNO>
    <DNAME>NEWYORK</DNAME>
  </ROW>
  <!-- additional rows ... -->
</ROWSET>

The following Procedure loads data into emp table.

declare
  insCtx_emp  DBMS_XMLSave.ctxType;
  doc clob;
  rows_emp number;
begin
   insCtx_emp := DBMS_XMLSave.newContext('EMP');
   DBMS_XMLSave.clearUpdateColumnList(insCtx_emp);
   DBMS_XMLSave.setUpdateColumn(insCtx_emp,'EMPNO');
   DBMS_XMLSave.setUpdateColumn(insCtx_emp,'ENAME');
   DBMS_XMLSave.setUpdateColumn(insCtx_emp,'DEPTNO');
   select filnam into doc from xml_documents where id =2;
   rows_emp := DBMS_XMLSave.insertXML(insCtx_emp, doc);
   DBMS_XMLSave.closeContext(insCtx_emp);
end;
/
Why I'm getting the following error, even after mapping the columns to the emp
table.

declare
*
ERROR at line 1:
ORA-29532: Java call terminated by uncaught Java exception:
oracle.xml.sql.OracleXMLSQLException: Exception ' The XML element tag 'DNAME'
does not match the name of any of the columns/attributes of the target database
object.' encountered during processing ROW element 0All prior XML row changes
were rolled back. in the XML document.
ORA-06512: at "RAPIDXML.DBMS_XMLSAVE", line 67
ORA-06512: at line 18

Can you please help how to solve the above problem.
I want to insert only the necessay data into table.

Thanks & Regards
Reddy.

      and we said...
I asked Sean Dillon, our local XML guru, to take a look at this and here's what
he had to say:
--

Hi Reddy,

The reason you are getting this error is because you have an element in your XML
document DNAME which is not in the EMP table.  The underlying Java code in the
XDK is trying to map this element to a column in the table, and therefore throws
the Java exception when it doesn't find a matching column (which then, of
course, propagates to PL/SQL).

The easiest solution to this problem is to install the latest XDK available on
the XML pages of Oracle Technet (
http://otn.oracle.com/tech/xml/content.html
.  In the 9.0 release of the XDK, this
issue is resolved.  It simply ignores any columns in your source XML document
that are not columns in the table you are inserting into:

SQL> connect sdillon/sdillon
Connected.
SQL> create table emp as select * from scott.emp where 1=0;

Table created.

SQL> declare
  2    l_ctx  dbms_xmlsave.ctxtype;
  3    l_rows pls_integer;
  4    l_clob clob;
  5    l_xml  varchar2(300) :=
  6  '<?xml version=''1.0''?>
  7  <ROWSET>
  8    <ROW num="1">
  9      <EMPNO>1001</EMPNO>
 10      <ENAME>TOM</ENAME>
 11      <DEPTNO>10</DEPTNO>
 12      <DNAME>NEWYORK</DNAME>
 13      <SURELY_AN_INVALID_COLUMN>NEWYORK</SURELY_AN_INVALID_COLUMN>
 14    </ROW>
 15  </ROWSET>';
 16
 17  begin
 18    dbms_lob.createtemporary(l_clob,true,dbms_lob.session);
 19    dbms_lob.write(l_clob,length(l_xml),1,l_xml);
 20    l_ctx := dbms_xmlsave.newcontext('EMP');
 21    l_rows := DBMS_XMLSave.insertXML(l_ctx, l_clob);
 22    dbms_xmlsave.closeContext(l_ctx);
 23  end;
 24  /

PL/SQL procedure successfully completed.

SQL> select * from emp;

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM
DEPTNO
---------- ---------- --------- ---------- --------- ---------- ----------
----------
      1001 TOM
  10

On lines 12 & 13, you'll see the elements "DNAME" and "SURELY_AN_INVALID_COLUMN"
which are not a part of the EMP table. When the insertXML call is made, success!
 You'll also notice I didn't have to use the "clearUpdateColumnList" or
"setUpdateColumn" procedures when performing an INSERT.  This is only necessary
when performing updates (even in the 8.1.7 version of the XDK, which you're
running).

IF YOU CAN'T INSTALL THE LATEST XDK (even though it's what I would advise above
all else ;-), you can use XSLT to weed out any columns that aren't in your
target table.  You would use the XSLPROCESSOR package to do this in your PL/SQL
block.  Once again, though, I think performing an XSL transformation on your XML
is a work around to installing the latest production release of the XDKs (did I
mention they're free?! ;-).  This ASKTOM question has an example of doing this:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1827993798549
_______________________________________________________
Sean Dillon - Senior Technologist, Oracle Corporation
Author "Beginning Oracle Programming"
http://www.amazon.com/exec/obidos/ASIN/186100690X

Review
      & Followup

      Rating: 4
Unauthorized
      link  May 08, 2002
Reviewer:  A reader

Hi,

I can not go into th link above, it requires username
and password?

Thanks,

          Followup:

There are three links above.

The first one for OTN requires an account -- it should say that when you try,
they are free, they are immediate, they are something every Oracle developer
should have.

The other two are "open", no passwords required

      Rating:
      5
Still  May 08, 2002
Reviewer:
      A reader
Hi,

I stil have the problem for the link:

http://asktom.oracle.com/pls/flows/f?p=4950:8:::::F4950_P8_DISPLAYID:1827993798549
of course, you book are there with no doubt.

and here is the error message:

Authorization Required
This server could not verify that you are authorized to access the document
requested. Either you supplied the wrong credentials (e.g., bad password), or
your browser doesn't understand how to supply the credentials required.

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

Oracle HTTP Server Powered by Apache/1.3.19 Server at asktom.oracle.com Port 80

Thanks and have a nice evening

          Followup:

Ahh, ok - now I see.  Sean accidently put our "author" link in there. I fixed
it.

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1827993798549
is the public link.

      Rating: 5
Is it possible
      to insert into multiple tables   May 09, 2002

Reviewer:  Lakshmi narasimhan R  from Dubai, UAE

Through the XML parser is it possible to insert into
multiple tables like what we used to do through sql loader.

from the following

<?xml version='1.0'?>
<ROWSET>
  <ROW num="1">
    <EMPNO>1001</EMPNO>
    <ENAME>TOM</ENAME>
    <DEPTNO>10</DEPTNO>
    <DNAME>NEWYORK</DNAME>
  </ROW>
  <!-- additional rows ... -->
</ROWSET>

if we have to insert empno,ename,deptno in emp table and deptno and dname in
dept table how we need do.

thanks in advance

          Followup:

Hi, Sean here...

DBMS_XMLSave doesn't have a native option for inserting into multiple tables in
a single pass, I would accomplish this using a view and an instead-of trigger:

SQL> create table emp as select * from scott.emp where 1=0;

Table created.

SQL> create table dept as select * from scott.dept where 1=0;

Table created.

SQL> create or replace view deptemps as
  2  select e.empno, e.ename, e.deptno, d.dname
  3    from emp e, dept d
  4   where d.deptno = e.deptno;

View created.

SQL> create trigger deptemps_bifer
  2  instead of insert on deptemps
  3  for each row
  4  begin
  5    insert into dept (deptno, dname)
  6    values (:new.deptno, :new.dname);
  7    --
  8    insert into emp (empno, ename, deptno)
  9    values (:new.empno, :new.ename, :new.deptno);
 10  end deptemps_bifer;
 11  /

Trigger created.

SQL> show errors
No errors.
SQL> declare
  2    l_ctx  dbms_xmlsave.ctxtype;
  3    l_rows pls_integer;
  4    l_clob clob;
  5    l_xml  varchar2(300) :=
  6  '<?xml version="1.0"?>
  7  <ROWSET>
  8    <ROW num="1">
  9      <EMPNO>1001</EMPNO>
 10      <ENAME>TOM</ENAME>
 11      <DEPTNO>60</DEPTNO>
 12      <DNAME>TECH_TEAM</DNAME>
 13      <SURELY_AN_INVALID_COLUMN>NEWYORK</SURELY_AN_INVALID_COLUMN>
 14    </ROW>
 15  </ROWSET>';
 16
 17  begin
 18    dbms_lob.createtemporary(l_clob,true,dbms_lob.session);
 19    dbms_lob.write(l_clob,length(l_xml),1,l_xml);
 20    l_ctx := dbms_xmlsave.newcontext('DEPTEMPS');
 21    l_rows := DBMS_XMLSave.insertXML(l_ctx, l_clob);
 22    dbms_xmlsave.closeContext(l_ctx);
 23  end;
 24  /

PL/SQL procedure successfully completed.

SQL> select * from dept;

    DEPTNO DNAME          LOC
---------- -------------- -------------
        60 TECH_TEAM

SQL> select * from emp;

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM
DEPTNO
---------- ---------- --------- ---------- --------- ---------- ----------
----------
      1001 TOM
  60

      Rating: 5
parsing XML and
      loading the data to a table  May 09, 2002

Reviewer:  nagaraju
tom,

i don't know whether the following question is simular to the above one or not.
i am asking this as my requirement also needs an XML string to parsed and loaded
into a table.

if this is question is not relevant, pl. ignore..

 we have an application comprises set of ASP pages that connect to ORACLE DB.
 each ASP page passes some number of hidden form variables. as these number of
variables is increasing.
 we thought we will store them in DB get an id for that and pass that id across
the ASPs get it from DB rather passing them for
 security and performance reasons. we have table that stores this info with
structure something like

 id
 param_name
 param_value

 now it is fine, when we retrieve these parameter values from DB it is fine
because it is only one call to db.
 but when we first pass these details, if have 50 parameters 50 calls we need to
make. we are thinking of an option creating procedure
 with input parameter as long/clob type, now parse that input insert into the
above table.

 1. now is this actually good way or it is better we go a head with passing
params through the pages?
 2. we are thinking of sending an XML to the above procedure that accepts
long/clob, but does pl/sql has built-in packages to parse XML
    and write to a table or do i have to manually parse and insert

 any suggestions/comments

thanks

          Followup:

You would just pass them as 50 parameters to a stored procedure -- one call, no
muss, no fuss.  Don't "xml" them -- not needed, just call a stored procedure.

May 09, 2002 Marc
      -- Thanks for the question regarding "How do I retrive vallues from LONG
      RAW's in 7.3.4 when there is no pakage!", version 7.3.4

      You Asked
Hi there,

I would like to retrieve x,y,z coordinates out of a LONG RAW
stored in an Oracle 7.3.4 table (LandMark OpenWorks Database).

There don't seem to be any packages available to do this, as in
Oracle 8.

Please could you sugest an alternate method of retrieving values
from LONG RAW's, that does not include and C programming.

The table looks like this:

GEOM_POLYLINE_VEC

VID:              INTEGER NOT NULL
BLK_NO:           INTEGER NOT NULL
DATATYPE:         NUMBER NULL
VEC_STORAGE_TYPE: VARCHAR2(12) NOT NULL
BYTES_USED:       INTEGER NULL
DATA: LONG        RAW NULL
OW_REL_PATH:      VARCHAR2(80) NULL
ORACLE_LOB:       VARCHAR2(12) NULL
ORACLE_BFILE:     VARCHAR2(12) NULL

COLUMN NAME NULLS DATA TYPE K REFERENCES NOTES DEFINITION

VID N NUMBER(,0) P n/a This is the system-assigned unique
identifier for a polyline vector.

BLK_NO N NUMBER(,0) P n/a This is a number specifying the
sequential order for a data vector when multiple data vectors
are required for the polyline points.

DATATYPE Y NUMBER(,) n/a The data type of the array elements. 1
= Character, 2 =Long, 3 = Float, 4 = Double, any other value =
void or undefined.

VEC_STORAGE_TYPE N VARCHAR2(12) n/a D=LRAW The  mechanism of
storage for the curve values vector. Values include LRAW =
oracle long raw ,LOB = oracle 8 large object (LOB) ,BFILE =
oracle 8 binary file , OW_FILE = file managed by OpenWorks
outside of oracle,
HEADER_ONLY= no trace data online., EXTERNAL = external log
catalog entry only.

BYTES_USED Y NUMBER(,0) n/a This is the actual number of bytes
of data in the data vector.

DATA Y LONG RAW(0) n/a These are the actual x, y, z triplets
defining a polyline stored in Binary Large OBject (BLOB) format.

OW_REL_PATH Y VARCHAR2(80) n/a Relative path below the project
flat file locations for vectors stored as external files. By
default this path will be generated as /VID_N_TO_M/VID where N
is the largest multiple of 250 less than VID and M is the N +
249.

ORACLE_LOB Y VARCHAR2(12) n/a Once we migrate to oracle 8 this
field will be used when storage_type = V

ORACLE_BFILE Y VARCHAR2(12) n/a Oracle BFILE to hold the vector
data when storage type is BFILE.

The max number of bytes used is 1536

Cheers and Thanx

MArc

      and we said...
We can use just regular PLSQL for this then.  As long as the
LONG RAW remains under 32k, we can do everything we need inside
of PLSQL.

Simply:

declare
  l_rec GEOM_POLYLINE_VEC%rowtype;
begin
  select * into l_rec from GEOM_POLYLINE_VEC where rownum=1;
end;
/

and it'll work.  You can use cursor for loops, whatever you need
to access the data.  You'll have a l_rec.data component that has
the RAW data in it.

You might want to look at the UTL_RAW package -- has lots of
functions to manipulate RAW data.  It might not be installed but
comes with the server from 7.1.6 on up.  If not installed --
goto:

cd $ORACLE_HOME/rdbms/admin
svrmgrl
connect internal OR sys (only these users may install this)
@utlraw
@prvtrawb.plb

and that will install it.

Review &
      Followup

      Rating: 4
How about just
      ....  May 09, 2002
Reviewer:  Lisa
       from Tx
I have a table which like this:
SQL> desc api_blob_item
 Name                                      Null?    Type
 ----------------------------------------- -------- --------------
 ENTITY_NO                                 NOT NULL NUMBER
 ISSUE                                     NOT NULL NUMBER
 BLOB                                               LONG RAW

I just want to know the length for the BLOB column, and I did the length check
with the following sql and got the error. How do I do this ? Thanks in advance.

SQL> select length(blob) from api_blob_item;
select length(blob) from api_blob_item
              *
ERROR at line 1:
ORA-00932: inconsistent datatypes

          Followup:

Umm, no that'll not work.  Please read my response above which shows how to do
this WHEN the long raw is 32k or less (above 32k -- no go, you cannot do it)

May 09, 2002 gaurang -- Thanks for the question regarding
      "CURSOR", version 8.1.6

      You Asked
Hi Tom,

I have question regarding application which users oracle.

We are using SIEBEL CRM.

 If  i run query from application like give me all account in ascending order.
And it will take some time and return rows.

 By default we can see 20 Record on an applet.If we want to see more record we
need to scroll down.

 Here is the question ..

  1)How long the cursor will be opened?( I become idal after issuing query)
  2) If it is opened for a long time, will it consume server resource.
  3)If we are doing sort operation, will it lock temp segment space.

  4)How application fetch data from database. (eg. Is there any array size
defined for applications like what we have in SQLPLUS?)

You replied my question regarding ora-01625. and it has been resolved by
implementing your suggestions

Thanks.

      and we said...
1) the cursor stays open until
   a) the session is ended
   b) the programmer closes the cursor programmatically in the session

2) sure it can.  For example see:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:956629961318
and search for the string:
     You were right upto the

on that page.  There I show an example of a cursor that remains open from call
to call.  It shows that for example the SORT SPACE needed by the query will be
maintained

3) see #2.  It will not "lock" the temp segment.  It will have ALLOCATED and
will be USING *part of* the temp segment.  Think about it -- that is what temp
is all about, what it is for.  A place to temporarily store results whilst you
are cursoring through them.

4) yes.  search for setRowPrefetch on this site.

Thanks for the feedback -- so, I guess that means Siebel wasn't able to give you
a "bug # for that 816 bug (that doesn't exist)" and in fact, you had
insufficient  temp space allocated for your given concurrent user load....

Review &
      Followup

      Rating:
      5
  May 09, 2002
Reviewer:  gaurang

Tom,

Thanks for valuable response.

I have performed following test and check values in v$sort_usage.

I have a table which has more then a million rows.

I have opened four SQLPLUS sesion and running this query from all four session

 select count(*),cust_no from ORDER group by cust_no

after some record has been fetch(e.g 5000 Record)  i am canceling the query
from all SQLPLUS(4) session by file --> CANCEL.

and i check result in V$SORT_USAGE.

I saw four rows in V$SORT_USAGE table. I am idal for 15 minutes. and check again
in V$SORT_USAGE table.Still there is four rows.

Now from One of the SQLPUS session i issue query

SELECT * FROM DUAL;(you can issue any query)

I am checking in V$SORT_USAGE, now there is three rows.

Perform above step in another session .

Check in V$SORT_USAGE, now there is two rows.

My question here is
1) If I CANCEL query from sqlplus, why a row still exists in v$sort_usage
table?. If i issue next sql query the row is gone from v$sort_usage for that
session.

2)One of the siebel consultant told us if problem appers again (ora-01562) MAKE
TEMP TABLESPACE AS PERMENENT TABLPSACE.
Why??

3)Should loogging be OFF for TEMP tablespace?

Hope this is not too much from me.

Thanks.

          Followup:

1) sqplus didn't close the cursor -- that is why.  nothihng wrong there.

2) they want your system to run reeeeaaaaalllllyyyy slow i guess.  Don't ask a
CRM consultant how to fix a database.

3) temporary tablespaces are alreadying in that mode.

      Rating:
      5
  May 09, 2002
Reviewer:
      A reader
OK Tom,

Now i have another question.

What are the advantage and disadvantage if i make
SORT operation in PERMENANT TABLESPACE.?

You told this will make database operation very solw.

My guess is if it is the case(slow) SORT segment will deallocate extent
immedaitely after SORT if you use PERMENANT tablespace(Which is done by SMON at
the time of reboot in the case of temp tablespace).

am i right?

Thanks.

          Followup:

Allocation of space (getting and releasing of extents) is sloooowwww.  It is to
be avoided....  It would be a really truly terrible idea.  It is why we have
TEMPORARY  tablespaces in the first place.

You are not hitting a bug.  Just keep saying that over and over.

You are experiencing normal things....

You have configured insufficient temporary space for your system.  Add more.

Temp space will be used exclusively by your session for the same duration if you
use a permenant tablespace or a temporary tablespace.  Your system will just run
slower with a permanent tablespace used for temp stuff.

May 09, 2002 Rui
      -- Thanks for the question regarding "UTL_FILE don´t write all text",
      version 8.1.6

      You Asked
Hello again
I´m the office now, and review my question. There´s about Triggers !
Here is a extract of my code to write the trigger in a text file.
The final part of loop(r_posicao) is because the slash (/) put in the end of
file stay with a left space, and because of that when I run the "my_trigger.trg"
they don´t start. If you remove this part and can help me, I appreciate.
If I execute this script to write all triggers of a table, then have many code
inside (like the generated by Table API of Oracle Designer), the code is
incomplete, like I said yesterday.

set serverout on
set long 32000000
declare
        cursor trig is select distinct table_name
                                from user_triggers
                                order by table_name;
        cursor trig_code(tabela in user_triggers.table_name%type) is
                                select table_name, trigger_name, trigger_type,
triggering_event, referencing_names
                                from user_triggers
                                where   table_name = tabela
                                order by trigger_name;

        l_cursor        integer;
        l_n             number;
        l_long_val      varchar2(32767);
        l_long_trab     varchar2(32767);
        l_long_len      number;
        l_curpos        number;
        l_query         varchar2(2000);

        r_nome_file     varchar2(100);
        r_nome_executar varchar2(100);
        r_path          varchar2(100) := 'c:\temp';
        r_file          utl_file.file_type;
        r_sql           utl_file.file_type;
        r_wrap          utl_file.file_type;
        r_separador     varchar2(1);
        r_posicao       number;
        r_parte1        varchar2(50);
        r_parte2        varchar2(50);
        r_trigger_name  user_triggers.trigger_name%type;
        r_table_name    user_triggers.table_name%type;

begin
        r_sql := utl_file.fopen(r_path, 'runall.sql', 'w');

        for cc1 in trig loop
                r_table_name := cc1.table_name;

                -- Compoe o Nome do File a criar
                r_nome_file := cc1.table_name||'.TRG';

                -- Cria o File para a Source do Objecto em tratamento
                r_file := utl_file.fopen(r_path, r_nome_file, 'w');
                if utl_file.is_open(r_file) then
                        -- Adiciona a View à Lista a executar
                        utl_file.put_line(r_sql, 'PROMPT Criar Triggers da
Tabela '||cc1.table_name);
                        utl_file.put_line(r_sql, 'start
'||r_path||'\'||r_nome_file);
                        utl_file.put_line(r_sql, '/');

                        -- Grava Triggers da Tabela
                        for cc2 in trig_code(cc1.table_name) loop
                                r_trigger_name := cc2.trigger_name;

                                r_posicao := instr(cc2.trigger_type, ' ');
                                r_parte1 := substr(cc2.trigger_type, 1,
r_posicao);
                                r_parte2 := substr(cc2.trigger_type, r_posicao +
1);
                                if r_parte2 = 'STATEMENT' then          --
Triggers de STATEMENT
                                        r_parte2 := null;
                                elsif r_parte2 = 'EACH ROW' then        --
Triggers de ROW
                                        r_parte2 := 'FOR EACH ROW';
                                elsif r_parte2 = 'OF' then              --
INSTEAD OF
                                        r_parte1 := r_parte1||' OF ';
                                        r_parte2 := null;
                                end if;

                                utl_file.new_line(r_file);
                                utl_file.put_line(r_file, 'create or replace
trigger '||cc2.trigger_name);
                                utl_file.put_line(r_file,
r_parte1||cc2.triggering_event||' ON '||cc2.table_name);
                                utl_file.put_line(r_file,
cc2.referencing_names);
                                if r_parte2 is not null then
                                        utl_file.put_line(r_file, r_parte2);
                                end if;

                                l_query := 'select trigger_body from
user_triggers where trigger_name = '||''''||r_trigger_name||'''';
                                l_curpos := 0;
                                l_cursor := dbms_sql.open_cursor;
                                dbms_sql.parse(l_cursor, l_query,
dbms_sql.native);
                                dbms_sql.define_column_long(l_cursor, 1);
                                l_n := dbms_sql.execute(l_cursor);
                                if dbms_sql.fetch_rows(l_cursor) > 0 then
                                        loop
                                                dbms_sql.column_value_long(c =>
l_cursor,

position => 1,

length => 32767,

offset => l_curpos,
                                                                           value
=> l_long_val,

value_length => l_long_len);
                                                if l_long_len > 0 then
                                                        r_posicao :=
instr(l_long_val, ';', -1, 1);
                                                        if r_posicao > 0 then
                                                                l_curpos :=
l_curpos + r_posicao;

utl_file.put(r_file, substr(l_long_val, 1, r_posicao));
                                                        else
                                                                if
rtrim(l_long_val) != '' then

utl_file.put(r_file, l_long_val);
                                                                else

utl_file.new_line(r_file);
                                                                end if;
                                                                l_curpos :=
l_curpos + l_long_len;
                                                        end if;
                                                end if;
                                        exit when l_long_len = 0;
                                        end loop;
                                end if;
                                utl_file.put_line(r_file, '/');
                                dbms_sql.close_cursor(l_cursor);
                        end loop;
                else
                        dbms_output.put_line('Erro ao criar file
'||r_nome_file||' no disco');
                end if;

                -- Fecha o ficheiro da Source
                utl_file.fclose(r_file);
        end loop;

        utl_file.fclose(r_sql);
end;
/

Thanks
Rui Brito

      and we said...
Well, personally I find the concept of a trigger with 32k of code horrifying.  I
cannot IMAGINE having that much code in a single routine.  I have a rule I
follow -- the code in a routine must be visible from top to bottom on a single
screen.  If it is bigger then that, it is neither understandable nor
maintainable.  It needs to be broken up into tiny chunks in a package somewhere.
 So, with that in mind I hope you don't mind -- I made a couple of bug fixes and
optimizations in the code (and made it so the code in the begin/end fits on a
screen).

This'll work for you.  It appears to be an issue with the linesize and such.  In
any case, this routine does it (and it uses BIND VARIABLES which are sooooo
important, soooo important.  never concatenate in the trigger name in a query
like that, don't parse in a loop like that -- do it once and reuse it over and
over and over)

declare
        l_cursor        integer := dbms_sql.open_cursor;
        l_n             number;
        l_long_val      varchar2(32767);
        l_long_len      number;
        l_curpos        number;
        r_path          varchar2(100) := '/tmp/test/';
        r_file          utl_file.file_type;
        r_sql           utl_file.file_type;
        l_last_table    varchar2(30) := '*not a table*';
        l_the_rest      varchar2(2000);
begin
    r_sql := utl_file.fopen(r_path, 'runall.sql', 'w');

    dbms_sql.parse(l_cursor, 'select trigger_body
                                from user_triggers
                               where trigger_name = :x', dbms_sql.native);
    dbms_sql.define_column_long(l_cursor, 1);

    for cc1 in (select table_name, trigger_name, triggering_event,
                       referencing_names,
                       decode( substr( trigger_type, 1, 1 ),
                              'A', 'AFTER ', 'B', 'BEFORE ', 'I', 'INSTEAD OF '
)
                       real_trigger_type,
                       decode( instr( trigger_type, 'EACH ROW' ),
                               0, null, ' FOR EACH ROW' ) each_row
                  from user_triggers
                 order by table_name, trigger_name )
    loop
        if ( l_last_table <> cc1.table_name )
        then
            if ( utl_file.is_open(r_file) )
            then
                utl_file.fclose(r_file);
            end if;
            -- Cria o File para a Source do Objecto em tratamento
            r_file := utl_file.fopen(r_path, cc1.table_name||'.TRG', 'w',
32765);
            utl_file.put_line(r_sql, 'PROMPT Criar Triggers da Tabela '||
                                                  cc1.table_name);
            utl_file.put_line(r_sql, 'start '||r_path||cc1.table_name||'.TRG');
            l_last_table := cc1.table_name;
        end if;
        utl_file.new_line(r_file);
        utl_file.put_line(r_file, 'create or replace trigger '||
                                          cc1.trigger_name);
        utl_file.put_line(r_file, cc1.real_trigger_type ||cc1.triggering_event||
                                  ' ON '||cc1.table_name);
        utl_file.put_line(r_file, cc1.referencing_names || cc1.each_row);

        dbms_sql.bind_variable( l_cursor, ':x', cc1.trigger_name );

        l_curpos := 0;
        l_n := dbms_sql.execute(l_cursor);
        if dbms_sql.fetch_rows(l_cursor) > 0
        then
            loop
                dbms_sql.column_value_long(c => l_cursor,
                                           position => 1,
                                           length => 30000,
                                           offset => l_curpos,
                                           value => l_long_val,
                                           value_length => l_long_len);
                l_long_val := l_the_rest || l_long_val;
                loop
                    l_n := instr( l_long_val, chr(10) );
                    exit when nvl(l_n,0) = 0;
                    utl_file.put_line(r_file, substr(l_long_val,1,l_n-1) );
                    l_long_val := substr( l_long_val, l_n+1 );
                end loop;
                l_the_rest := l_long_val;

                l_curpos := l_curpos + l_long_len;
                exit when l_long_len = 0;
             end loop;
         end if;
         utl_file.put_line(r_file, '/');
    end loop;

    if ( utl_file.is_open(r_file) )
    then
        utl_file.fclose(r_file);
    end if;
    utl_file.fclose(r_sql);
    dbms_sql.close_cursor(l_cursor);
end;
/
Review &
      Followup

      Rating: 5
UTL_FILE don't
      write all text  April 10, 2002
Reviewer:
      abeginner  from asia

      Rating: 4
Problem in
      compare the bind variable  May 09, 2002
Reviewer:
      Rui Brito  from Portugal
Thanks Tom
Only today I have time to test your sugestion and I get error
"ORA-06502 numeric or value error: character to number conversion error"
I change your line to dbms_sql.bind_variable( l_cursor, ':x',
''''||cc1.trigger_name||'''') and works, only don´t give me the trigger body.
How can I compare a string, because the trigger_name is a string inside a
statement using bind variable ?
I need to define the bind variable ?

Best
Rui Brito

          Followup:

show me -- use MY code as is.  show me the line that "blows up".  run the block
in sqlplus with SET ECHO ON-- show the exact error

      Rating: 5
You are the
      best !!!  May 09, 2002
Reviewer:  Rui Brito
       from Portugal
Sorry Tom
Your code is correct and work´s wonderful!
I make a mistake copying your code and ...

Sorry again
Rui Brito

May 09, 2002 Kumar -- Thanks for the question regarding
      "executing findstr command through SQLplus Host", version 8.1.6.0.0

      You Asked
Tom,

I running a script through SQLPLUS to update many procedures and functions which
have many dependencies. As a part of my spool files, first I grab into one
spoolfile(say c:\tempproc.log) and then I use "findstr" command (NT) to replace
unwanted test in the spool file. Finally I move the exact log messages into a
separate file(say c:\proc.log)after filtering the unwanted messges.

During this process to achieve the above filtering I use the Findstr (NT)
command to filter the messages like Warning:. Which does the job excellent. See
the below example

C:\>findstr /v Warning: c:\runsql\tempproc.log > c:\runsql\proc.log

Now, If I give the same in my SQL scripts through HOST command the results are
different. It did not generate the output, then I changed with different path (
say c:\findstr .....). Not worked well no filtering happened.(see below)

SQL> host findstr /v Warning: c:\runsql\tempproc.log > c:\runsql\proc.log
.......> no result happened

or
SQL> host c:\findstr /v Warning: c:\runsql\tempproc.log > c:\runsql\proc.log
...........> only proc.log empty file returned but supposed to be a lot output
with other errors there to come.

When I save the same command as cmd file (say fnd.cmd ) and run through sqlplus
host the result is fine.

SQL> host @c:\fnd.cmd

Why this difference is SQLPLUS runs Host layer as something different or did I
make any mistake? I want to run this command as a host command in my scripts
instead of this cmd file.

Note: my fnd.cmd file contains only this line

findstr /v Warning: c:\runsql\tempproc.log > c:\runsql\proc.log

Thanks

      and we said...
host "findstr /v Warning x.log > x.dat"

use quotes.
Review & Followup

      Rating: 5
findstr command
      through SQLPLUS  May 09, 2002
Reviewer:
      Kumar  from San Diego
Excellent, It worked well. May I know the exact reason why that was not worked
without Quotes? ("), but the same is working well if I run through any cmd
command (without Quotes) Does SQLPLUS Layer needs these Quotes for every command
that we use through HOST command?

          Followup:

don't know, for some reason the > is getting "lost".

No, plus doesn't need it for everything, but it doesn't hurt.

May 10, 2002 minnie -- Thanks for the question regarding "How
      to get a date including millisecond in PL/QL", version Oracle 8i 8.1.5

      You Asked
Hi,Tom
Our application needs to get a date value including millisecond
in PL/SQL packages,but the date datatype in Oracle doesn't
support millisecond. Do you have some ideas to solve this kind
of problem?

I appreciate your helps about my previous questions.
I have solved my problems according to your methods.

thank you again.

minnie
software engineer
Mediol online,Inc.

      and we said...
2 solutions:

1) write a java stored procedure.  java has time functionality
that is much more granular then the Oracle DATE type.  This will
work in 8.1.5 and up.  It can look like this:

ops$tkyte@DEV8I.WORLD> CREATE or replace JAVA SOURCE
  2  NAMED "MyTimestamp"
  3  AS
  4  import java.lang.String;
  5  import java.sql.Timestamp;
  6
  7  public class MyTimestamp
  8  {
  9      public static String getTimestamp()
 10      {
 11        return (new
 12           Timestamp(System.currentTimeMillis())).toString();
 13      }
 14  };
 15  /

Java created.

ops$tkyte@DEV8I.WORLD> create or replace function my_timestamp
return varchar2
  2  AS LANGUAGE JAVA
  3  NAME 'MyTimestamp.getTimestamp() return java.lang.String';
  4  /

Function created.

ops$tkyte@DEV8I.WORLD> l
  1* select my_timestamp, to_char(sysdate,'yyyy-mm-dd
hh24:mi:ss') from dual
ops$tkyte@DEV8I.WORLD> /

MY_TIMESTAMP
-----------------------------------------------------------------
-----------------------------------
TO_CHAR(SYSDATE,'YY
-------------------
2000-06-22 13:47:53.376
2000-06-22 13:47:53

ops$tkyte@DEV8I.WORLD>

2) use an external procedure written in C.  C has api's to the
system that allow for much more granular time components.  This
will work in 8.0 and up.  For example if you run something like:

ops$tkyte@DEV8I.WORLD> create or replace library timelib as
  2  '/export/home/tkyte/src/t/extproc.so'
  3  /
Library created.

ops$tkyte@DEV8I.WORLD> create or replace
  2  procedure get_extended_time( p_timestring  out varchar2 )
  3  is external
  4  name "get_extended_time"
  5  library timelib
  6  language C
  7  with context
  8          parameters ( CONTEXT,
  9                       p_timestring     STRING,
 10                       p_timestring     INDICATOR short,
 11                       p_timestring     MAXLEN int,
 12                       p_timestring     LENGTH int );
 13
 14  /

Procedure created.
ops$tkyte@DEV8I.WORLD> declare
  2          l_timestring varchar2(30);
  3  begin
  4          get_extended_time( l_timestring );
  5          dbms_output.put_line(
                   to_char( sysdate, 'mm/dd/yy hh24:mi:ss' ) );
  6          dbms_output.put_line( l_timestring );
  7  end;
  8  /
06/22/00 13:26:28
06/22/00 13:26:28.103243

PL/SQL procedure successfully completed.

In sqlplus after compiling the following C code into a .so or
.dll or .sl (depending on platform) you can get the
milliseconds.

Here is the C code:

#include <stdio.h>
#include <stdarg.h>
#include <time.h>

#ifndef OCI_ORACLE
# include <oci.h>
#endif

#define raise_application_error return raise_application_error_x

static long raise_application_error_x( OCIExtProcContext * ctx,
                                     int errCode,
                                     char * errMsg, ...)
{
char    msg[8192];
va_list ap;

    va_start(ap,errMsg);
    vsprintf( msg, errMsg, ap );
    va_end(ap);

    OCIExtProcRaiseExcpWithMsg(ctx,errCode,msg,strlen(msg));
    return -1;
}

long
get_extended_time( OCIExtProcContext  * ctx,
                   char  * p_data,
                   short * p_data_i,
                   int   * p_data_maxl,
                   int   * p_data_l )
{
struct timeval tp;

    if ( *p_data_maxl < 25 )
        raise_application_error( ctx, 20001,
                       "String must be 25 bytes or more" );

    gettimeofday(&tp, NULL);
    cftime( p_data, "%D %T",  &tp.tv_sec );
    sprintf( p_data+strlen(p_data), ".%d", tp.tv_usec );

    *p_data_l = strlen(p_data);
    *p_data_i = 0;

    return 0;
}
Review &
      Followup

      Rating: 4
or this in
      9i  November 02, 2001
Reviewer:  steveh
       from UK
select current_timestamp from dual;

      Rating:
      4
  November 19, 2001
Reviewer:
      A reader
Could use some compilation instructions

      Rating: 5
Milliseconds in
      Oracle  January 11, 2002
Reviewer:  Kamlesh
       from India
It helped me a lot in retrieving miliseconds in oracle

      Rating: 5
Extract Only
      the MILLISEC part  February 20, 2002
Reviewer:
      Pascal  from HH,Germany
Hi Tom,

Thanks for  My_Timestamp  JSP.

I have tried to Exract only the Millisec part , but you can  see  from the Query

below  that there's some LAG ...now , how do i Extract only the Millisec without

this LAG.

column my_timestamp format  a30
column  millisec format  a30
select  substr(my_timestamp,instr(my_timestamp,'.')+1 ) millisec , my_timestamp
from dual;

MILLISEC                       MY_TIMESTAMP
------------------------------ -----------------------------
594                            2002-02-20 19:34:26.596

Thanks in advance

Best Regards

Pascal

      Rating: 4
Need a make
      file,  May 10, 2002
Reviewer:  Mogan
       from Kansas USA
I tried to compile this in SunSolaris 5.6 With oracle 8.1.7. My make file is
giving some trouble.
Could you please give me the make for this C code. Thanks,

          Followup:

MAKEFILE= $(ORACLE_HOME)/rdbms/demo/demo_rdbms.mk

INCLUDE= -I$(ORACLE_HOME)/rdbms/demo \
         -I$(ORACLE_HOME)/rdbms/public \
         -I$(ORACLE_HOME)/plsql/public \
         -I$(ORACLE_HOME)/network/public

all: extproc.so

include $(MAKEFILE)

CC=cc

extproc.so: extproc.c extproc.o
    $(MAKE) -f $(MAKEFILE) extproc_callback \
     SHARED_LIBNAME=extproc.so OBJS="extproc.o"

CFLAGS= -g -I. $(INCLUDE)

May 12, 2002 Nikunj -- Thanks for the question regarding
      "plsql stuff & backup", version 8.1.6

      You Asked
hi,

Q.1
here i give u an some pl/sql stuff.

i have below table
grp_all
---------
grp_code varchar2(2)
all_code    varchar2(3)
all_formula varchar2(100)

i have below values
grp_code    all_code    all_formula
-----------    ----------    ---------------------------------------------------
G1    HRA    BASIC * 40%
G1    CON    ( HRA * 10% ) + 500
G1    MED    ( HRA <= 1000 = 500 , HRA > 1000 = 1000 )
G1    EDU    ( MED + CON ) / 2

if u see formula every where between two things there is a space.

if i pass 4000 as basic then my output as below
all_code    all_amt
----------    --------
HRA    1600
CON     900
MED    1000
EDU     950

Q.2
what r the best method of taking backup of database ?
what r the method of testing where is problem ?

thanks in advance,
nikunj

      and we said...
Your keyboard is malfunctioning, might be time for a new one.  It drops the
letters Y, O, A, and E sometimes (but not everytime).

If you are willing to store things in SQL, not some language you make up, we can
do this easily.  If you store it in some language you make up (as above -- for
example "( HRA <= 1000 = 500 , HRA > 1000 = 1000 )") you'll have to write your
own parser.

I disagree with your results -- if BASIC starts at 4000, then

HRA = 4000 * .4 = 1600             (OK)
CON = (1600 * .1) + 500 = 660      (not 900)
MED = if ( hra <= 1000 ) then 500, else if (hra > 1000) then 1000 = 1000  (OK)
CON = ( 1000+660 ) / 2 = 830   (not 950)

Also, you MUST have some sequence number in there so that we KNOW that HRA must
be first, CON second, MED third and EDU fourth -- you cannot rely on the order
of rows in a table unless you have some method of ordering them.  So, say the
table has in it instead:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

GR      SEQNO ALL ALL_FORMULA
-- ---------- ---
---------------------------------------------------------------------------------
-------------------
G1          1 HRA :basic * 0.4
G1          2 CON (:hra*0.1)+500
G1          3 MED case when :hra <= 1000 then 500 when :hra > 1000 then 1000
else null end
G1          3 EDU (:med+:con)/2

(i fixed up your MED formula as it would fail if HRA was neither <= 1000 or >
1000).  Now, skipping to the end result (before I show the code, I'll show the
answer):

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec do_formula.set_val( 'basic', 4000 );

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> column result format a40
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select grp_code, seqno, all_code,
  2             do_formula.do_formula( all_code, all_formula ) result
  3   from (select * from t where grp_code = 'G1' order by seqno )
  4  /

GR      SEQNO ALL RESULT
-- ---------- --- ----------------------------------------
G1          1 HRA 1600
G1          2 CON 660
G1          3 MED 1000
G1          3 EDU 830

Now for the code:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package do_formula
  2  as

this is where we will save our "values" in -- a pair of arrays, one of names
and the other of values:

  3      type array is table of varchar2(4000) index by binary_integer;
  4
  5      g_names   array;
  6      g_values  array;
  7

three routines -- one to set a value (like basic), one to run formulas that
rely on previously set values and the other to "reset" the names/values arrays
to be empty

  8      procedure set_val( p_name in varchar2, p_val in varchar2 );
  9      function do_formula( p_code in varchar2, p_formula in varchar2 ) return
varchar2;
 10      procedure reset;
 11
 12  end;
 13  /

Package created.

and now for the code:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package body do_formula
  2  as
  3

this is our dbms_sql cursor, we'll open it once in the do_formula routine:

  4  g_cursor  int;
  5

set val is pretty straight forward.  It just looks in the array for the name,
when it finds it (or not) it stores the value in the same array index in the
values array, creating one if need be

  6  procedure set_val( p_name in varchar2, p_val in varchar2 )
  7  is
  8      l_found boolean default FALSE;
  9  begin
 10      for i in 1 .. g_names.count
 11      loop
 12          if ( g_names(i) = UPPER(p_name) )
 13          then
 14              g_values(i) := p_val;
 15              l_found := TRUE;
 16          end if;
 17          exit when l_found;
 18      end loop;
 19      if NOT l_found
 20      then
 21          g_names(g_names.count+1) := upper(p_name);
 22          g_values(g_values.count+1) := p_val;
 23      end if;
 24  end;
 25

Here is the meat of the code.  We use dbms_sql to dynamically bind in our
names/values for us.  Then all we need to do it "select" our functions from dual
and wa-lah -- the database does the work for us.

Note how we BIND all names in -- we don't know which ones we don't need so we
just bind them all, ignoring the error when one is reported as "not being
needed"

 26  function do_formula( p_code in varchar2, p_formula in varchar2 ) return
varchar2
 27  is
 28      not_a_bind  exception;
 29      pragma exception_init( not_a_bind, -1006 );
 30      l_value varchar(4000);
 31  begin
 32      if ( g_cursor is null )
 33      then
 34          g_cursor := dbms_sql.open_cursor;
 35      end if;
 36
 37      dbms_sql.parse( g_cursor,
 38                     'select ' || p_formula || ' from dual',
 39                      dbms_sql.native );
 40
 41      for i in 1 .. g_names.count
 42      loop
 43          begin
 44              dbms_sql.bind_variable( g_cursor, g_names(i), g_values(i) );
 45          exception
 46              when not_a_bind then
 47                  null;
 48          end;
 49      end loop;
 50      dbms_sql.define_column( g_cursor, 1, l_value, 4000 );
 51
 52      if ( dbms_sql.execute(g_cursor) = 0 AND dbms_sql.fetch_rows( g_cursor )
> 0 )
 53      then
 54          dbms_sql.column_value( g_cursor, 1, l_value );
 55          set_val( p_code, l_value );
 56      end if;
 57
 58      return l_value;
 59  end;
 60
 61  procedure reset
 62  is
 63      l_empty array;
 64  begin
 65      g_names := l_empty;
 66      g_values := l_empty;
 67  end;
 68
 69  end;
 70  /
Package body created.

and that is it.  Nifty eh?

q2) read the backup and recovery documentation here:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/index.htm
there is no shortcut for knowledge on this one, you have to, you MUST learn
this backwards and forwards.

It is the most crucial aspect of managing the database.  If you don't master
this, you might as well just erase your database now cause it'll happen
sometime.

Suggestion for you:
o use rman
o test every possible scenario -- have someone come in and "damage" your
database and see if you can recover from it (on a test system of course!)

Review &
      Followup

      Rating:
      5
case  April 16, 2002
Reviewer:
      Nikunj  from Ahmedabad / India
case statment works after 8i what about older version how can i archives the
same ?

          Followup:

You can use decode.

You told me your version was 816 -- case works in 816 and up.

anyway:

case when :hra <= 1000 then 500 when :hra > 1000 then 1000
else null end

is the same as:

decode( sign( :hra-1000 ),
            1, 1000,
            0,  500,
           -1,  500,
                NULL )

      Rating: 5
What a
      Tom  April 16, 2002
Reviewer:  A reader

Hi, Tom,

After learning from your site, it seems to me we can do
anything we want in the DB which is not noticeable to
everyone.

Thanks for your great code.

      Rating: 5
package inside
      forms   May 11, 2002
Reviewer:  Nikunj
       from Gujarat, India
hi tom,

if i want to use this package inside forms (oracle developer 6.0) then which
changes i have to made ?

thanks,
nikunj

          Followup:

You would probably want to change dbms_sql to EXEC_SQL, using the client side
dynamic sql package.

May 12, 2002 shamim -- Thanks for the question regarding
      "restrict user more than specefied", version forms 6

      You Asked
HI Tom
We have developed a software that is selled per license or per user based. e.g
We sold this for 20 users. Now using forms I want to restrict  that they could
not use this software for more than 20 user.
Now I implemented it by creating  a table , that care of the logins and logouts.
IS any other more secure alternate , if yes ps help me out.
thanx
shamim

      and we said...
Get yourself access to the V$SESSION table.

Use
begin
  dbms_application_info.set_client_info( 'YOUR_APPLICATION' );
end;

in a when-new-form-instance trigger.

Now a simple "select count(*) from v$session where client_info =
'YOUR_APPLICATION'" will return an accurate count of sessions.  You don't have
to worry about cleaning out any tables before logging out or anything -- your
v$session record will disappear when you are logged out automaticall.
Review &
      Followup

      Rating: 5
Very good
      solution  August 21, 2001
Reviewer:  Rengasamy
      Balasubramanian  from Singapore
I checked this solution. It is very useful to me.
Thanks.

      Rating: 4
How about
      running a PL/SQL in SQL*PLUS session?  May 11, 2002

Reviewer:  Reader  from INDIA
Will this work in the case of PL/SQL package run from SQL*PLUS session also.

I have a package called my_app.  I open a sqlplus session and call a procedure
in my_app.  If I open second session of sqlplus and start my_app again it should
not run.  But if my_app has successfully completed on the first sql*plus
session, I should be able to run my_app in the second sql*plus session without
quitting the first sql*plus session.  Is this possible?

          Followup:

yes.

Your app would call dbms_application_info at the start and at the stop of its
run.

      Rating: 4
Session
      Track  May 11, 2002
Reviewer:  Jyotiprakash
      Nandi  from UK
Thanks Tom.

It has helped me a lot.

With Warm Regards
JP

      Rating: 5
how to send a
      message  May 12, 2002
Reviewer:
      A reader
suppose i want to restart the database and i need
to send a message to all users connect to the database
how can be done

          Followup:

No need -- when you shutdown they will most certainly be notified ;)

There is no way for the database to "broadcast" a message like that UNLESS the
clients are listening for that specific message.

Think about it -- a client of the database can be anything, from an App server,
to a perl script to an ASP to a JSP to a pro*c running on OS/390 to a VB program
running on windows to a thin jdbc running on anything.

There is no way we could "alert" the end user in any or even most of these
situations.

If you shutdown immediate, they'll be told the next time they try to use the
database that the database was shutdown.

If you shutdown normal, the database will wait patiently for them to finish.

      Rating: 5
restrict the
      piracy  May 14, 2002
Reviewer:  Nikunj
       from Gujarat, India
hi tom,

i want to restrict user for doing piracy of my sotware. how can i do that.

i think about creating an dummy user & storing some keys but when he export the
full user & import it.
i had created user in his database so he have password of all super user
(system, sys or with dba rights)

then its work. so is there any way to restrict the piracy.

thanks,
nikunj.

      Rating:
      5
  May 20, 2002
Reviewer:  reader

hi,

i had developed application on oracle developer 6 & oracle database 8x.

i want to restrict user for doing piracy of my application. how can i do that.

first i think i create a table which stored some keys but then i think if he
export full user & import it then this restriction can't work because i had
created user in his database so he have password of all super user (system, sys
or with dba rights).

so is there any way to restrict the piracy.

thanks,

      Rating: 5
restrict user
      for doing piracy   May 20, 2002
Reviewer:
      reader
hi,

i had developed application on oracle developer 6 & oracle database 8x.

i want to restrict user for doing piracy of my application. how can i do that.

first i think i create a table which stored some keys but then i think if he
export full user & import it then this restriction can't work because i had
created user in his database so he have password of all super user (system, sys
or with dba rights).

so is there any way to restrict the piracy.

thanks,

May 13, 2002 Daniel -- Thanks for the question regarding
      "Compress Primary Key", version 9.0.1

      You Asked
Hi Tom,

The primary key for a non IOT table consists of 3 columns, the leading 2 columns
may be duplicated.
I tried using "compress 2" option to create the primary key but got an error:
"ORA-14071: invalid option for an index used to enforce a constraint".

Is that true compress only applied to index not constraint ?

Thanks

      and we said...
ops$tkyte@ORA9I.WORLD> create table t
  2  ( x int, y int, z int,
  3    constraint t_pk primary key(x,y,z) )
  4  organization index compress 2;

Table created.

works -- perhaps you were using a different syntax (like a "using index" clause
or something...)

Review &
      Followup

      Rating: 4
Compress
      Primary key  April 21, 2002
Reviewer:
      A reader
Hi Tom,

Does "organization index" make it an Index Organized Table,
Can compress be done on regular table ?

Thanks

          Followup:

Yup -- organization index = index organized.

Now I see the problem with my answer!!!  "a non IOT table"... I read over the
work NON....

Ok, here we go:

ops$tkyte@ORA9I.WORLD> create table t
  2  ( x int, y int, z int )
  3  /

Table created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> create unique index t_idx on t(x,y,z) compress 2;

Index created.

ops$tkyte@ORA9I.WORLD> alter table t add constraint t_pk primary key (x,y,z);

Table altered.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> drop index t_idx;
drop index t_idx
           *
ERROR at line 1:
ORA-02429: cannot drop index used for enforcement of unique/primary key

sorry about that.

      Rating: 4
Compress
      Primary key  April 21, 2002
Reviewer:  Daniel H.
      Liu  from East Brunswick, NJ
Hi Tom,

Sorry, I forgot my Email last time.

Does "organization index" make it an Index Organized Table,
Can compress be done on regular table ?

Thanks

          Followup:

See above, i misread -- sorry about that.

      Rating: 4
Rule vs
      Cost.  May 12, 2002
Reviewer:  A reader

Tom,

Does it matter what optimizer_mode my database is running in for using
compressed indexes. I know that compression is for storage but if i use a
compressed index will my query use the index in a rule based setup.

Your comments are valuable.

Regards,
Ganesh R

          Followup:

It is RBO "safe"

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int, y int, z int );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx on t(x,y,z) COMPRESS 2;
Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace on

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where x=1 and y=1 and z=1;
no rows selected

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE)

See -- no bytes/card info to indicate the CBO being used like this does:

ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table t compute statistics;
Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where x=1 and y=1 and z=1;
no rows selected

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=39)
   1    0   INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE) (Cost=1 Card=1 Bytes=39)

      Rating: 5
Does it
      means  May 13, 2002
Reviewer:  Reader

Hello Tom

Sorry. Dont want to get wrong interpretation out of your answer.  So RBO Safe
means,  Should not be advisable to use Compress with RBO or Can use irrespective
of Optimizer selected ?

Thanks in advance for your detailed answer.

          Followup:

What I mean is that index key compression will not affect the usage of RBO.  You
can use it if you like.

May 13, 2002 Vikas -- Thanks for the question regarding
      "BLOBS/CLOBS", version 8.0.5

      You Asked
Hello Tom,

Can you please tell us how to handle BLOBS/CLOBS in DML Statements with specific
example(s).ie. How to Insert/Update a Blob in the database.

Any other specific functions which we can use with the DBMS_LOB package for
doing certain operations on BLOBS/CLOBS.

How they are stored, when the size exceeds 4K. I have been reading that if the
size exceeds 4K then the column has a locator pointing to the BLOB Segment which
resides in the database actually the tablespaces where that table segment is
created.

Is mine understanding correct? Can we mention a different tablespace for the
BLOB/CLOB Object instead of the table itself?

Thanks and regards,

Vikas Khanna

      and we said...
see

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/index.htm
and read the  Oracle8i Application Developer's Guide - Large Objects (LOBs).
95% of it is relevant to 8.0 (your release).

All of your questions are answered there.

By default, lobs of 4,000 bytes or less are stored "inline" -- in the table,
like a varchar2.  When they exceed that, they are moved out of line and can
reside in a tablespace separate from the table itself.

May 13, 2002 Jesse -- Thanks for the question regarding "SQL
      Query Help", version 8.1.7

      You Asked
Hi Tom,

I have a table EQP_HIS which consists of:
CTY, ST, DPT, EQP, MVDATE, EQPSTS

I want to find out number of days equipments of a (country, state, department)is
in used.
That is the number of days the status of equipments changed from (I or N) to
next status.

CTY ST DPT EQP MVDATE EQPSTS
=== == === === ======== ======
P1 S1 L E1 20020101 I --
P1 S1 L E1 20020102 F -- 1 day
P1 S1 L E1 20020102 A

P1 S1 L E1 20020201 I
P1 S1 L E1 20020228 F -- 27 days

P1 S1 L E1 20020301 N
P1 S1 L E1 20020310 A -- 9 days

P1 S1 L E2 20020105 I --
P1 S1 L E2 20020110 A -- 5 days

P1 S1 L E3 20020102 I -- 0 days

P2 S2 L E3 20020105 I --
P2 S2 L E3 20020112 A -- 7 days

P2 S2 L E4 20020201 I
P2 S2 L E4 20020201 F -- 0 days
P2 S2 L E4 20020202 A

P2 S2 L E5 20020301 I -- 1 day
P2 S2 L E5 20020302 N -- 1 day
P2 S2 L E5 20020303 F

status
F=Faulty
A=Available
I=Inuse
N=Not Available

The final results should be
P1 S1 L = 42 days
P1 S2 L = 9 days

I think single query is faster than doing the calculation in loops equipment by
equipment.

I have written this query and it runs fine in SQLPLUS as a SQL block.

select SUM(LASTDATE - MVDATE)
FROM (
select A.cty CTY, A.st ST, A.dpt DPT, A.eqp EQP, TO_DATE(A.mvdate,'YYYYMMDD')
MVDATE,
(select min(TO_DATE(B.mvdate,'YYYYMMDD')) from eqp_his B
where
B.cty=A.cty
and B.st=A.st
and B.dpt=A.dpt
and B.eqp=A.eqp
and B.mvdate >= A.mvdate
and B.eqpsts != A.eqpsts
group by B.cty,B.st,B.dpt,B.eqp) as LASTDATE
from eqp_his A where A.eqpsts in ('I', 'N')
)
GROUP BY CTY, ST, DPT
/

SUM(LASTDATE-MVDATE)
~~~~~~~~~~~~~~~~~~~~
42
9

But I want to put this query in a PLSQL procedure block like this:

create or replace procedure cal_idle_days is

cursor c_idle_days is
select SUM(LASTDATE - MVDATE) as IDLE_DAYS
FROM (
SELECT A.cty CTY, A.st ST, A.dpt DPT, A.eqp EQP, TO_DATE(A.mvdate,'YYYYMMDD')
MVDATE,
(select min(TO_DATE(B.mvdate,'YYYYMMDD'))
from eqp_his B
where B.cty=A.cty
and B.st=A.st
and B.dpt=A.dpt
and B.eqp=A.eqp
and B.mvdate >= A.mvdate
and B.eqpsts != A.eqpsts
group by B.cty,B.st,B.dpt,B.eqp) as LASTDATE
FROM eqp_his A where A.eqpsts in ('I', 'N')
)
GROUP BY CTY, ST, DPT;

begin
FOR c1 in c_idle_days LOOP
dbms_output.put_line ('idle_days='||c1.idle_days);
END LOOP;
end;
/
It gives me this error:

LINE/COL ERROR
~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6/9 PLS-00103: Encountered the symbol "SELECT" when expecting one of
the following:
( - + mod not null others
avg
count current exists max min prior sql stddev sum variance
execute forall time timestamp interval date

14/42 PLS-00103: Encountered the symbol "AS" when expecting one of the
following:
; return returning and or

Why is the query runs ok in sqlplus but not in a plsql block? Is there any other
way to do this? Any advice please.

Thanks.

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> select cty, st, dpt, sum(mvdate-last_mvdate)
  2    from (
  3  select cty, st, dpt, eqp, mvdate, eqpsts,
  4         lag(mvdate)
            over (partition by cty, st, dpt, eqp order by mvdate) last_mvdate,
  5         lag(eqpsts)
            over ( partition by cty, st, dpt, eqp order by mvdate ) last_eqpsts
  6    from t
  7  )
  8  where last_eqpsts in ( 'I', 'N' )
  9  group by cty, st, dpt
 10  /

CTY   ST    DPT   SUM(MVDATE-LAST_MVDATE)
----- ----- ----- -----------------------
P1    S1    L                          42
P2    S2    L                           9

ops$tkyte@ORA817DEV.US.ORACLE.COM>

would be better (analytic functions rock)....

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3027089372477
for the solution to the problem you have above (it'll happen with analytics as
well -- until 9i).  Easily solvable though.

Review &
      Followup

      Rating: 4
Getting an
      error  May 13, 2002
Reviewer:  rennie
       from Boston
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3027089372477
Iam getting error while trying one of your examples.
SQL> ed
Wrote file afiedt.buf

  1  begin
  2    open :x for 'select :bv_uid from dual' using 1;
  3* end;
  4  /
Bind variable "X" not declared.

Iam getting this error when i try the example you've given. It doesnt give error
if i declare x as REF CURSOR and give OPEN x for 'select...'. But then,
x%ROWCOUNT gives me 0. Please advice.

regards
rennie

          Followup:

you missed part of the example:

ops$tkyte@ORA817DEV.US.ORACLE.COM> variable x refcursor

ops$tkyte@ORA817DEV.US.ORACLE.COM> ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2          open :x for 'select :uid from dual' using 1;
  3  end;
  4  /

May
      13, 2002 Ivan -- Thanks for the
      question regarding "Same table but different record sets per user",
      version 8.1.7

      You Asked
Tom,

My Problem: I have one table that contains an address field that needs to be
parsed into two addresses (ie address_1 and address_2).  It needs to be done
manually as the data is really bad. I have editors who are going to log in at
any time and parse the address.  My delimma is how do I get different record
sets to all the people without having one person editing the same record at the
same time as someone else. I will be using forms (v6.0.5).

I have got to think the solution revolves around views but I am stumped at the
moment.

Thanks,

Ivan

      and we said...
See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1786336182160
and the get_record routine i have there.
Review &
      Followup

      Rating: 4
Extremely
      Useful  December 02, 2002
Reviewer:  Ivan
       from USA
Perfect response to my needs - thanks

May 13, 2002 -- Thanks for the question regarding "How did the
      optimizer arrive at the following plan.", version 8.1.7

      You Asked
Tom,

  First, I would like to say that I agree with the plan Oracle choose but my
question is how Oracle came up with it?  What I am trying to do is allow a user
to query data in any language depending on a system_context variable.  Assuming
the context is set to ENGLISH, I run the query I have included below.  My goal
is to put this query in a view so I can query a drug and return which ever
language the user's context is set up for.

What confuses me about the plan below is the NL join between drug_t and
language.  My query has no direct join condition between these two tables so how
can a NL join take place (unless it was a cartesian product but that is not
indicated or Oracle is using transitivity to figure this out).  To me I would
have expected something like

DRUG_T                 (1 row using PK)
 NL WITH DRUG_NAME_T   (n rows depending on # of languges supported)
  NL WITH LANGUAGE     (1 row)

but using my plan would have been less than optimal as I have joined DRUG_T with
DRUG_NAME_T and now the temp resultset would have rows for each language
supported which would be filterd with the last join condidtion using the context
variable.

Can you please provide some insite on how oracle is implementing the JOIN
between DRUG_T and LANGUAGE without a join condition in my query.

Thanks,
Scott.

select
         d.ID,
         d.VERSION_NO,
         d.CREATED_DATETIME,
         d.CREATED_BY,
         d.DELETED_DATETIME,
         d.DELETED_BY,
         d.IS_LONG_ACTING,
         d.MIN_MENU_DOSAGE,
         d.MAX_MENU_DOSAGE,
         d.IS_ALLERGY,
         d.LAB_SITE_CODE,
         d.IS_INSULIN_SLIDING_SCALE,
         d.COMPOUND_DOSE_FORMULA_CODE,
         dn.name
    from
         drug_t       d,
         drug_name_t  dn,
         language     lang1
   where
         d.id       = dn.drug_id
     and d.id = 14
     and dn.lang_id = lang1.id
     and lang1.name = sys_context('vm_app_context','default_language');

SQL> @explain TOM

Plan Table
---------------------------------------------------------------------------------
-----
| Operation                                     |  Name               |  Rows |
Bytes|
---------------------------------------------------------------------------------
-----
| SELECT STATEMENT                              |                     |     1 |
119 |
|  NESTED LOOPS                                 |                     |     1 |
119 |
|   NESTED LOOPS                                |                     |     1 |
 94 |
|    TABLE ACCESS BY INDEX ROWID                |DRUG_T               |     1 |
 84 |
|     INDEX UNIQUE SCAN                         |DRUG_T_PK            |     1 |
    |
|    TABLE ACCESS FULL                          |LANGUAGE             |     1 |
 10 |
|   INDEX UNIQUE SCAN                           |DRUG_NAME_T_PK       |     2 |
 50 |
---------------------------------------------------------------------------------
-----

SQL> desc drug_name_t  (IOT) (3607 rows)
 Name                                      Null?    Type
 ----------------------------------------- -------- --------------
*DRUG_ID                                   NOT NULL NUMBER
*LANG_ID                                   NOT NULL NUMBER
 NAME                                               VARCHAR2(120)

SQL> desc language (IOT) (4 rows)
 Name                                      Null?    Type
 ----------------------------------------- -------- ---------------
*ID                                        NOT NULL NUMBER
 NAME                                               VARCHAR2(30)

      and we said...
Interesting -- it is in fact a cartesian product (drug_t X language) but they
know that since drug_t will contribute a single row -- they can do it like a
nested loops join (1 row from drug_t X n rows from language = N rows).

Take away the primary key on drug_t and it becomes:

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=1 Bytes=218)
   1    0   NESTED LOOPS (Cost=4 Card=1 Bytes=218)
   2    1     MERGE JOIN (CARTESIAN) (Cost=4 Card=1 Bytes=130)
   3    2       TABLE ACCESS (FULL) OF 'DRUG_T' (Cost=3 Card=1 Bytes=100)
   4    2       SORT (JOIN) (Cost=1 Card=1 Bytes=30)
   5    4         INDEX (FAST FULL SCAN) OF 'LANG_PK' (UNIQUE) (Cost=1 Card=1 By
   6    1     INDEX (UNIQUE SCAN) OF 'DN_PK' (UNIQUE)

A true cartesian product.  Nice subtle optimization it did there.
Review &
      Followup

      Rating: 5
Cudos to the
      optimizer  May 13, 2002
Reviewer:
      A reader
Thanks for the quick reply.  Another reason to get people to move to cost based
optimizer.

May 13, 2002 Bill -- Thanks for the question regarding "Top-N
      query FOR UPDATE", version 8.1.7

      You Asked
Tom,

My developer has the following table:
SQL> desc ads_admin.t_pending_report
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 PENDING_ID                                NOT NULL NUMBER(5)
 REPORT_ID                                          NUMBER(5)
 USER_ID                                   NOT NULL VARCHAR2(20)
 NAME                                      NOT NULL VARCHAR2(60)
 DUE_DT                                    NOT NULL DATE
 PROCESSED                                          CHAR(1)

and wants to run the following query:

SELECT *
FROM (SELECT T_PENDING_REPORT.PENDING_ID,
           T_PENDING_REPORT.REPORT_ID,
           T_PENDING_REPORT.USER_ID
      FROM T_PENDING_REPORT
     WHERE T_PENDING_REPORT.DUE_DT <= SYSDATE
       AND T_PENDING_REPORT.PROCESSED = 'F'
     ORDER BY T_PENDING_REPORT.DUE_DT)
 WHERE ROWNUM = 1
 FOR UPDATE

The query does fine except for the FOR UPDATE, at which point it returns an
ORA-02014.  Can you offer an alternative way to code this?

Thanks,
Bill

      and we said...
If you are looking for a way to get the first "non locked" row -- see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1786336182160
This is the logical equivalent of yours:

scott@ORA817DEV.US.ORACLE.COM> select *
  2    from emp
  3   where rowid = ( select r
  4                     from ( select rowid r
  5                              from emp
  6                             where deptno = 20
  7                             order by sal )
  8                      where rownum = 1 )
  9   for update;

     EMPNO ENAME      JOB              MGR HIREDATE
EPTNO
---------- ---------- --------- ---------- ---------
-----
      7369 x          CLERK           7902 17-DEC-80

But -- doesn't work too well in a multi-user environment (the other approach
does)
Review
      & Followup

      Rating: 5
Just what I
      needed  May 14, 2002
Reviewer:  Bill
       from New Orleans, LA USA
Thanks, Tom.  I'll pass your SQL on to my developer; it looks great.  I hadn't
considered advanced queuing, but I think we'll end up going that way since there
will be multiple processes going after the data.


Subject | Article | Last Updated (East coast time)
Date Time Stamp | article only | May 14, 2002 05:50 am
Import and Export ! | article only | May 16, 2002 11:46 am
Java Wrapper for converting LONG RAW to BLOB | article only | May 16, 2002 11:48 am
Resource Bundles in the DB? | article only | May 17, 2002 08:00 am
How to auto increment a column - like a sqlserver Identity or Informix serial# | article only | May 17, 2002 08:12 am
Create users thru Developer 2000 | article only | May 17, 2002 10:12 am
Show Users using DB(s) from UNIX OS command line | article only | May 17, 2002 01:41 pm
This Query | article only | May 17, 2002 01:43 pm
Nested Table of Objects vs. Reference Cursor to Interact with Java | article only | May 17, 2002 01:58 pm
Security policy of password expiry causes problems | article only | May 18, 2002 09:39 am
physical reads | article only | May 18, 2002 09:59 am
job elapsed time | article only | May 18, 2002 11:20 am
Oracle 9i and PHP | article only | May 18, 2002 05:31 pm
Scheduling Jobs? | article only | May 18, 2002 05:53 pm
row(s) 1 - 14 of 14
May 14, 2002 Munz -- Thanks for the question regarding "Date Time Stamp", version 8.1.7
You Asked
Tom:

1.  When you use "sysdate" in an insert statement it saves time stamp.  If you
do trunc(sysdate) it only saves date with no time.  How do you save time if you
let user enters date in a html form.  Do you read the system time and
concatenate it to the date??

2.  How do you order the only the second part of the SQL statement.

  group_cursor:=owa_util.bind_variables('select null,''Pick a Group -->'',''x''
from dual
       union all
       select to_char(group_id),group_name,null from group order by 2');
       owa_util.listprint(group_cursor,'i_user_id',null,null);
       dbms_sql.close_cursor(group_cursor);

I am trying to get the "Pick a Group" always at top of list and other list
sorted?

3.  What SQL statment you would use to copy procedures,functions,sequences from
one instance to another instance.

Thank you very much,
and we said...
1) makes no sense -- why wouldn't you just be using SYSDATE?

2) add a forth column:

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2    group_cursor number;
  3  begin
  4     group_cursor:=owa_util.bind_variables
  5     ('select null,''Pick a Group -->'',''x'', 1
  6         from dual
  7       union all
  8       select to_char(user_id), username, null, 2
  9         from all_users
 10            order by 4, 2');
 11       owa_util.listprint(group_cursor,'i_user_id',null,null);
 12       dbms_sql.close_cursor(group_cursor);
 13  end;
 14  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec owa_util.showpage
<SELECT NAME="i_user_id">
<OPTION SELECTED value="">Pick a Group -->
<OPTION value="395">A
<OPTION value="177">ADMIN
<OPTION value="209">APPS
<OPTION value="385">B
<OPTION value="102">C
<OPTION value="386">CLBECK
<OPTION value="59">COMMON
<OPTION value="25">CTXSYS
<OPTION value="334">DBSNMP
<OPTION value="79">DIRLIST
<OPTION value="147">LSC
<OPTION value="136">MIRCODE
<OPTION value="363">MYOWNER
<OPTION value="30">OPS$CLBECK
<OPTION value="27">OPS$ORA817
<OPTION value="216">OPS$TKYTE
<OPTION value="11">OUTLN
<OPTION value="185">PERFSTAT
<OPTION value="387">RMATTAMA
<OPTION value="212">SCOTT
<OPTION value="46">SDILLON
<OPTION value="78">SINGLE_ACCT
<OPTION value="180">SUP_USER
<OPTION
value="0">SYS
<OPTION value="5">SYSTEM
<OPTION value="360">TEST
<OPTION value="364">TEST_OWNER
<OPTION value="37">TMC
<OPTION value="58">TPS_SECURITY_MANAGER
<OPTION value="50">TYPES
<OPTION value="161">U1
<OPTION value="162">U2
<OPTION
value="91">USER_A
<OPTION value="92">USER_B
<OPTION value="93">USER_C
<OPTION value="38">WEB$CLBECK
</SELECT>

PL/SQL procedure successfully completed.

and order by it.  it'll be ignored.

3)
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:743825617527
Review & Followup
Rating: 5
Time Stamp May 13, 2002
Reviewer: Munz from Reston, USA
Tom:

AS a followup to one, the date field can be a an optional date that may be
different than sysdate.

I am defaulting the column to sysdate in html but user can change it if he
wants.  90% of time it will be sysdate, so if I show him the date I have to
insert a date in the SQL insert statement.

Thank you,

Followup:
Then, you simply need to have the user supply the time as well.  In the HTML
form -- use a to_char:

   htp.p( '<input type=text name=foo value="' ||
                to_char(dt,'dd-mon-yyyy hh24:mi:ss') || '">' );

On input, use a to_date:

   dt := to_date( input_data, 'dd-mon-yyyy hh24:mi:ss' );

Or, if they can optionally supply a date or date/time:

   if ( length(input_data) = 11 )
   then
         dt := to_date( input_data, 'dd-mon-yyyy' );
   else         dt := to_date( input_data, 'dd-mon-yyyy hh24:mi:ss' );
   end if;

May 16, 2002 OP -- Thanks for the question regarding "Import and Export !", version 8.1.5
You Asked
Dear Tom,

Hi !

Thanks for the support and guidance.I am really
greatful to you.

Quest)How to export and import a single table
      or a set of tables for one user or the
      complete database On Windows Nt platform.
      Can you suggest me some scripts for these
      things.

Thanks & Regards
OP
and we said...
The commands are simple.  To export a single table or a set of
tables:

exp .... tables=(t1,t2,t3,...tN) ...

To export an entire user,

exp ... owner=username

To import a single table from a dmp file:

imp ... fromuser=username touser=username tables=t1

Just type IMP and EXP on the command line to get a synopsis of
their usage and see the server utilities guide for all of the
details.
Review & Followup
Rating: 3
if there are hundreds of tables May 14, 2002
Reviewer: A reader
Tom,

I need to transfer my existing data to new database. 328 table's data to be
transfered.I have tables created in my new database.I don't want all the tables
from schema and I don't want indexes,grants and triggers to be exported. I have
disabled triggers and constraints on tables of new database.So that when I
import the data,the process will be smooth.

Here is my script for export,

exp userid=clinicopia30/clinicopia30@pzdemo file=c:\gb\export\expdat1.dmp
grants=N indexes=N
tables= (328 tables name)

My questions are

1) How do list all my tables names in script ? If I break the line of tables,
exp is not recognozing the rest of the tables names.

2) Could you suggest me how to achive this in better/foolproof way ?

Export/Import version is 8.1.7
DB version 8.1.7

Followup:
1) use a parameter file, list the tables there

$ exp userid=scott/tiger parfile=exp.par

Export: Release 8.1.7.3.0 - Production on Thu May 16 11:45:31 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.
Connected to: Oracle8i Enterprise Edition Release 8.1.7.3.0 - Production
With the Partitioning option
JServer Release 8.1.7.3.0 - Production
Export done in WE8ISO8859P1 character set and WE8ISO8859P1 NCHAR character set

About to export specified tables via Conventional Path ...
. . exporting table                            EMP         14 rows exported
. . exporting table                           DEPT          4 rows exported
Export terminated successfully without warnings.

$ cat exp.par
tables=(emp,
dept
)

Rating: 3
if there are hundreds of tables - II May 14, 2002
Reviewer: A reader
Sorry I missed to inform you about my O.S.
I am using windows NT

May 16, 2002 Ram -- Thanks for the question regarding "Java Wrapper for converting LONG RAW to BLOB", version 8.1.7
You Asked
I was trying to write a java stored procedure for manipulating long raw fields
and convert them to BLOBS so that client programs can use BLOBS and not have to
deal with LONG RAWS. Here is my problem
oracle.sql.BLOB needs to be bound to a BLOB column in a real table. I do not
want to create a table but want to create a BLOB in memory and write the byte
stream to it and return the BLOB locator to the client. I tried using select
empty_blob() from dual as the SQL but, you get an exception when you try to
write to an empty locator. I have heard of dbms_lob.create_temporary but, can
that locator be returned to the client? This is what I have so far. I was just
trying to write to the BLOB locally, figuring out that I would try to tackle
returning it after I could write to it. Any suggestions?

import java.sql.*;
import java.io.*;
import oracle.jdbc.driver.*;
import oracle.sql.*;

public class LR2BLOB {

  public static String p() throws SQLException, java.io.IOException {
    Connection conn = DriverManager.getConnection("jdbc:default:connection:");
    byte[] data = new byte[200000];
  //  BLOB my_blob;
    try {
       Statement stmt = conn.createStatement();
       String sql = "SELECT image FROM per_images WHERE parent_id=122";
       ResultSet rset = stmt.executeQuery(sql);
       if (rset.next()) {
           data= rset.getBytes(1);
           rset.close();
           stmt.close();
       }
     } catch (SQLException e) {
        System.err.println(e.getMessage());
     }

       Statement stmt = conn.createStatement();
       String sql = "SELECT empty_blob() FROM sys.dual";
       ResultSet rset = stmt.executeQuery(sql);
       rset.next();
    BLOB my_blob = ((OracleResultSet)rset).getBLOB(1);
          rset.close();
           stmt.close();
        java.io.OutputStream outstream;
     outstream = ((BLOB)my_blob).getBinaryOutputStream();
     outstream.write(data);

     return ret;

   }
}
and we said...
No need.

scott@ORA8I.WORLD> create global temporary table temp ( x blob ) ;
Table created.

scott@ORA8I.WORLD> insert into temp select to_lob(image) from image;

1 row created.

scott@ORA8I.WORLD> select dbms_lob.getlength(x) from temp;

DBMS_LOB.GETLENGTH(X)
---------------------
                58880

scott@ORA8I.WORLD>

That one line insert replaces all of the java code you would have to write
otherwise.

(your approach is flawed in that an empty_blob MUST live in a table, you have to
insert it first.  You could use a temporary blob -- dbms_lob.create_temporary --
but a simple insert -- coupled with a returning clause even "begin insert into
temp select to_lob(lr_column) from t where .... returning X into ?; end;" --
would be superior and simplier)
Review & Followup
Rating: 4
Thanks May 14, 2002
Reviewer: Ram Pudupet
Thanks. I like the temporary lob approach better because I seem to have a mental
block against creating temporary tables :-).

Followup:

You seem to have a desire to write tons of code in my opinion.

You want easy -- use a temp table (where do you thing a tempory lob is going to
be???)

I would use the temp table -- mental block, mental smock...  Use the right tool
for the right job at the right time.

The goal -- write the least amount of code possible.

May 17, 2002 Marty -- Thanks for the question regarding "Resource Bundles in the DB?", version 8i
You Asked
Is there any docs or other publications available that reference using the DB to
store resource bundles rather than using flat files?  I am sure that some one
has done this or is currently doing this but I can't find any docs related to
this.  I am trying to find all the pros and cons of this design and hopefully
some tips on what to consider based on someone's experiance with this.

ADDITIONAL INFO (6/5/01)
------------------------
In a database driven application which needs to be customizable, in both display
and content, we are making the decision of how to treat language, locale, and
variant (customer) specific requirements. For instance, by default the labels on
a form, validation expressions, and help text may be different from customer to
customer. Resource bundles in java seem like a good way to address this, giving
us customization, basically in the form of internationalization. I was
interested in whether schemas already existed which oracle recommended for
implementing this solution at the database level instead of storing sets of
key/value pairs per language/locale/variant on disk, which we would retrieve
when the app server starts, therefore requiring a bounce of the app server (or
sending an event to a "resource bundle service") whenever you want to refresh
this data.
and we said...
I asked Mark Piermarini to help me out on this one and he says:

...
While I don't know of any documentation, offhand, I think using resource bundles
in the database works somewhat "normally". What you
have to do it put all your resource bundles in a JAR and then use loadjava to
put it into the database. From there, it's just
calling the Java as normal. What I first did was to create three resource files:
one default, one for French, and one for German.
They are named:

    LabelsBundle.properties
    LabelsBundle_de.properties
    LabelsBundle_fr.properties

I then just jar'ed them up using:

    jar -cf labels.jar LabelsBundle*

Then I loaded them into the database:

    loadjava -u scott/tiger -o -f -r -v labels.jar

I created/loaded the Java demo source code using the "CREATE OR REPLACE JAVA"
syntax which also created a PL/SQL wrapper procedure.
I've put the property files and the SQL in an attached file named
ResourceBundle.zip. I did not include the labels.jar file since
that's part of the process. Here is a transcript of what I did:

Wed Jun  6 14:13:56 EDT 2001
(mbpierma@oaxaca) /tmp
> zip ResourceBundle.zip LabelsBund* resour*.sql
  adding: LabelsBundle_de.properties (deflated 10%)
  adding: LabelsBundle_fr.properties (deflated 13%)
  adding: LabelsBundle.properties (deflated 10%)
  adding: resource_bundle.sql (deflated 61%)

Wed Jun  6 14:13:56 EDT 2001
(mbpierma@oaxaca) /tmp
> unzip -v Res*
Archive:  ResourceBundle.zip
 Length   Method    Size  Ratio   Date   Time   CRC-32    Name
--------  ------  ------- -----   ----   ----   ------    ----
      99  Defl:N       89  10%  06-06-01 14:15  08d3b183
LabelsBundle_de.properties
     106  Defl:N       92  13%  06-06-01 14:17  718eb772
LabelsBundle_fr.properties
     102  Defl:N       92  10%  06-06-01 14:14  f32c7415
LabelsBundle.properties
    1297  Defl:N      508  61%  06-06-01 14:34  73dcf2bb  resource_bundle.sql
--------          -------  ---                            -------
    1604              781  51%                            4 files

Wed Jun  6 14:13:56 EDT 2001
(mbpierma@oaxaca) /tmp
> !jar
jar -cf labels.jar LabelsBundle*

Wed Jun  6 14:13:56 EDT 2001
(mbpierma@oaxaca) /tmp
> loadjava -u scott/tiger -o -f -r -v labels.jar
initialization complete
loading  : LabelsBundle_fr.properties
creating : LabelsBundle_fr.properties
loading  : LabelsBundle_de.properties
creating : LabelsBundle_de.properties
loading  : LabelsBundle.properties
creating : LabelsBundle.properties
loading  : META-INF/MANIFEST.MF
creating : META-INF/MANIFEST.MF
resolver :

Wed Jun  6 14:13:56 EDT 2001
(mbpierma@oaxaca) /tmp
> sqlplus scott/tiger

SQL*Plus: Release 8.1.7.0.0 - Production on Wed Jun 6 14:37:25 2001

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected to:
Oracle8i Enterprise Edition Release 8.1.7.1.0 - Production
With the Partitioning option
JServer Release 8.1.7.1.0 - Production

SQL> @/tmp/resource_bundle.sql

Java created.

No errors.

Procedure created.

No errors.
SQL> exec dbms_java.set_output(100000)

PL/SQL procedure successfully completed.

SQL> set serveroutput on size 100000
SQL> exec bundle_demo
Locale = fr, key = s2, value = disque
Locale = de, key = s2, value = Platte
Locale = en, key = s2, value = disk
key = s4, value = clavier
key = s3, value = surveillent
key = s2, value = disque
key = s1, value = ordinateur

PL/SQL procedure successfully completed.

SQL>
Review & Followup
Rating: 5
Huh? May 16, 2002
Reviewer: Daniel Testa from USA
I cannot get my stored procedures to load propertie files stored in the database
but it looks like we are doing that here so I was wondering if I can get the
source you used for the bundle_demo.

Followup:
Here is resource_bundle.sql:

set define off

create or replace and compile java source named "ResourceBundleDemo" as
import java.util.*;

public class ResourceBundleDemo {

  static void displayValue(Locale currentLocale, String key) {
    ResourceBundle labels = ResourceBundle.getBundle("LabelsBundle",
currentLocale);
    String value  = labels.getString(key);
    System.out.println(
      "Locale = " + currentLocale.toString() + ", " +
      "key = " + key + ", " +
      "value = " + value);
  }

  static void iterateKeys(Locale currentLocale) {
    ResourceBundle labels = ResourceBundle.getBundle("LabelsBundle",
currentLocale);
    Enumeration bundleKeys = labels.getKeys();

    while (bundleKeys.hasMoreElements()) {
      String key = (String) bundleKeys.nextElement();
      String value = labels.getString(key);
      System.out.println("key = " + key + ", " + "value = " + value);
    }
  }

  static public void runme() {
    Locale[] supportedLocales = { Locale.FRENCH, Locale.GERMAN, Locale.ENGLISH
};

    for (int i = 0; i < supportedLocales.length; i ++) {
      displayValue(supportedLocales[i], "s2");
    }

    System.out.println();
    iterateKeys(supportedLocales[0]);
  }
}
/
show errors

create or replace procedure bundle_demo as language java name
'ResourceBundleDemo.runme()';
/
show errors

And then:

# This is the LabelsBundle_fr.properties file
s1 = ordinateur
s2 = disque
s3 = surveillent
s4 = clavier

# This is the LabelsBundle_de.properties file
s1 = Computer
s2 = Platte
s3 = Monitor
s4 = Tastatur

# This is the default LabelsBundle.properties file
s1 = computer
s2 = disk
s3 = monitor
s4 = keyboard

May 17, 2002 Chun -- Thanks for the question regarding "How to auto increment a column - like a sqlserver Identity or Informix serial#", version 8.0.5
You Asked
Hi Tom,

How can I automatic insert a serial number in the column when
inserting a row into a table?

Thanks,

Chun
and we said...
The answer in Oracle is to use a sequence + trigger.  It might
look like this:

ops$tkyte@8i> create table t ( x int primary key, y int );
Table created.

ops$tkyte@8i> create sequence myseq;
Sequence created.

ops$tkyte@8i> create trigger t_trigger
  2  before insert on t for each row
  3  begin
  4          if ( :new.x is null ) then
  5                  select myseq.nextval into :new.x from dual;
  6          end if;
  7  end;
  8  /
Trigger created.

I used "if ( :new.x is null ) then..." in order to allow the
application to supply a value for the primary key if it desires.
 You may leave that off -- it would then assign a unique value
for EVERY row

ops$tkyte@8i> insert into t (x,y) values (-1,2);
1 row created.

ops$tkyte@8i> insert into t (y)   values (3);
1 row created.

ops$tkyte@8i> select * from t;

         X          Y
---------- ----------
        -1          2
         1          3

ops$tkyte@8i>
Review & Followup
Rating: 5
Most Helpful. February 12, 2002
Reviewer: Christian Maslen from Sydney, Australia.
Excellent and to the point.

The Oracle docs said you couldn't assign the nexval in a trigger so I didn't
think to use a select from dual into the transition variable.

Rating: 3
Auto number trigger May 16, 2002
Reviewer: Calvin Chu from USA
I got an error message like this: "Warning: Trigger created with compilation
errors." when compiling the trigger to do auto numbering.

After inserting a value into the table, I got "ORA-04098: trigger XXX is invalid
and failed re-validation".

Please help!!

Followup:
show errors trigger TRIGGER_NAME

and fix your coding error.

Rating: 5
Why Oracle doesn't provide something like identity May 17, 2002
Reviewer: Moorthy Rekapalli from Atlanta GA USA
Tom,

First of all, let me thank you for your excellent responses.  I learned a lot
from your site.  In my view, you are doing a great service to the Oracle
community.

Generating a sequence number for primary key is very common in lot of
applications.  It would be great if Oracle provided the capability like identity
in SQL Server.  This eliminates the need for everybody on the planet writing
before insert trigger to generate a primary key.

I hope that Oracle development team is reading this post and add this feature in
a future release.

Rating: 5
I hope that Oracle development team is not!!! May 17, 2002
Reviewer: J from ma
create or replace function emp_ident return number is
l_return number;
begin
 select id.nextval
   into l_return
   FROM dual;
 if mainoff then l_return = l_return +1000000; end if;
 if othoff then then l_return = l_return +2000000;end if;
 --if what>> then then l_return = l_return +3000000;
 return l_return ;
end;
/
insert into emp(prim_key, first) values (emp_ident,'Joe');
/
-- disclaimer - not tested half ranting
write your own!!!

Rating: 5
April 12, 2003
Reviewer: A reader
thanks Tom.  very useful.

May 17, 2002 Santhosh -- Thanks for the question regarding "Create users thru Developer 2000", version 7,8 x
You Asked
Hi Tom,

I have read a great deal of questions from ur site. It's indeed quite nice to
read ur answers & it's applicability.

Kindly answer these questions for me :

1. I have a commercial project in which i want to include the Auditing features.

How do I create a User from Developer 2000 (Forms) in Oracle Database ? I want
to track the user name who made the changes to the records.

2. What is the best way to create an Auditing session / procedure in Developer
2000 for a commercial project ?

3. Should the Username & password for an application be stored in a database or
should the user directly connect using his oracle Username & password, so that
Oracle itself will take care of the security. But, the problem here is, he can
gain access to the database records thru SQL*Plus. I do not want the user to
enter thru sql*plus or any other way to the database other than my application.

Thanks in advance.

Santhosh
and we said...
1) that seems to be two entirely different questions???

To create a user, you might:

  begin
      forms_ddl( 'create user ' || username || ' identified by ' || password );
  end;

to track the user who made the change -- just use the USER function, it'll
return the name of the user who is logged in.

2) People typically use triggers to record the changes automatically, eg:

create or replace trigger t_bifer
before insert on T for each row
begin
   :new.created_by := USER;
   :new.created_on := sysdate;
end;
/

create or replace trigger t_bufer
before update on T for each row
begin
   :new.last_updated_by := USER;
   :new.last_updated_on := sysdate;
end;
/

would track the initial creator of the record and the name of the last person to
update it.  It does not matter what environment/tool they do this update via --
it'll be audited.

3) that'll be upto you to decide.  Me, I like to use the database to do the
work.  You can consider using a password protected role that your form enables
instead of a common username/password.
Review & Followup
Rating: 1
Ambiguity .... May 16, 2002
Reviewer: Riaz Shahid from Islamabad,Pakistan
Hello Tom!

I have a form which creates the users. Now i want to assign a role to the user
being created. For this i had the following code:

Create Role Supervisor;
create role Bankkpo;
Create Role ReturnKPo;
Grant create session to supervisor,ReturnKPO,Bankkpo;
--------------------------------------------------------
--------------------- FOR Return KPOs
--------------------------------------------------------------------------------
GRANT SELECT              ON ST.ST_BANKS TO ReturnKPO;
GRANT SELECT             ON ST.city TO ReturnKPO;
GRANT SELECT             ON ST.st_office TO ReturnKPO;
GRANT SELECT, INSERT         ON st.st_profile TO ReturnKPO;
GRANT SELECT, INSERT         ON st.st_return to ReturnKPO;

--------------------------------------------------------
--------------------- FOR Bank KPOs ------------------
--------------------------------------------------------
GRANT SELECT              ON ST.ST_BANKS TO BankKPO;
GRANT SELECT             ON ST.city TO BankKPO;
GRANT SELECT             ON ST.st_office TO BankKPO;
GRANT SELECT, update         ON st.st_profile TO BankKPO;
GRANT SELECT, update         ON st.st_return to BankKPO;

-----------------------------------------
------------ FOR SUPERVISORs ------------
-----------------------------------------
GRANT SELECT              ON SYS.DBA_ROLE_PRIVS TO SUPERVISOR ;
GRANT all              ON ST.ST_BANKS TO SUPERVISOR ;
GRANT all             ON ST.city TO SUPERVISOR;
GRANT all             ON ST.st_office TO SUPERVISOR;
GRANT all                    ON st.st_profile TO SUPERVISOR;
GRANT all                    ON st.st_return to SUPERVISOR;

Then create a procedure as follows:

CREATE  OR REPLACE  Procedure create_user(pusername in varchar2,ppassword in
varchar2,put in number) iS
v_numrows integer;
cursor_name INTEGER;
STRING VARCHAR2(100);
v_role varchar2(30);
begin
  execute immediate 'Create User '||pusername||
  ' Identified by '||ppassword||
  ' Default Tablespace system Temporary Tablespace TEMP';
  if put=1 then
   v_role:='ReturnKPO';
  elsif put=2 then
   v_role:='SUPERVISOR';
   execute immediate 'Create     synonym '||pusername||'.'||'DBA_ROLE_PRIVS for
SYS.DBA_ROLE_PRIVS';
elsif put=3 then
v_role:='BANKKPO';
end if;
execute immediate  'Grant '||v_role||' to '||pusername;
if put in(1,2,3) then
execute immediate 'Create synonym '||pusername||'.'||'city for st.city';
execute immediate 'Create synonym '||pusername||'.'||'st_banks for st.st_banks';
execute immediate 'Create synonym '||pusername||'.'||'st_office for
st.st_office';
execute immediate 'Create synonym '||pusername||'.'||'st_profile for
st.st_profile';
execute immediate 'Create synonym '||pusername||'.'||'st_return for
st.st_return';
end if;
end;
/

But when i run that procedure, it gives me following error:

st@ORACLE>  execute create_user('riaz','riaz',1);
BEGIN create_user('riaz','riaz',1); END;

*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at "ST.CREATE_USER", line 21
ORA-06512: at line 1

What is the cause of that....?

rEGARDS rIAZ

Followup:
see
http://asktom.oracle.com/~tkyte/Misc/RolesAndProcedures.html
you either need create user or create synonym granted directly to you.

Rating: 2
Problem in Dev2000 USer Creation May 17, 2002
Reviewer: Riaz Shahid from ISlamabad, Pakistan
Hi Tom!

Thanks for your repsonse...

I did solved the problem of "insufficient privilages" for the procedure
"create_user" in the SQLPLUS.

But here another problem: When i try to create the user through form (from the
same user as that of SQLPLUS), it gives me message "Insufficient Privilages"...
What is the cause for that?

WHY THE SAME PROCEDURE SUCCESSFULL RUN ON SQLPLUS AND NOT RUNNING THROUGH FORM ?

Regards

Riaz Shahid

Followup:
Take take this the wrong way but...

I THINK YOU ARE IN DIFFERENT DATABASES (i'll stop shouting now) when you tested
in sqlplus vs forms.

SQLPlus is just a client.
Forms is just a client.

If it ran in plus, it'll run in forms.  Take your time, check out your database
connection.  Verify you are in the same instance AS the same user (eg: if you
have multiple copies of this procedure in different schemas, that could be the
issue as well)

Rating: 4
Very good one there May 17, 2002
Reviewer: Godwin from Ghana
But I will be interested in seeing the solution implemented in
Developer2000/Forms 6i as asked by  Riaz Shahid.
Thanks.

Followup:
The solution *is right here*.  Nothing more to be added.  It is done.

Rating: 5
Execllent November 06, 2002
Reviewer: A Reader from Pakistan
Thats was very useful.

May 17, 2002 jim -- Thanks for the question regarding "Show Users using DB(s) from UNIX OS command line", version 8.1.5
You Asked
Using Oracle versions 734, 815, and 816 on OS HP-UX 10.20 and
11.00 platforms.
Searching for an Oracle/utility or script – to report or
screen/print users logged into a server/DB(s) using SQL/server
(from NT workstation’s/application’s).
I’m wanting to execute the utility/script from the OS level
without logging into SvrMgr.  I understand  sybase has a utility
that does this that is called sp_who.
Thank you in advance for your DBA site / query support!
and we said...
sp_who is a stored procedure that is run inside of Sybases isql
utility.

In order to see who is logged in, we are going to have to use
svrmgrl or sqlplus. I would just as soon use sqlplus.

Here is a .cmd file to do just that:

(this should ALL be on one big line)

echo select username, status, last_call_et,
to_char(logon_time,'dd-mon hh24:mi') from v$session where
username is not null; | sqlplus user/pw@%1

Now, at the command line I can:

C:\> whoson ora8i.us.oracle.com

SQL*Plus: Release 8.1.6.0.0 - Production on Thu Feb 15 19:14:31
2001

(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Connected to:
Oracle8i Enterprise Edition Release 8.1.6.2.0 - Production
With the Partitioning option
JServer Release 8.1.6.2.0 - Production

system@8i>
USERNAME                      STATUS   LAST_CALL_ET TO_CHAR(LOGO
----------------------------- -------- ------------ ------------
PUBLIC_USER                   INACTIVE            0 15-feb 19:11
WEB$MEWILSON                  INACTIVE           71 15-feb 19:13
OLR                           INACTIVE         6597 15-feb 13:39
PUBLIC_USER                   INACTIVE            1 15-feb 19:11
PUBLIC_USER                   INACTIVE            1 15-feb 19:13
WEB$JSALCEDO                  INACTIVE          127 15-feb 19:12
PUBLIC_USER                   INACTIVE            1 15-feb 19:14
PUBLIC_USER                   INACTIVE            1 15-feb 19:12
......

the last_call_et tells me in seconds how long the person has
been in their status -- OLR has been inactive for almost 2 hours
for example.

Same script will work virtually the same for Unix -- just need
to quote it a little differently.
Review & Followup
Rating: 4
Reader May 17, 2002
Reviewer: A reader
Tom,

I am constructing a "date format" of alert<sid>.log type and pass it on to
a sed unix script

ksh script 1 :
--------------

fromdt=`sqlplus -s internal <<EOF
set heading off trimsp on
select to_char(sysdate-14,'Dy Mon')||' '||replace(to_char(sysdate-14,'dd'),'0','
') from dual;
EOF`
echo "/$fromdt/,\${"

Script1 returns

/                <==== extrsa <CR>
Fri May  3/,$

ksh script 2 :
--------------

(sqplus -s internal <<EOF
select 'TAG '||to_char(sysdate-14,'Dy Mon')||'
'||replace(to_char(sysdate-14,'dd'),'0',' ') from dual;
EOF
)|grep 'TAG '|read TAG fromdt
echo "/$fromdt/,\${"

Script2 returns:

/Fri May  3/,$    <==== This is what I want

In the first script, it seems Sqlplus has output a <CR> as the first character.
The second scripts has
successfully stripped the <CR>. Is this the only way to get the desired result
or any
simpler way to do it

Thanks

Followup:
I pretty much use the grep trick myself to get just what I want.  it *always*
works and is not "hard" (seeing as how you actually did it already).

I usually pipe to 'sed s/^TAG //' myself...

May 17, 2002 Shivaswamy -- Thanks for the question regarding "This Query", version 817
You Asked
Tom,
I want to list the size of the datafiles of the tablespaces which have more than
one datafiles. While I can get their names by following query, I can't get other
info like size or autoextensible. Can you pl. help me..?

SELECT TABLESPACE_NAME FROM (
SELECT TABLESPACE_NAME,  COUNT(*) ctr
FROM DBA_DATA_FILES
GROUP BY TABLESPACE_NAME
ORDER BY 2 DESC ) WHERE CTR  > 1;

TIA,
Shivaswamy
and we said...
select *
 from dba_data_files ddf1
 where exists ( select null
                  from dba_data_files ddf2
                 where ddf1.tablespace_name = ddf2.tablespace_name
                    and ddf1.file_name <> ddf2.file_name
               );
Review & Followup
Rating: 5
May 17, 2002
Reviewer: A reader
Thanks, TOM.

Rating: 4
EXISTS v/s IN May 17, 2002
Reviewer: shivaswamy from Dallas, TX
Tom,

My database has 1588 datafiles. Can "EXISTS v/s IN"  make sooo much of
difference..?

CASE 1:
11:12:52 SQL> select *
11:13:10   2   from dba_data_files ddf1
11:13:10   3   where exists ( select null
11:13:10   4                    from dba_data_files ddf2
11:13:10   5                   where ddf1.tablespace_name = ddf2.tablespace_name
11:13:11   6                      and ddf1.file_name <> ddf2.file_name
11:13:11   7                 );

FILE_NAME                                                  FILE_ID
TABLESPACE_NAME                     BYTES     BLOCKS STATUS
------------------------------------------------------- ----------
------------------------------ --
RELATIVE_FNO AUT   MAXBYTES  MAXBLOCKS INCREMENT_BY USER_BYTES USER_BLOCKS
===== i hve cut the output here =========

33 rows selected.
Elapsed: 00:26:1591.70

CASE 2:
select tablespace_name, file_name, trunc(blocks*16/1024/1024, 2) "Gig",
Autoextensible "Auto_Extend"
from dba_data_files
where tablespace_name in (
select ddf1.tablespace_name
                  from dba_data_files ddf1, dba_data_files ddf2
                 where ddf1.tablespace_name = ddf2.tablespace_name
                    and ddf1.file_name <> ddf2.file_name)
order by 4, 3
/
TABLESPACE_NAME                FILE_NAME
             Gig Aut
------------------------------
------------------------------------------------------- ---------- --
MASTER_DATA_GOOD_267           X:\ORACLE\ORADATA\DGDR\MASTER_DATA_GOOD_267.DBF
            1.71 YES
MASTER_DATA_GOOD_267           L:\ORACLE\ORADATA\DGDR\MASTER_DATA_267_2.DBF
               0 YES
===== i hve cut the output here =========

33 rows selected.
Elapsed: 00:00:02.40

Thanks,
Shivaswamy

Followup:
sure, these are complex views underneath.

What might actually be better (might not, don't know) is:

select *
  from dba_data_files
 where tablespace_name in (select tablespace_name
                             from dba_data_files
                            group by tablespace_name
                            having count(*) > 1 );

May 17, 2002 Alan -- Thanks for the question regarding "Nested Table of Objects vs. Reference Cursor to Interact with Java", version 8.1.7.3
You Asked
Our situation:

We are building a Swing application using Java.
The application will be calling PL/SQL packages for the majority of DB-based
processing.
Most of the time we will be passing back multiple rows of information from
PL/SQL to Java.
Ninety percent of the time we will be passing back less than forty rows of data.
Most of the PL/SQL side processing is multi-table with conditional logic and
calls to "get" procedures included in the data gathering process.

We see two viable options for passing the data:
1) Use reference cursors.  We would use a function that returns an object to
handle the conditional logic and to avoid a big union.
2) Use a nested table of objects.

I can't find an opinion from both a Java perspective and PL/SQL perspective of
which is best.   Do you have any experience?

Thanks,
Alan Slay
als@akc.org
and we said...
If it can be solved using ref cursors - go for it.  That is the most natural way
to interact with the data.
May 18, 2002 Jay -- Thanks for the question regarding "Security policy of password expiry causes problems", version 9.0.1
You Asked
Tom

I have been asked to implement a security policy on a system that will have many
users. One of the requirements of the system is that the password is expired on
creation and after every 90 days, prompting the user for a new one - fairly
standard. The issue is that the Application used to access the database is
Business Objects and I have been told by them that it can't handle this
situation. I believe the Oracle error is "ORA-28001: the password has expired".
If the application was clever, it would check for this Oracle error and pop up a
little form to retrieve the relevant data etc. Can I trap this exception at
logon and handle it in some other way - perhaps send an email to the user and an
administrator to get them to change the password, or perhaps get the system to
auto change the password and email the new logon details to the user and the
Administrator. Do you have any other ideas?

Once again, thanks in anticipation of your invaluable advice!

Cheers
Jay
and we said...
Sure, you can have an on-logon trigger that would inspect the EXPIRY_DATE in the
dba_users table.

You might create a table like this:

create table user_notified ( username varchar2(30), expiry_date date );

and in the logon trigger, query dba_users to find if this user is set to expire.
 If they are -- check this other table to see if they were already notified and
if not

o insert a row
o schedule a DBMS_JOB job that will send them an email (using utl_smtp)
o commit

That way, you won't send them more then one email and the insertion of the row
and sending of the email will be a "transaction" (either both happen or neither
happens -- you can rely on dbms_job to send the email in the background and if
it fails, you'll be notified of that via dba_jobs AND the alert log)
Review & Followup
Rating: 4
I'll give that a go May 22, 2002
Reviewer: Jay from Scotland
I'll have a go at that - just another though (relevant to my app) Since Busness
Objects uses an Oracle database as it's repository, I could look at updating the
BO password as part of the process, thereby removing the requirement for an
administrator to manually change the BO password at the same time.

Thanks
Jay

May 18, 2002 gaurang -- Thanks for the question regarding "physical reads ", version 8.1.7
You Asked
Hi Tom,

If my query plan return this value

          0  recursive calls
         24  db block gets
        828  consistent gets
      10000  physical reads  <<--------------------
          0  redo size
    4733537  bytes sent via SQL*Net to client
     170255  bytes received via SQL*Net from client
       1532  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      22962  rows processed

and

          0  recursive calls
         24  db block gets
    1000000  consistent gets <<-----------------
         10  physical reads
          0  redo size
    4733537  bytes sent via SQL*Net to client
     170255  bytes received via SQL*Net from client
       1532  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      22962  rows processed

Which plan is better and why?

 As far as i know physical reads is very expensive OVER consistent gets(Am i
right?),So i think the bottom one is good.

Can you explain?

Thanks,
and we said...
Interesting -- I just now answered another question that shows this same
effect.

Consider:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3109440977616

There, I show by that simply upping the hash_area size, a query goes from:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select       * from t t1, t t2 where
t1.object_id = t2.object_id;

22962 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=129 Card=22962 Bytes=4041312)
   1    0   HASH JOIN (Cost=129 Card=22962 Bytes=4041312)
   2    1     TABLE ACCESS (FULL) OF 'T' (Cost=10 Card=22962 Bytes=2020656)
   3    1     TABLE ACCESS (FULL) OF 'T' (Cost=10 Card=22962 Bytes=2020656)

Statistics
----------------------------------------------------------
          0  recursive calls
         24  db block gets
        828  consistent gets
        714  physical reads
          0  redo size
    4733537  bytes sent via SQL*Net to client
     170255  bytes received via SQL*Net from client
       1532  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      22962  rows processed

.......

(surprising that you have also 4733537  bytes transmited, hmmm, wonder where you
got your info from!! ;)

It is a trick question here -- the plans are the same, the ENVIRONMENT was
different!!

It is a truly trick question though.  Given the example I used -- taking the
hash area size from 2m to 20m it could be that the FIRST query was better (with
2m and 714 physical writes) because by using 20m of ram I forced the system to
swap (hypothetically speaking here).  The 20m hash area size query
appears better from the statistics -- but the system was swapping like crazy
so it wasn't better.

The answer to this is a big non-answer.  I fall back to the same old "it
depends".  So, let's put this on to the runstats test
http://asktom.oracle.com/~tkyte/runstats.html
and see what happens on my system:

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_dummy number;
  3      l_start number;
  4  begin
  5      insert into run_stats select 'before', stats.* from stats;
  6
  7      l_start := dbms_utility.get_time;
  8          execute immediate 'alter session set hash_area_size = 2048000';
  9
 10      for x in ( select t1.OWNER OWNER1, t1.OBJECT_NAME OBJECT_NAME1,
t1.SUBOBJECT_NAME SUBOBJECT_NAME1,
 11                        t1.OBJECT_ID OBJECT_ID1, t1.DATA_OBJECT_ID
DATA_OBJECT_ID1, t1.OBJECT_TYPE OBJECT_TYPE1,
 12                        t1.CREATED CREATED1, t1.LAST_DDL_TIME LAST_DDL_TIME1,
t1.TIMESTAMP TIMESTAMP1,
 13                        t1.STATUS STATUS1, t1.TEMPORARY TEMPORARY1,
t1.GENERATED GENERATED1, t1.SECONDARY SECONDARY1,
 14                        t2.OWNER OWNER2, t2.OBJECT_NAME OBJECT_NAME2,
t2.SUBOBJECT_NAME SUBOBJECT_NAME2, t2.OBJECT_ID OBJECT_ID2,
 15                        t2.DATA_OBJECT_ID DATA_OBJECT_ID2, t2.OBJECT_TYPE
OBJECT_TYPE2, t2.CREATED CREATED2,
 16                        t2.LAST_DDL_TIME LAST_DDL_TIME2, t2.TIMESTAMP
TIMESTAMP2, t2.STATUS STATUS2, t2.TEMPORARY TEMPORARY2,
 17                        t2.GENERATED GENERATED2, t2.SECONDARY SECONDARY2
 18                       from t t1, t t2 where t1.object_id = t2.object_id )
 19          loop
 20                  null;
 21          end loop;
 22
 23      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 24
 25
 26      insert into run_stats select 'after 1', stats.* from stats;
 27      l_start := dbms_utility.get_time;
 28          execute immediate 'alter session set hash_area_size = 20480000';
 29
 30      for x in ( select t1.OWNER OWNER1, t1.OBJECT_NAME OBJECT_NAME1,
t1.SUBOBJECT_NAME SUBOBJECT_NAME1,
 31                        t1.OBJECT_ID OBJECT_ID1, t1.DATA_OBJECT_ID
DATA_OBJECT_ID1, t1.OBJECT_TYPE OBJECT_TYPE1,
 32                        t1.CREATED CREATED1, t1.LAST_DDL_TIME LAST_DDL_TIME1,
t1.TIMESTAMP TIMESTAMP1,
 33                        t1.STATUS STATUS1, t1.TEMPORARY TEMPORARY1,
t1.GENERATED GENERATED1, t1.SECONDARY SECONDARY1,
 34                        t2.OWNER OWNER2, t2.OBJECT_NAME OBJECT_NAME2,
t2.SUBOBJECT_NAME SUBOBJECT_NAME2, t2.OBJECT_ID OBJECT_ID2,
 35                        t2.DATA_OBJECT_ID DATA_OBJECT_ID2, t2.OBJECT_TYPE
OBJECT_TYPE2, t2.CREATED CREATED2,
 36                        t2.LAST_DDL_TIME LAST_DDL_TIME2, t2.TIMESTAMP
TIMESTAMP2, t2.STATUS STATUS2, t2.TEMPORARY TEMPORARY2,
 37                        t2.GENERATED GENERATED2, t2.SECONDARY SECONDARY2
 38                       from t t1, t t2 where t1.object_id = t2.object_id )
 39          loop
 40                  null;
 41          end loop;
 42      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 43
 44      insert into run_stats select 'after 2', stats.* from stats;
 45  end;
 46  /
966 hsecs
434 hsecs

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value-a.value run1,
c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11   order by abs( (c.value-b.value)-(b.value-a.value))
 12  /

NAME                                 RUN1       RUN2       DIFF
------------------------------ ---------- ---------- ----------
...
LATCH.messages                         18          8        -10
STAT...enqueue releases                17          2        -15
STAT...enqueue requests                18          2        -16
LATCH.sort extent pool                 19          0        -19
STAT...prefetched blocks               25          0        -25
LATCH.enqueue hash chains              32          4        -28
STAT...free buffer requested           30          2        -28
STAT...redo size                    21424      21392        -32
LATCH.enqueues                         65          9        -56
STAT...recursive cpu usage            303        380         77
LATCH.cache buffers lru chain         612        314       -298
STAT...physical reads direct          714          0       -714
STAT...physical writes non che        714          0       -714
ckpoint

STAT...physical writes                714          0       -714
STAT...physical writes direct         714          0       -714
STAT...physical reads                 741          0       -741
STAT...session logical reads         3554      23338      19784
STAT...buffer is not pinned co       3503      23293      19790
unt

STAT...table scan blocks gotte       3487      23277      19790
n

STAT...no work - consistent re       3495      23285      19790
ad gets

STAT...consistent gets               3506      23297      19791
LATCH.cache buffers chains           8055      46751      38696
STAT...session pga memory         1995120    7548992    5553872
STAT...session pga memory max     1995120    7548992    5553872
STAT...session uga memory max     1812652    7513016    5700364

47 rows selected.

Here we can see that while the first does more physical IO, the second tends to
do a bit more latching.  In isolation, the second will best the first (if there
is sufficient ram of course).  In scaling up -- maybe not so (as latches = locks
= serialization).

So, it is still a big "well, it just depends"
Review & Followup
Rating: 4
why physical reads can be bigger than consistent gets? May 17, 2002
Reviewer: Mikito Harakiri
Also, when I see entries like this in 10046 trace

WAIT #1: nam='db file sequential read' ela= 9399 p1=73 p2=14221 p3=1
WAIT #1: nam='db file sequential read' ela= 13598 p1=73 p2=10141 p3=1
WAIT #1: nam='db file sequential read' ela= 8203 p1=73 p2=15471 p3=1
WAIT #1: nam='db file sequential read' ela= 8421 p1=72 p2=40345 p3=1

is every of them a consistent get?

The answer that would be very easy to me to understand is something like:
"consistent gets is an aggregate count of ... events from 10046 trace". (And
btw, for some reason I fail to understand why the arraysize matters, it is just
a client parameter after all).

Followup:
Not all physical reads are consistent gets -- reads from a temporary segment
won't be for example (this example here is a case of that)

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:880343948514
for why arraysize matters.

May 18, 2002 -- Thanks for the question regarding "job elapsed time", version 8.1.7
You Asked
hi Tom

I want to know how long did my job take to finish so I checked dba_jobs however,
for example for a statspack job it takes 2800 units. I read manual and it says
that the units are in seconds which sounds unbelievable!
and we said...
it is cumulative -- for all executions of the job.

If you need job run by job run numbers -- you'll need to keep a history table,
eg, shedule:

  insert into t ( id, name, start_time values ( my_job_seq.nextval,
                                                'job_name', sysdate );
  the_procedure_to_run;
  update t set end_time = sysdate where id  my_job_seq.currval;
  commit;

instead of just

   the_procedure_to_run;
May 18, 2002 Schoeib -- Thanks for the question regarding "Oracle 9i and PHP", version 9i
You Asked
Hi Tom,
I have ínstalled Oracle 9i successfully.
After installing the database I configure the httpd.conf file of the
(Oracle)Apache Server
for using PHP.
But when I start Apache I become the following error:

[warn] Loaded DSO d:/php4/sapi/php4apache.dll uses plain Apache 1.3 API, this
module might crash
under EAPI! (please recomplie it with -DEAPI)

I use the newest PHP version 4.2.1.

What is my mistake??
Or is there another way to make PHP running under the
Apache Web Server from Oracle??

I hope you can help me.

thanxx

Schoeib
and we said...
see
http://www.geocrawler.com/archives/3/5/2002/1/0/7710484/
for example (search google for

php4apache apache 1.3 api eapi

) to find many other hits on this issue with apache.
Review & Followup
Rating: 3
Oracle 9i and PHP May 18, 2002
Reviewer: Schoeib Amin from Frankfurt/germany
Hi Tom,
first thanx for your answer.
I searched really all sites I found to this topic, but
the only thing I found is that you have to recompile
php4apache.dll. But how and with which compiler you don't
find anywhere.

I hope that maybe you know how!

thanxx

Schoeib

Followup:
Nope, I'm a unix guy who has never considered using php.  Sorry -- you'll have
to ask a windows/apache guy (try the newsgroups, free access on google)

Rating: 4
How to compile PHP May 19, 2002
Reviewer: Vande Matram from NJ, USA.....Mera dil he Hindustan me!
Hi Schoeib,
You might find the answer you are looking for at,
http://www.enderunix.org/documents/eng/php_oracle_en.html
It shows you how to compile, etc. Good luck.

This and other useful articles can be found at
http://www.dbresources.com/modules.php?name=db_articles

May 18, 2002 Daya -- Thanks for the question regarding
      "Scheduling Jobs?", version 8.1.5

      You Asked
Hi Tom!
  I have a question regarding Job scheduling. I want to schedule a job say
(Procedure X) to run on every weekend (Saturday) at a given time (say 5:30 pm).
   How can we do this?
Thanks
Daya

      and we said...
Use an interval of

trunc( next_day( sysdate, 'SAT' ) )+17.5/24

If you are concerned about internationalization, use

to_char( to_date('05012002','ddmmyyyy'), 'DY' )

instead of SAT.
Review & Followup

      Rating: 5
also....
      ;)  May 19, 2002
Reviewer:  Yogeeraj
       from Mauritius
....and go through instructions as in the Documentation supplied by Oracle
(Supplied PLSQL Packages)

<quote>
SUBMIT Procedure
This procedure submits a new job. It chooses the job from the sequence
sys.jobseq.
Syntax
    DBMS_JOB.SUBMIT (
        job OUT BINARY_INTEGER,
        what IN VARCHAR2,
        next_date IN DATE DEFAULT sysdate,
        interval IN VARCHAR2 DEFAULT ’null’,
        no_parse IN BOOLEAN DEFAULT FALSE,
        instance IN BINARY_INTEGER DEFAULT any_instance,
        force IN BOOLEAN DEFAULT FALSE);

Parameters
----------------------------------------
Table 17-2 SUBMIT Procedure Parameters
----------------------------------------
Parameter    Description
~~~~~~~~~~~    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
job         Number of the job being run.

what         PL/SQL procedure to run.

next_date     Next date when the job will be run.

interval     Date function that calculates the next time to run the job. The
        default is NULL. This must evaluate to a either a future point in
        time or NULL.
no_parse     A flag. The default is FALSE. If this is set to FALSE, then Oracle
        parses the procedure associated with the job. If this is set to
        TRUE, then Oracle parses the procedure associated with the job
        the first time that the job is run.

        For example, if you want to submit a job before you have
        created the tables associated with the job, then set this to TRUE.

instance     When a job is submitted, specifies which instance can run the
        job.

force         If this is TRUE, then any positive integer is acceptable as the
job
        instance. If this is FALSE (the default), then the specified
        instance must be running; otherwise the routine raises an
        exception.

Usage Notes
The parameters instance and force are added for job queue affinity. Job queue
affinity gives users the ability to indicate whether a particular instance or
any
instance can run a submitted job.

Example
This submits a new job to the job queue. The job calls the procedure DBMS_
DDL.ANALYZE_OBJECT to generate optimizer statistics for the table
DQUON.ACCOUNTS. The statistics are based on a sample of half the rows of the
ACCOUNTS table. The job is run every 24 hours:

VARIABLE jobno number;
BEGIN
DBMS_JOB.SUBMIT(:jobno,
’dbms_ddl.analyze_object(’’TABLE’’,
’’DQUON’’, ’’ACCOUNTS’’,
’’ESTIMATE’’, NULL, 50);’
SYSDATE, ’SYSDATE + 1’);
commit;
END;
/
Statement processed.
print jobno

JOBNO
----------
14144

</quote>

Hope this helps.

Best Regards
Yogeeraj


Subject | Article | Last Updated (East coast time)
Stand By database | article only | May 21, 2002 08:16 am
Defragmentation of Tablespaces | article only | May 21, 2002 08:25 am
Pinning Java Stored Procedure | article only | May 22, 2002 07:56 am
Index Orgenized Table chained rows | article only | May 22, 2002 10:45 pm
How to use 'long' dataType? | article only | May 23, 2002 07:54 am
Doubts on Import/Export + temprary tablespace+shutdown/startup | article only | May 23, 2002 03:33 pm
Decode function | article only | May 23, 2002 03:35 pm
RE:Chained and migrated rows | article only | May 23, 2002 03:38 pm
Dynamic SQL | article only | May 24, 2002 09:13 am
Reg :- Trigger Execution | article only | May 24, 2002 09:41 am
row chaining | article only | May 24, 2002 10:52 am
Question on indexing strategy | article only | May 24, 2002 11:21 am
Qustions about CROSS TAB queries/reports | article only | May 24, 2002 12:03 pm
Primary Keys | article only | May 24, 2002 08:09 pm
Sql Loader Problem while Loading data from a flat file | article only | May 24, 2002 08:12 pm
Index/Data for Very Large Object | article only | May 24, 2002 08:15 pm
Collapsing one column on an effective-dated table (using CONNECT BY?) | article only | May 24, 2002 08:33 pm
VLDB Performance | article only | May 24, 2002 08:35 pm
Database Link security question | article only | May 25, 2002 09:03 am
checking for null values | article only | May 25, 2002 09:06 am
Rows locked before Commit when open and close Cursor with FOR UPDATE select ?? | article only | May 25, 2002 09:07 am
Using Java stored procedure to execute stored procedures in remote database | article only | May 26, 2002 10:38 am
Oracle Text's surprising behaviour | article only | May 26, 2002 10:51 am
Procedures, Parameters, Parameter Modes | article only | May 26, 2002 11:18 am
how does this SQL run | article only | May 26, 2002 02:53 pm
A tool for data inflation for volume tests | article only | May 26, 2002 04:42 pm
Is there a difference in which privileges 'system' has when running a pl/sql procedure and anonymous blocks? | article only | May 27, 2002 09:37 am
Recovery !!!! | article only | May 27, 2002 08:17 pm
row(s) 1 - 28 of 28
May 21, 2002 Yogesh -- Thanks for the question regarding "Stand By database", version 8.1.7
You Asked
When we configure the standby database, how can we have same database names
(i.e. the name of the production database and standby database) this is
essential requiremenat if both of the databases are on same machine !!
and we said...
read:
http://technet.oracle.com/doc/oracle8i_816/server.816/a76995/standbys.htm#29841
it goes through all of the steps.  The db_names can (have to be) the same.  All
of the steps for doing this are documented fully there.

Hopefully you are doing this just to TEST.  It makes absolutely ZERO (none,
nada) sense to have a standby on the same machine as production.  It would be an
utter waste of time unless this is just a test.
Review & Followup
Rating: 5
A challenge to 'nada' October 09, 2001
Reviewer: Connor from Scotland
I would contend that you could use a standby (even on the same machine) for
reporting purposes - but yes, I happily concede that under most circumstance it
does not get you a lot of benefit

Rating: 3
Thanx October 09, 2001
Reviewer: Yogesh Bhandarkar from PUNE, INDIA
Thanx for the useful link. Definately I'm doing it for testing purpose.

Rating: 3
What if we are using Jdbc to make a connection May 18, 2002
Reviewer: Nihal from S'pore
Hi,
Thanks for the information, but what if we are using Jdbc thin drivers to make a
connection, can we connect to the standby database without making any changes to
the application code.
As the machine name port etc is specified in the application server's
datasource.

Followup:
Well, you could

o update DNS so the standby takes over the primaries hostname
o use a hosts file locally on the app server to do the same
o get info like this from a properties file (you aren't HARD CODING the connect
string in your apps are you???)

Rating: 5
May 19, 2002
Reviewer: Tony Chung from Republic of China
Dear Tom,

When configured properly, standby databases provide fast and sophisticated
unplanned outage protection. But UNRECOVERABLE or NOLOGGING operations are not
propagated. How can a DBA develope a mechanism to detech and prevent from this
situation?

Best Regards

Followup:
9iR2 has a setting now to force all things to be logged, even if they are asked
to be "non-logged".

For now, you need to have "policies" in place to protect your production system
and review the V$DATAFILE view for UNRECOVERABLE_TIME to make sure no one is
slipping some by you.

May 21, 2002 Ravi -- Thanks for the question regarding "Defragmentation of Tablespaces", version 8.1.7
You Asked
Hello Tom,

Is there any other means by which one can defragment the tablespaces and utilize
the unused but allocated space without recreating them.
I searched but I found no good solutions except recreating them.
Please suggest,

Regards,
Sudhakar
and we said...
Use locally managed tablespaces with uniform extent sizes and you'll never ever
in a million years need to do this again.

It involves recreating them but it is 100% worth it since YOU'LL NEVER DO IT
AGAIN.

Create new tablespaces, use alter table move and alter index rebuild to move the
objects into the new tablespaces, nuke the old ones and forget about it.
Review & Followup
Rating: 4
May 20, 2002
Reviewer: Sudhakar from Delhi, India
Thanks, I got u Idea ....

Rating: 3
why does a dictionary managed and not uniform sized Tbs have fragments May 20, 2002
Reviewer: Frank from FL
could you pls explain it briefly. and why does local managed and uniform sized
TBS have no fragments. Thanks a lot

Followup:
In a dictionary managed tablespace, each and every extent in there might be a
different size.   If you drop a table in that tablespace, rebuild an index, move
a table, whatever -- you will leave behind empty extents of various sizes.
These "holes" are of some arbitrary size.

Now, the sum of the sizes of these extents might be 100m.  The largest might be
512k.  If some object comes along in that tablespace and says "I want my NEXT
extent and that is 1m" -- it will FAIL.  You do not have 1m of contigous space
available.  It cannot extent.  You have 100m free but it is usless.  It is too
fragmented to use.

Enter the locally managed tablespace with UNIFORM sizes.  Every extent is the
same size.  If you have 100m free and some object askes for a next extent - IT
WILL SUCCEED NO MATTER WHAT.  All extents are the same size -- we know that
every free extent is exactly the size we need.  There is no fragmentation of
free space.

Rating: 4
Answer to Frank May 21, 2002
Reviewer: Alper from Istanbul - Turkiye
Hi,

From oracle documentation..
"...LOCAL allows for automatic mapping of adjacent free extents "into a single
large extent eliminating the need for coalescing of free space... " can be the
answers you are looking for..

Alper

May 22, 2002 Peter -- Thanks for the question regarding "Pinning Java Stored Procedure", version 8.1.6
You Asked
Hi Tom:

We can pin stored Procedure into SGA, Can we pin java stored procedure into
Java_pool for increasing performance?

Do you have any recommendations to increase performance of Java stored
procedure?

Thanks,
Peter Cheng
and we said...
It is oh so extremely rare to have to pin anything these days -- only if you
have poorly written apps that do not use bind variables would you have to pin.
I am not aware of any mechanism to pin a java sp into the java shared pool.
Since it is a dedicated heap for java classes and such -- it is not full of sql,
plsql, etc, etc, it does not suffer from some of the issues that used to affect
the shared pool (with the addition of the large pool, if your apps are written
to use bind variables -- you don't need to pin them).

pinning never affected the runtime performance really, it just helped to avoid
4031 errors.

The best way to increase performance of java stored procedures is to have well
written java.  make sure you use callable/prepared statements if you are using
SQL (if you are doing lots of SQL, you would be well benefited by using PLSQL
instead).
Review & Followup
Rating: 3
Converse views................ May 22, 2002
Reviewer: Ashok from Seoul
Hi Tom
You are strongly advocating against pinning of procedures/packages. But every
other book on oracle suggests to implement it for normal big procedures/packages
used in the applications. To cap it all it is recommended in the Oracle
applications guide to pin the packages/procedures used in 11i applications.
Kindly deliberate more on this.

Thanks,
Ashok

Followup:
I'm against wasting time.  Once upon a time ago, pinning was needed -- over
time, it is not (in fact, the package itself says

  procedure keep(name varchar2, flag char DEFAULT 'P');
  --  Keep an object in the shared pool.  Once an object has been keeped in
  --    the shared pool, it is not subject to aging out of the pool.  This
  --    may be useful for certain semi-frequently used large objects since
  --    when large objects are brought into the shared pool, a larger
  --    number of other objects (much more than the size of the object
  --    being brought in, may need to be aged out in order to create a
  --    contiguous area large enough.
  --    WARNING:  This procedure may not be supported in the future when
  --    and if automatic mechanisms are implemented to make this
  --    unnecessary.

Now, since things are paged into the shared pool in small chunks, we have the
large pool for MTS/shared server memory allocation and general improvements over
the years -- we do not *need* to pin things.

I am a proponent of pinning WHEN there is an identified need to do so, not as a
matter of standard operating procedure.

May 22, 2002 Lee -- Thanks for the question regarding "Index Orgenized Table chained rows", version 8.1.7
You Asked
 Hi Tom,
I have a question regarding index orgenized tables.

I have a table script as follows:

CREATE TABLE KEY_FORM (
FORM_ID NUMBER NOT NULL,
FORM VARCHAR2 (500),
FORM_TYPE_ID NUMBER,
SITE_ID NUMBER NOT NULL,
INSERT_USER VARCHAR2 (100),
INSERT_TSP DATE DEFAULT SYSDATE,
UPDATE_USER VARCHAR2 (100),
UPDATE_TSP DATE,
CONSTRAINT PK_KEY_FORM
PRIMARY KEY ( FORM_ID ))
ORGANIZATION INDEX NOCOMPRESS PCTTHRESHOLD 50 INCLUDING FORM OVERFLOW;

And 100% of rows chained

Is that normal? if not, what can I do to fix it.

Thank you.
and we said...
You did it to yourself.  You have both INCLUDING and PCTTHRESHOLD.  They are
"or'ed" together.

So, you are saying "keep the fields upto and including FORM on the index leaf
block" OR "when we exceed 50% of the block, overflow us".

Since site_id (after FORM) is NOT NULL -- every row by definition will be
chained.

Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> CREATE TABLE KEY_FORM (
  2  FORM_ID NUMBER NOT NULL,
  3  FORM       VARCHAR2(500),
  4  SITE_ID NUMBER,
  5  CONSTRAINT PK_KEY_FORM
  6  PRIMARY KEY ( FORM_ID ))
  7  ORGANIZATION INDEX NOCOMPRESS PCTTHRESHOLD 50 INCLUDING FORM OVERFLOW;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into key_form ( form_id)
  2  select object_id from all_objects;

22987 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table key_form compute statistics;

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select num_rows, chain_cnt from user_tables
where table_name = 'KEY_FORM';

  NUM_ROWS  CHAIN_CNT
---------- ----------
     22987          0

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> truncate table key_form;

Table truncated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into key_form ( form_id, site_id)
  2  select object_id, object_id from all_objects;

22987 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table key_form compute statistics;

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select num_rows, chain_cnt from user_tables
where table_name = 'KEY_FORM';

  NUM_ROWS  CHAIN_CNT
---------- ----------
     22987      22987

Now, we know that room exists for form_id, site_id - it is very small.  But all
columns AFTER FORM will be overflowed.  If you remove the INCLUDING FORM
OVERFLOW:

ops$tkyte@ORA817DEV.US.ORACLE.COM> CREATE TABLE KEY_FORM (
  2  FORM_ID NUMBER NOT NULL,
  3  FORM       VARCHAR2(500),
  4  SITE_ID NUMBER,
  5  CONSTRAINT PK_KEY_FORM
  6  PRIMARY KEY ( FORM_ID ))
  7  ORGANIZATION INDEX NOCOMPRESS PCTTHRESHOLD 50 ;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into key_form ( form_id, site_id)
  2  select object_id, object_id from all_objects;

22986 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table key_form compute statistics;

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select num_rows, chain_cnt from user_tables
where table_name = 'KEY_FORM';

  NUM_ROWS  CHAIN_CNT
---------- ----------
     22986          0

So, make sure you really want to use BOTH of these!!! (or move the NOT NULL
columns upto the front of the table, before the INCLUDING column)
Review & Followup
Rating: 4
Preformance issue May 21, 2002
Reviewer: A reader
Very intresting,
So how will this affact preformance? most (if not all) queries are form_id in
the "where" and form_id + form in the "select". If I omit the "incloding" like
you suggested will that hurt preformance?

Followup:
I don't know -- do you frequently need access to the other columns?  If not --
no, if so -- maybe, it'll have to piece the row back together.  One row -- not
bad, do you need to do it for thousands of rows -- if so, bad.

Rating: 5
May 22, 2002
Reviewer: Lee
Tom,
I’m sorry to burden you with this issue but I really want to optimize it as much
as possible.

I have this table: (the same idea as the original table I was talking about but
much bigger and used much more often)

CREATE TABLE KEY_PERMUTATION (
  PERMUTATION_ID  NUMBER        NOT NULL,
  PERMUTATION     VARCHAR2 (500),
  PLURAL_FLAG     CHAR (1),
  INSERT_USER     VARCHAR2 (100),
  INSERT_TSP      DATE          DEFAULT SYSDATE,
  UPDATE_USER     VARCHAR2 (100),
  UPDATE_TSP      DATE,
   CONSTRAINT CHK_KEY_PERMUTATION_PLURAL
          CHECK (PLURAL_FLAG IN ('Y', 'N')) ,
  CONSTRAINT UQ_KEY_PERMUTATION_PERMUTATION
          UNIQUE (PERMUTATION)    USING INDEX
  TABLESPACE INDX PCTFREE 10
  STORAGE ( INITIAL 1048576 NEXT 1048576 PCTINCREASE 0 ),
  CONSTRAINT PK_KEY_PERMUTATION
  PRIMARY KEY ( PERMUTATION_ID ))
 ORGANIZATION INDEX  NOCOMPRESS  PCTTHRESHOLD 50 INCLUDING PERMUTATION OVERFLOW;

Avg row length = 64,
Degree = 4,
Num rows = 6,605,145

KEYWORD.KEY_PERMUTATION : 100% of rows chained

This table is being queried many times a day and the queries return from 100’s
to 1000’s to millions of recoreds.

I see improvement from regular table with regular indexes but maybe I can
improve it even more.

All queries have “Permutation_id” in the “WHERE” and some have “permutation” in
the “WHERE” + most queries have “permutation_id” and/or “permutation” in the
“SELECT”.

I really apritiate it if you can give me some recommendations of how to use IOT
for this example (I have about 10 tables with the same idea)

Followup:
If you don't select out the fields AFTER the column permutation -- it is fine.

If you select out those fields for the millions/1000's of records -- the
chaining is not desirable.

If the chaining is not desirable and since the average row length is 64 (small),
lose the INCLUDING, keep the pctthreshold.  The chaining should disappear.

Not knowing your entire schema and what you do with it -- how this table is
really used -- I cannot say if your use of an IOT is appropriate or not.

May 23, 2002 Roy -- Thanks for the question regarding "How to use 'long' dataType?", version 8.0.5.0.0
You Asked
How to store a large number character (more than 4k) into a 'long' dataType and
to use it?
What is the different between varchar2 and long dataType?
Thanks!

Roy
and we said...
You should not use the LONG type in 8.x.  You should use the CLOB type.

In any case, if you are having issues with more then 4000 characters of text,
you must be using "constants" in the character string.  YOu must use bind
variables (even if the text is smaller then 4000 bytes!  bind variables are OH
so important).

see
http://osi.oracle.com/~tkyte/Misc/LargeStrings.html
for an example of what I mean.

Long vs varchar2.

Longs cannot be used in a where clause, you cannot use them in the select list
of an insert into select statement, you cannot access them in a trigger, you
cannot create table as select long column, you cannot replicate them, .....

CLOBS -- you can use them in a where clause
you can use them in the select list of an insert into
you can access them in a trigger
you can create table as select clob
you can replicate them
.....
Review & Followup
Rating: 4
Clob May 23, 2002
Reviewer: Rahul Sharma from India
Hi Tom,
You told that we can use CLOB data type in WHERE clause but when I tried to use
this gave me an error

ORA-00932: inconsistent datatypes

so please give me an example, how can I perform a search operation on CLOB
datatype in WHERE clause.

Thanks in Advance.

Followup:
look at the DBMS_LOB package

dbms_lob.instr
dbms_lob.substr

select * from t where dbms_lob.instr( clob_column, 'ABC' ) > 0;
select * from t where dbms_lob.substr( clob_column, 5, 22 ) = 'Hello';

and so on.  Also, interMedia text can index these easily so you can use the
contains predicate (also true of longs).

In 9i, you can just go:

select * from t where clob_column like '%ABC%';

May 23, 2002 Praveen -- Thanks for the question regarding "Doubts on Import/Export + temprary tablespace+shutdown/startup", version 8.1.6
You Asked
Hi Tom,
I look after a warehouse which has feeds going on it basically
using the insert into... as select from ...the ERP systems.

Now often this blows up my temporary tablespace which is of the
type temporary and is around 6 gb in size.Now to get my temp
tablespace freed up i have no option but to shut it down.

Earlier you told me another way using
alter tablespace .... permanent;
and then again
alter tablespace .... temporary;
and also told to be careful to use it so that it shall slow down
the system when the data dictionary starts to keep track of the
temp segments.so i do not use it.
So i generally shutdown and startup,

Now i do
1)alter system checkpoint;
2)shutdown abort
3)startup
and the database is back started in less than 60-70 seconds.
After this  startup if i  fire  shutdown immediate
then it takes up hell lot of time to shutdown .
Why is this so when the startup has already recovered it and is
perfectly fine and is not in need to any media recovery,so why
should my shutdown immediate take time when i fire it right
after startup.
Also i have this doubt
1)Say one load used my temp by 3 gb and fired a commit;
2)then say another load started and did heavy sorts again ,so
would oracle reuse the existing 3 gb or would it try to use only
the free space and no segments would be dropped of the unused
temp segments.

----------------------------------------------
2)
When importing a user's tables from one db to another i got an
error
> oerr ora 2437
02437, 00000, "cannot validate (%s.%s) - primary key violated"
// *Cause: attempted to validate a primary key with duplicate
values or null
//         values.
// *Action: remove the duplicates and null values before
enabling a primary
//          key.

so i thought that there was a duplicate row in this table.
which i got also a duplicate row.
----------------------------------------------------
Now my question is when the user had taken an export was the
primary key constraint enabled/disabled  at export time in his
db.

It couldn't have been enabled because the primary key would be
violated there also.

Then why was the export file haveing this statement

----------------------------------------------------
IMP-00017: following statement failed with ORACLE error 2437:
"ALTER TABLE "ANILRCUSTOMER" ADD CONSTRAINT "PK_ANILRCUSTOMER"
PRIMARY KEY ("US"
"ERNAME") USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
STORAANIL (INITIAL 53"
"2480 NEXT 524288 MINEXTENTS 1 MAXEXTENTS 249 PCTINCREASE 1
FREELISTS 1 FREE"
"LIST GROUPS 1 BUFFER_POOL DEFAULT) ENABLE NOVALIDATE"
IMP-00003: ORACLE error 2437 encountered
ORA-02437: cannot validate (ANILER.PK_ANILRCUSTOMER) - primary
key violated
----------------------------------------------------

Also if the user export had constarints=N the above alter table
should  not have been  in export file.

So it reduces to it was disabled in his db and then when he took
an export it was written to the exp file and imp was trying to
enable it at my end.
---------------------------------------------------------------
---------------------------------------------------------------
Here is the complete log
---------------------------------------------------------------
Connected to: Oracle8i Enterprise Edition Release 8.1.5.0.0 -
Production
With the Partitioning and Java options
PL/SQL Release 8.1.5.0.0 - Production

Import file: expdat.dmp > ANILER.dmp

Enter insert buffer size (minimum is 8192) 30720>

Export file created by EXPORT:V08.00.05 via conventional path
import done in US7ASCII character set and US7ASCII NCHAR
character set
export client uses WE8ISO8859P1 character set (possible charset
conversion)
export server uses WE8ISO8859P1 NCHAR character set (possible
ncharset conversio
n)
List contents of import file only (yes/no): no >

Ignore create error due to object existence (yes/no): no >

Import grants (yes/no): yes >

Import table data (yes/no): yes >

Import entire export file (yes/no): no > y

. importing ANILER's objects into ANILER
. . importing table "ANILRCUSTOMER" 1865 rows imported
IMP-00017: following statement failed with ORACLE error 2437:
"ALTER TABLE "ANILRCUSTOMER" ADD CONSTRAINT "PK_ANILRCUSTOMER"
PRIMARY KEY ("US"
"ERNAME") USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
STORAANIL (INITIAL 53"
"2480 NEXT 524288 MINEXTENTS 1 MAXEXTENTS 249 PCTINCREASE 1
FREELISTS 1 FREE"
"LIST GROUPS 1 BUFFER_POOL DEFAULT) ENABLE NOVALIDATE"
IMP-00003: ORACLE error 2437 encountered
ORA-02437: cannot validate (ANILER.PK_ANILRCUSTOMER) - primary
key violated
About to enable constraints...
IMP-00015: following statement failed because the object already
exists:
"ALTER TABLE "ANILRCUSTOMER" ENABLE CONSTRAINT
"PK_ANILRCUSTOMER""
Import terminated successfully with warnings.
---------------------------------------------------------------

More than Thanks in advance and Happy New Year too
Praveen
and we said...
You are confusing the sympton with the solution.

Temporary segments are allocated once and then managed by the
system.  By shutting down, what you have done is KICKED out
sessions that were using the temporary segments - preventing you
from using them.  You didn't cause anything to be "more free"
then it would have been otherwise -- you removed users from the
database -- killed their work -- and took over their temporary
tablespace extents!

This statement:

"and the database is back started in less than 60-70 seconds.
After this  startup if i  fire  shutdown immediate
then it takes up hell lot of time to shutdown ."

proves that.  A shutdown immediate WAITS for the people who are
USING the temporary segments to finish with them and log out.
The database will not shutdown with a shutdown immediate until
everyone logs out gracefully.  By doing a shutdown abort, you
just "kick them out" causing them to lose their work (there is
probably a group of very angry people out there somewhere
wondering who keeps killing their database!)

You need MORE temporary space to be allocated to your system in
order to meet your day to day needs, that is all.  the alter to
a permanent and then temporary would not give you back any space
since the space is actually being used.

See the dynamic performance views v$sort_segment and
v$sort_usage to see whats allocated and who is currently using
it.

2) go back to the source data and issue:

select /*+ full(t) */ pk_value, count(*)
 from t
 group by pk_value
having count(*) > 1;

and see what that says.
Review & Followup
Rating: 3
error:ora 1659 in import the dmp file May 22, 2002
Reviewer: cgf from Hangzhou China
Tom
I used the following command for import the dmp file.
C:>imp develop/develop file=c:\backup.dmp full=y
but the computer tell me the following information:
error:ora 1659

please tell me the reason.
OK.
           cgf

Followup:
you don't have the error messages manual?

01659, 00000, "unable to allocate MINEXTENTS beyond %s in tablespace %s"
// *Cause:  Failed to find sufficient contiguous space to allocate MINEXTENTS
//          for the segment being created.
// *Action: Use ALTER TABLESPACE ADD DATAFILE to add additional space to the
//          tablespace or retry with smaller value for MINEXTENTS, NEXT or
//          PCTINCREASE

Rating: 2
is this stement correct....... May 23, 2002
Reviewer: a reader from UK
Tom, I thought that 'The database will not
shutdown with a shutdown immediate until everyone logs out gracefully' was a
shutdown normal and NOT immediate. I'm sure that immediate kill user sessions
and then exits gracefully.

Followup:
what I meant is that everyone is logged out gracefully -- they are rolled back.
they are not aborted.  the shutdown immediate results in a "clean" shutdown.

It will kill AND WAIT for the session to be cleaned out before shutting down.
That was my point.

A shutdown abort -- that just STOPS the transactions right there, right then.
The database is shutdown in a "fuzzy" state -- requires instance recovery upon
restart.

sorry for the confusion.

Rating: 4
Who is using Temp May 23, 2002
Reviewer: Andrew Allen from Detroit, MI
Here is a query I use to see who is using temp, how much they are using, and the
sql doing it.
- - - - - - - -
SELECT /*+ ORDERED */
       u.tablespace,
       s.sid || ',' || s.serial# as sid_ser,
       s.username,
       s.osuser,
       (SUM(u.blocks)*TO_NUMBER(a.value))/1048576 as blk_mb,
       x.sql_text,
       s.module || ':' || s.action as "Module:Action"
  FROM v$sort_usage u
       ,v$session S
       ,v$sql X
       ,v$parameter a
 WHERE s.saddr                = u.session_addr
   AND s.sql_address          = x.address
   AND s.sql_hash_value       = x.hash_value
   AND a.name                 = 'db_block_size'
 GROUP
    BY u.tablespace
     , s.sid
     , s.serial#
     , s.username
     , s.osuser
     , a.value
     , x.sql_text
     , s.module
     , s.action
 ORDER
     BY u.tablespace
      , s.sid
;

Rating: 4
A Little Clarification May 23, 2002
Reviewer: TAF from Carmel, IN USA
Tom,

    You might mention to him to convert this temp tablespace to a tempfile
instead of being dictionary managed.  I had the same problem with a 5g temp
tablespace.  When it blew it "locked" the tablespace until a system bounce or
alter tablespace command.  This is an Oracle bug, so to speak.  I made the temp
tablespace a truly tempfile and even after blowing the tablespace, it was
immediately available for reuse.  No system bounce needed.

Followup:
...
Now often this blows up my temporary tablespace which is of the type temporary
and is around 6 gb in size.
.....

I assume based on his description -- that it is using tempfiles.

May 23, 2002 Ai -- Thanks for the question regarding "Decode function", version 8
You Asked
How can I do a range comparison within the decode function?

In pseudo-code, this is what I want to do:

select priority_level,
       decode(value < 1, true, subtituted_value, null),
       decode(value between 2 and 4, true, subtituted_value,
null),
       decode(value > 4, true, subtituted_value, null)
from   table_name

Can this be complished?

Thanks,

Ai
and we said...
Yes, decode can do this:

ps$tkyte@ORA8I.WORLD> select x,
  2         decode( sign(x-1),
  3                  -1, 'Less then 1',
  4                      'Greater or equal to 1' ) c1,
  5         decode( sign(x-2),
  6                  -1, 'Less then 2',
  7                       decode( sign(x-4),
  8                                 1, 'Greater then 4',
  9                                    'Between 2 and 4' ) ) c2,
 10         decode( sign(x-4),
 11                   1, 'Greater then 4',
 12                      'Less then 4' ) c3
 13    from t
 14  /

         X C1                    C2              C3
---------- --------------------- --------------- --------------
         0 Less then 1           Less then 2     Less then 4
         1 Greater or equal to 1 Less then 2     Less then 4
         2 Greater or equal to 1 Between 2 and 4 Less then 4
         3 Greater or equal to 1 Between 2 and 4 Less then 4
         4 Greater or equal to 1 Between 2 and 4 Less then 4
         5 Greater or equal to 1 Greater then 4  Greater then 4

6 rows selected.

ops$tkyte@ORA8I.WORLD> select x,
  2         decode( sign(x-1),
  3                  -1, 'Less then 1',
  4                       null ) c1,
  5         decode( sign(x-2),
  6                  -1, null,
  7                      decode( sign(x-4),
  8                                1, null,
  9                                  'Between 2 and 4' ) ) c2,
 10         decode( sign(x-4),
 11                   1, 'Greater then 4',
 12                       null ) c3
 13    from t
 14  /

         X C1          C2              C3
---------- ----------- --------------- --------------
         0 Less then 1
         1
         2             Between 2 and 4
         3             Between 2 and 4
         4             Between 2 and 4
         5                             Greater then 4

6 rows selected.
Review & Followup
Rating: 4
March 17, 2001
Reviewer: Pauline from USA

Rating: 4
Simon Says "Been Wanting To Know This!" April 30, 2001
Reviewer: Simon Huggins from Northampton, UK
I have been wondering this one for some time - it would have been an enormous
help in a number of situations, where I have been forced to do unions of very
similar looking queries instead!

Rating: 5
Outstanding ! September 06, 2001
Reviewer: Barry from Australia
Thats great stuff Tom !
The is a VERY common requirement.
Very nicely done

Rating: 5
perfect example November 14, 2001
Reviewer: christian tremo from ct, usa
thanks for a code example on the first try....

cheers
ct

Rating: 4
Can the same be done with strings? May 23, 2002
Reviewer: A reader from Portland, OR
This was a great help, but I'm wondering if the same can be done with strings?

Followup:
in 816 and up with CASE -- yes

USER is "SCOTT"
scott@ORA817DEV.US.ORACLE.COM> select case when ename < 'HI' then 'Less Than'
else 'Greater Than' end, ename from emp
  2  /

CASEWHENENAM ENAME
------------ ----------
Greater Than x
Greater Than y
Greater Than WARD
Greater Than JONES
Greater Than MARTIN
Less Than    BLAKE
Less Than    CLARK
Greater Than SCOTT

May 23, 2002 Pauline -- Thanks for the question regarding "RE:Chained and migrated rows", version oracle 8.1.5
You Asked
Hi Tom,
For finding and eliminating migrated or chained rows, I have a
question.
To identify the migrated or chained rows, I need to run
$ORACLE_HOME/rdbms/admin/utlchain.sql
and then do

SQL> analyze table table_name list chained rows;
SQL> select * from chained_rows;

Here I may guess some talbe has migrated or chained rows,and put
the table name in analyze command. But assume I have lot of
tables but don't know which table has migrated or chained rows,
how should I do the analyze?

Thanks.

Pauline
and we said...
If you have lots of tables -- and you do not know which may or
may not contain chained rows, you'll need to analyze them all to
discover which do and how many they have.

In order to discover which have chained rows, an analyze will be
needed -- or, a SELECT COUNT() can be used if you don't want to
analyze.  A SELECT COUNT can be used to tell you if you have
lots of chained/migrated rows in a table.  We can do this by
watching the statistic 'table fetch continued row'.  You might
even combine this with SAMPLE to only look at some percentage of
the table.

Consider this small example. It will use a SELECT to determine
the amount of chaining.

ops$tkyte@ORA8I.WORLD> create table t1
                     ( a char(2000) default '*',
  2                    b char(2000) default '*',
  3                    c char(2000) default '*',
  4                    d char(2000) default '*',
  5                    e char(2000) default '*' );

Table created.

That is a table where if a,b,c,d,e have values -- the row will
be chained as the rowsize is 10,000 bytes which is bigger then
my 8k blocksize

ops$tkyte@ORA8I.WORLD> create table t2 ( a varchar2(4000),
  2                    b varchar2(4000),
  3                    c varchar2(4000) );

Table created.

 this is a table that could be chained, if we put 4000 bytes
into each of a,b,c

ops$tkyte@ORA8I.WORLD> insert into t1 values ( 'a', 'b', 'c',
'd', 'e' );
ops$tkyte@ORA8I.WORLD> insert into t1 values ( 'a', 'b', 'c',
'd', 'e' );
ops$tkyte@ORA8I.WORLD> insert into t1 values ( 'a', 'b', 'c',
'd', 'e' );
ops$tkyte@ORA8I.WORLD> insert into t1 values ( 'a', 'b', 'c',
'd', 'e' );
ops$tkyte@ORA8I.WORLD> insert into t1 values ( 'a', 'b', 'c',
'd', 'e' );

ops$tkyte@ORA8I.WORLD> insert into t2 values ( 'a', 'b', 'c' );
ops$tkyte@ORA8I.WORLD> insert into t2 values ( 'a', 'b', 'c' );
ops$tkyte@ORA8I.WORLD> insert into t2 values ( 'a', 'b', 'c' );
ops$tkyte@ORA8I.WORLD> insert into t2 values ( 'a', 'b', 'c' );
ops$tkyte@ORA8I.WORLD> insert into t2 values ( 'a', 'b', 'c' );

So, we put 5 rows into each...  Now we are ready to test.
We'll start by using COLUMN VALUE NEW_VAL V to let us save the
starting "table fetch continued row" value in our session

ops$tkyte@ORA8I.WORLD> column value new_val V

ops$tkyte@ORA8I.WORLD> select b.value
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4    and a.name = 'table fetch continued row'
  5  /

     VALUE
----------
        50

Now, to show how to do the count correctly, I'll start by
doing it wrong!  I'll select the count of the first column A:

ops$tkyte@ORA8I.WORLD> select count( a ) from t1;

  COUNT(A)
----------
         5

ops$tkyte@ORA8I.WORLD> select b.value-&V
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4    and a.name = 'table fetch continued row'
  5  /
old   1: select b.value-&V
new   1: select b.value-        50

B.VALUE-50
----------
         0

According to that, we did NO 'table fetch continued row'.
That makes sense because A would be expected to be on the first
block of the row.  We never fetched the rest of the row -- we
didn't need to  (this also shows why you would want to put your
"stable" columns that are frequently fetched FIRST in the create
table -- put the columns that will cause chaining LAST.  If you
fetch the stable columns like dates, ids and such freqently and
don't fetch the "big" columns that cause chaining as often --
chaining won't affect you as much)

So, lets see how to measure this correctly now:

ops$tkyte@ORA8I.WORLD> select b.value
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4    and a.name = 'table fetch continued row'
  5  /

     VALUE
----------
        50

ops$tkyte@ORA8I.WORLD> select count( e ) from t1;

  COUNT(E)
----------
         5

ops$tkyte@ORA8I.WORLD> select b.value-&V
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4    and a.name = 'table fetch continued row'
  5  /
old   1: select b.value-&V
new   1: select b.value-        50

B.VALUE-50
----------
         5

There we go -- this shows that we did 5 table fetch continued
rows in order to count the E column.  This is what we were
expecting.  What you need to do is count the LAST column in the
table -- if the row is chained, we will need to fetch the
continued row to get that column.

Now lets try our unchained table:

ops$tkyte@ORA8I.WORLD> select b.value
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4    and a.name = 'table fetch continued row'
  5  /

     VALUE
----------
        55

ops$tkyte@ORA8I.WORLD> select count( c ) from t2;

  COUNT(C)
----------
         5

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> select b.value-&V
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4    and a.name = 'table fetch continued row'
  5  /
old   1: select b.value-&V
new   1: select b.value-        55

B.VALUE-55
----------
         0

Ok, it has no chaining yet, as expected.  We'll update to
force some and check again:

ops$tkyte@ORA8I.WORLD> update t2 set a = rpad('*',4000,'*'),
  2                b = rpad('*',4000,'*'),
  3                c = rpad('*',4000,'*')
  4  /

5 rows updated.

ops$tkyte@ORA8I.WORLD> select b.value
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4    and a.name = 'table fetch continued row'
  5  /

     VALUE
----------
        55

ops$tkyte@ORA8I.WORLD> select count( c ) from t2;

  COUNT(C)
----------
         5

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> select b.value-&V
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4    and a.name = 'table fetch continued row'
  5  /
old   1: select b.value-&V
new   1: select b.value-        55

B.VALUE-55
----------
         5

That shows that the update cause the 5 rows we had to chain
(they became 12,000 bytes in length, they have to be chained).

Now, this also points out something interesting -- if your row
size is close to your blocksize OR you have a long/long raw, you
will have chained rows and you will not be able to get rid of
them by definition.  Don't even try, it won't be possible!
Review & Followup
Rating: 5
Should we reconsider the design? May 23, 2002
Reviewer: Drew from Trevose PA, USA
Tom,
What is exactly effects do chained rows has on performance?  I know it's not
good but in what way? We have a table that has 80 columns, among them are more
than 10 VARCHAR2(1000).  That certainly will cause chained rows no matter what
because our db_block_size is 8K. Sould we consider changing the table structure
or it won't hurt?  We don't have much data now.  With time going on and more
data are in the table, will it cause any performance problem?
Thank you very much in advance.

Followup:
it won't cause chaining "no mater what" -- only when you have more then 8000
bytes of data in a row will it cause chaining (since they are varchar2's, they
VARY in length).

Do not change the data structure -- you would be introducing a join or something
and that is even worse.

Chaining just means we have to read more then one block in order to piece the
row together (when you want columns that are on the chained block).  that is
better then breaking the table into two or something.  it is unavoidable when
the row exceeds the block size

May 24, 2002 john -- Thanks for the question regarding "Dynamic SQL", version 8
You Asked
Dear Mr. Tom,
I am trying to write this dynamic procedure and I encountered
the following problem. (I have writen this type of code before)
This is what I have

v_execute       integer;
v_cursorID      integer;
v_SQLstmt       varchar2(500);
--
BEGIN
v_SQLstmt := 'SELECT count (*) INTO :store_count FROM
store_call_counts;';
--
v_cursorID := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(v_cursorID,v_SQLstmt,DBMS_SQL.NATIVE);
DBMS_SQL.CLOSE_CURSOR( v_cursorID );
--
--
The program compiles fine but when it runs I get the error:
ORA 00911.
I know the error is comming from the PARSE statement, but I do
not know why.  While testing, I have replace the v_SQLstmt for a
'commit;' - and the error is still the same.

Any help on this matter will be greatly appreciated.

--John Carrion
and we said...
the SELECT ... INTO ... from ... syntax is used purely in
STATIC sql or in the new Oracle8i dynamic sql (but then the
placement is different).

Select .. Into .. can be utilized by a preprocessor when it
rewrites the query (removing the into component).

When using dbms_sql to do the above, you would code:

create or replace function get_rows( p_tname in varchar2 )
return number
as
    l_theCursor        integer default dbms_sql.open_cursor;
    l_columnValue    number default NULL;
    l_status        integer;
begin
       dbms_sql.parse(  l_theCursor,
                    'select count(*) from ' || p_tname,
                     dbms_sql.native );

    dbms_sql.define_column( l_theCursor, 1, l_columnValue );
    l_status := dbms_sql.execute(l_theCursor);
    if ( dbms_sql.fetch_rows(l_theCursor) > 0 )
     then
           dbms_sql.column_value( l_theCursor, 1,
                                  l_columnValue );
    end if;
    dbms_sql.close_cursor( l_theCursor );
    return l_columnValue;
end ;
/

for example.  In Oracle8i, release 8.1 this can be more simply:

create or replace function get_rows( p_tname in varchar2 )
return number
as
    l_columnValue    number default NULL;
begin
    execute immediate 'select count(*)
                         from ' || p_tname INTO l_columnValue;

    return l_columnValue;
end ;
/

but notice that even though I use the INTO keyword -- I do it
where the plsql compiler can see it (and it'll generate the code
to do the parse, open, fetch and close for me based on that.)
Review & Followup
Rating: 4
Could you show us a Mutliple Row Query May 23, 2002
Reviewer: Paul Duer from Carmel, IN
This seems like a very good option for keeping hardcoding out of code.

But could you please show us an example Function that creates a multi-row cursor
and loops through the records of the dynamic SQL query?

Thanks a Million!

Followup:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:68212348056

May 24, 2002 Dinesh -- Thanks for the question regarding "Reg
      :- Trigger Execution ", version 7.3.4.2

      You Asked
Dear Tom,
   I have a trigger which updates certain tables and that invokes other triggers
( in turn calls functions, procedures ). Sometimes these things seem difficult
to debug. My questions: Is there a way by which we can find out what are the
objects ran when the trigger was executed ( or atleast the Oracle statements
that were executed)?

Thanks
dinesh ...

      and we said...
issue alter sesssion set sql_trace=true;

then use tkprof -- it'll show you what took place

http://asktom.oracle.com/~tkyte/tkprof.html

May 24, 2002 Venkatasubramanian -- Thanks for the question
      regarding "row chaining", version 8.1.7

      You Asked
Dear Tom,

We have a chained table lg010tb.While creating the temp table towards  the
process of removing the row chaining, we got an error.The table contains a long
datatype column. The table structure and the error are given below:

SQL> desc lg010tb
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 BR_CD                                     NOT NULL VARCHAR2(6)
 DATE_REQ                                  NOT NULL DATE
 LET_REF_CD                                NOT NULL CHAR(10)
 GENERIC_FLAG                                       VARCHAR2(1)
 LET_CD                                             CHAR(6)
 TMP_CD                                             NUMBER(3)
 TEXT_RESULT                               NOT NULL LONG
 CIF_NO                                             VARCHAR2(10)
 ZIP_CD                                             VARCHAR2(20)
 HOLD_ADV                                           VARCHAR2(1)
 HOLD_LRS                                           VARCHAR2(1)
 DND_FLG                                            VARCHAR2(1)
 FREQ                                               VARCHAR2(1)

SQL> select head_rowid from chained_rows where table_name='LG010TB';

HEAD_ROWID
------------------
AAAA4qAAFAAAAILAAB
AAAA4qAAFAAAAINAAB

SQL> create table temp as select * from lg010tb
  2  where rowid in(select head_rowid from chained_rows
  3  where table_name='LG010TB');
create table temp as select * from lg010tb
                            *
ERROR at line 1:
ORA-00997: illegal use of LONG datatype

Can you pl. suggest the way to get over this?

Thanks and Regards,
Venkat

      and we said...
A table with a long column will ALMOST ALWAYS have chained rows by
definition!!!!

LONGS are big -- bigger then a block normally.  By definition that row will be
chained.

See

http://asktom.oracle.com/~tkyte/Misc/MoveLongs.html
for howto move longs but it will be an utter waste of time in your case as
the rows will still be chained when you move them BACK!

In 8.0 and up -- you should use a CLOB type -- NOT a LONG.  CLOBS don't suffer
from chaining issues.  You should seriously consider changing this datatype.

May 24, 2002 Tom -- Thanks for the question regarding
      "Question on indexing strategy", version 8.1.7.,2

      You Asked
Tom,

Can you please explain whether or not it matters to use the most selective
column first in the order of column that consists the index ?.

I had always assumed that the most selective column should be the first column
of an index. But if this rule of thumb is used, we cannot take advantage of
"compress" indexes (this is my understanding of how compress works).

Can you please comment on what the best practise is ?.

Thanks & Regards

      and we said...
Well, I actually go into this in some depth in my book.

It has not been true since at least version 6 that having the most selective
first is better.

The PRIMARY rule that drive the order of columns in a concatenated index should
be the types of predicates you use.  If you query:

select * from t where a = :a and b = :b and c = :c;
select * from t where b = :b and c = :c;
select * from t where c = :c;

The most logical index might be on (c,b,a) as it can EASILY be used for all
three.  Note that I don't really care about the selectivity of anything here --
the optimizer will take care of that for me (if C is not selective enough by
itself, it'll not use the index, if it is -- it will).

In 8i, as you pointed out -- there is a compelling reason to put the LEAST
discriminating columns first -- compression.

In 9i, there is even a MORE compelling reason.  Index skip scans -- see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1156161921465
here, having the least discriminating entry first can allow us to use an index
even when the columns in the predicate are NOT on the leading edge of the index.
 For example given a table t with an index t_idx(a,b), if A is not selective, we
might be able to use a skip scan on a query like:

   select * from t where b = 5;

Review &
      Followup

      Rating: 4
Tom, could you
      please elaborate on "compress index" feature  May 09, 2002

Reviewer:  Andrew  from Calgary Canada
I always find something new in your answers. This time "skip index" (explained
very well in your previous link) and "compressed index". Could you please
elaborate on this compressed index feature, how it works and how it is related
to the "most selective" column. I probably shouldn't ask this, because it is
probably in the docs, but, Tom, you have such a talent to make it clear....

          Followup:

Better yet -- I've explained it in great deatil in my book in the chapter on
indexes!  I explained tons of stuff in there.

      Rating: 3
column order -
      oracle docs  May 22, 2002
Reviewer:  kapil

Hi Tom,
You have said that since version 6 it is not better to have the most selective
column in the leading position
But in the oracle documentation - Oracle8i Designing and Tuning for Performance
- Data Access Methods
the following statement is mentioned
"If all keys are used in WHERE clauses equally often, then ordering these keys
from most selective to least selective in the CREATE INDEX statement best
improves query performance"
Could you throw some light on that statement?

Kapil

          Followup:

it is in my experience -- a mistatement on their part.  I would say it is
inaccurate.

In my book -- I actually benchmark it:

tkyte@TKYTE816> create table t
  2  nologging
  3  as
  4  select * from all_objects;
Table created.

tkyte@TKYTE816> create index t_idx_1 on t(owner,object_type,object_name)
  2  nologging pctfree 0;
Index created.

tkyte@TKYTE816> create index t_idx_2 on t(object_name,object_type,owner)
  2  nologging pctfree 0;
Index created.

tkyte@TKYTE816> select count(distinct owner), count(distinct object_type),
  2   count(distinct object_name ), count(*)
  3  from t;

(DISTINCTOWNER) (DISTINCTOBJECT_TYPE) (DISTINCTOBJECT_NAME)   COUNT(*)
--------------- --------------------- --------------------- ----------
             24                    23                 12265      21975

Now, to show that neither is more efficient space wise, we?ll measure their
space utilization:

tkyte@TKYTE816> exec show_space( 'T_IDX_1', user, 'INDEX' );
Free Blocks.............................0
Total Blocks............................192
Total Bytes.............................1572864
Unused Blocks...........................51
Unused Bytes............................417792
Last Used Ext FileId....................6
Last Used Ext BlockId...................4745
Last Used Block.........................13

PL/SQL procedure successfully completed.

tkyte@TKYTE816> exec show_space( 'T_IDX_2', user, 'INDEX' );
Free Blocks.............................0
Total Blocks............................192
Total Bytes.............................1572864
Unused Blocks...........................51
Unused Bytes............................417792
Last Used Ext FileId....................6
Last Used Ext BlockId...................4937
Last Used Block.........................13

PL/SQL procedure successfully completed.

They use exactly the same amount of space ? there are no differences there
(however, the first index is a lot more COMPRESSABLE if we used index key
compression!  There is an argument for going from least discriminating to most
discriminating).  Now, lets see how they perform ? if either index is generally
more efficient than the other.  In order to test that, I used a PLSQL block with
hinted queries (so as to use one index or the other) like this:

tkyte@TKYTE816> alter session set sql_trace=true;
Session altered.

tkyte@TKYTE816> declare
  2          cnt int;
  3  begin
  4          for x in ( select owner, object_type, object_name from t )
  5          loop
  6                  select /*+ INDEX( t t_idx_1 ) */ count(*) into cnt
  7                    from t
  8                   where object_name = x.object_name
  9                     and object_type = x.object_type
 10                     and owner = x.owner;
 11
 12                  select /*+ INDEX( t t_idx_2 ) */ count(*) into cnt
 13                    from t
 14                   where object_name = x.object_name
 15                     and object_type = x.object_type
 16                     and owner = x.owner;
 17          end loop;
 18  end;
 19  /

PL/SQL procedure successfully completed.

That read ever single row in the index one at a time.  The TKPROF report shows
us:

SELECT /*+ INDEX( t t_idx_1 )  */COUNT(*)
FROM
 T  WHERE OBJECT_NAME = :b1  AND OBJECT_TYPE = :b2  AND OWNER = :b3

call     count       cpu    elapsed  disk      query    current        rows
------- ------  -------- ---------- ----- ---------- ----------  ----------
Parse        1      0.00       0.00     0          0          0           0
Execute  21975      2.35       2.55     0          0          0           0
Fetch    21975      1.40       1.57     0      44088          0       21975
------- ------  -------- ---------- ----- ---------- ----------  ----------
total    43951      3.75       4.12     0      44088          0       21975

Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: CHOOSE
  21975   SORT (AGGREGATE)
  21975    INDEX (RANGE SCAN) OF 'T_IDX_1' (NON-UNIQUE)

********************************************************************************

SELECT /*+ INDEX( t t_idx_2 )  */COUNT(*)
FROM
 T  WHERE OBJECT_NAME = :b1  AND OBJECT_TYPE = :b2  AND OWNER = :b3

call     count       cpu    elapsed  disk      query    current        rows
------- ------  -------- ---------- ----- ---------- ----------  ----------
Parse        1      0.00       0.00     0          0          0           0
Execute  21975      2.10       2.44     0          0          0           0
Fetch    21975      1.65       1.60     0      44088          0       21975
------- ------  -------- ---------- ----- ---------- ----------  ----------
total    43951      3.75       4.04     0      44088          0       21975

Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: CHOOSE
  21975   SORT (AGGREGATE)
  21975    INDEX (RANGE SCAN) OF 'T_IDX_2' (NON-UNIQUE)

They processed the same exact number of rows, blocks, used equivalent amounts of
CPU time and ran in about the same elapsed time (run this same test again and
the CPU/ELAPSED numbers will be a little different but on average ? they will be
the same).  There are no inherit efficiencies to be had ordering the columns in
order how discriminating they are.

      Rating:
      4
comparison  May 24, 2002
Reviewer:
      kapil  from india
Hi Tom
That was helpful indeed.
Well the only thing which could be added to the above analysis is comparing the
space and performance with the same index in the compressed form.
Would it be possible for you to draw that comparison?
Also, could you share the code of the PL/SQL block "show_space"?

          Followup:

simple search for show_space on this site will find the code (its in the book as
well)

Sort of thought with the above -- anyone would be able to do the compress
example if interested....  go for it.

      Rating:
      4
thanks  May 27, 2002
Reviewer:
      Kapil  from india
Hi Tom,
No problem
Thanks for all your help

May 24, 2002 Chad -- Thanks for the question regarding
      "Qustions about CROSS TAB queries/reports", version 8.1.7

      You Asked
Hi Tom,

First, I love this site and your book is excellent.  Highly recommend it!

I have a developer here that is trying to create a "cross tab" report (for lack
of a better work) using SQL.  What we have is three tables, one employee, one
rights, and one that joins the two. So one employee can have many rights, and
one right can go to many employees.

We want the output to be similar to the following:

EMP_ID  NAME  ADMINISTRATIVE SUPERVISOR AGENT Etc...
1       BOB         X            X
2       JOE                      X        X
3       MARY
4       KATE        X            X        X

Now the "catch" is we need the rights to be dynamic cause we will be adding and
subtracting rights.

I greatly apprecite you help with this...

      and we said...
You need to dynamically generate the query then -- you cannot do it in a single
static query since the number of columns may change over time.

Something like this will do it (doesn't matter if you have 2 tables as I do, or
three as you do -- the idea is the same)

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package dyn_query
  2  as
  3     type rc is ref cursor;
  4
  5     procedure get_query( p_cursor in out rc );
  6  end;
  7  /

Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package body dyn_query
  2  as
  3
  4  procedure get_query( p_cursor in out rc )
  5  is
  6      l_query long := 'select user_id, username, ';
  7  begin
  8      for x in ( select distinct privilege from dba_tab_privs )
  9      loop
 10          l_query := l_query ||
 11                     'max( decode(privilege,''' || x.privilege ||
 12                     ''', ''X'', null ) ) "'||x.privilege||'",';
 13      end loop;
 14      l_query := rtrim(l_query,',') ||
 15                ' from all_users, dba_tab_privs
 16                 where username = grantee
 17                 group by user_id, username';
 18
 19      open p_cursor for l_query;
 20  end;
 21
 22  end;
 23  /

Package body created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> variable x refcursor
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dyn_query.get_query( :x )

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> print x

   USER_ID USERNAME                       A D E I I R R S U
---------- ------------------------------ - - - - - - - - -
         5 SYSTEM                         X X X X X   X X X
        11 OUTLN                              X
        25 CTXSYS                                       X
        30 OPS$CLBECK                         X
        93 USER_C                             X
       185 PERFSTAT                           X         X
       209 APPS                               X
       212 SCOTT                                        X
       216 OPS$TKYTE                                X

9 rows selected.

Review &
      Followup

      Rating: 5
Thanks -
        May 24, 2002
Reviewer:  Chad  from
      SLC, UTAH
Thanks Tom - I wasn't even thinking about using a reference cursor.  Works
Great!

May 24, 2002 Susan -- Thanks for the question regarding
      "Primary Keys", version 8.1.7.

      You Asked
Hi Tom,

I was under the impression that primary keys should always be unique.  Can you
think of a scenario or strategy where you would want to have nonunique keys?
Thanks.

      and we said...
It is impossible -- a primary key is always unique by definition.

You cannot have duplicate primary keys.

A primary key, at the end of your transaction, will always be unique.
Deferrable constraints may allow them to go temporarily "duplicate" but only
during the course of your transaction/statement.
Review &
      Followup

      Rating:
      3
  May 24, 2002
Reviewer:
      A reader
I may have confused myself by running this code which is supposed to check for
tables with nonunique PK indexes.

SELECT c.constraint_name, i.tablespace_name, i.uniqueness
FROM all_constraints c , all_indexes i
WHERE c.owner = UPPER ( '&OWNER' ) AND i.uniqueness = 'NONUNIQUE'
AND c.constraint_type = 'P' AND i.index_name = c.constraint_name

it returned many rows similar to these

PK_FM01                IDX        NONUNIQUE
PK_UNQ_ALGORITHM_CODE  DATA       NONUNIQUE

Does this mean developers are creating indexes that they think are unique but
really aren't?

          Followup:

No one said a primary key must be enforced by a UNIQUE index -- we just need an
index, unique or otherwise.  A deferrable constraint will use a non-unique
index.  If an index (non-unique) already exists, the primary key will "kidnap"
it and use it.

Just has to be an index, that is not what enforces the uniqueness -- the
constraint does.

      Rating: 5
Response to the
      question posed by "a reader"  May 24, 2002

Reviewer:  Arup Nanda  from Norwalk, CT, USA

The developers are actually creating the indexes first and then creating the
primakry key constraint. While creating then index they must be omitting the
UNIQUE keyword. So the index is non unique but sine the constraint is defined as
the same name, oracle allows the constraint.

Consider this

sys@CGR1> l
  1  create table t1 as select object_id, object_name
  2* from user_objects

Table created.

sys@CGR1>

sys@CGR1> create index in_t1 on t1 (object_id); /* So how I missed the UNIQUE
key word */

Index created.

sys@CGR1> alter table t1 add constraint in_t1
  2  primary key (object_id)
  3  /

Table altered.

sys@CGR1> SELECT c.constraint_name, i.tablespace_name, i.uniqueness
  2  FROM all_constraints c , all_indexes i
  3  WHERE c.owner = UPPER ( '&OWNER' ) AND i.uniqueness = 'NONUNIQUE'
  4  AND c.constraint_type = 'P' AND i.index_name = c.constraint_name

sys@CGR1> /
Enter value for owner: ANANDA

Enter value for owner: ANANDA
old   3: WHERE c.owner = UPPER ( '&OWNER' ) AND i.uniqueness = 'NONUNIQUE'
new   3: WHERE c.owner = UPPER ( 'ANANDA' ) AND i.uniqueness = 'NONUNIQUE'

CONSTRAINT_NAME                TABLESPACE_NAME                UNIQUENES
------------------------------ ------------------------------ ---------
IN_T1                          TOOLS                          NONUNIQUE

1 row selected.

sys@CGR1> select * from t1
  2  where rownum < 2;

 OBJECT_ID
----------
OBJECT_NAME
--------------------------------------------------------------------------------
     45981
AQ$PROCQ_TAB1

1 row selected.

sys@CGR1> insert into t1 values (45981,'AAAA');
insert into t1 values (45981,'AAAA')
*
ERROR at line 1:
ORA-00001: unique constraint (ANANDA.IN_T1) violated

So, you have a non-unique index but it does enforce the PK constraint. So what
sis the problem?

sys@CGR1> set autot traceonly explain
sys@CGR1> select * from t1 where object_id = 45981
  2  /

Execution Plan
----------------------------------------------------------
   0
SELECT STATEMENT Optimizer=RULE
   1    0
  TABLE ACCESS (BY INDEX ROWID) OF 'T1'
   2    1
    INDEX (RANGE SCAN) OF 'IN_T1' (NON-UNIQUE)

Even though the index is unique (or supossed to be) the optimize does a range
scan, and that is inefficient. You might have to drop and recreate all the pk
indexes to be unique.

Hope this helps.

Arup Nanda

May 24, 2002 Satyendu -- Thanks for the question regarding
      "Sql Loader Problem while Loading data from a flat file", version
      8.1.7/8.0.6

      You Asked
Hi ,
I have got following doubts.

1. When I am loading Data from a text file into a single table, I want the data
to be inserted in the same order which is availble in the text file. Sometimes
it's not getting loaded in the same order. When I truncate the table and start
loading into the table, that time it's getting loaded coreectly. Can you please
let me know the problem ?

2. We have a application running on Power Builder and back end is Oracle. There
is a application user id through which user can acess the application and do all
sort of transaction. I would like to know about the security features which has
to be assigned to that application user. Is it advisable to create a role and
give necessary privileges to all relvant objects to that role and assign the
same role to that user ?
Secondly we we create a new application user, what rights/privileges do we need
to give. Also what roles should we need to assign? is it CONNECT only or both
CONNECT, RESOURCE also..What is the difference between these two ?What about DBA
role ? What kind of operation is possible if one is having DBA role granted to
him ?

We have Oracle 8.0.6 and 8.1.7.

Best Regards,
SP

      and we said...
1) you have made the 100% erroneous assumption that rows will ever get inserted
in the order they are loaded.

You might be getting LUCKY with the truncation -- but even then it is NOT
ASSURED.

One of the basic tenets of a relational database is that there is NO ORDER to
the data (no order you can assume).  The ONLY way to get data returned in any
sort of predicable order is to use an ORDER BY on the select.

There is quite simple no way to accomplish what you ask with any tool -- the
database just doesn't do that.

The rows are getting loaded correctly -- 100% correctly.  You just cannot ever
assume anything about the "order" of them -- ever.

2) I would suggest you create your OWN role (and do not use connect, resource,
dba).

Grant to this role CREATE SESSION and the needed privs for your app (select on
this table, update on that table, execute on this procedure and so on).

Grant that role to your users.

Read the admin guide for more information regaring the roles you ask about.  It
is best to create your own roles as you control EXACTLY what privs users will
have and that is best.
Review & Followup

      Rating:
      4
Sqloader  May 24, 2002
Reviewer:
      A reader
The information/ tips given my Toms is very useful.

      Rating:
      4
IOT  May 24, 2002
Reviewer:  Suresh
       from New York
Tom,
  Regarding the first question, will he not be able to get the rows in the table
in order by using Index Organized table? Thanks.

          Followup:

In order of PRIMARY KEY during a full scan -- yes.

In order of insertion -- no
In order of primary key during all queries -- no

If you want the stuff ordered, ORDER BY is the only thing going.

May 24, 2002 Steve -- Thanks for the question regarding
      "Index/Data for Very Large Object", version 8.1.6

      You Asked
In an OLTP database I have a single table that is 6GB and 12M rows. It has four
indexes, ranging from 300MB to 800MB.  For maintenance reasons I have segregated
this large table into its own tablespace.
Q: Since we are using an HP XP256 for storage (large SAN with lots of cache),
what is the argument _against_ combining index space into the data tablespace
for this object (let's say all extent sizes will be the same)?
Thanks in Advance, Steve

      and we said...
If you are striping the data -- it matters not (performance wise) where the data
may be.

If you are not striping the data -- start.

The purpose of using >1 tablespace for stuff is purely 100% administrative.  In
8i, the unit of recovery is a tablespace.  Lost a file for whatever reason --
you have a smaller tablespace to recover if you use a tablespace/object or
object type.

The unit of backup is a tablespace.  You can have things in backup mode for less
time if you aren't using RMAN by segregating them.

It is purely for your convienence that you would want to segregate them.
May 24, 2002 Art
      -- Thanks for the question regarding "Collapsing one column on an
      effective-dated table (using CONNECT BY?)", version 8.1.7

      You Asked
Hi, Tom.  I have the following columns on a person table:

PERSON_ID   PLAN_ID    PER_EFF_START_DT   PER_EFF_END_DT
---------   -------    ----------------   --------------
54          29         07-AUG-1998        09-FEB-1999
54          29         10-FEB-1999        16-AUG-1999
54          29         17-AUG-1999        30-DEC-1999
54          48         31-DEC-1999        06-SEP-2000
54          48         07-SEP-2000        02-JAN-2001
54          48         03-JAN-2001        18-NOV-2001
54          29         19-NOV-2001        31-DEC-2010

Each person ID can belong to a plan for a range of time.  Other changes to
person can also introduce new effective-dated records.
I can guarantee that the date ranges for a single PERSON_ID will
be contiguous and will never overlap, as shown.  (I.e., every
day between a person's earliest start date and latest end date
will always be accounted for exactly once.)

I would like to "collapse" the above rows into what you might call
a plan-centric view of the data, so that I only see the date limits
of when the plan changes (I don't care about the other changes that
prompted the addition of effective-dated rows).  So that as a
result of this SQL query, I'd have:

PERSON_ID   PLAN_ID    PLAN_EFF_START_DT   PLAN_EFF_END_DT
---------   -------    -----------------   ---------------
54          29         07-AUG-1998         30-DEC-1999
54          48         31-DEC-1999         18-NOV-2001
54          29         19-NOV-2001         31-DEC-2010

Doing a simple MAX/GROUP BY causes me to lose the fact that
29 existed as a part of two distinct intervals, a fact which
I need to retain.  I've tried some CONNECT BY logic, but I'm
not sure what, if anything, to use as a START WITH condition.

Can you help?

Thanks, Tom.

Art

      and we said...
We'll need to run a connect by twice for each row -- once to look "backwards"
for the start and one to look "forwards" for the end -- and then distinct it

ops$tkyte@ORA817DEV.US.ORACLE.COM> select distinct person_id, plan_id,
  2         (select min(per_eff_start_dt)
  3            from t t2
  4           start with t2.person_id = t.person_id
  5                  and t2.plan_id = t.plan_id
  6                  and t2.per_eff_start_dt = t.per_eff_start_dt
  7          connect by prior per_eff_start_dt-1 = per_eff_end_dt
  8                  and person_id = t.person_id
  9                  and plan_id = t.plan_id ) st,
 10         (select max(per_eff_end_dt)
 11            from t t2
 12           start with t2.person_id = t.person_id
 13             and t2.plan_id = t.plan_id
 14             and t2.per_eff_start_dt = t.per_eff_start_dt
 15         connect by prior per_eff_end_dt+1 = per_eff_start_dt
 16             and person_id = t.person_id
 17             and plan_id = t.plan_id ) en
 18   from t
 19  order by 3 ;

 PERSON_ID    PLAN_ID ST        EN
---------- ---------- --------- ---------
        54         29 07-AUG-98 30-DEC-99
        54         48 31-DEC-99 18-NOV-01
        54         29 19-NOV-01 31-DEC-10

Won't be horribly efficient on really really large sets of data!

Review &
      Followup

      Rating: 5
Thank
      you  May 26, 2002
Reviewer:  Art Metzer

May 24, 2002 Preeti -- Thanks for the question regarding "VLDB
      Performance", version 8.1.7

      You Asked
Tom,
We have a property appraisal system where users can upload PDF files. The size
of the table with these BLOBs is expected to be 1000GB. We would like to store
data for 2 years before flushing any records. What kind of servers, Oracle
version are we talking about? How does it affect performance of the Oracle
server.

Thanks.

      and we said...
Any version of Oracle 7.3 and up would be able to handle a terabyte of data
easily.  You should of course use 9i Release 2 since this is the current version
to implement a new system.  No reason to be out of date before you even start.

Any server (sized correctly of course) would be able to do this.

A terabyte isn't excessively large these days....

May 25, 2002 Bob -- Thanks for the question regarding
      "Database Link security question", version 8.1.7

      You Asked
I am unclear on the security issues, if any, when creating a database link.
If I, as a DBA role, create a public database link to a remote database, and a
regular (connect) user uses the link in a query, how are rights granted to the
objects across the link? Or how should the link be created? As an example:
We have a database PROD located Server1. We want to grant access to users on
Server2 to select against table T on PROD on Server1. We do not want to give
them Update capability. From where and as who should the link be created to
allow that?
Thanks
Bob Maggio

      and we said...
How do you create the database link?  is it like:

create public database link foo
connect to SCOTT
identified by TIGER
using 'foobar';

(that is known as a PROXY link -- all users are proxied into the foobar database
using SCOTT/TIGER)

or

create public database link foo
using 'foobar';

(that link uses the current users credentials -- whomever logged into the
database -- to connect)

If you use a PROXY link, then whatever privs that user (scott/tiger@foobar) has
-- all users will have on the remote site.

If you use a non-proxy link, then that user must exist on the remote site and
will have ONLY THE PRIVS associated with their account.

If you want users (all users) on server2 to select against table T on PROD -- I
might do this:

on prod, create user "PUBLIC_READ"
on prod, grant user PUBLIC_READ create session, select on T (and nothing else)
on prod, review all things granted to PUBLIC and make sure you are OK with that

on server2 create a public database link that connects to PUBLIC_READ on PROD.
Through this database link, users on server2 will be able to create a session,
they will be able to select from T, they will be able to do anything PUBLIC can
-- but nothing else.
Review & Followup

      Rating:
      4
username/password for database link  May 24, 2002

Reviewer:  Rao  from India
Hello Tom,
" create public database link foo
using 'foobar'; "
U told to create the database link like the above but, I guess we need to give
the username/password for the username in the dtabase we are connecting to i.e
we should create the private database link like this

create database link connect to
PUBLIC_READ identified by password using 'foobar';
This is my way of thinking as we are connecting to foobar and dont we need to
give the username/password? I might be wrong .Please verify..
Thanks

          Followup:

please re-read the answer.

I described the two types -- a PROXY link that includes the username/password
and a non-proxy link that does NOT include that.  We discussed the ins and outs
of each type.

      Rating:
      4
Reader  May 24, 2002
Reviewer:
      A reader
"
If you use a non-proxy link, then that user must exist on the remote site and
will have ONLY THE PRIVS associated with their account.
"

Why does Oracle requires same password also in local and
remote databases

          Followup:

because there is no mechanism to ask you for the password for the remote site --
and we would NEED to (else consider the SYS and SYSTEM accounts, you could just
install Oracle on your PC and connect as SYS or SYSTEM in any database if we
just "trusted" you were who you said you were)

      Rating: 5
Database
      Links  May 24, 2002
Reviewer:  Munz
       from Reston, USA
Tom:

By creating a user 'PUBLIC_READ' are you implementing the 1st  approach (proxy
links) and having everyone login in using it.

create public database link foo
connect to PUBLIC_READ
identified by password
using 'foobar'

Is this correct?

          Followup:

Yes, based on this "If you want users (all users) on server2 to select against
table T on PROD -- I might do this:"

that created a proxy link, accessible to everyone, that conveys the privs of the
PUBLIC_READ user.

      Rating: 5
Snappy Tom does
      it again  September 03, 2003
Reviewer:  Patrick
      Saunders  from Melbourne, Australia
In his usual succint manner, Tom details the specifics steps needed to create
database links, highlighting the two types PROXY and NON-PROXY.

Tom also details the security need for the user to have same password in the
local and remote database when using NON-PROXY database links.

In the next paragraph Tom slaps a reader around the ears for being lazy. Go Tom!

May 25, 2002 Munzer -- Thanks for the question regarding
      "checking for null values", version 8.1.7

      You Asked
Tom:

1.  Do you know how in PL/SQL you can check if an input from <TEXTAREA> tag is
null or not.  I created it a blank <TEXTAREA> tag and submitted the form and
PL/SQL read it like it has data even though there is nothing there.  It seems it
is submitted differently than <INPUT> tags.

2.  Would oracle save the hard returns generated in a <textarea> tag in the
database?

3.  did you stop sending email notifcations for your responses?

Thank you,

      and we said...
1) I just wrote this procedure:

create or replace procedure testing( x in varchar2 )
as
begin
    if ( x is null ) then
        htp.bold( 'x is null' );
    else
        htp.bold( 'x is not null' );
    end if;
end;
/

and using this form:

<form action=/wa/webdemo/owa/testing>

<textarea name=x>
</textarea>

<input type=submit>
</form>

When I submit that, i get:

x is null

Look for white space in the text area (spaces, newlines, tabs).

2) yes.

3) no, they get sent, if you didn't get one, you put in the wrong email address
most likely.  I get LOTS of bounces and I just delete them..
Review &
      Followup

      Rating: 5
textarea
      tag  January 28, 2002
Reviewer:  munzer
       from reston, va
Tom:

As a followup I have defined rows=2 and cols=50 with the
textarea tag.  This will create the problem

          Followup:

not for me it didn't.

<form action=/wa/webdemo/owa/testing>

<textarea name=x rows=2 cols=50>
</textarea>

<input type=submit>
</form>

resulted in "x is null"

In anycase, use a series of ltrim/rtrim's to trim blanks or newlines and then
see if the string has anything.

This behaviour could easily vary by BROWSER as well -- i use netscape 6.2, your
mileage may vary, it would be the browser causing any issues here (NOT plsql).

It'll be best to "scrub" the data, check it out a little more aggresively in
your code, trim whitespace.

      Rating: 5
checking for
      null values  February 03, 2002
Reviewer:
      Munz  from Reston, VA
Tom:

1. You are correct.  The reason it was not working because I have a spce between
opeing tag and closing tag.

<TEXTAREA name="source" rows=5 cols=50> </TEXTAREA>

2.  Also it seems that you can not really resttrict the user from entering more
than a certain number of characters in the bix lik the INPUT tag.  Is this
correct.

Thanks,

          Followup:

2) javascript can be used to enforce this.  You can use client side javascript
to inspect the length before submitting the form.

      Rating: 5
textarea
      tag  May 09, 2002
Reviewer:  Munz  from
      Reston, USA
Tom:

I have a form that has several <textarea> tags where I display to the user data
from the database to update.

Basically I do:

FOR x in (SELECT * from table where req_no=v_req_no)
LOOP
<textarea name=i_note rows=5 columns=60>
htp.p(x.note);
</textarea>
END LOOP;

After user updates information, I submit this to a procedure whre i update the
record.

  update table set note=i_note where req_no=v_req_no.

THe problem I am having is that every time I hit the update button, a hard
retund gets added to the end of information of each textarea tag even though I
do not add anything.

I looked at what is submitted in the URL and I notice some %0 characters.

After several updates the note column even though it is null it gets 4 or 5 or
more hard returns that i am puzzled wher they are coming from.

Do you know what is happening?

Thank you,

          Followup:

Try it with different browsers and you'll see different behaviour.

Try this:

htp.p( '<textarea .....>' || HTF.ESCAPE_SC(x.note) || '</textarea>' );

I believe you are adding the newline by accident by putting the /textarea tag by
itself on a new line.

Also -- get in the serious habit of using htp.escape_sc on your form item
values!! Otherwise, someone will type in something like:

After the show, Bill & Mary went to the mall.  It was < 5 miles from there.

Consider what the effect of &, <, and other special characters would be!!!

      Rating: 4
Thanks for the
      HTF.ESCAPE_SC tip  May 10, 2002
Reviewer:
      Andrew  from ca, usa

      Rating: 5
Textarea
      tag  May 10, 2002
Reviewer:  Munz  from
      Reston, USA
Tom:

You are right as usual.  It works now.

Thanks a lot.

      Rating: 5
textarea
      tag  May 24, 2002
Reviewer:  Munz  from
      Reston, USA
Tom:

I did this:

htp.p( '<textarea .....>' || HTF.ESCAPE_SC(x.note) || '</textarea>' );

with htf.escape_sc and without and got the same results.  I did input what you
said

After the show, Bill & Mary went to the mall.  It was < 5 miles from there.

THere was no difference.  Are you sure that the browser may be doing what the
escape_sc does.

          Followup:

I don't know what you mean by "got the same results"

If you view source in the browser, you'll find the text is REALLY

After the show, Bill &amp; Mary went to the mall.  It was &lt; 5 miles
from there.

May
      25, 2002 Pasko -- Thanks for the
      question regarding "Rows locked before Commit when open and close Cursor
      with FOR UPDATE select ??", version 8.1.7

      You Asked
Hi Tom

This one is definetely easy for you..me , i'm not sure..

I saw a colleague of mine coding like this,although that's not the way i'm used
to coding  myself..

cursor c  is
select c1 , c2 from some_table for update ;

c_rec   c%rowType ;

then  she is using this logic to lock the ROWS.

open  c ;
fetch c  into  c_rec ;
close c;

My question is , do we expect Oracle to Lock the rows in  Cursor c
before we COMMIT -  i mean by just opening and fetch the cursor , then Close ?

Best Regards

pasko

      and we said...
the open locks the rows.  you do not even need to fetch them.

to see this, just run:

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2    cursor c is select * from emp for update;
  3  begin
  4    open c;
  5  end;
  6  /

PL/SQL procedure successfully completed.

(in scott/tiger or copy the emp table)  and in another session just issue:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select* from emp for update;

that second session will BLOCK.

Review &
      Followup

      Rating: 5
Releasing
      locks  May 25, 2002
Reviewer:  Yamani
       from KSA
Hi Tom,

Can you tell us the other ways of releasing a lock other then commit.

Thanks,

          Followup:

commit;
rollback;
rollback to savepoint;

if you use dbms_lock to create your own "locks", you can release them
programatically (the ones created by dbms_lock, no others)

May 26, 2002 Tom
      -- Thanks for the question regarding "Using Java stored procedure to
      execute stored procedures in remote database", version 8.1.7

      You Asked
Hi Tom:

Can I execute a stored procedure in a remote database using Java stored
procedure in Oracle?  If so, would you provide me some sample codes on how to do
it?  Please help.

Thanks,
Tom

      and we said...
just prepare and execute the statement:

  begin p@remote_site; end;

just like you would in any other environment.
Review &
      Followup

      Rating: 3
Thanks, but
      need more help...  May 26, 2002
Reviewer:
      Tom  from OK
Tom:

Since I am totally new to Java stored procedure, would you give me some samples
how to set it up and run it in Oracle database?

Thanks,
Tom

          Followup:

search for

create replace java source named compile

on this site.  And if you have my book -- I have a whole chapter on this topic
as well with lots more info.

May 26, 2002 Bhavesh -- Thanks for the question regarding
      "Oracle Text's surprising behaviour", version 8.1.7

      You Asked
Hi Tom,
I am facing some strange behaviour with Oracle Text. I will be thankful if you
can let me know where I am wrong. I have the a table called spec which contains
cod_spec Number(15) as primary
key and some other fields including nam_manufac which is varchar2(256).
Now when I query the table :

SQL> select count(*) from spec;
COUNT(*)
---------
3738

I have created a context index on manufac as follows :
create index spec_manufac on spec(nam_manufac) indextype is ctxsys.context;

In my table for nam_manufac I have a data 'NEC San-ei Instruments'. When I
execute the query :

SQL> select count(*) from spec where contains (nam_manufac, 'NEC San-ei
Instruments', 1)>0;
COUNT(*)
---------
0

Surprisingly when I perform following query I get 14 records as shown below.
SQL> select count(*) from spec where nam_manufac='NEC San-ei Instruments';
COUNT(*)
---------
14

SQL> set linesize 70
SQL> select cod_spec, substr(nam_manufac,1,30), length(nam_manufac) from spec
where nam_manufac='NEC San-ei Instruments';
COD_SPEC SUBSTR(NAM_MANUFAC,1,30) LENGTH(NAM_MANUFAC)
--------- ------------------------------ -------------------
422946 NEC San-ei Instruments 22
422947 NEC San-ei Instruments 22
422948 NEC San-ei Instruments 22
419401 NEC San-ei Instruments 22
419402 NEC San-ei Instruments 22
419403 NEC San-ei Instruments 22
419404 NEC San-ei Instruments 22
419405 NEC San-ei Instruments 22
419406 NEC San-ei Instruments 22
419407 NEC San-ei Instruments 22
419408 NEC San-ei Instruments 22
419409 NEC San-ei Instruments 22
419410 NEC San-ei Instruments 22
419411 NEC San-ei Instruments 22
14 rows selected.
SQL >

I've tried to drop and re-create the index couple of times after inserting the
data.
Even I tried to change the name from 'NEC San-ei Instruments' to 'NEC San ei
Instruments' i.e removing the '-' sign considering that the contain clause in
the query might take it as a minus sign, but in vain.
Can you please tell me where I am missing and how can I overcome this problem.

Thanks in advance.
Regards

Bhavesh

      and we said...
Because you have special characters in there:

ops$tkyte@ORA8I.WORLD> create table t ( x varchar2(50) );

Table created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> insert into t values ( 'NEC San-ei Instruments' );

1 row created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> create index t_idx on t(x) indextype is ctxsys.context;

Index created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> select * from t where contains( x, 'NEC San-ei
Instruments' ) > 0;

no rows selected

ops$tkyte@ORA8I.WORLD> select * from t where contains( x, '{NEC San-ei
Instruments}' ) > 0;

X
--------------------------------------------------
NEC San-ei Instruments

ops$tkyte@ORA8I.WORLD> select * from t where contains( x, 'NEC San\-ei
Instruments' ) > 0;

X
--------------------------------------------------
NEC San-ei Instruments

The last two queries show different ways to get the results -- you will want to
read the interMedia guide on queries with contains to get a full understanding
of what you can / cannot do.

May 26, 2002 Vasaku -- Thanks for the question regarding
      "Procedures, Parameters, Parameter Modes", version 8.1.6

      You Asked
1.

Which two statements are true
1.A functin can be invoked from within an pl/sql expression.
2.A procedure must be invoked from withnin a pl/sql expression.

Tom, what do the above two reflect

2.

(i)Why is it that we cannot have boolean as returning values when we call
functions in sql.

(ii)When we execute functins from within a sql statement , can we use named
notation to send it parameters.

3.
in- parameter behaves like  constant variable
in/out behaves like a initialized variable
out-behaves like a uninitialized variable.

What do you mean by a initialized and uninitiazed in the above context.

what does the behave the way it does i.e. like a constant, initialized variable,
uninitialized variable in the above said cases.

Thank you

      and we said...
1) the plsql guide, chapter 2, defines:

...
Expressions and Comparisons

Expressions are constructed using operands and operators. An operand is a
variable,
constant, literal, or function call that contributes a value to an expression.An
example of a simple arithmetic expression follows:
-X / 2 + 3
.......

therefore, a procedure does not fall into their definition of what may be in an
expression.  An expression is something that could be on the right hand side of
an assignment:

     variable := EXPRESSION;

a procedure cannot go there.

2)

i) because SQL does not have a BOOLEAN type.  When working in SQL, you are
constrained to pure SQL types (or types you have created yourself with the
CREATE TYPE command)

ii) no.  documented restriction.

3) I do not agree with that list:

IN:     behaves like a constant
IN/OUT: behaves like a varaible initialized by the caller
OUT:    behaves like a variable initialized to NULL

initialized just means "its initial value".  All variables in PLSQL are
initialized (contain some value) and by default they are initialized to NULL.
It is not like C where if you do not initialize a variable -- it contains some
random values, in PLSQL all variables are initialized.
May 26, 2002 --
      Thanks for the question regarding "how does this SQL run", version 8.1.7

      You Asked
Hi TOm

this is probably an academic question but if you could give a hand it would be
great.

Basically i have this query

select nom_proceso
from md_grupo a, md_aplicacion b, md_tipo_carga c, md_proceso d
where a.cod_grupo=b.cod_grupo
and b.cod_aplicacion=c.cod_aplicacion
and c.cod_tipo_carga=d.cod_tipo_carga
and d.cod_tipo_carga=4

and the excution plan is:
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=2 Bytes=66)
   1    0   NESTED LOOPS (Cost=3 Card=2 Bytes=66)
   2    1     NESTED LOOPS (Cost=2 Card=1 Bytes=10)
   3    2       NESTED LOOPS (Cost=2 Card=1 Bytes=8)
   4    3         TABLE ACCESS (BY INDEX ROWID) OF 'MD_TIPO_CARGA' (Cost=1
Card=1 Bytes=4)
   5    4           INDEX (UNIQUE SCAN) OF 'PK_MD_TIPO_CARGA' (UNIQUE)
   6    3         TABLE ACCESS (BY INDEX ROWID) OF 'MD_APLICACION' (Cost=1
Card=6 Bytes=24)
   7    6           INDEX (UNIQUE SCAN) OF 'PK_MD_APLICACION' (UNIQUE)
   8    2       INDEX (UNIQUE SCAN) OF 'PK_MD_GRUPO' (UNIQUE)
   9    1     TABLE ACCESS (BY INDEX ROWID) OF 'MD_PROCESO' (Cost=1 Card=2
Bytes=46)
  10    9       INDEX (RANGE SCAN) OF 'FK1_MD_PROCESO' (NON-UNIQUE)

a.cod_grupo is PK of md_grupo
b.cod_grupo is FK of md_aplicacion to md_grupo, indexed
b.cod_aplicacion is PK of md_aplicacion
c.cod_aplicacion is FK of md_tipo_carga to md_aplicacion, indexed
c.cod_tipo_carga is PK of md_tipo_carga
d.cod_tipo_carga is FK of md_proceso to md_tipo_carga, indexed

My question is how the steps of joins is performed, my understanding is as
follows

first MD_TIPO_CARGA and MD_APLICACION are joined however I dont see this very
clear because I thought we would have to perform a full table scan in nested
loop
second MD_GRUPO is joined with previous result set
thirdly the result set is joined with MD_PROCESO... in which step is predicate
and d.cod_tipo_carga=4 filtered?

thank you

      and we said...
since:

and c.cod_tipo_carga=d.cod_tipo_carga
and d.cod_tipo_carga=4

is the same as:

and c.cod_tipo_carga=d.cod_tipo_carga
and c.cod_tipo_carga=4

and we like that better (better access plan), that is what we did.  We just
rewrote your predicate for you using standard relational rules.

May 26, 2002 Orna -- Thanks for the question regarding "A tool
      for data inflation for volume tests", version 8.1.7

      You Asked
Hi,

Are you aware of any good tool ( Oracle's, third party ) - that can artificialy
inflate a small amount of data intelligently - so we can perform volume tests
when we do not have a large volume yet ?

this is a very common situation with our projects - many times we still do not
have large volume conversion or production data - but we still need to test our
software on a large volume

thanks a lot
Orna

      and we said...
No, I am not.  I typically have as part of my implementation plan a test data
generator when needed for volume testing like this.

Anyone out there aware of anything more "intelligent"?
Review &
      Followup

      Rating: 4
A tool for data
      inflation for volume tests  May 28, 2002
Reviewer:
      George Spears  from Dayton, OH
You can use any of the following tools

PumpItUp,
http://www.hallogram.com/pumpitup/
CA-Datamacs -
http://www3.ca.com/Files/ProductAnnouncements/datamacs_12pa.pdf
Data Xccelerator -
http://www.tangiblesoftware.com/products/xccelerator/

      Rating: 5
another good
      tool  May 28, 2002
Reviewer:  Paul Druker
       from Fairfax, VA USA
You may also want to look at DataFactory:

http://www.quest.com/datafactory/

May 27, 2002 Eric -- Thanks for the question regarding "Is
      there a difference in which privileges 'system' has when running a pl/sql
      procedure and anonymous blocks?", version 8.1.7

      You Asked
Connected as 'system' in SQL*PLUS, I run the following:
------------------------
drop table system.tablenames;

create table system.tablenames (
    sql_type varchar2(10),
    tablename varchar2(100)
)
/

CREATE OR REPLACE procedure system.getAllTableNames
is
begin
    insert into system.tablenames (sql_type, tablename)
    select 'PROC' , owner || '.' || table_name from sys.all_tables;
end;

/

begin
    insert into tablenames (sql_type, tablename)
    select 'SQL', owner || '.' || table_name from sys.all_tables;

    system.getAllTableNames;
end;
/
------------------------
Basically I insert the contents of sys.all_tables into my own table. I do it
once from within a procedure, and once using 'regular' sql. Since both are done
as system there should be no difference in the number of rows seen. But the
following query says there's a difference:

SQL> select count(sql_type), sql_type from tablenames group by sql_type;

COUNT(SQL_TYPE) SQL_TYPE
--------------- ----------
             28 PROC
            481 SQL

Why?

Thanks,
Eric

      and we said...
Roles are not enabled during the execution of a definers rights routine:

http://asktom.oracle.com/~tkyte/Misc/RolesAndProcedures.html
Therefore, things SYSTEM sees because of the DBA role it has are not visible in
the  procedure.  You could use invokers rights (authid current_user) and the
procedure would run as the invoker -- with the roles that are currently enabled.

Please don't use SYSTEM or SYS -- those are our accounts.  You can do heavy duty
damage to your database by accident using them.  Create your own account to
develop/test with.
May 27, 2002
      Justin -- Thanks for the question regarding
      "Recovery !!!!", version 8.1.7

      You Asked
Hi Tom,

Had one question...If I have a media failure and I lose my RBS Tablespace, will
i still be able to recover up to the point of failure assuming my archiving is
enabled...If I perform recovery how will oracle perform recovery in such
situations ( w/o RBS)....

Thanks in Advance...

OCP Guy...

      and we said...
You will restore your RBS from your last backup and roll forward just as you
would for any other tablespace.

You will NOT lose your RBS, you will RESTORE it.  You MUST (100%, absolutely
MUST) back it up like anything else (other them a temp tablespace)


Subject | Article | Last Updated (East coast time)
how to concatenate string in the resultset | article only | May 28, 2002 07:29 am
DB 8.1.7 | article only | May 28, 2002 07:57 am
CBO | article only | May 28, 2002 08:19 am
How can execute/parse ratio be small or negative, and soft parse ratio be so high? | article only | May 28, 2002 09:16 am
What is XML? | article only | May 28, 2002 10:46 am
Reorganisation | article only | May 28, 2002 12:58 pm
Uniqueness between a group of rows | article only | May 28, 2002 01:02 pm
Package Vs Simple SQL | article only | May 29, 2002 07:32 am
Example for array fetch using OCI8 (obndra seems to be obsolete) | article only | May 29, 2002 07:48 am
roles -> privileges | article only | May 29, 2002 08:04 am
Capability of Pipelined functions | article only | May 29, 2002 09:13 am
Outer Join When Join Variable Needs to be calculated | article only | May 29, 2002 09:19 am
2 distinct uses of rownum in where clause. Why did this work? | article only | May 29, 2002 10:14 am
Delete Duplicate Records. | article only | May 29, 2002 11:14 am
Group reporting to return 0 counts | article only | May 29, 2002 02:09 pm
SQL Tuning | article only | May 29, 2002 02:44 pm
Performance Tuning | article only | May 30, 2002 07:14 am
Privileges needed to created the function based index. | article only | May 30, 2002 07:48 am
Get OS User | article only | May 30, 2002 07:51 am
Problem of UTL_FILE | article only | May 30, 2002 08:28 am
Flashback query | article only | May 30, 2002 08:58 am
Secure application roles | article only | May 30, 2002 09:04 am
Oracle features query - space management auto, alter table monitoring, db_keep_cache | article only | May 30, 2002 10:08 am
Rollback Segments | article only | May 30, 2002 10:41 am
Statement in Java | article only | May 30, 2002 11:17 am
Meaning of ctxsrv log entries | article only | May 30, 2002 03:30 pm
Talking to MS SQL server 2000 from an Oracle Trigger using java stored procedure | article only | May 30, 2002 03:40 pm
Indexing at very Large Table | article only | May 30, 2002 08:14 pm
How can I find Max sessions | article only | May 31, 2002 07:25 am
existence-based key and Oracle | article only | May 31, 2002 07:27 am
User database transfer | article only | May 31, 2002 07:29 am
Data Trigger that updates another table in another tablespace | article only | May 31, 2002 07:42 am
INDEX SKIP SCANS | article only | May 31, 2002 07:44 am
Loader Performance Optimization | article only | May 31, 2002 10:05 am
Passing resultsets from advanced searches to an application | article only | May 31, 2002 10:55 am
SQLCODE values | article only | May 31, 2002 11:10 am
Fuction to replace a STRING ! | article only | May 31, 2002 12:32 pm
How to find out the number of rows processed for an active SQL of active session?. | article only | May 31, 2002 12:45 pm
what is the scope of pl/sql tables | article only | May 31, 2002 02:40 pm
trigger insert clob field | article only | Jun 02, 2002 03:06 pm
SELECT QUERY | article only | Jun 02, 2002 03:23 pm
Preserving the execution plan for a query on a table that varies greatly | article only | Jun 02, 2002 05:41 pm
how to write to a array and send it back to jdbc | article only | Jun 03, 2002 06:41 am
Archive Log Cleaning | article only | Jun 03, 2002 06:50 am
Uploading of data from a flat file | article only | Jun 03, 2002 06:52 am
Follow up question to "RAW datatypes and CRC32 " | article only | Jun 03, 2002 09:11 am
Unix Back NT Restore | article only | Jun 03, 2002 09:17 am
migration, snapshot | article only | Jun 03, 2002 09:24 am
Index rebuild | article only | Jun 03, 2002 09:39 am
Mutating trigger | article only | Jun 03, 2002 06:04 pm
Create Table Nologging As Select * vs Insert /*+ append */ Into Table Select * | article only | Jun 03, 2002 06:21 pm
row(s) 1 - 51 of 51
May 28, 2002 shankar ganesh -- Thanks for the question regarding "how to concatenate string in the resultset", version 8i
You Asked
i have a query and it gives a resultset as follows.

select distinct sih.name as item_name ,ae.well_uwi as well_uwi,ae.survey_name as
survey_name,
ae.line_name as line_name from
asset_item ai,storage_item_hdr sih,asset_hdr ah,asset_element ae  where
ai.asset_id=ah.asset_id and

ai.item_id=sih.item_id and

ai.asset_id=ae.asset_id and

sih.item_id is not null

"NAME"    "WELL_UWI"      "SURVEY_NAME"                "LINE_NAME"

"rough"    "GURJEET1"        null                   null
"rough"    "ROTA WELL"        null                   "line1"
"rough"     null               "rota survey"           null

i want the output to be as follows

"name"                  "location_name"
"rough"           "GURJEET1","ROTAWELL","rota survey","line1"

Please do the needful to me.It will be of great help

thanks in advance
shankar ganesh.
and we said...
If there is some maximum number of expected rows / name -- yes.

tkyte@TKYTE816> select * from t;

NAME                           WELL_UWI             SURVEY_NAME
LINE_NAME
------------------------------ -------------------- --------------------
--------------------
rough                          gurjeet1
rough                          rotawell                                  line1
rough                                               rota survey

tkyte@TKYTE816>
tkyte@TKYTE816> select name,
  2         rtrim(max(decode(r,1,txt,null))||max(decode(r,2,txt,null))||
  3                             max(decode(r,3,txt,null)),',') data
  4    from (
  5  select name,
  6         rownum r,
  7         decode(well_uwi,null,null,well_uwi||',')||
  8         decode(survey_name,null,null,survey_name||',')||
  9         decode(line_name,null,null,line_name||',') txt
 10    from t)
 11   group by name ;

NAME                           DATA
------------------------------ ----------------------------------------
rough                          gurjeet1,rotawell,line1,rota survey

shows how to do it if there are THREE rows/name -- if you have more than that,
change the query.
Review & Followup
Rating: 5
it is very much useful June 15, 2001
Reviewer: shankar ganesh from India
The reply was very much useful and the most important aspect is that the reply
was timely

Rating: 3
SAME RESULT FOR MULTIPLE ROWS May 28, 2002
Reviewer: Rahul Sharma from India
Hi Tom
What you told is work for one row only but what about more than one row.
I have a table with two column having following data-

select * from try;
Name       N
----    ----------
A          1
B          1
C          2
D          2
E          2

In this case I want same result like below-
         N NAME
---------- ----
         1 A,B
         2 C,D,E

Thanks.

Followup:
use the analytic functions to get a row number per GROUP (partition):

ops$tkyte@ORA817DEV.US.ORACLE.COM> select n, name, row_number() over ( partition
by n order by name ) rnum
  2    from t
  3  /

         N NAME                                 RNUM
---------- ------------------------------ ----------
         1 A                                       1
         1 B                                       2
         2 C                                       1
         2 D                                       2
         2 E                                       3

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select n, rtrim( max( decode( rnum, 1,
name||',', null ) ) ||
  2                                   max( decode( rnum, 2, name||',', null ) )
||
  3                                   max( decode( rnum, 3, name||',', null ) ),
',' ) names
  4    from ( select n, name, row_number() over ( partition by n order by name )
rnum
  5             from t
  6             )
  7    group by n
  8  /

         N NAMES
---------- --------------------
         1 A,B
         2 C,D,E

ops$tkyte@ORA817DEV.US.ORACLE.COM>

Rating: 5
Great May 29, 2002
Reviewer: Rahul Sharma from India
It was really great.

Rating: 5
Great May 29, 2002
Reviewer: Rahul Sharma from India
It was really great.

May 28, 2002 Suresh -- Thanks for the question regarding "DB 8.1.7", version 8.1.7
You Asked
Hi Tom,
   I have created a partitioned table as below with Parallel degree 2.

SQL> create table emp(empno   number(4), ename varchar2(20))
  2  partition by range(empno)
  3  (partition part1 values less than (100),
  4  partition part2 values less than (200),
  5  partition part3 values less than (maxvalue))
  6  parallel 2;
Table created.

SQL> insert into emp select rownum, substr(object_name, 1,15)
  2  from dba_objects where rownum < 200;
199 rows created.

SQL> analyze table emp compute statistics;
Table analyzed.

SQL> select min(empno), max(empno) from emp partition (part1);
MIN(EMPNO) MAX(EMPNO)
---------- ----------
         1         99

SQL> select min(empno), max(empno) from emp partition (part2);
MIN(EMPNO) MAX(EMPNO)
---------- ----------
       100        199

   I issue the following queries and the partition elimination does not seem to
work as it is going in for a FULL TABLE SCAN. Please let me know as I am
expecting the partition elimination to work.

SQL> select * from emp where empno = 110;
     EMPNO ENAME
---------- --------------------
       110 asdhdf

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=9)
   1    0   TABLE ACCESS* (FULL) OF 'EMP' (Cost=1 Card=1 Bytes=9)      :Q31000

  1 PARALLEL_TO_SERIAL            SELECT /*+ Q31000 NO_EXPAND ROWID(A1) */ A1.
                                "EMPNO",A1."ENAME" FROM "EMP" PX_GRA

SQL> select count(*) from emp where empno between 50 and 70;
  COUNT(*)
----------
        21

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=2)
   1    0   SORT (AGGREGATE)
   2    1     SORT* (AGGREGATE)                                        :Q33000
   3    2       TABLE ACCESS* (FULL) OF 'EMP' (Cost=1 Card=23 Bytes=46 :Q33000
          )
   2 PARALLEL_TO_SERIAL            SELECT /*+ PIV_SSF */ SYS_OP_MSR(COUNT(*))
FROM (SELECT /*+ NO_EXPAND ROWID(A2)

   3 PARALLEL_COMBINED_WITH_PARENT

Thanks,
Suresh
and we said...
autotrace isn't showing you the partition key start/stop fields avaiable with a
plan.  By simply settting sql_trace=true and using tkprof:

select *
from
 emp where empno = 110

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.01          0          0          3           0
Fetch        2      0.00       0.00          0          0          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.00       0.01          0          0          3           1

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 216

Rows     Row Source Operation
-------  ---------------------------------------------------
      0  TABLE ACCESS FULL EMP PARTITION: START=2 STOP=2

Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: CHOOSE
      0   TABLE ACCESS   GOAL: ANALYZED (FULL) OF 'EMP' [:Q96000]
              PARTITION: START=2 STOP=2
             SELECT /*+ Q96000 NO_EXPAND ROWID(A1) */ A1."EMPNO",A1."ENAME"
             FROM "EMP" PX_GRANULE(0, BLOCK_RANGE, DYNAMIC)  A1 WHERE
               A1."EMPNO"=110

********************************************************************************

select count(*)
from
 emp where empno between 50 and 70

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          3           0
Fetch        2      0.00       0.00          0          0          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.00       0.00          0          0          3           1

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 216

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE
      0   SORT AGGREGATE
      0    TABLE ACCESS FULL EMP PARTITION: START=1 STOP=1

Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: CHOOSE
      1   SORT (AGGREGATE)
      0    SORT (AGGREGATE) [:Q97000]
              SELECT /*+ PIV_SSF */ SYS_OP_MSR(COUNT(*)) FROM (SELECT /*+
              NO_EXPAND ROWID(A2) */ A2."EMPNO" C0 FROM "EMP" PX_GRANULE(0,
              BLOCK_RANGE, DYNAMIC)  A2 WHERE A2."EMPNO">=50 AND
                A2."EMPNO"<=70) A1
      0     TABLE ACCESS   GOAL: ANALYZED (FULL) OF 'EMP' [:Q97000]
                PARTITION: START=1 STOP=1

You can see the partition elimination happening.
Review & Followup
Rating: 4
Thanks so much for your help May 28, 2002
Reviewer: Suresh from New York

Rating: 4
Very Useful Question May 28, 2002
Reviewer: Kumar Bhogaraju from Chicago, IL USA
Suresh,
Thanks for asking.

May 28, 2002 Alex -- Thanks for the question regarding "CBO", version 8.1.7
You Asked
Hi Tom!

    I read in SQL Statement Tuning Book (Oracle University - AT8i)
the following:

    "When statistics are available for at least one referenced object,
cost-based optimization is used; any missing statistics are estimated or
replaced by hard-coded, built-in values."

    I'd like you to explain more about "estimated, replaced by head-coded". How
is it done? Default values? Worst case, best case? What tools does optimizer use
to estimate cost when there are no statistics colleted?

    Another question related:

    Cost-based optimization uses:
    - number of logical reads;
    - CPU utlization;
    - Network transmissions.

    Logical read is OK, but how does optimizer compute CPU utilization and
network. Is it based on hardware only? The load of the very moment does not
influence, does it?

    I really like your site! It's great, thank you!

Regrads,
Alex
and we said...
See
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76992/stats.htm#25056
it is all documented....

I don't know where you are quoting the second part from -- what document does
that come from?  Maybe they are talking about the new 9i feature (not in 8i or
before) whereby you can "analyze the system" to gather CPU and IO stats that
will influence the optimizer..
Review & Followup
Rating: 4
Document... May 28, 2002
Reviewer: Alex from Brazil
I quoted from the same, Oracle University Book. It's 8i.
I would like to know how Optimizer compute CPU and network utilization...

Thanks!

Followup:
It does not, not in 8i.  From the designing and tuning for performance guide:

....
Cost-Based Optimizer (CBO)

In general, you should always use the cost-based approach. The rule-based
approach is available for the benefit of existing applications.

The CBO determines which execution plan is most efficient by considering
available access paths and by factoring in information based on statistics for
the schema objects (tables or indexes) accessed by the SQL statement. The CBO
also considers hints, which are optimization suggestions placed in a comment in
the statement.

The CBO consists of the following steps:

1. The optimizer generates a set of potential plans for the SQL statement based
on its available access paths and hints.

2. The optimizer estimates the cost of each plan based on statistics in the data
dictionary for the data distribution and storage characteristics of the tables,
indexes, and partitions accessed by the statement. The cost is an estimated
value proportional to the expected resource use needed to execute the statement
with a particular plan. The optimizer calculates the cost of each possible
access method and join order based on the estimated computer resources,
including (but not limited to) I/O and memory, that are required to execute the
statement using the plan. See Also: For more information on hints, see Chapter
7, "Using Optimizer Hints".
Serial plans with greater costs take more time to execute than those with
smaller costs. When using a parallel plan, however, resource use is not directly
related to elapsed time.

3. The optimizer compares the costs of the plans and chooses the one with the
smallest cost.
..............

Rating: 4
Problems in a production base May 28, 2002
Reviewer: Alex from Brazil
Tom, the reason of my question:

A query is taking seconds in a test enviroment, and minutes(!) in the
production. The only diference is the enviroment (machine, network. Strange: the
production machine is faster - it's a cluster!).
The tables and the data are just the same.
We did an "analyze table <x> compute statistics;", that's all. No histograms nor
index stats.
Do you have any idea?

Thanks!

Followup:
http://asktom.oracle.com/~tkyte/tkprof.html
would be a great tool to use to start to diagnose this.

May 28, 2002 Doug
      -- Thanks for the question regarding "How can execute/parse ratio be small
      or negative, and soft parse ratio be so high?", version 8.1.7

      You Asked
Tom, I have statistics during a certain period at night that always indicate a
soft parse ratio of 99.99% and an execute to parse ratio that is something like
-.01 or .01 or .02 or .1  i.e., something very low.   How is this possible?  I
understand from a different post that a negative execute to parse ratio can be
caused by parsing statments and never executing them.  But, I thought the soft
parse ratio was indicitive of finding a statement in the shared pool already
that we need to soft parse and execute.  How can the two ratios be so different?
 Cursor_sharing=FORCED in this example.  Is this an idiosyncrasy of
cursor_sharing=forced?

      and we said...
That means the PARSES are greater than or equal to the EXECUTES.  The formula
used it:

      ,'         Execute to Parse %:'                  dscr
      , round(100*(1-:prse/:exe),2)                    pctval

As prse = exe, the execute/parse % goes to ZERO.  As prse starts to exceed
executes, the % goes NEGATIVE.

This happens when an application parses a query but never executes it.  Some
applications (generic apps typically) parse a "select * from T" to describe a
table, never execute the cursor -- their parses exceed their executes.

The soft parse ratio is good cause they parse "select * from t" hundreds of
times -- and each parse is in fact a soft parse.  They just parse way too much.

A soft parse happens during the PARSE.  You never need execute the query to
increase the soft parse count.  So the two are not really related in any way.
You can have a low execute to parse with a high soft parse ratio, you can have a
high execute to parse with ah low soft parse ratio as well.  They are two
indicators that you look at independent of each other.

Review &
      Followup

      Rating: 5
Nothing to do
      with Cursor_sharing=forced?  May 28, 2002

Reviewer:  Doug  from Danbury, CT USA
Tom - if I turn cursor_sharing back to exact during the same period of activity,
the execute/parse goes up to around 20-30% and the soft parse goes down.  Does
that make any sense?  Can the over-binding you discuss in your book cause excess
parsing?

          Followup:

I ran this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter system flush shared_pool;

System altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter system set cursor_sharing=exact;

System altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec perfstat.statspack.snap

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          type rc is ref cursor;
  3
  4          l_cursor rc;
  5  begin
  6          for i in 1 .. 1000
  7          loop
  8                  open l_cursor for 'select * from dual where dummy = ''' ||
i || '''';
  9                  close l_cursor;
 10          end loop;
 11  end;
 12  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec perfstat.statspack.snap

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter system flush shared_pool;

System altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter system set cursor_sharing=force;

System altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec perfstat.statspack.snap

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          type rc is ref cursor;
  3
  4          l_cursor rc;
  5  begin
  6          for i in 1 .. 1000
  7          loop
  8                  open l_cursor for 'select * from dual where dummy = ''' ||
i || '''';
  9                  close l_cursor;
 10          end loop;
 11  end;
 12  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec perfstat.statspack.snap

PL/SQL procedure successfully completed.

and the stats pack shows

a) no measurable difference in the execute to parse
b) a huge difference in the soft parse %

The only thing I can hypothesize in your case is that the execute/parse would
have gone upto 20-30% all by itself anyway as it is 100% controlled by the
client code and cannot be affected by the server settings at all.  It is 100% a
function of the number of times the client parses a sql statement and how many
times they execute it.  There must have been something different about the
execution path of your code during that time.

stats pack with cursor_sharing=exact and hard parse of 1,000 statements:

Load Profile
~~~~~~~~~~~~                            Per Second       Per Transaction
                                   ---------------       ---------------
                  Redo size:            159,598.00            319,196.00
              Logical reads:              1,281.00              2,562.00
              Block changes:                183.00                366.00
             Physical reads:                  6.50                 13.00
            Physical writes:                  0.00                  0.00
                 User calls:                  3.00                  6.00
                     Parses:                570.00              1,140.00
                Hard parses:                505.50              1,011.00
                      Sorts:                 42.50                 85.00
                     Logons:                  0.00                  0.00
                   Executes:                594.50              1,189.00
               Transactions:                  0.50

  % Blocks changed per Read:   14.29    Recursive Call %:               99.89
 Rollback per transaction %:    0.00       Rows per Sort:               59.49

Instance Efficiency Percentages (Target 100%)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            Buffer Nowait %:  100.00       Redo NoWait %:              100.00
            Buffer  Hit   %:   99.49    In-memory Sort %:              100.00
            Library Hit   %:   73.12        Soft Parse %:               11.32
         Execute to Parse %:    4.12         Latch Hit %:              100.00
Parse CPU to Parse Elapsd %:   97.98     % Non-Parse CPU:               99.61

-------------------------------------------------------------------------
stats pack with cursor_sharing=force

Load Profile
~~~~~~~~~~~~                            Per Second       Per Transaction
                                   ---------------       ---------------
                  Redo size:            178,840.00            357,680.00
              Logical reads:              1,310.00              2,620.00
              Block changes:                226.00                452.00
             Physical reads:                  0.00                  0.00
            Physical writes:                 62.00                124.00
                 User calls:                  3.00                  6.00
                     Parses:                568.00              1,136.00
                Hard parses:                  6.00                 12.00
                      Sorts:                 42.50                 85.00
                     Logons:                  0.00                  0.00
                   Executes:                592.50              1,185.00
               Transactions:                  0.50

  % Blocks changed per Read:   17.25    Recursive Call %:               99.88
 Rollback per transaction %:    0.00       Rows per Sort:               59.45

Instance Efficiency Percentages (Target 100%)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            Buffer Nowait %:  100.00       Redo NoWait %:              100.00
            Buffer  Hit   %:  100.00    In-memory Sort %:              100.00
            Library Hit   %:   93.33        Soft Parse %:               98.94
         Execute to Parse %:    4.14         Latch Hit %:               99.99
Parse CPU to Parse Elapsd %:   76.92     % Non-Parse CPU:               99.94

      Rating: 4
Very
      Puzzling  May 28, 2002
Reviewer:  anil
       from India
Sir

How a Statement given for parsing cannot Execute
Can u give me an Example of this

Regards

          Followup:

    dbms_sql.parse(  l_theCursor,  p_query, dbms_sql.native );
    dbms_sql.describe_columns
    ( l_theCursor, l_colCnt, l_descTbl );

that just parsed the query, described it (to find the number of columns, data
types, names, sizes, etc).

It never executed it -- If you closed the cursor, you would have a PARSE without
an execute.

May 28, 2002 Ashish -- Thanks for the question regarding "What
      is XML?", version 8.1.5

      You Asked
Hi Tom,
Please explain how exactly XML works with Oracle. We have an
application on Oracle 8.1.5 + OAS. We are using OWA pack to
generate the dynamic HTML pages. Can I use XML to do the same
thing? If yes then how? And what is the advantage of using XML?
I have gone through lots of articles but still couldn't find a
simple example which takes one simple table and illustrates.
also guide me the software required to try it out.

Thanks,

ashish

      and we said...
Well, XML is not HTML.  HTML is a markup language, it lets us
specify that a certain piece of text is a H1 (heading 1).  That
connotates a certain look and feel will be applied to the text.
HTML can specify  that a horizontal rule (<HR>) should be
displayed.  XML on the other hand does not convey any formatting
-- rather it conveys "content".  It lets you look at a clump of
data and see that this "text" is a phone number, that text is a
name and so on.

XML can be converted into HTML using technologies such as XSL
(eXtensible Stylesheet Language).  XSL is basically a set of
formatting objects.  The most popular formatting objects are
table cells, blocks and so on.  Each formatting object has a
number of properties that can be used to layout the document
according to the tastes of the designer.  If I was given an XML
document instance:

<?xml version="1.0"?>
<AddressBookEntry>
       <name>Thomas Kyte</name>
       <phone>(xxx)-yyy-zzzz</phone>
</AddressBookEntryEntry>

And an XSL instance:

<xsl:stylesheet
  xmlns:xsl='http ://www.w3.org/1999/XSL/Transform'>
<xsl:template match="/">
<H1><xsl:value-of select="//name"/></H1>
<b><xsl:value-of select="//phone"/></b>
</xsl:template>
</xsl:stylesheet>

I could merge the two to produce:

<H1>Thomas Kyte</H1>
<b>(xxx)-yyy-zzzz</b>

The reasons for doing this are many - one of the predominant
might be so you can have many XSL transformations for the same
XML instance -- one that displays well on a browser, another on
a cell phone, another on a pager, another on a palm pilot and so
on.

At this point, I'll refer you to

http://www.oracle.com/xml/
to get started as far as where to get the software.  If you
follow the technet link from there, you'll find other links such
as

o About XML A short overview of database-related XML
  technologies

o Using XML and Relational Databases for Internet Applications

o XML Support in Oracle8i and Beyond

and so on.

Review &
      Followup

      Rating:
      2
  April 23, 2001
Reviewer:
      A reader

      Rating: 4
Is a XML Parser
      available in Oracle 8.1.5  May 24, 2002
Reviewer:
      Pratap Kumar Mishra  from Calcutta,India
Hi Tom,
     I want to know if any PL/SQL XML parser is available for Oracle 8.1.5
database. I downloaded XDK 9.2 but while installing it geve me a lot of errors.
Can you please tell me how to get the PL/SQL parser for Oracle 8.1.5.

          Followup:

I asked Sean Dillon, our local XML guru, to take a look at this and here's what
he had to say:
--

The XDK supports 8.1.6 and above.  The errors you experienced were because of
version incompatability.  There is a utility on technet called PLSXML that may
be what you're looking for.  It has a few components useful for performing
various XML-related processing.

DBXML for formatting SQL query results as XML.
DBDOM for the most common and useful parts of the DOM API in PL/SQL.
DBXSL for performing XSL transformations in PL/SQL.

It comes w/ install instructions, various demonstrations so you can see PLSXML
in action, etc.  Keep in mind, ALL the functionality in PLSXML is outdated in
8.1.6, so I would recommend an upgrade of your database before I'd recommend
PLSXML... but if you are constrained to 8.1.5 PLSXML is a good option.

You'll need an Oracle TechNet registration (they're free, you just need to sign
up).  Once you have your OTN account, you can get the utility here:
http://otn.oracle.com/tech/xml/htdocs/plsxml/xml4plsql.htm
Good luck!

Sean Dillon

May 28, 2002 atul
      -- Thanks for the question regarding "Reorganisation", version 8.0.4

      You Asked
Sir,

1)If we have a table with 10,000,00 records and say if we DELETE
haif of records from that table.

In this case should we exp/imp the table or
New insert will use blocks from which we deleted the records..

What about speed ie will speed of execution remains same with exp/imp and
without exp/imp..

Or it will be sufficient to only recreate all the indexes(we are using B-tree
indexes) for speed..

2)In case if we do,

create table A as select * from B;

and if we drop B and rename A to B..

What will happen to database triggers which was on table A?

3)Many times i read you suggest not to recreate indexes..
In many books they mention about recreating Indexes is essential.
Say as in question 1 case why not to recreate indexes?

Thanks,
atul

      and we said...
1) I might be tempted to:

exp userid=u/p tables=that_table ROWS=N;

CREATE TABLE temp unrecoverable as select the records to KEEP;
drop table that_table;
rename temp to that_table;

imp userid=u/p full=y ignore=y

(that'll bring back the indexes, the triggers, the constraints)  For example:

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop user a cascade;
User dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> grant dba to a identified by a;
Grant succeeded.

ops$tkyte@ORA817DEV.US.ORACLE.COM> @connect a/a

a@ORA817DEV.US.ORACLE.COM> create table t as select * from all_objects;
Table created.

a@ORA817DEV.US.ORACLE.COM> alter table t add constraint t_pk primary
key(object_id);
Table altered.

a@ORA817DEV.US.ORACLE.COM> create index t_idx on t(object_name);
Index created.

a@ORA817DEV.US.ORACLE.COM> create sequence t_seq;
Sequence created.

a@ORA817DEV.US.ORACLE.COM> create or replace trigger t_trigger
  2  before insert on t
  3  for each row
  4  begin
  5          select t_seq.nextval into :new.object_id from dual;
  6  end;
  7  /
Trigger created.

a@ORA817DEV.US.ORACLE.COM> grant all on t to scott;
Grant succeeded.

a@ORA817DEV.US.ORACLE.COM> grant select on t to public;
Grant succeeded.

a@ORA817DEV.US.ORACLE.COM> column table_name format a8
a@ORA817DEV.US.ORACLE.COM> column search_condition format a35

a@ORA817DEV.US.ORACLE.COM> select object_name, object_type from user_objects;

OBJECT_NAME                    OBJECT_TYPE
------------------------------ ------------------
T                              TABLE
T_IDX                          INDEX
T_PK                           INDEX
T_SEQ                          SEQUENCE
T_TRIGGER                      TRIGGER

a@ORA817DEV.US.ORACLE.COM> select constraint_name, constraint_type,
  2  table_name,  search_condition
  3  from user_constraints;

CONSTRAINT_NAME                C TABLE_NA SEARCH_CONDITION
------------------------------ - -------- -----------------------------------
SYS_C006897                    C T        "OWNER" IS NOT NULL
SYS_C006898                    C T        "OBJECT_NAME" IS NOT NULL
SYS_C006899                    C T        "OBJECT_ID" IS NOT NULL
SYS_C006900                    C T        "CREATED" IS NOT NULL
SYS_C006901                    C T        "LAST_DDL_TIME" IS NOT NULL
T_PK                           P T

6 rows selected.

a@ORA817DEV.US.ORACLE.COM> select grantee, privilege
  2  from dba_tab_privs
  3  where table_name = 'T' and grantor = 'A';

GRANTEE                        PRIVILEGE
------------------------------ ----------------------------------------
SCOTT                          DELETE
SCOTT                          INDEX
SCOTT                          INSERT
SCOTT                          SELECT
SCOTT                          UPDATE
SCOTT                          REFERENCES
PUBLIC                         SELECT
SCOTT                          ALTER

8 rows selected.

So, there is my schema, with triggers, constraints, grants, etc...

a@ORA817DEV.US.ORACLE.COM> host exp userid=a/a tables=t rows=N
Export: Release 8.1.7.3.0 - Production on Mon Apr 29 08:03:48 2002
(c) Copyright 2000 Oracle Corporation.  All rights reserved.
Connected to: Oracle8i Enterprise Edition Release 8.1.7.3.0 - Production
With the Partitioning option
JServer Release 8.1.7.3.0 - Production
Export done in WE8ISO8859P1 character set and WE8ISO8859P1 NCHAR character set
Note: table data (rows) will not be exported

About to export specified tables via Conventional Path ...
. . exporting table                              T
Export terminated successfully without warnings.

Now, we'll KEEP the data we want:

a@ORA817DEV.US.ORACLE.COM> create table temp unrecoverable
  2  as
  3  select * from t
  4  where mod(object_id,2) = 0;
Table created.

a@ORA817DEV.US.ORACLE.COM> drop table t;
Table dropped.

a@ORA817DEV.US.ORACLE.COM> rename temp to t;
Table renamed.

but notice that lots of stuff is missing:

a@ORA817DEV.US.ORACLE.COM> select object_name, object_type from user_objects;

OBJECT_NAME                    OBJECT_TYPE
------------------------------ ------------------
T                              TABLE
T_SEQ                          SEQUENCE

a@ORA817DEV.US.ORACLE.COM> select constraint_name, constraint_type,
  2  table_name,  search_condition
  3  from user_constraints;

CONSTRAINT_NAME                C TABLE_NA SEARCH_CONDITION
------------------------------ - -------- -----------------------------------
SYS_C006903                    C T        "OWNER" IS NOT NULL
SYS_C006904                    C T        "OBJECT_NAME" IS NOT NULL
SYS_C006905                    C T        "OBJECT_ID" IS NOT NULL
SYS_C006906                    C T        "CREATED" IS NOT NULL
SYS_C006907                    C T        "LAST_DDL_TIME" IS NOT NULL

a@ORA817DEV.US.ORACLE.COM> select grantee, privilege
  2  from dba_tab_privs
  3  where table_name = 'T' and grantor = 'A';
no rows selected

So, we'll get it all back (REMEMBER THIS SPOT, I'll give an alternate ending
to this story below):

a@ORA817DEV.US.ORACLE.COM> host imp userid=a/a full=y ignore=y
Import: Release 8.1.7.3.0 - Production on Mon Apr 29 08:03:50 2002
(c) Copyright 2000 Oracle Corporation.  All rights reserved.
Connected to: Oracle8i Enterprise Edition Release 8.1.7.3.0 - Production
With the Partitioning option
JServer Release 8.1.7.3.0 - Production
Export file created by EXPORT:V08.01.07 via conventional path
import done in WE8ISO8859P1 character set and WE8ISO8859P1 NCHAR character set
. importing A's objects into A
About to enable constraints...
Import terminated successfully without warnings.

a@ORA817DEV.US.ORACLE.COM> select object_name, object_type from user_objects;

OBJECT_NAME                    OBJECT_TYPE
------------------------------ ------------------
T                              TABLE
T_IDX                          INDEX
T_PK                           INDEX
T_SEQ                          SEQUENCE
T_TRIGGER                      TRIGGER

a@ORA817DEV.US.ORACLE.COM> select constraint_name, constraint_type,
  2  table_name,  search_condition
  3  from user_constraints;

CONSTRAINT_NAME                C TABLE_NA SEARCH_CONDITION
------------------------------ - -------- -----------------------------------
T_PK                           P T
SYS_C006903                    C T        "OWNER" IS NOT NULL
SYS_C006904                    C T        "OBJECT_NAME" IS NOT NULL
SYS_C006905                    C T        "OBJECT_ID" IS NOT NULL
SYS_C006906                    C T        "CREATED" IS NOT NULL
SYS_C006907                    C T        "LAST_DDL_TIME" IS NOT NULL

6 rows selected.

a@ORA817DEV.US.ORACLE.COM> select grantee, privilege
  2  from dba_tab_privs
  3  where table_name = 'T' and grantor = 'A';

GRANTEE                        PRIVILEGE
------------------------------ ----------------------------------------
SCOTT                          DELETE
SCOTT                          INDEX
SCOTT                          INSERT
SCOTT                          SELECT
SCOTT                          UPDATE
SCOTT                          REFERENCES
PUBLIC                         SELECT
SCOTT                          ALTER

8 rows selected.

a@ORA817DEV.US.ORACLE.COM>

It's all back.  Now, for the alternate ending.  We can instead:

a@ORA817DEV.US.ORACLE.COM> host imp userid=a/a full=y indexfile=foo.sql

Import: Release 8.1.7.3.0 - Production on Mon Apr 29 08:10:14 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.7.3.0 - Production
With the Partitioning option
JServer Release 8.1.7.3.0 - Production

Export file created by EXPORT:V08.01.07 via conventional path
import done in WE8ISO8859P1 character set and WE8ISO8859P1 NCHAR character set
Import terminated successfully without warnings.

a@ORA817DEV.US.ORACLE.COM> pause

Now, at this point I hopped out of sqlplus, edited foo.sql -- grabbed the
create indexes from there and redid them with nologging and parallel (to have
them create faster....)

a@ORA817DEV.US.ORACLE.COM> CREATE UNIQUE INDEX "A"."T_PK" ON "T" ("OBJECT_ID" )
PCTFREE 10 INITRANS 2
  2  MAXTRANS 255 STORAGE(INITIAL 401408 NEXT 73728 MINEXTENTS 1 MAXEXTENTS
  3  2147483645 PCTINCREASE 10 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL
  4  DEFAULT) TABLESPACE "SYSTEM" NOLOGGING PARALLEL 4;

Index created.

a@ORA817DEV.US.ORACLE.COM>
a@ORA817DEV.US.ORACLE.COM> CREATE INDEX "A"."T_IDX" ON "T" ("OBJECT_NAME" )
PCTFREE 10 INITRANS 2
  2  MAXTRANS 255 STORAGE(INITIAL 974848 NEXT 139264 MINEXTENTS 1 MAXEXTENTS
  3  2147483645 PCTINCREASE 10 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL
  4  DEFAULT) TABLESPACE "SYSTEM" NOLOGGING PARALLEL 4;

Index created.

then, we resumed

a@ORA817DEV.US.ORACLE.COM> host imp userid=a/a full=y ignore=y

Import: Release 8.1.7.3.0 - Production on Mon Apr 29 08:12:34 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.7.3.0 - Production
With the Partitioning option
JServer Release 8.1.7.3.0 - Production

Export file created by EXPORT:V08.01.07 via conventional path
import done in WE8ISO8859P1 character set and WE8ISO8859P1 NCHAR character set
. importing A's objects into A
About to enable constraints...
Import terminated successfully without warnings.

a@ORA817DEV.US.ORACLE.COM> select object_name, object_type from user_objects;

OBJECT_NAME                    OBJECT_TYPE
------------------------------ ------------------
T                              TABLE
T_IDX                          INDEX
T_PK                           INDEX
T_SEQ                          SEQUENCE
T_TRIGGER                      TRIGGER

a@ORA817DEV.US.ORACLE.COM> select constraint_name, constraint_type,
  2  table_name,  search_condition
  3  from user_constraints;

CONSTRAINT_NAME                C TABLE_NA SEARCH_CONDITION
------------------------------ - -------- -----------------------------------
T_PK                           P T
SYS_C006915                    C T        "OWNER" IS NOT NULL
SYS_C006916                    C T        "OBJECT_NAME" IS NOT NULL
SYS_C006917                    C T        "OBJECT_ID" IS NOT NULL
SYS_C006918                    C T        "CREATED" IS NOT NULL
SYS_C006919                    C T        "LAST_DDL_TIME" IS NOT NULL

6 rows selected.

a@ORA817DEV.US.ORACLE.COM> select grantee, privilege
  2  from dba_tab_privs
  3  where table_name = 'T' and grantor = 'A';

GRANTEE                        PRIVILEGE
------------------------------ ----------------------------------------
SCOTT                          DELETE
SCOTT                          INDEX
SCOTT                          INSERT
SCOTT                          SELECT
SCOTT                          UPDATE
SCOTT                          REFERENCES
PUBLIC                         SELECT
SCOTT                          ALTER

8 rows selected.

and all is back again...

2) see #1

3) I suggest that people recreate indexes at the drop of a hat, when they need
not be created.  I suggest that people do not measure what they've done, they
cannot quantify the performance gain (or loss -- yes, you can slow down by
rebuilding!!!) they achieved.  I suggest that people just blindly rebuild
indexes without thinking it through.  I suggest that it is the RARE index that
needs to be rebuilt on a regular basis.  I suggest that you think 10 times
before rebuilding an index.

This is a case where rebuilding makes sense.
Review &
      Followup

      Rating:
      5
Thanks  April 29, 2002
Reviewer:
      atul  from India
Sir,

It was great output..
It is really helpful to me.

Thanks.
atul

      Rating:
      5
  May 28, 2002
Reviewer:  Ram
       from NY, USA
Thanks Tom. That was a great demonstration of your point. So helpful.

I was trying to export a large number of tables. So I have a long list to the
tables parameter like
exp user/paswd tables=\(t1, t2, t3....\) rows=n

Is there a way I can pass the list of tables (t1, t2, t3 etc.) from another
file. I was trying to generate the exp script using dynamic sql. But there will
be a line break after the table name and exp will give an error there. I used
sed to collate them but then it gives a line is too long error. Is there any
short-cut for passing a large amount of tables names to exp?

Thanks for your help.
Regards,

Ram

          Followup:

You can give us a parfile with:

tables=(
t1,
t2,
t3,
...
tN
)

in it and use parfile=that_file_name.par

      Rating:
      5
Thanks  May 28, 2002
Reviewer:  ram
       from NY
Cool. Thank you very much.

May 28, 2002 Picksley -- Thanks for the question regarding
      "Uniqueness between a group of rows", version 8.1.7

      You Asked
Hi Tom,

I'm working with a group of guys who have written a C++ application server which
can spawn many threads each of which can connect to the database. The situation
I need to over come is as follows:

we have a parent/child relationship such that there can be 1 parent row in a
table called COMPOSITEATTRIBUTE and 1 or more child rows in a table called
COMPOSITEATTRIBUTENODE.

I've sent up the tables and constraints etc with no problems. However, the C++
guys are telling me that it is possible that each thread may recognize the fact
that a particular parent/child (COMPOSITEATTRIBUTE/COMPOSITEATTRIBUTENODE) group
is missing and each of the threads may try to create the COMPOSITEATTRIBUTE/
COMPOSITEATTRIBUTENODE group.

This would be incorrect because each group must be unique.

I have have the following tables:

CREATE TABLE compositeattribute
 (composite_attribute_id NUMBER(38) NOT NULL, -- Primary key
  attribute_name_id      NUMBER(38) NOT NULL,
  node_count             NUMBER(5) NOT NULL)

ALTER TABLE compositeattribute
ADD PRIMARY KEY (composite_attribute_id)
USING INDEX

CREATE TABLE compositeattributenode
 (node_id                NUMBER(38) NOT NULL,
  composite_attribute_id NUMBER(38) NOT NULL REFERENCES compositeattribute
(composite_attribute_id),
  inclusive_node         VARCHAR2(1) DEFAULT 'Y'   NOT NULL)

ALTER TABLE compositeattributenode
ADD PRIMARY KEY (node_id, composite_attribute_id)
USING INDEX

CREATE UNIQUE INDEX indx_1 ON compositeattributenode
  (composite_attribute_id ASC,
   node_id                ASC )

Now both threads identify that the following group needs to be created

compositeattribute.attribute_name_id
123

compositeattributenodes.node_ids
101
102
103
104
105
106
107

and thread 1 generates and executes the following SQL

-- Create parent
INSERT INTO COMPOSITEATTRIBUTE VALUES (13,123,7)

-- Create children
INSERT INTO COMPOSITEATTRIBUTENODE VALUES (101,13,'Y')
INSERT INTO COMPOSITEATTRIBUTENODE VALUES (102,13,'Y')
INSERT INTO COMPOSITEATTRIBUTENODE VALUES (103,13,'Y')
INSERT INTO COMPOSITEATTRIBUTENODE VALUES (104,13,'Y')
INSERT INTO COMPOSITEATTRIBUTENODE VALUES (105,13,'Y')
INSERT INTO COMPOSITEATTRIBUTENODE VALUES (106,13,'Y')
INSERT INTO COMPOSITEATTRIBUTENODE VALUES (107,13,'Y')

And thread 2 generates and executes the following SQL

-- Create parent
INSERT INTO COMPOSITEATTRIBUTE VALUES (14,123,7)

-- Create children
INSERT INTO COMPOSITEATTRIBUTENODE VALUES (101,14,'Y')
INSERT INTO COMPOSITEATTRIBUTENODE VALUES (102,14,'Y')
INSERT INTO COMPOSITEATTRIBUTENODE VALUES (103,14,'Y')
INSERT INTO COMPOSITEATTRIBUTENODE VALUES (104,14,'Y')
INSERT INTO COMPOSITEATTRIBUTENODE VALUES (105,14,'Y')
INSERT INTO COMPOSITEATTRIBUTENODE VALUES (106,14,'Y')
INSERT INTO COMPOSITEATTRIBUTENODE VALUES (107,14,'Y')

The would lead to duplicate groups being created, which is incorrect - although
the composite_attribute_id changes the node_ids are the same for each group.

I've thought of using the DBMS_LOCK package to take out a lock so only one
thread at a time can create its rows the others will be blocked (I know this
will serialize access to the tables, which is a not good). I was also going to
use a counter which would be incremented when a thread wants to write.

The steps would be something like

1. Read Counter
2. Prepare database changes - this can be a time consuming step and
   I dont'want to lock the table at this point
3. Issue Lock using DBMS_LOCK package
4. Read Counter
5. If Counter is the same as in 1.
      write database changes (nobody else has written to the database)
   Else if the Counter has been incremented (by a different thread )
      then a database write has occurred
      Read the database to ensure the parent/child relationship does
      not exist if it does abandon the changes and release the lock
      else proceed.
6. Write database changes
7. Increment Counter
8. Commit/release lock.

Is this the best way to enforce this type of business rule? Could you also
advise where this code is best placed in a trigger or package( probably a
package.

Thanks in advance for your help.

Picksley

Tom,

Thanks for your reply. I understand the point you're making. However, mine is a
temporal database and nothing is ever deleted. In order to save space I left out
the temporal columns, the definition of the table is. I apologise for any
confusion this has caused.

CREATE TABLE compositeattribute
 (composite_attribute_id NUMBER(38) NOT NULL, -- Primary key
  attribute_name_id      NUMBER(38) NOT NULL,
  node_count             NUMBER(5) NOT NULL,
  create_user            VARCHAR2(30),
  create_date            DATE,
  close_user             VARCHAR2(30),
  close_date             DATE)

Now from this you can see that duplicates of composite_attribute_id are allowed,
but there will only be one row where the close_date and close_user are both null
and that will be the active attribute/attribute set.

Thanks for your help

      and we said...
create unique index t_idx on t(decode(close_date, NULL, attribute_name_id,
NULL));

will do it then.

Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( attribute_name_id number,
close_date date );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create unique index t_idx on t(decode(
close_date, NULL, attribute_name_id, NULL ));

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1 , null );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1 , null );
insert into t values ( 1 , null )
*
ERROR at line 1:
ORA-00001: unique constraint (OPS$TKYTE.T_IDX) violated

ops$tkyte@ORA817DEV.US.ORACLE.COM> update t set close_date = sysdate;

1 row updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1 , null );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1 , null );
insert into t values ( 1 , null )
*
ERROR at line 1:
ORA-00001: unique constraint (OPS$TKYTE.T_IDX) violated

ops$tkyte@ORA817DEV.US.ORACLE.COM> update t set close_date = sysdate where
close_date is null;

1 row updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1 , null );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

ATTRIBUTE_NAME_ID CLOSE_DAT
----------------- ---------
                1 22-MAY-02
                1 22-MAY-02
                1

ops$tkyte@ORA817DEV.US.ORACLE.COM>

That is a unique index on attribute_name_id when close_date is NULL....  That'll
solve the issue without resorting to lots of code...

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

It looks like you picked the wrong primary key?  Or that attribute_name_id  in
the parent table is UNIQUE as well -- adding a UNIQUE to that would solve the
issue simply.

This is a data model problem -- you are using a surrogate key, just unique the
real key and the problem is solved isn't it?

Review &
      Followup

      Rating: 4
How differente
      is the Decoded index  May 23, 2002
Reviewer:
      Yamani  from KSA
Tom,

I really don't get the idea off the unique index that you create as:

create unique index t_idx on t(decode( close_date, NULL, attribute_name_id, NULL
));

Because i have tried creating an index like:
create unique index on t ( attribute_name_id, close_date);

..
and it works exactly like you example..

Would you please explain the difference?

Thanks,

          Followup:

My index is only indexing rows where by the CLOSE_DATE is NULL.

If you have 100 rows in the table, 50 of which have a non-null close date, my
index will have 50 entries.  Yours will have 100.

Entirely NULL entries are not made in B*Tree indexes -- hence the decode only
indexes the rows that have NULL (active) CLOSE_DATE's

      Rating: 4
Uniqueness
      between a group of rows  May 23, 2002
Reviewer:
      Picksley  from UK
Thanks Tom, A very useful answer.

      Rating: 5
Uniqueness
      between a group of rows  May 28, 2002
Reviewer:
      Bradley  from UK
If another column was added say COB_DATE, could you still use a function based
index in order to maintain the data as shown below?

ATTRIBUTE_NAME_ID COB_DATE  CLOSE_DAT
----------------- --------- ---------
                1 01-OCT-02 22-MAY-02
                1 01-OCT-02 23-MAY-02
                1 01-OCT-02
                2 25-OCT-02 28-MAY-02
                2 25-OCT-02 29-MAY-02
                2 25-OCT-02

I tried using
create unique index t_idx1 on t( COB_DATE, ATTRIBUTE_NAME_ID , decode(
close_date, NULL, ATTRIBUTE_NAME_ID , NULL )); without much success - I could
insert and close a row but not add another

          Followup:

Tell me -- what are you trying to make unique?

What columns need to be unique within what set of rows?

      Rating:
      5
  May 28, 2002
Reviewer:
      A reader

May 29, 2002 Geeta -- Thanks for the question regarding
      "Package Vs Simple SQL", version 81.17

      You Asked
Good Morning Mr Tom,

I am very new to Oracle and have written my first Package and after lots of
problems have been able to compile the package without errors. Now I don’t know
how to execute it. Do I just go the SQL prompt and type

SQL> Exceute pkg_realign;

Here is the code of my Package

machinename/ username >vi pkg_realign.sql

create or replace package pkg_realign as
procedure pr_realign_primary;
procedure pr_realign_specialty;
function  fn_getnewterrid(p_zip   VARCHAR2,
p_terr_type VARCHAR2)
RETURN VARCHAR2;
END;
/
show err;
create or replace package body pkg_realign as
function  fn_getnewterrid(p_zip   VARCHAR2,
p_terr_type VARCHAR2)
RETURN VARCHAR2 IS
v_territoryid  VARCHAR2(12) := NULL;
CURSOR f1 IS
SELECT territoryid
FROM   stg_zip_to_terr_tb
WHERE  zip = p_zip
AND    substr(territoryid,8,1) = p_terr_type;
f1_rec f1%ROWTYPE;
BEGIN
OPEN f1;
FETCH f1 INTO f1_rec;
IF (f1%NOTFOUND)
THEN
v_territoryid :=  NULL;
ELSE
v_territoryid := f1_rec.territoryid;
END IF;
return v_territoryid;
CLOSE f1;
EXCEPTION
WHEN OTHERS
THEN
RAISE;
END;
procedure pr_realign_primary IS
cursor c1 is
select rowid row_id,
acctid acctid,
territoryid territoryid,
substr(territoryid,8,1) terr_type,
substr(zippc,1,5) zip
from   month.arc_prof
where substr(territoryid,8,1) in ('1','2','3','4','A','B','M','C','G')
UNION ALL
select rowid row_id,
acctid acctid,
territoryid territoryid,
substr(territoryid,8,1) terr_type,
substr(zippc,1,5) zip
from   month.arc_prof
where substr(territoryid,8,1) = 'H'
and   substr(territoryid,1,2) = '01';
c1_rec c1%rowtype;
t_territoryid VARCHAR2(12)  := NULL;
BEGIN
For c1_rec in c1
Loop
--- InitialiZing variable
t_territoryid := NULL;
IF (c1_rec.terr_type = 'M')
THEN
t_territoryid := fn_getnewterrid(c1_rec.zip,'M');
ELSIF(c1_rec.terr_type = 'C')
THEN
t_territoryid := fn_getnewterrid(c1_rec.zip,'C');
ELSE
t_territoryid := fn_getnewterrid(c1_rec.zip,'A');
END IF;

UPDATE month.arc_prof a
set    a.territoryid = t_territoryid
WHERE  rowid = c1_rec.row_id;
IF (MOD(c1%rowcount,10000) = 0)
THEN
commit;
END IF;
END LOOP;
commit;
END;

procedure pr_realign_specialty IS
cursor c1 is
select rowid row_id,
acctid acctid,
territoryid territoryid,
substr(territoryid,8,1) terr_type,
substr(zippc,1,5) zip
from   month.arc_prof
where substr(territoryid,8,1) in ('S','O','D','P','T','V','I')
UNION ALL
select rowid row_id,
acctid acctid,
territoryid territoryid,
substr(territoryid,8,1) terr_type,
substr(zippc,1,5) zip
from   month.arc_prof
where substr(territoryid,8,1) = 'H'
and   substr(territoryid,1,2) = '04';
c1_rec c1%rowtype;
t_territoryid VARCHAR2(12)  := NULL;
BEGIN

For c1_rec in c1
Loop
--- InitialiZing variable
t_territoryid := NULL;

IF (c1_rec.terr_type in ('S','O'))
THEN
t_territoryid := fn_getnewterrid(c1_rec.zip,'O');
ELSIF(c1_rec.terr_type in ('D','P'))
THEN
t_territoryid := fn_getnewterrid(c1_rec.zip,'P');
ELSIF(c1_rec.terr_type in ('V','I'))
THEN
t_territoryid := fn_getnewterrid(c1_rec.zip,'I');
ELSIF(c1_rec.terr_type = 'H')
THEN
t_territoryid := fn_getnewterrid(c1_rec.zip,'H');
END IF;

UPDATE month.arc_prof a
set    a.territoryid = t_territoryid
WHERE  rowid = c1_rec.row_id;

IF (MOD(c1%rowcount,10000) = 0)
THEN
commit;
END IF;

END LOOP;
commit;

END;
END;-- Packed End
/

I compile it by doing the following at the SQL prompt.

SQL> @pkg_realign

Package created.

No errors.

Package body created.

SQL>

Now I need to execute it – I have no manuals here. Can you please help.

Also my next question is can this be achieved it in Simple SQL. I have read on
your site that you prefer to use to SQL rather than Packages (if possible). If
yes then How to do it in SQL?

Thanks in advance for your help.

      and we said...
Allow me to critique.  This routine:

function  fn_getnewterrid(p_zip   VARCHAR2, p_terr_type VARCHAR2) RETURN
VARCHAR2
IS
   v_territoryid  VARCHAR2(12) := NULL;
   CURSOR f1 IS
    SELECT territoryid
      FROM stg_zip_to_terr_tb
     WHERE zip = p_zip
       AND substr(territoryid,8,1) = p_terr_type;
   f1_rec f1%ROWTYPE;
BEGIN
   OPEN f1;
   FETCH f1 INTO f1_rec;
   IF (f1%NOTFOUND)
   THEN
      v_territoryid :=  NULL;
   ELSE
      v_territoryid := f1_rec.territoryid;
   END IF;
   return v_territoryid;
   CLOSE f1;            <<<<<<====== bug, this line never executes
EXCEPTION
   WHEN OTHERS
   THEN
       RAISE;
END;

function  fn_getnewterrid(p_zip VARCHAR2, p_terr_type VARCHAR2) RETURN VARCHAR2
IS
   v_territoryid stg_zip_to_terr_tb.territoryid%type;
begin
   SELECT territoryid
     into v_territoryid
     FROM stg_zip_to_terr_tb
    WHERE zip = p_zip
      AND substr(territoryid,8,1) = p_terr_type;

   return v_territoryid;
exception
   when no_data_found then
       return NULL;
end;

Now this routine:

procedure pr_realign_primary
IS
   cursor c1 is
   select rowid row_id,
          acctid acctid,
          territoryid territoryid,
          substr(territoryid,8,1) terr_type,
          substr(zippc,1,5) zip
   from   month.arc_prof
  where substr(territoryid,8,1) in ('1','2','3','4','A','B','M','C','G')
  UNION ALL
        select rowid row_id,
               acctid acctid,
               territoryid territoryid,
               substr(territoryid,8,1) terr_type,
                substr(zippc,1,5) zip
         from   month.arc_prof
        where substr(territoryid,8,1) = 'H'
          and   substr(territoryid,1,2) = '01';

   c1_rec c1%rowtype;
   t_territoryid VARCHAR2(12)  := NULL;
BEGIN
   For c1_rec in c1
   Loop
       --- InitialiZing variable
       t_territoryid := NULL;
       IF (c1_rec.terr_type = 'M')
       THEN
           t_territoryid := fn_getnewterrid(c1_rec.zip,'M');
       ELSIF(c1_rec.terr_type = 'C')
       THEN
           t_territoryid := fn_getnewterrid(c1_rec.zip,'C');
       ELSE
           t_territoryid := fn_getnewterrid(c1_rec.zip,'A');
       END IF;

       UPDATE month.arc_prof a
          set    a.territoryid = t_territoryid
        WHERE  rowid = c1_rec.row_id;

       IF (MOD(c1%rowcount,10000) = 0)
       THEN
           commit;
       END IF;
   END LOOP;
   commit;
END;

boils down to this:

procedure pr_realign_primary
IS
begin
update arc_prof
   set territoryid = ( SELECT territoryid
                         FROM stg_zip_to_terr_tb
                        WHERE zip = ARC_PROF.ZIP
                          AND substr(territoryid,8,1) =
                                    decode( ARC_PROF.TERR_TYPE,
                                            'M', 'M', 'C', 'C', 'A' )
                     )
 where substr(territoryid,8,1) in ('1','2','3','4','A','B','M','C','G')
   OR ( ubstr(territoryid,8,1) = 'H'
          and   substr(territoryid,1,2) = '01');
end;

I have some comments on that:

o never commit mid-transaction.  You are *not* saving resources, you are not
saving time.  In fact, you will generate more redo, more undo and take longer
that way.

o never do procedurally what you can do in a single statement.  I think your
first routine is not necessary as the entire thing can be done in a single
update.

o i do not believe that the plsql code should COMMIT or ROLLBACK.  It should be
upto the CLIENT to make that decision.

And now that you've seen the first routine reduced to "nothing" and the second
routine to a single line -- you can do the third (it should be a single update
as well)....

In anycase, to execute your existing code in sqlplus:

SQL> execute pkgname.procedure_name

Review &
      Followup

      Rating: 4
Should it not
      be this  April 22, 2002
Reviewer:  geeta
       from USA
update arc_prof
set territoryid = ( SELECT territoryid
                    FROM stg_zip_to_terr_tb
                    WHERE zip = substr(ARC_PROF.ZIPPC,1,5)
                    AND substr(territoryid,8,1) =
                    decode( substr( ARC_PROF.territoryuid,8,1),
'M', 'M', 'C', 'C', 'A' )
                     )
 where substr(territoryid,8,1) in ('1','2','3','4','A','B','M','C','G')
   OR ( ubstr(territoryid,8,1) = 'H'
          and   substr(territoryid,1,2) = '01')

Because in ARC_PROF table the length of the ZIPPC field is more than 5 and in
STG_ZIP_TO_TERR the length of ZIP is 5
AND there is no field called SF_TYPE in ARC_PROF table SF_TYPE is computed by
taking the 8th(last) position of the TERRITORYID field.

Also can simple update be used for pr_realign_specialty ??

What do you mean when you say "CLOSE f1;            <<<<<<====== bug, this line
never executes "

Thanks for the help

          Followup:

Yes, you are right - needed to push the substr down as well.  thanks

What I meant by "bug, this line never executes" is that, well, that line cannot
execute.  The line above it was "return" -- that code is never reached.

      Rating: 4
What if the the
      Field lengths in the two tables are different  April 22,
      2002
Reviewer:  geeta  from USA
In my case the TERRITROYID field in
ARC_PROF in Varchar2(12) while in
STG_ZIP_TO_TERR_TB is Varchar2(8). Will it effect
v_territoryid stg_zip_to_terr_tb.territoryid%type declaration ?

Thanks

          Followup:

...
 v_territoryid stg_zip_to_terr_tb.territoryid%type;
begin
   SELECT territoryid
     into v_territoryid
     FROM stg_zip_to_terr_tb
....

i based the type on the column being fetched from - the length is correct for
the usage in this case.

      Rating: 4
I get an
      error  April 22, 2002
Reviewer:  geeta
       from USA
Whn I try to run the Update statement

SQL> l
update arc_prof
set territoryid = ( SELECT territoryid
FROM stg_zip_to_terr_tb
WHERE zip = substr(ARC_PROF_TEST.ZIPPC,1,5)
AND substr(territoryid,8,1) =
decode( substr(ARC_PROF.territoryid,8,1),
'M', 'M', 'C', 'C', 'A' )
)
where substr(territoryid,8,1) in ('1','2','3','4','A','B','M','C','G')
OR ( substr(territoryid,8,1) = 'H'
and   substr(territoryid,1,2) = '01')
SQL> /
set territoryid = ( SELECT territoryid
*
ERROR at line 2:
ORA-01427: single-row subquery returns more than one row

Thanks

          Followup:

That means that ZIP and substr(territoryid,8,1) is NOT unique.

That means that you would be getting a RANDOM row from stg_zip_to_terr_tb -- if
you used your function.  You would be setting the territoryid in ACR_PROF to
*some* value.

If your table stg_zip_to_terr_tb has in it:

ZIP       TERRITORYID
12345     xxxxxxxY
12345     yyyyyyyY
12345     zzzzzzzY

which territoryid should be assigned to the row in arc_prof???

      Rating: 5
you used the
      function ubstr  April 22, 2002
Reviewer:
      A reader
What is the difference between ubstr and substr.

          Followup:

It is an obvious typo missing an S.

      Rating: 4
Another myth
      debunked?  April 23, 2002
Reviewer:  Andrew
      Clarke  from London, England
Tom

In this piece you state:

never commit mid-transaction.  You are *not* saving resources, you are not
saving time.  In fact, you will generate more redo, more undo and take longer
that way.

Now I've always thought that if (say) I was deleting 1000000 rows of data a
commit ever 10000 would save time because it would cut down the amount of
rollback segment management.  It would also reduce the chances of blowing the
rollback segment (not such an issue now with DBMS_RESUMEABLE.SPACE_ERROR).

Of course, as is the way with these pieces of received wisdom, I've never tested
it for myself.  I will probably have a go at testing this on a brute "time
elapsed" basis but do you happen to have a small demo about your person with
stats that show the difference in the internal management?

Thanks, APC

          Followup:

It will increase the amount of rollback segment management.

It will increase the total amount of rollback segment space you NEED at a point
in time.

So yes, by committing frequently you can reduce the rollback segment space you
NEED at POINT IN TIME to say 1m.  If you didn't commit say you would need 1gig
at that point in time (the commit releases the space for reuse).  BUT you

o generate more total rollback -- you'll generate more then 1gig
o it'll take longer to do it
o you'll generate more redo then doing it as a single statement
o you've exponetially increased the complexity of the update.  It is not a
single
  statement, it is a procedural piece of code
o you have to make it RESTARTABLE which is really really hard to do properly in
  a couple of minutes.

o it would be cheaper (your time costs big $$$$$) to have a room full of 36gig
   drives.  Whenever tempted to commit in a procedural piece of logic to "save"
   resources  -- goto this room, pull out a drive, attach it, use it, throw it
   away.  That would cost less then it costs you to code it, debug it, maintain
   it.

Pages 143 through 148 of my book are dedicated to this exact topic with all of
the benchmarks/proofs and examples you need.

      Rating:
      4
Cheers  April 24, 2002
Reviewer:  Andrew
      Clarke  from London, England
Thanks for saying my time's worth "big $$$$".  Can I quote you to my boss on
that?

I guess I'm just going to have to buy your book; I've been tempted for some
time, but I promised the wife after I splurged on Amazon a couple of months
back....

APC

      Rating: 4
a question on
      mid - commit   May 28, 2002
Reviewer:  Asnate
      Dimza  from Riga,Latvia
I am used to do a commit after every 1000 or 10000 records when inserting or
updating a table in a pl/sql procedure - in order to prevent the overflow of the
rollback segment.
So what would be the proper way ?

Thanks,

Asnate

          Followup:

Size your rollback segments based on your workload.

Tell me -- if you commit after every 1000 records -- what happens when you have
10,000 records to load and the system fails after the 4th commit?  did you write
your code to be "restartable"?  Is the integrity of your data in question?

Did you know that frequent committing

o makes your code run slower
o does not result in reduced IO (in fact, it generates MORE IO)
o results in more redo being generated?
o makes your code run slower
o results in processes that are hard to maintain (what is easier, a single
update or a bunch of procedural code?)
o results in data of questionable integrity (what happens when there is a
failure and you've already committed some stuff?  did you write the logic to be
restartable -- most people don't)
o makes your code run slower

      Rating: 5
Missing RETURN
      ?  May 28, 2002
Reviewer:  Pasko  from
      HAmburg,Germany
Hi Tom

Thanks for the above Reply ..

got snippet  from above..
....
.....
 END IF;
   return v_territoryid;
   CLOSE f1;     <<<<<<====== bug, this line never executes
EXCEPTION
   WHEN OTHERS
   THEN
       RAISE;
END;

Do we have to put  RETURN  after RAISE statement   or BEFORE RAISE  in the
EXCEPTIONS  Section ?

How does RETURN affect a PROCEDURE :

say i have  :

 procedure  test_ret  is

 begin

  for rec  in  ( some  select  ) loop

  if ( some condition )  then
     return ;
    end if ;

  end loop;

 --Do i ever get here ?

end ;

          Followup:

The return v_territoryId should have been AFTER the close is all.

return "returns" from the procedure, skipping the rest of the code.  No, you
would never get to "do i ever get here" if some_condition was met.

      Rating:
      5
  May 28, 2002
Reviewer:  Helena
      Markova  from Bratislava, Slovakia
Answer a question on mid - commit

      Rating:
      5
  May 28, 2002
Reviewer:  Pasko

Hi Tom
Thanks for your prompt Response,
But you forgotto answer  this :

Do we have to put  RETURN  after RAISE statement   or BEFORE RAISE  in the
EXCEPTIONS  Section ?

EXCEPTION
   WHEN OTHERS
   THEN
    ----- return v_territoryid;
         RAISE;

OR

EXCEPTION
   WHEN OTHERS
   THEN
          RAISE;
        ----- return v_territoryid;

Which one is Correct ..

Regards

          Followup:

No -- definitely NOT.

The first one (before the raise) would be a BUG.  You would silently ignore all
errors (the RAISE is never executed)

The second one is just "wrong" -- the return statement would never get executed.

      Rating: 5
Then how do i
      return when i get an Exeption .....  May 29, 2002

Reviewer:  Pasko  from HH,Germany
Hi Tom
Thanks again for your Response ...

In that case...

The snippet from above:
-----
EXCEPTION
   WHEN OTHERS
   THEN
       RAISE;
END;

Wouldn't the code above give an Oracle Error  when i Get an Exception because
there's no return ? or it's  unnecessary ..

Thanks

Regards

Pasko

          Followup:

An exception means you AREN'T going to do a normal return.

A higher level block of code can CATCH the exception but there will be no return
value from the function (the function threw an error -- it is bailing out --
quitting -- not finishing.  there is nothing to return)

May 29, 2002 Sven
      -- Thanks for the question regarding "Example for array fetch using OCI8
      (obndra seems to be obsolete)", version 8.1.7

      You Asked
Hello Tom!

I am looking for an example doing an array fetch from the
database using OCI8.  The only examples I know from oracle and
using OCI73, and there doesn't seem to be an updated example.

Please note that I don't want to use Pro*C, since I didn't understand
it's inner workings, when I looked at the source code that it generated. I want
to have as much control as possible, since
I need to make high-performance queries with very much data being
transferred.

All the best,

Sven

      and we said...
Well, I totally disagree with your premise that you'll get the best performance
with OCI over Pro*C.  Pro*C is pretty darn good (and you can write the code
soooo much faster and effectively).  Unless you are going to direct path insert
-- give pro*c a good look see.  Control doesn't imply, mean, or infer "speed"
(think of the amount of time you are going to spend learning the X hundred OCI
functions when I could write the program in Pro*c in a tiny amount of time using
1/10th of the functionality of pro*c available to me).

In any case, look at

cdemodp.c/cdemodp_lip.c in $ORACLE_HOME/rdbms/demo.  shows high speed direct
path loading using oci.

cdemorid.c in same.  Bulk (array) updates.

Review &
      Followup

      Rating: 5
Pro*C and
      Dynamics  May 29, 2002
Reviewer:  Sven C.
      Koehler  from Berlin Germany
Thank you very much for giving me a hint to these
examples!

Nope, I am not using direct path.  I am writing a
generic interface using Oracle's array bind
functionality.  So, I basically don't know
how many parameters will be bound and how the
tables will look like.  This is all up to the clients
of this interface I am writing.

By looking at Pro*C code examples, it seemed to me
as if the sql commands to be executed needed to be known
at compile time (Please correct me if I am wrong).

Best regards,

Sven

          Followup:

You can use dynamic sql -- but if it is all "generic", OCI may be easier.

HOWEVER, since sqlldr is already written for you -- you might consider just
reusing that.  No need to write a loader when a loader (and a very very good one
at that) already exists.  I would be extremely tempted to write a very thin
layer on top of sqlldr and just use it.

May 29, 2002 Nag
      -- Thanks for the question regarding "roles -> privileges", version
      8.1.7

      You Asked
Tom

I want three scripts...

1.which gives me all the privileges assigned to me directly.

2.which gives me all the roles assigned to me

3.Which gives me all the privileges associated with the roles    assigned to me.

( these 3 scripts will allow me to display to my dba what I dont have)

and lastly

4.in 8i(8.1.6, and 8.1.7) , how can I recreate an already existing table as a
partitioned table( please dont consider export and import)

Thank you

      and we said...
#1) very trivial queries against dba_tab_privs, dba_sys_privs

#2) very trivial query against dba_role_privs

#3) is tricky since there is a hierarchical relationship here (a user can have a
role that has a role that has a role that has privs).

To that end, I provide:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:853427230099
#4) CREATE NEW_TABLE (columns) <partitioning info> AS SELECT * FROM OLD_TABLE;
    drop table old_table;
    rename new_table to old_table;

In 9i, there is an online ability to do this with the dbms_redefinition package.
May 29, 2002 Nick
      -- Thanks for the question regarding "Capability of Pipelined functions",
      version 9.0.1

      You Asked
Hello, Tom!
Is it possible to pipeline date from table function using bulk?
In other words, can I use in <PIPE ROW ()> statement collection?
Or, does it exist <PIPE TABLE ()> statement or anything else?

Thank you in advance.

Miroshnichenko Nick
Software Engineer

      and we said...
No, you pipe a row -- an instance of a collection element -- not the entire
collection.

To "pipe" an entire collection, you do not need to use a pipelined function at
all.  Just select * from function_returning_a_collection.

create type myRecordType as object
( seq int,
  a int,
  b varchar2(10),
  c date
)
/

create or replace type myTableType
as table of myRecordType
/
create or replace function my_func( p_inputs in number default 5 ) return
myTableType
is
    l_data myTableType;
begin
    l_data := myTableType();

    for i in 1..p_inputs
    loop
        l_data.extend;
        l_data(i) := myRecordType( i, mod(i,5), 'row ' || i, sysdate+i );
    end loop;

    return l_data;
end;
/
select *
  from TABLE( cast( my_func as myTableType ) )
/
select *
  from TABLE( cast( my_func() as myTableType ) )
/

variable x number
exec :x := 5

select *
  from TABLE( cast( my_func(:x) as myTableType ) )
/

for example.
May 29, 2002 Lisa -- Thanks for the question regarding "Outer
      Join When Join Variable Needs to be calculated", version 8i

      You Asked
Dear Tom,

I did search on outer joins and inline views but did not find anything that
would help me with this. Any help would be greatly appreciated.

I am developing a query for use in Reports Dev 6i on an Oracle 8i db.

Much simplified, the report needs to look like this:

Security Type Years to Maturity  Total Outstandings

BOND        0 to 1            1000
        1-2            5000
        2-3            7000
        3-4
        4-5            10000
        5-6
        .
        .
        .
        20-40

There are cases where for a years to maturity range I have no data.  The
customer requires that a row appear for
every year to maturity range with just a null in the total outstanding column,
as seen above.

The Years_To_Maturity table was created for the sole purpose of using it in the
inline view.  It is just a table
with 1 column containing the years ranging from 1 to 40.

I think the following inline view would give me what I want.  As you can see I
am joining a view with 3 tables:

select v.st, v.ytm, sum(v.amt)
from (
select security_type st, years_to_maturity ytm, amount amt
from v_auction_history a, rollups b, holdings c, years_to_maturity d
where a.years_to_maturity = d.years
union
select security_type, years_to_maturity, 0
from v_auction_history a, rollups b, holdings c, years_to_maturity d
where a.years_to_maturity(+) <> d.years) v
group by v.st, v.ytm

but my big problem is that a.years_to_maturity is a calculated value that uses a
combination of database
values and a parameter (:P_EFFECTIVE_DATE) that is being passed to the report.
It looks like this:

    CEIL(DECODE(a.Security_Type, 'BOND',
          (TO_DATE(DECODE(NVL(a.Call_Payoff_Date, a.Current_Call_Date), NULL,
                              a.Maturity_Date, NVL(a.Call_Payoff_Date,
a.Current_Call_Date)), 'DD-MON-RRRR') -
           TO_DATE(:P_EFFECTIVE_DATE, 'DD-MON-RRRR'))/(365.24219/12),
          (TO_DATE(a.Maturity_Date, 'DD-MON-RRRR') - TO_DATE(:P_EFFECTIVE_DATE,
'DD-MON-RRRR'))/(365.24219/12))/12) YTM,

and so I cannot do the outer join, or if there is a way I don't know how.

I don't know what to do at this point.  I can't really create a regular view for
the user because the
years to maturity is always dependent upon the effective date.

Sincerely,

Lisa Greisen

      and we said...
Well, you need more then just a set of 40 rows for each YTM -- you need a set of
40 rows for each ST (security type)!

In the following -- the inline view A is your "complex query" with the
computations and such.  Inline view B is what gives you your set of YTM/ST 's
you need to outer join to:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select b.st, b.ytm || '-' || (b.ytm+1) ytm,
sum(amt)
  2    from ( select st, decode( st, 'BOND', trunc(
months_between(sysdate,dt)/12 ),
  3                                  'xxxx', trunc(
months_between(:some_date,dt)/12 ) ) ytm,
  4                  amt
  5             from t2 ) A,
  6          (select ytm, st from t1, (select distinct st from t2) ) B
  7   where b.ytm = a.ytm(+)
  8     and b.st = a.st(+)
  9  group by b.st, b.ytm || '-' || (b.ytm+1)
 10  /

ST    YTM                                              SUM(AMT)
----- ---------------------------------------------- ----------
BOND  0-1
BOND  1-2                                                   372
BOND  2-3                                                  1569
xxxx  0-1
xxxx  1-2
xxxx  2-3                                                   352

6 rows selected.
Review & Followup

      Rating: 3
Clarification
      of response  May 28, 2002
Reviewer:  Lisa
      Greisen  from Washington DC, USA
Tom,

Thank you for the response.

In your response you refer to tables t1 and t2.  In trying to decipher this and
make it work for me t2 would refer to the join of the 3 tables that contain the
"real" data, i.e., v_auction_history, holdings and rollups, whereas t1 would
refer to the "artificial" table that I created with all the years called
years_to_maturity.

If this is the case, then I am confused regarding your inline view B construct
as you say "(select ytm, st from t1, (select distinct st from t2)) B", because
if t1 is the years_to_maturity table then the variable st does not exist in that
table.

So my question is:

1) Is the years_to_maturity table supposed to have all of the security types in
it as an additional column?
or
2) Am I missing something in understanding your construct?

Any help would be greatly appreciated.  Thank you.

Lisa

          Followup:

What I needed, in order to make the outer join work, was a set of rows with
every YTM/ST combination in it.  The easiest way to do that was:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select b.st, b.ytm || '-' || (b.ytm+1) ytm,
sum(amt)
  2    from ( select st, decode( st, 'BOND', trunc(
months_between(sysdate,dt)/12 ),
  3                                  'xxxx', trunc(
months_between(:some_date,dt)/12 ) ) ytm,
  4                  amt
  5             from t2 ) A,
  6          (select ytm, st from t1, (select distinct st from t2) ) B
  7   where b.ytm = a.ytm(+)
  8     and b.st = a.st(+)
  9  group by b.st, b.ytm || '-' || (b.ytm+1)
 10  /

That is a cartesian product of the YTM's from T1 and distinct ST's from T2 (t2
is your 3 table join -- if there is a BETTER way to get the set of distinct ST's
rapidly, by all means use it)

You could have a table with all of the values in it -- but that would be less
flexible if the set of ST values can change over time.

      Rating: 2
More
      clarification needed  May 28, 2002
Reviewer:
      Lisa G  from Washington, DC USA
I'm sorry if I am being dense, but if you answered what I asked I am still
confused.  First, to simplifiy the problem I left out some info that I will now
add.  In addition to security_type I also have rollup_type.  There are about 15
rollup_types, 5 security_types and 40 years_to_maturity.  Security_type will
probably not change, but rollup_type may.

If T1 is the years_to_maturity table, which at this point only has one column,
years, in it.  I don't see how I can have the following statement as you have in
line 6:

(select ytm, st from t1, (select distinct st from t2) ) B

If you are saying that I need to have the years_to_maturity table have three
columns: years, security_type and rollup_type which would give me 15x5x40 = 3000
rows then please say that specifically.

          Followup:

I am NOT saying that you need to have years, security_type and rollup_type in a
single table.

Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t1 ( ytm int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t1 values (1);

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t1 values (2);

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t1 values (3);

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t1 values (4);

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t1 values (5);

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t2 ( /*.... other columns ... */
st varchar2(5) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t2 values ( 'BOND' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t2 values ( 'XXXX' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select *
  2    from (select ytm, st from t1, (select distinct st from t2) ) B
  3  /

       YTM ST
---------- -----
         1 BOND
         2 BOND
         3 BOND
         4 BOND
         5 BOND
         1 XXXX
         2 XXXX
         3 XXXX
         4 XXXX
         5 XXXX

10 rows selected.

All you need is:

a) a query that returns all YTM values
b) another query that returns all ST (and apparently ROLLUP_TYPE) values

We "join" them together:

   (select ytm, st from t1, (select distinct st from t2) ) B

(maybe that would be more clear if I coded:

(select T1.ytm, T2.st from t1, (select distinct st from t2) T2 ) B

-- ytm comes from T2, ST from T2)

      Rating: 5
Returns no
      data  May 29, 2002
Reviewer:  Lisa G
       from Washington, DC, USA
Thank you.  That finally clarified it for me.  I put it all together though and
got no rows, actually it seems to be having the variables from the inline view
v1 as null.  As follows:

If I run just part of query for v1 it gives me OK data:

SQLWKS> SELECT v1.RT,
     2> v1.ST,
     3> v1.YTM,
     4> SUM(v1.A) Amount
     5> FROM (
     6>     SELECT DECODE(c.Rollup_Type, 'TO_MAT_DIST', '1',
'PRIVATE_MAT_DIST', '2',  'FED_MAT_DIST', '3',  'GOVT_MAT_DIST', '4',
     7>                                      'FED_GOVT_MAT_DIST', '5',
'FED_REPOS_MAT_DIST', '6',  'FGN_MAT_DIST', '7') RT,
     8>         DECODE(a.Security_Type, 'BILL', '1', 'NOTE', '2', 'BOND', '3',
'IIS NOTE', '4', 'IIS BOND', '5') ST,
     9>     CEIL(DECODE(a.Security_Type, 'BOND',
    10>           (TO_DATE(DECODE(NVL(a.Call_Payoff_Date, a.Current_Call_Date),
NULL,
    11>                               a.Maturity_Date, NVL(a.Call_Payoff_Date,
a.Current_Call_Date)), 'DD-MON-RRRR') -
    12>            TO_DATE('31-MAR-2002', 'DD-MON-RRRR'))/(365.24219/12),
    13>           (TO_DATE(a.Maturity_Date, 'DD-MON-RRRR') -
TO_DATE('31-MAR-2002', 'DD-MON-RRRR'))/(365.24219/12))/12) YTM,
    14>     b.Amount A
    15>     FROM  V_Auction_History a, Holdings b, Rollups c
    16>     WHERE a.Cusip = b.Cusip
    17>     AND   b.CT_Hold_Category = c.Code_Sys_Uid
    18>     AND   a.Seq_Num = 0
    19>     AND   c.Rollup_Type IN ('TO_MAT_DIST',  'PRIVATE_MAT_DIST',
'FED_MAT_DIST',  'GOVT_MAT_DIST', 'FED_GOVT_MAT_DIST', 'FED_REPOS_MAT_DIST',
'FGN_MAT_DIST')
    20>     AND   NVL(NVL(a.Call_Payoff_Date, a.Current_Call_Date),
a.Maturity_Date) > '31-MAR-2002'
    21>     AND   a.Issue_Date <= '31-MAR-2002'
    22>     AND   b.Effective_Date =
    23>           (SELECT MAX(d.Effective_Date)
    24>            FROM   Holdings d
    25>            WHERE  d.Cusip = a.Cusip
    26>            AND    d.Effective_Date <= '31-MAR-2002')) v1
    27> GROUP BY v1.RT, v1.ST, v1.YTM
    28> ORDER BY 1, 2, 3
    29>
R S YTM        AMOUNT
- - ---------- ----------
1 1          1  834408560
1 2          1  396204652
1 2          2  378292082
1 2          3  125668687
1 2          4  117090758
1 2          5  137497255
1 2          6   53178401
1 2          7   52274086
1 2          8   65550393
1 2          9   45873923

If I run second view v2 it is OK:

SQLWKS>     SELECT f.Years YTM, g.Rollup_Type RT, h.Security_Type ST FROM
Years_To_Maturity f,
     2>               (SELECT DISTINCT Rollup_Type FROM Rollups
     3>                WHERE Rollup_Type IN ('TO_MAT_DIST',  'PRIVATE_MAT_DIST',
  'FED_MAT_DIST',  'GOVT_MAT_DIST', 'FED_GOVT_MAT_DIST', 'FED_REPOS_MAT_DIST',
'FGN_MAT_DIST')) g,
     4>               (SELECT DISTINCT Security_Type FROM V_Auction_History) h
     5> ORDER BY 2,3,1
     6>
YTM        RT                   ST
---------- -------------------- --------------------
         1 FED_GOVT_MAT_DIST    BILL
         2 FED_GOVT_MAT_DIST    BILL
         3 FED_GOVT_MAT_DIST    BILL
         4 FED_GOVT_MAT_DIST    BILL

Together I get one null row:

SQLWKS> SELECT v1.RT,
     2> v1.ST,
     3> v1.YTM,
     4> SUM(v1.A) Amount
     5> FROM (
     6>     SELECT DECODE(c.Rollup_Type, 'TO_MAT_DIST', '1',
'PRIVATE_MAT_DIST', '2',  'FED_MAT_DIST', '3',  'GOVT_MAT_DIST', '4',
     7>                                      'FED_GOVT_MAT_DIST', '5',
'FED_REPOS_MAT_DIST', '6',  'FGN_MAT_DIST', '7') RT,
     8>         DECODE(a.Security_Type, 'BILL', '1', 'NOTE', '2', 'BOND', '3',
'IIS NOTE', '4', 'IIS BOND', '5') ST,
     9>     CEIL(DECODE(a.Security_Type, 'BOND',
    10>           (TO_DATE(DECODE(NVL(a.Call_Payoff_Date, a.Current_Call_Date),
NULL,
    11>                               a.Maturity_Date, NVL(a.Call_Payoff_Date,
a.Current_Call_Date)), 'DD-MON-RRRR') -
    12>            TO_DATE('31-MAR-2002', 'DD-MON-RRRR'))/(365.24219/12),
    13>           (TO_DATE(a.Maturity_Date, 'DD-MON-RRRR') -
TO_DATE('31-MAR-2002', 'DD-MON-RRRR'))/(365.24219/12))/12) YTM,
    14>     b.Amount A
    15>     FROM  V_Auction_History a, Holdings b, Rollups c
    16>     WHERE a.Cusip = b.Cusip
    17>     AND   b.CT_Hold_Category = c.Code_Sys_Uid
    18>     AND   a.Seq_Num = 0
    19>     AND   c.Rollup_Type IN ('TO_MAT_DIST',  'PRIVATE_MAT_DIST',
'FED_MAT_DIST',  'GOVT_MAT_DIST', 'FED_GOVT_MAT_DIST', 'FED_REPOS_MAT_DIST',
'FGN_MAT_DIST')
    20>     AND   NVL(NVL(a.Call_Payoff_Date, a.Current_Call_Date),
a.Maturity_Date) > '31-MAR-2002'
    21>     AND   a.Issue_Date <= '31-MAR-2002'
    22>     AND   b.Effective_Date =
    23>           (SELECT MAX(d.Effective_Date)
    24>            FROM   Holdings d
    25>            WHERE  d.Cusip = a.Cusip
    26>            AND    d.Effective_Date <= '31-MAR-2002')) v1,
    27>        (
    28>     SELECT f.Years YTM, g.Rollup_Type RT, h.Security_Type ST FROM
Years_To_Maturity f,
    29>               (SELECT DISTINCT Rollup_Type FROM Rollups
    30>                WHERE Rollup_Type IN ('TO_MAT_DIST',  'PRIVATE_MAT_DIST',
  'FED_MAT_DIST',  'GOVT_MAT_DIST', 'FED_GOVT_MAT_DIST', 'FED_REPOS_MAT_DIST',
'FGN_MAT_DIST')) g,
    31>               (SELECT DISTINCT Security_Type FROM V_Auction_History) h )
v2
    32> WHERE v2.YTM = v1.YTM(+)
    33> AND   v2.ST  = v1.ST(+)
    34> AND   v2.RT  = v1.RT(+)
    35> GROUP BY v1.RT, v1.ST, v1.YTM
    36> ORDER BY 1, 2, 3
    37>
R S YTM        AMOUNT
- - ---------- ----------

1 row selected.
SQLWKS> SPOOL OFF

          Followup:

Well, the problem is that you've turned the RT and ST values into "numbers" in
v1 and left them as "strings" in v2.

They'll never join.

And you want to select out V2.ST, V2.YTM, V2.RT in the final query (and group by
them as well).  You are expecting some (if not many) of the V1 values to be
missing -- so you need to get them from V2, not V1.

      Rating: 5
Thank
      you!!!!!!!  May 29, 2002
Reviewer:  Lisa G
       from Washington, DC
Thank you so much.  That worked just great!  I really appreciated all the time
you spent in helping me.  I actually think I can write one of these on my own
now.

May 29, 2002 -- Thanks for the question regarding "2 distinct
      uses of rownum in where clause. Why did this work?", version 8.1.7

      You Asked
I was looking for a pattern in some data and typed out this query(with more
columns)
very quickly without thinking it through.
The result was what I wanted, but after a second of thought, not what I
expected.
I know from the distribution of data that the rownum that forms the stopkey
can't be
the same rownum as the one used in the filter. They are 80% 'E' and the first
100, at
least, on disk are 'E'.
What's going on? Can the Oracle engine read minds? ;)
Thanks,
Chuck

SQL> select rownum,
  2         sale_v_endo
  3    from tax.lien_sales
  4   where rownum <= 10
  5     and ((mod(rownum, 2) = 0 and sale_v_endo = 'S')
  6        or(mod(rownum, 2) = 1 and sale_v_endo = 'E'));

    ROWNUM S
---------- -
         1 E
         2 S
         3 E
         4 S
         5 E
         6 S
         7 E
         8 S
         9 E
        10 S

10 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=84 Card=54071 Bytes=
          54071)

   1    0   COUNT (STOPKEY)
   2    1     FILTER
   3    2       TABLE ACCESS (FULL) OF 'LIEN_SALES' (Cost=84 Card=5407
          1 Bytes=54071)

      and we said...
Well, it worked because rownum is only incremented after a SUCCESSFUL row is
returned.

That query is somewhat equivalent to this procedural code:

   rownum = 1
   for x in ( select * from tax.lien_sales )
   loop
       exit when rownum > 10;    (your where rownum <= 10 condition)
       if ( (mod(rownum,2) = 0 and x.sale_v_endo = 'S')
            or
            (mod(rownum,2) = 1 and x.sale_v_endo = 'E') )
       then
            OUTPUT ROW;
            rownum = rownum + 1;
       end if;
   end loop

So, this query started reading tax.lien_sales.  rownum is <= 10 (it is one).
mod(rownum,2) = 1.  Until it found a row with 'E' in it -- rownum would stay at
1.  So, suppose the first row we found had sale_v_endo = 'X' -- the predicate
would not be satisfied, the rownum would not be incremented.  We would goto the
second fetched row.  Suppose this did have an 'E'.  Now we would output the row
and increment rownum.  Now it would continue through the rows until it found an
'S' (rownum = 2 until we find an 'S')

As long as there were enough 'E's and 'S's in the right order -- we would get
what we wanted (as long as there were at least 5 E's in the table that were
followed by at least one S -- PHYSICALLY on disk.

Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( sale_v_endo varchar2(1) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select 'E' from all_users where
rownum <= 10;

10 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select 'S' from all_users where
rownum <= 10;

10 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select sale_v_endo, count(*) from t group by
sale_v_endo;

S   COUNT(*)
- ----------
E         10
S         10

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select rownum, sale_v_endo
  2    from t
  3   where rownum <= 10
  4     and ((mod(rownum, 2) = 0 and sale_v_endo = 'S')
  5        or(mod(rownum, 2) = 1 and sale_v_endo = 'E'));

    ROWNUM S
---------- -
         1 E
         2 S

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> truncate table t;

Table truncated.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select decode( mod(rownum,2)
,1,'E','S' ) from all_users where rownum <= 20;

20 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select sale_v_endo, count(*) from t group by
sale_v_endo;

S   COUNT(*)
- ----------
E         10
S         10

ops$tkyte@ORA817DEV.US.ORACLE.COM> select rownum, sale_v_endo
  2    from t
  3   where rownum <= 10
  4     and ((mod(rownum, 2) = 0 and sale_v_endo = 'S')
  5        or(mod(rownum, 2) = 1 and sale_v_endo = 'E'));

    ROWNUM S
---------- -
         1 E
         2 S
         3 E
         4 S
         5 E
         6 S
         7 E
         8 S
         9 E
        10 S

10 rows selected.

now, in both cases -- there were 20 rows, 10 of each.  Note how the rownum
query was RADICALLY affected by the physical order of the data in the table!!!

Beware!!! (not really beware -- just be aware of how it works)

Review &
      Followup

      Rating: 5
Great
      !!!!  May 29, 2002
Reviewer:  A reader
       from Logan Palanisamy, Santa Clara, CA, USA

      Rating: 5
Interesting,
      Thanks!  May 29, 2002
Reviewer:  Chuck
      Jolley  from Oklahoma City, OK USA
Well, I wanted a sample with both types and that's what I got.
The explanation of rownum is very interesting.
Thanks,
Chuck

May 29, 2002 Ganesh -- Thanks for the question regarding
      "Delete Duplicate Records.", version 8.1.6

      You Asked
Hi Tom,

We have a Table that has Duplicate Rows.

The Table Structure is as follows.

Id    Date
================
1     18-Dec-2000        <<======
1     20-dec-2000        <<======
1     30-dec-2000        <<======
1     12-dec-2000
2     01-dec-2000        <<======
2     03-dec-2000        <<======
3     12-jan-2000
3     20-feb-2000
3     16-dec-2000         <<=====
3     21-dec-2000         <<=====
3     15-dec-2000         <<=====

[This is a Sample of my table]

Now i would like to retain the rows marked with an arrow there and delete the
rest of them.

In Short i need to have the last three dates of transaction from each id
retained.

I Tried this select

Delete Dates A1 Where Rowid In (Select RId From (Select Rid, Rownum R From
(Select Rowid rid, Id, Date  from Dates B1 Where a1.Id = B1.ID Order By Dates
Desc)) Where R > 3)

But the above stmt was not able to access the A1 Inside the SubQuery.

So i was Left with the Only Option of doing it id wise. Like Below.

For In In (Select Distinct id id from Dates)
Loop
Delete Dates A1 Where Rowid In (Select RId From (Select Rid, Rownum R From
(Select Rowid rid, Id, Date  from Dates B1 Where B1.ID = i.id Order By Dates
Desc)) Where R > 3)
End Loop;

Tom,

Is there any other way to achieve this using one single stmt instead of a Block.

Thanks for the help.

Regards,
Ganesh R

      and we said...
this will do that

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

        ID DT
---------- ---------
         1 18-DEC-00
         1 20-DEC-00
         1 30-DEC-00
         1 12-DEC-00
         2 01-DEC-00
         2 03-DEC-00
         3 12-JAN-00
         3 20-FEB-00
         3 16-DEC-00
         3 21-DEC-00
         3 15-DEC-00

11 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace on explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> delete from t
  2   where (id,dt) in ( select id, dt
  3                        from ( select id,
  4                                      dt,
  5                                      row_number() over ( partition by id
  6                                                              order by dt
DESC ) rn
  7                                 from t
  8                             )
  9                       where rn > 3 )
 10  /

3 rows deleted.

Execution Plan
----------------------------------------------------------
   0      DELETE STATEMENT Optimizer=CHOOSE
   1    0   DELETE OF 'T'
   2    1     NESTED LOOPS
   3    2       VIEW OF 'VW_NSO_1'
   4    3         SORT (UNIQUE)
   5    4           VIEW
   6    5             WINDOW (SORT)
   7    6               TABLE ACCESS (FULL) OF 'T'
   8    2       INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE)

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

        ID DT
---------- ---------
         1 18-DEC-00
         1 20-DEC-00
         1 30-DEC-00
         2 01-DEC-00
         2 03-DEC-00
         3 16-DEC-00
         3 21-DEC-00
         3 15-DEC-00

8 rows selected.

Review &
      Followup

      Rating: 5
Just
      Brilliant  May 30, 2002
Reviewer:  Yamani
       from KSA
Tom,

It is really a bright idea.

Tom, you are just Brilliant.

Regards,

May 29, 2002 Paul -- Thanks for the question regarding "Group
      reporting to return 0 counts", version 7.3

      You Asked
Hi Tom

On numerous occasions I have been asked to provide data reports detailing the
counts of certain objects. The requestors want the format like:

Group     No of type W    No of type X     No of type Y     No of type Z
A    12        45        0        0
B    0        56        6        3
C    13        0        4        24

Assuming that I can select the unique 'Group' identifier how do I then proceed
to use this unique identifier to then generate the counts for each related
entity W,X,Y,Z even if that entity has a zero count in relation to the 'Group'.

Can you provide a generic example I could apply to different data (usually 100's
of Groups over 100,000's records). This would make my life a whole lot easier.

Many Thanks

Paul

      and we said...
Sure, it would just be:

select group_name,
       sum( decode( type, 'W', 1, 0 ) ) "Type W",
       sum( decode( type, 'X', 1, 0 ) ) "Type X",
       ....
       sum( decode( type, 'Z', 1, 0 ) ) "Type Z"
  from t
 group by group_name
/

Review &
      Followup

      Rating: 2
Not sure I
      explained problem correctly...  May 29, 2002

Reviewer:  Paul Sutherland  from Scotland
If I run a query:

select object
from table_1
where name LIKE '%SOMENAME%'

This gives a list of (say) 500 unique object_1 identifiers. In turn these object
numbers are used as the driver in other queries:

select count(*) '2'
from table_2
where table_2.object = table_1.object_1

select count(*) '3'
from table_3
where table_3.object = table_1.object_1

select count(*) '4'
from table_4
where table_4.object = table_1.object_1

I want to be able to run a script that would use the initial table_1.object_1
records in the subsequent COUNT(*) statements and return an entry in every
instance even if this was 0.

so say for objects 1000000,1000001,1000002 ....100000n
the record returned would look like
         '2'  '3'  '4'
1000000   0    45   12
1000001   12   0    13
1000002   0    1    0
.
.
.
100000n   n    n    n

I can't think of a way in static SQL to do this as I need to pass the object
from the initial list into the COUNT(*) queries to get the listed output
expected. Would Pl-Sql allowing the passing of these objects to the COUNT(*)
queries and provide the output.

Any help much appreciated.

Paul

          Followup:

In 7.3 -- no go, not generically anyway.

In 8i trivial and fast.

select object_1,
      ( select count(*) from table_2 where table_2.object = table_1.object_1 ),
      ( select count(*) from table_3 where table_3.object = table_1.object_1
),....
      ( select count(*) from table_N where table_N.object = table_1.object_1 )
  from table_1
/

In 7.3, the only things I can think of are:

a) write a function/ table, then

   select object_1,
          get_count_table_2( object_1 ),
          ...
          get_count_table_N( object_1)
     from table_1

b) use inline views:

   select table_1.object_1,
          nvl(table_2.cnt,0),
          ...
          nvl(table_N.cnt,0)
     from table_1,
          (select object, count(*) cnt from table_2 group by object) table_2,
          ....
          (select object, count(*) cnt from table_N group by object) table_N
    where table_1.object_1 = table_2.object(+)
      ...
      and table_1.object_1 = table_N.object(+)

May
      29, 2002 -- Thanks for the question
      regarding "SQL Tuning", version 8.1.6

      You Asked
Tom

While writing a SQL what are the steps we need to follow to use proper SQL
hints.  Is there any pre-requisite to be followed when using a hint. If so what
are they ?

Is Nested Loop better than Hash Join, will Use Index give better performance.

What are the options / clauses I should not use when I try to retrieve millions
of records.   What are the possible bottle in SQL statements. (eg should not use
like '%abc%' anything else).

What is SQL Rewrite, where do we use it and what is the advantage of SQL
Rewrite.

      and we said...
The server tuning manual goes over the hinting mechanism:

http://technet.oracle.com/doc/oracle8i_816/server.816/a76992/toc.htm
it is well documented there.

If nested loop was inheritly BETTER then hash join, we would not offer both.
Neither is better, each performs its job very well under different conditions.
I've seen nested loops blow away a hash join.  I've seen a hash join blow the
doors off of a nested loops.

If you are going to retrieve MILLIONS of records -- use anything you want.
It'll take a while anyway.

If you are going to retrive a small number of records FROM MILLIONS of records
-- try to use things that would enable the use of indexes -- eg, avoid '%abc%'
as that would need a full scan of either the index or table.

SQL rewrite is when the database rewrites your sql to take advantage of another
smaller but equivalent table (materialized views), or when it wants to use a
function based index, or when it merges statements into complex views.

See the server tuning manual (link above) and

http://technet.oracle.com/doc/oracle8i_816/server.816/a76994/mv.htm#721
for materialized views and

http://osi.oracle.com/~tkyte/article1/index.html
for function based indexes.

Review &
      Followup

      Rating:
      4
  April 06, 2001
Reviewer:
      A reader

      Rating: 4
More on
      nested_loops vs. hash?  May 29, 2002
Reviewer:
      Doug  from Danbury, CT USA
Tom - I understand what you mean when you say Oracle would not offer both unless
they were good for different things.  I've noticed an dramatic improvement
setting always_semi_join to nested_loops for about 4 queries.  Can you elaborate
on what sort of circumstances hash joins bean nested loops and vice versa?  My
understanding was basically if you were matching a small number of rows in one
table to a large numer in a nother, nested_loops was better.

          Followup:

Not necessarily -- if you are retrieving MOST of the data, a hash join will best
a nested loops.  If you are interested in the first rows, nested loops is
"best".  Interested in all rows hash join may well be best

May 30, 2002 claudia -- Thanks for the question regarding
      "Performance Tuning", version 8.0.5

      You Asked
Hello Tom,

Thank you for taking my question.

I am running the ultbstat/utlestat performance scripts.  I am wondering which
init.ora parameters to tune to achieve the best performance results, if the
Latches and Library Cache results from the report.txt are indicating that
performance is being affected.

Thank you,
Claudia

      and we said...
Well, if you see lots of latching and library cache contention -- I'll betcha
you are not using bind variables in your application.  That is, instead of
submitting:

select * from emp where ename = :x;

You'll application issues queries like:

select * from emp where ename = 'KING'
select * from emp where ename = 'SMITH'

and so on.  this it the #1 cause of this -- it is the #1 performance inhibitor,
it kill scalability and works your CPU overtime.

You should note that latches are just "locks" -- it is almost certainly an
application issue, not a database instance tuning thing given that.

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1163635055580
for a script that'll help you identify the problem statements.  Given that you
can track down the applications that do this very bad thing and make them fix
it.

90% of tuning happens in the app!

(if you want lots more info on this -- this happens to be one of my pet peeves
-- my book goes over this again and again and again, giving you a really good
understanding why this is pretty important)
Review &
      Followup

      Rating: 5
Performance
      Tuning 8.0.5  October 15, 2001
Reviewer:
      Claudia  from CA
I was impressed by the quick turn around time to my question.  Your response and
additional reference to my question were extremely helpful.  It assured me that
I need to concentrate on application tuning, due to the results from the
reports.txt of the ultbstat/utlebstat scripts.

      Rating:
      5
dc_used_extents  May 29, 2002
Reviewer:
      A reader  from Dayton, OH
This is slightly related..
what can contribute to the dc_used_extents in the v$rowcache having a horrible
hit /high miss ratios?

I can't find any mention of this column in any performance tuning materials.

          Followup:

what is the value, 50% is good.

      Rating: 5
Library cache
      latches  May 29, 2002
Reviewer:  Andre
       from Brazil
Short answers would be great, thanks:

1- How many library cache latches are there ? Is this number related to the
number of CPUs ?
2- Is it true that ONLY ONE server process (or thread) may hold the library
cache latch at the same time -- or more than one may ?
3 - Are there different latches for the different internal lib cache areas, like
SQL area and db object cache ?

          Followup:

1) number of cpus+1 (with 7.2 and up)

2) latches are serialization devices, one at a time.

3) if you look in v$latchname, you'll see a list of the latches we have, but yes
there are various latches for the variable portion of the SGA.

      Rating: 5
Latches
      cont'd  May 29, 2002
Reviewer:  Andre
       from Brazil
I gather from above that only one process can hold a latch at a time, and there
can be more than one latch. I understand only one process can parse a statement
at a time. What are these additional latches, which are CPU dependent ?

          Followup:

There are a number of library cache latches -- each protecting a group of
objects in the library cache.  There is in fact the possibility of concurrency
there as the library cache is divided up (just like there are multiple cache
buffers chains -- used to protect the many lists of cached buffers we have).

These additional latches are each used to protect separate areas of the library
cache -- they are "peers" of eachother.

May 30, 2002 Ravi
      -- Thanks for the question regarding "Privileges needed to created the
      function based index.", version 816

      You Asked
Tom,

I have set the necessary init.ora parameters to create the
function based index. When I tried to create the function based
index in scott schema, it says insufficient privs. Where as when
I tried with system schema, it could create.

system@LEARN.RAVI.COM> create index scott.idx on scott.emp
(upper(ename)) tablespace users;

Index created.

scott@LEARN.RAVI.COM> create index scott.idx on scott.emp
(upper(ename))
  2  tablespace users;
create index scott.idx on scott.emp (upper(ename))
                                           *
ERROR at line 1:
ORA-01031: insufficient privileges

scott is having the following privs and roles.

scott@LEARN.RAVI.COM> select * from user_sys_privs;

USERNAME                       PRIVILEGE
       ADM
------------------------------
---------------------------------------- ---
SCOTT                          UNLIMITED TABLESPACE
       NO

scott@LEARN.RAVI.COM> select * from role_sys_privs;

ROLE                           PRIVILEGE
       ADM
------------------------------
---------------------------------------- ---
CONNECT                        ALTER SESSION
       NO
CONNECT                        CREATE CLUSTER
       NO
CONNECT                        CREATE DATABASE LINK
       NO
CONNECT                        CREATE SEQUENCE
       NO
CONNECT                        CREATE SESSION
       NO
CONNECT                        CREATE SYNONYM
       NO
CONNECT                        CREATE TABLE
       NO
CONNECT                        CREATE VIEW
       NO
RESOURCE                       CREATE CLUSTER
       NO
RESOURCE                       CREATE INDEXTYPE
       NO
RESOURCE                       CREATE OPERATOR
       NO
RESOURCE                       CREATE PROCEDURE
       NO
RESOURCE                       CREATE SEQUENCE
       NO
RESOURCE                       CREATE TABLE
       NO
RESOURCE                       CREATE TRIGGER
       NO
RESOURCE                       CREATE TYPE
       NO

16 rows selected.

Could you please tell, what privileges needs to be granted for
scott in order that he can create the function based index.

Thanks a lot.

Regards
Ravi

      and we said...
see

http://osi.oracle.com/~tkyte/article1/index.html#how
....
Review
      & Followup

      Rating: 1
Privileges
      needed to created the function based index  May 30, 2002

Reviewer:  Ju  from Liege, Belgium
The link is broken in your response !

Anyway, I managed to reconstruct it :
http://osi.oracle.com/~tkyte/article1/index.html
But you should correct it !

Link to the question :
http://asktom.oracle.com/pls/ask/f?p=4950:8:878033::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:957629973595,%7BFunction%7D%20and%20%7Bbased%7D%20and%20%7BIndex%7D

          Followup:

It is a side effect of the search and hilight.

the link is OK, it is just that the hilighting messed it up -- never have fixed
that yet.

If you use the link:

Bookmark this page with the link HERE

on the bottom of the page -- it'll always show it to you the page without
hilighting.

May 30, 2002 Ajith
      -- Thanks for the question regarding "Get OS User", version Oracle 7.3/8.x

      You Asked
How can I retrieve the OS user name from v$session, for the user
who is currently in the system?

For eg: Let us say there are 2 users ABC and DEF both know the
DB admin password and logged in as admin.
One of them deleted some data and we want to track the user.
I have a trigger before delete for that particular table. I want
to track the DB user and OS user for that particular person when
the trigger fires.
I am getting DB user from Dual, but tried to get OS user from
v$session and it returns 2 rows, since ABC & DEF are logged in
as admin.

It would be great if somebody can help me ASAP.
My address is ajithkumar15@hotmail.com.
Thanks,
Ajith

      and we said...
select *
  from v$session
 where audsid = userenv('sessionid')
/

will get YOUR current sessions information from v$session.  What
you might do is:

SQL> create or replace view my_session as
     select *
       from v$session
      where audsid = userenv('sessionid')
     /
SQL> grant select on my_session to public;

Then, you would use my_session in your triggers.  You will have
to grant select on v_$session directly to the person creating
the view.

Review &
      Followup

      Rating: 3
I've
      implemented similar before reading but....  May 29, 2002

Reviewer:  Don Smar  from NJ, USA
We have are using a packaged enterprise application for asset management. For
every log on into the application, it creates 2 sessions in Oracle. One of the
sessions is for the user that owns all application objects. The application is a
black box. What we needed to do was determine who was logged into the database
(the "real" users login) in order to determine if they could perform certain
actions. I did the following to determine the "real" user as opposed to the user
actually doing the inserts/updates:

CURSOR userNameCur
   IS
      SELECT username
        FROM sys.v_$session
       WHERE username <> 'MAXIMO'
         AND terminal = (SELECT userenv('TERMINAL')
                       FROM dual);

Where MAXIMO is the username we do not want. This works fine except where
TERMINAL is not correctly filled in with the workstation id. In our
organization, all our PCs have a unique id and all clients supposedly use the
same image for installation. However, when placed into production we have found
that some users consistently have there TERMINAL value loaded as "Windows NT PC"
when they log into the application (which of course logs them into the
database). When these same users log in through SQL Plus, we always correctly
populate the TERMINAL with the PC name. When "Windows NT PC" populates the
TERMINAL column, this invalidates our whole approach if more than one user has
this issue in the same database. We may well be using the wrong username when
authenticating them.

WHERE DOES TERMINAL GET IT'S VALUE FROM? This must be a client installation
issue but noone has been able to tell me where to look.

Thanks

          Followup:

It is set by sqlnet on the client.  You cannot influence its value.  having a
version of what is installed would be helpful.

If it is really old stuff (7.3) -- you could have some sqlnet 2.3.2 clients
(thats the last release that put "Windows NT PC" in there.  2.3.3 and above
should put the machine name in there.

      Rating: 3
What about
      Application Contexts?  May 30, 2002
Reviewer:
      Adrian Billington  from UK
Tom

What about referencing the SYS_CONTEXT USERENV namespace(8i+) or the USERENV
function (pre-8i) in the triggers instead? Requires no setup then and no table
access.

Regards

AB

          Followup:

yes you could -- there is a lot of information available with sys_context(
'userenv', .... )

      Rating: 5
I've puzzeled
      over getting the OS username for some time  June 11, 2002

Reviewer:  Tony Cox  from BASILDON, ENGLAND

Thanks for this reply, it was a great help.
I needed to set up an update trigger on our 7.3 database but couldn't get the os
name directly from the v$sessions view, only the username which was the same for
all connections.

Just tried your solution and it works fine.

Thanks again.

May 30, 2002 Darshan -- Thanks for the question regarding
      "Problem of UTL_FILE", version 8.1.7

      You Asked
Hello Tom,
          I have a strange problem.Can a UTL_FILE program read a file on another
server on which there is no database.Suppose there are two Unix boxes A and
B.Box A has unix and oracle.I have written a UTL_FILE program on box A.Box B has
just unix but no oracle.So can my program which has been written on box A read
the file which resides on the filesystem of Box B ?

Thanks in anticipation,
Darshan D. Mhatre
Software Engineer.
SHCIL - IT

      and we said...
An NFS mount will allow you to do this, yes.  The file must appear to be
available on the database server and NFS will do that easily.
May 30, 2002 mathew -- Thanks for the question regarding
      "Flashback query", version 9i

      You Asked
hi tom,
   With 9i 2 out, what are the differences between 9i R1 and 9i R2?
What features are avaliable for the developers ?
How can the flashback query be used in a SQL statement in R2?

thanks

Regards,
Mathew

      and we said...
See

http://otn.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96531/toc.htm
for 9iR2 new features.

See

http://otn.oracle.com/docs/products/oracle9i/doc_library/release2/appdev.920/a96590/adg08sql.htm#10851
for using 9iR2 flashback abilities.
May 30, 2002 Pratap -- Thanks for the question regarding
      "Secure application roles", version 9.0.0

      You Asked
Hi Tom

Can you please give an example of using secure application roles in 9i. In the
application developers guide an example is given but some things are not clear
like -

How I will prevent anyone anyone other than my application to connect to the
database and say execute a procedure. Like I have a weblogic server on machine x
and I want to ensure that only my application on weblogic server would be able
to access my procedures.

Can you please give a detailed (practical) example of how you would use this
feature in your application?

Thanks and regards

Pratap

      and we said...
You would use Middle Tier Proxy Connections.

Your client (say TKYTE) identifies itself to your application server (weblogic).
Weblogic uses the THICK (oci) jdbc drivers to connect to the database.
Weblogic will connect with the username YOUR_APPLICATION and some password.
It will connect on behalf of TKYTE

So, what was have now is that:

o the user TKYTE is loggd in (select user from dual = TKYTE)

o the proxy_user is your_application (sys_context('userenv','proxy_user') =
YOUR_APPLICATION)

o your application will now call a stored procedure to enable a role (the role
can ONLY be enabled by a stored procedure).  This stored procedure will verify
the sys_context value (it now knows it is YOUR_APPLICATION) and will issue the
command to enable the role. If TKYTE was granted the role and YOUR_APPLICATION
is allowed to enable that role - the role will be turned on.

if the procedure doesn't see the proxy_user set to YOUR_APPLICATION, the role is
not enabled (and cannot be enable).

if YOUR_APPLICATION isn't allow to enable the role for TKYTE (admins take care
of that), then the role is not enabled (and cannot be)

See

http://otn.oracle.com/docs/products/oracle9i/doc_library/901_doc/java.901/a90211/oci_func.htm#1014235
for details on this connection capability.

May 30, 2002 TJ -- Thanks for the question regarding "Oracle
      features query - space management auto, alter table monitoring,
      db_keep_cache", version 9.0.1

      You Asked
Hello Tom

1. What is the best way to find out the most active tables & indexes in the
databases. Do you have a code sample please?
2. What is the real overhead of 'alter table monitoring' in a high DML activity
oriented database? In one of earlier posts you said -
"It appears to have minimal impact (no measurable impact that I
could observe) and I see nothing in the problem database about
it being a performance issue."
3. You said you did a test to see space management auto in Oracle9i is better
than manual freelist management. Is it possible to see you r example?
4. How would you designate a large table, which contains BLOB and underdoes
significant number of inserts & deletes with moderate number of updates, to be a
candidate for db_keep_cache or db_recycle_cache? please give your explanation.

Thanks very much

      and we said...
1) it is hard to say -- unless you have your objects segregated by FILE or
TABLESPACE.

In some cases, you can use the view v$bh -- but if you have a really big table
with lots and lots of blocks that are read randomly it might not help you.  For
example, you could have one small table with a hot block.  This block will be in
the cache pretty much for sure.  Say it is accessed once per second.  Now, on
the really big table you are accessing a hundred blocks per second (but a
hundred different blocks every second) and you tend to not re-read these blocks
frequently.  They will age out of the buffer cache faster -- that small table
might appear more active.

If you segregate objects by tablespace or by file, it can help you isolate hot
spots as we'll keep IOs (physical) recorded for you.

I've never really ever looked for "most active tables", "most active indexes".
I look for top queries, points of contention.  statspack is good for that.

2) None that I can measure.  I haven't seen or heard of any (as my comment says)

3) I used this script to create the test:

set echo on

alter system set DB_CREATE_FILE_DEST = 'c:\temp\' scope = memory
/

create tablespace scratch datafile size 10m
/

drop table sess_event;
drop table t;

create table t
( c1 int, c2 int, c3 int, c4 int )
tablespace scratch;

create global temporary table sess_event
on commit preserve rows
as
select * from v$session_event where 1=0;

pause             <<<<================== run other script in parallel here.

drop table t;
create table t
( c1 int, c2 int, c3 int, c4 int )
tablespace scratch storage ( freelists 5 );

pause             <<<<================== run other script in parallel here.

drop table t;

drop tablespace scratch;

create tablespace scratch datafile size 10m
segment space management auto;

create table t
( c1 int, c2 int, c3 int, c4 int )
tablespace scratch;

pause             <<<<================== run other script in parallel here.

drop table t;
drop tablespace scratch;

And here is the other script:

truncate table sess_event;

insert into sess_event
select * from v$session_event
where sid = ( select sid from v$mystat where rownum = 1 );

declare
    l_number number;
begin
    for i in 1 .. 10000
    loop
        l_number := dbms_random.random;
        insert into t values ( l_number, l_number, l_number, l_number );
    end loop;
    commit;
end;
/

select a.event,
       (a.total_waits-nvl(b.total_waits,0)) total_waits,
       (a.time_waited-nvl(b.time_waited,0)) time_waited
  from ( select *
           from v$session_event
          where sid = (select sid from v$mystat where rownum = 1 )) a,
       sess_event b
 where a.event = b.event(+)
   and (a.total_waits-nvl(b.total_waits,0)) > 0
/

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

You want to run 5 or more copies of that script "at the same time" more or less
to get some contention on the table.

4) The blob part of it does not factor into the equation -- it would be treated
differently.  blobs are stored out of line (when over 4000 bytes).  They are
many times stored in an entirely different tablespace (but are definitely in
another segment).  They may not even be cachable! (or they may be, you set that
up).

So, the same thought that would go into ANY table (small, med or large) about
where to put it applies.  Blobs don't change a thing.
May 30, 2002 Manoharan -- Thanks for the question regarding
      "Rollback Segments", version 8.1.7

      You Asked
Hi Tom,

   Even after reading many books(including expert one on one),i have some doubts
about the rollback segment.

   1.How does Database Buffer cache allocates spaces for rollaback information.
   2.How frequent, by which process(server process???) these buffers will be
updated over the physical rollback segments(datafile).

Thanks/regards
Jeyaseelan.M

      and we said...
1) no differently then ANY OTHER block.  (page 150 goes over this stuff.  On
page 151 for example I state:

A rollback segment is stored in a tablespace, and (this is the important part)
is protected by the redo log just like any other segment.  In other words,
rollback data is treated just like table data or index data ......  Unod data is
added to the rollback segment, and is cached in the buffer cache just like any
other piece of data would be......

2) Read pages 150 on.  Rollback is flushed from the buffer cache just like any
other data block might be.  In fact, the rollback you generate MAY NEVER
actually be written to disk EVER, your rollback may never get flushed (just like
if you insert a row and another session deletes that row -- before the block is
flushed -- your row will never make it to disk)
Review &
      Followup

      Rating:
      4
Reader  May 29, 2002
Reviewer:
      A reader
Tom,
"
other data block might be.  In fact, the rollback you generate MAY NEVER
actually be written to disk EVER, your rollback may never get flushed (just like

if you insert a row and another session deletes that row -- before the block is
flushed -- your row will never make it to disk)
"

If RBS block is similar to any other data block, the block gets filled up from
bottom of the block to top of block.
1. There is an insert TX1 => generates undo , might write a rowid+, generates
redo
                             for the undo
2. Another session deletes the row TX2 => assuming the new TX uses the same
partly
                                          used block writes the complete row of
data
                                          to the undo block, filling up
additional
                                          space of the block. Generates redo for
this
                                          change also.

If the event 2. is not true, how the TX2 , will rollback its changes (undo the
delete).
If the event 2. is true, all the undo DOES get flushed to disk eventually. Is
this
a fair statement

Thanks

          Followup:

Only one transaction will use a block in rollback.  Your transaction owns the
entire block.

Even if event 2 was true, I fail to see how that implies all redo would get
flushed eventually.

      Rating:
      4
Reader  May 29, 2002
Reviewer:
      A reader
Tom,
"
other data block might be.  In fact, the rollback you generate MAY NEVER
actually be written to disk EVER, your rollback may never get flushed (just like
if you insert a row and another session deletes that row -- before the block is
flushed -- your row will never make it to disk)
"

If RBS block is similar to any other data block, the block gets filled up from
bottom of the block to top of block.
1. There is an insert TX1 => generates undo , might write a rowid+, generates
                             redo for the undo
2. Another session deletes the row TX2 => assuming the new TX uses the same
                                          partly used block writes the
                                          complete row of data to the undo
                                          block, filling up additional
                                          space of the block. Generates redo
                                          for this change also.

If the event 2. is not true, how the TX2 , will rollback its changes (undo the
delete). If the event 2. is true, all the undo DOES get flushed to disk
eventually. Is thisa fair statement

Thanks

      Rating:
      5
Reader  May 30, 2002
Reviewer:
      A reader
Tom,
"
(just like if you insert a row and another session deletes that row -- before
the block is flushed -- your row will never make it to disk)
"
If there are two blocks modified by these 2 TXs,

1. If CKPT triggers a checkpoint and DBWRn writes all modified blocks to
   disk, then these undo is flushed to disk
2. If the blocks have been reused by the cyclic nature of the RBS, these
   undo will not be flushed to disk

However I am trying to relate the "<insert>, <delete>" scenario with the undo
not being flushed to disk.

Thanks

          Followup:

what <insert>, <delete> scenario are you referring to?

If you just mean the simple example above (all "readers" look alike you know) it
is not any different they any other sequence of events.

transaction 1 inserts a row.  This generates UNDO in undo block 1.
transaction 1 commits.  Undo block 1 is now available for reuse if the RBS
wraps.
transaction 2 deletes that row.  This generates UNDO in undo block 2.
transaction 2 commits.  Undo block 2 is now available for reuse if the RBS
wraps.

transactions 3... N happen -- RBS wraps and we reuse undo block 1.  That is OK,
we don't need it.  Undo block 1 might not have made it to disk, that is OK, we
quite simply don't need it, haven't needed it since transaction 1 committed.

      Rating:
      5
Reader  May 30, 2002
Reviewer:
      A reader
Tom,

Thanks for your time

I was confusing "insert a row into RBS" with "insert
a row into a TABLE "

      Rating:
      5
Reader  May 30, 2002
Reviewer:
      A reader
Thanks, I understood.

May 30, 2002 -- Thanks for the question regarding "Statement
      in Java", version 8.1.7

      You Asked
Hi Tom,

I would like to be able to write a procedure in Java (Oracle 8i) to run the
following query :

select file_system, free_space from ??? ;

whith a result coming from os command : 'df -k'

Could you help me ?

Thanks in advance

Marc

      and we said...
Here is a quick and dirty implementation that builds on:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:952229840241
(read the whole trail there...)

Then, we can do this:

rt_test@ORA817DEV.US.ORACLE.COM> create or replace type myScalarType
  2  as
  3  object
  4  ( filesystem varchar2(255),
  5    free_space number
  6  )
  7  /

Type created.

rt_test@ORA817DEV.US.ORACLE.COM> create or replace type myTableType
  2  as
  3  table of myScalarType
  4  /

Type created.

rt_test@ORA817DEV.US.ORACLE.COM>
rt_test@ORA817DEV.US.ORACLE.COM>
rt_test@ORA817DEV.US.ORACLE.COM> create or replace function df(p_cmd in
varchar2) return myTableType
  2  as
  3    l_status number;
  4    l_line   varchar2(255);
  5    l_data   myTableType := myTableType();
  6  begin
  7    l_status := run_cmd(p_cmd);
  8    dbms_output.get_line( l_line, l_status );
  9    loop
 10      dbms_output.get_line( l_line, l_status );
 11      exit when l_status = 1;
 12      l_data.extend;
 13      l_data(l_data.count) := myScalarType( substr( l_line, 1, instr(l_line,'
')+1 ), 0 );
 14      l_line := ltrim(substr(l_line,instr(l_line,' ')));
 15      l_line := ltrim(substr(l_line,instr(l_line,' ')));
 16      l_line := ltrim(substr(l_line,instr(l_line,' ')));
 17      l_data(l_data.count).free_space := substr( l_line, 1, instr(l_line,'
')+1 );
 18    end loop;
 19    return l_data;
 20  end;
 21  /

Function created.

rt_test@ORA817DEV.US.ORACLE.COM>
rt_test@ORA817DEV.US.ORACLE.COM> set serveroutput on size 1000000
rt_test@ORA817DEV.US.ORACLE.COM> exec dbms_java.set_output(1000000)

PL/SQL procedure successfully completed.

rt_test@ORA817DEV.US.ORACLE.COM> /

Function created.

rt_test@ORA817DEV.US.ORACLE.COM>
rt_test@ORA817DEV.US.ORACLE.COM> column filesystem format a35
rt_test@ORA817DEV.US.ORACLE.COM>
rt_test@ORA817DEV.US.ORACLE.COM> select *
  2  from TABLE ( cast ( df('/usr/bin/df -k' ) as myTableType ) )
  3  /

FILESYSTEM                          FREE_SPACE
----------------------------------- ----------
/proc                                        0
/dev/dsk/c0t0d0s0                       200043
fd                                           0
/dev/dsk/c0t0d0s1                        52641
/dev/dsk/c0t3d0s3                      4505218
/dev/dsk/c0t1d0s0                      2331161
/dev/dsk/c2t2d0s3                      2956077
/dev/dsk/c3t1d0s1                      3057383
/dev/dsk/c3t0d0s0                      2491558
/dev/dsk/c2t1d0s0                      2527326
/dev/dsk/c3t1d0s0                       218210
/dev/dsk/c2t3d0s3                      2084348
/dev/dsk/c0t2d0s3                      2605532
aria:/export/home                      4278107
/vol/dev/dsk/c1t6d0/orcl901_1                0
aria:/export/home                      4278107

16 rows selected.
Review & Followup

      Rating: 5
Just what I was
      waiting for...  May 30, 2002
Reviewer:  Marc
       from Belgium
Thanks Tom

May 30, 2002 Tom -- Thanks for the question regarding "Meaning
      of ctxsrv log entries", version 8.1.7

      You Asked
I set up "ctxsrv" here, and I'm checking the log contents to make sure it's
doing what it should be.  Based on the app behaviour, it is.  But still I want
to understand the log.  Here's the beginning of the log below.  It goes on like
that continually.  What does "cid=1026" mean?  I love return codes of 0 as much
as anyone, but I can't find any docs that explain why I should love it.  Thanks,
Tom.

Here's the ctxsrv log:

14:39:37 05/15/02 === OCO server startup ===
14:39:37 05/15/02 Initialized CORE
14:39:38 05/15/02 Connected to database.
14:39:38 05/15/02 === Initializing dispatcher ===
14:39:38 05/15/02 === Server startup completed. ===
14:39:40 05/15/02 DML batch: cid=1026
14:39:51 05/15/02 End batch, return code is 0
14:39:52 05/15/02 DML batch: cid=1026
14:39:52 05/15/02 End batch, return code is 0
14:47:28 05/15/02 DML batch: cid=1026
14:47:28 05/15/02 End batch, return code is 0
15:09:24 05/15/02 DML batch: cid=1026
15:09:25 05/15/02 End batch, return code is 0
15:12:21 05/15/02 DML batch: cid=1026
15:12:21 05/15/02 End batch, return code is 0

PS - now that I got my foot in the door... any chance you could put a timestamp
on the asktom entries?  It would help to know whether you're reading something
you already read a while ago.  Along the same lines, on the main listing of
subjects, a marker beside the "new" ones would be nice too... basically, those
that consist only of a question, and the first reply by you... as opposed to
those that have been going back and forth for a while.

      and we said...
it is Context InDex command, the 1026 corresponds to the index.  When you add a
row to your table, context adds a row to the dr$pending table.  In there you'll
see colums:

ctxsys@ORA8I.WORLD> desc dr$pending
 Name                    Null?    Type
 ----------------------- -------- ----------------
 PND_CID                 NOT NULL NUMBER
 PND_ROWID               NOT NULL ROWID
 PND_TIMESTAMP                    DATE

the CID is the cid you are seeing (what index).  they have the rowid or primary
key to be indexed in there as well.

Return code would be zero on success, not zero (with details) otherwise.  Eg: an
error might look like this:

13:51:57 02/10/99 End batch, return code is 1
13:51:57 02/10/99 DRG-50857: oracle error in drtldml(dml)
13:51:57 02/10/99 ORA-20000: ConText error:
ORA-28579: network error during callback from external procedure agent
ORA-6512: at "CTXSYS.DRUE", line 122
ORA-6512: at line 1
.

As for the timestamp, what I've done is:

o added the original date the question was "asked" in ()'s after the last
updated date (what I've been showing) on the home page.  Also, the number of
days between the two is there.

o same for the search page.

o the "new" marker -- well, thats a bit hard.  I guess I could put a "*" next to
anything such that the last updated date is within X days/hours of the time they
submitted it.  The problem is that sometimes

  a) a question takes a bit of time to answer -- hence it'll be NEW but not
     marked as "new"
  b) a question can be new yet have a back and forth trail on it very quickly
     (meaning it stays at the top of the list for a while).  This happens very
     often

So, for now, I'll leave it with the two dates and number of days and we'll see
how that goes.
Review & Followup

      Rating:
      5
perfect  May 30, 2002
Reviewer:  Tom
      Best  from Exton, PA
Thanks for the answer, and for taking my suggestion.

May 30, 2002 Nour -- Thanks for the question regarding
      "Talking to MS SQL server 2000 from an Oracle Trigger using java stored
      procedure", version 8i

      You Asked
Hi Tom,
Can you please give me a little example how I can configure my Oracle 8i to talk
to SQL server using a Java stored procedure. I have created one and it does not
plug in the data in SQL server; may be because I'm missing some configurations
of the Oracle. If I run the same class in the OS (win2000) JVM, it will work
just fine, and I can see the data in my SQL server, but not when transforming
the class to a java stored procedure. I added the jdbc drivers paths to the
Oracle JRE ClassPath by editing the loadjava.bat file located near the
sqlPlus.exe. However, I coul not load the drivers by using the loadjava command
from SQLPlus. It says "Command not found". How can I use the loadjava? DO I have
to run some scripts?
Also, in different articles, you have given an example of running a program from
inside a java storeed procedure using java RunTime. I have followed the same
principe as you described by examples and was able to call an outside exe from
Oracle stored procedure. The exe was written in VB, and through it,I was able to
plug the data passed by oracle in the SQL server. My storede procedure was:
--------
CREATE OR REPLACE procedure test00 as
begin
rc('C:\talktosql.exe param1');
end;
---------
RC was a java stored procedure that will execute the path passed to it as
parameter.
talktosql is again an exe that reside in the opearating system and was made in
VB. With this everything works fine.
Now I'm trying to change my call to call an application written in Java instead
of my exe. I tried to pass different path, but none works. I did for example:
rc('C:\jdk1.3\bin\java.exe myclass param1');
But it does not work.
so my question is how can I pass a java application in a path like I did for the
tolktosql.exe .
Thanks

Malek

      and we said...
What you want to do is load the type 4 (thin) jdbc drivers for MS sqlserver into
the database.

the program (script, it is actually a .bat file) loadjava should be in
$ORACLE_HOME/bin.  If not, you can use dbms_java right from the command line in
sqlplus.  dbms_java has a loadjava entry point (examples in my book if you have
that)

You cannot juse add the paths - you have to put the java into the database, we
only resolve within the database.

Another option may be:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76960/hs_genco.htm#173
which is a generic ODBC gateway.

May 30, 2002 Shailandra -- Thanks for the question regarding
      "Indexing at very Large Table", version 9.0.1

      You Asked
Hello Tom,

I am indexing a very large table with more than 500 million rows. This table is
partitioned. I needed to create the index on the table. I am creating the local
index with parallel and nologging option. Once I was doing it, it was running
for more than 24 hours before I had to abort the process due to client reasons.
But now I have to start the process again. What is your suggestion, to make
index creation faster on this table.

Thanks in Advance.

      and we said...
Check out your sort area size, make sure it is "ok" (big enough)

Also, consider using the dbms_pclxutil package.  It provides intra-partition
parallelism for creating partition wise local indexes:

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/appdev.901/a89852/dbms_pcl.htm#998100
meaning you can parallelize the index builds against each PARTITION in addition
to ACROSS partitions.
Review & Followup

      Rating: 4
Very
      useful  May 30, 2002
Reviewer:  Shailandra

My current sort_area_size was 64K. Now I will try building index using 2MB
sort_area_size.

          Followup:

2mb -- make that much much larger please -- for an index build, 10 to 200mb
isn't unreasonable!

May 31, 2002 Ashish -- Thanks for the question regarding "How
      can I find Max sessions", version Oracle 8.1.5

      You Asked
Hi Tom,

I wanted to know the maximum users connected to the database.
Does "License high water mark" value in the alert log
corresponds to max sessions? If yes then how do I know the value
without shutting down the instance?

      and we said...
The dynamic performance view V$LICENSE has this info in it.
Just "select * from v$license" and you'll get this.
Review &
      Followup

      Rating: 3
Need more
      Information  May 30, 2002
Reviewer:  Raman
      Pujani  from India
Hi Tom,

I had fired :

select * from v$license;

SESSIONS_MAX SESSIONS_WARNING SESSIONS_CURRENT SESSIONS_HIGHWATER  USERS_MAX
------------ ---------------- ---------------- ------------------ ----------
           0                0               11                 23          0

In Oracle Help, SESSION_MAX say that number of concurrent users allowed. In may
case its 0. What does this mean and can you bried about this view in detail.

2. My instance crashed by display this message in alert :

ARC1: Beginning to archive log# 2 seq# 1382
ARC1: Completed archiving log# 2 seq# 1382
Thu May 30 15:39:52 2002
Shutting down instance (immediate)
License high water mark = 63
Thu May 30 15:40:01 2002
ARC1: terminating instance due to error 472
Instance terminated by ARC1, pid = 1240

What could be the reason.

Is there any link between this and v$license.

Regards,
Raman.

          Followup:

1) users_max is driven by the init.ora parameter license_max_users.  If at 0, it
is unlimited.  You can use this to help you comply with licensing.  It is 100%
optional.

sessions max from license_max_sessions.

See the Oracle Reference manual for the details on the V$ views.  The sessions
current and highwater are the most interesting columns.  The first shows the
number of current sessions, the second the max number of sessions since the db
was started.

2) Contact support for that (no, there would be no tie in between v$license and
this).  You will have other trace files on the server, support can help you
diagnose the issue in detail.

      Rating: 3
Some more
      information  May 31, 2002
Reviewer:  Raman
      Pujani  from India
Hi Tom,

I am facing this in alert :

ARC1: Beginning to archive log# 3 seq# 1380
ARC1: Completed archiving log# 3 seq# 1380
Thu May 30 15:34:16 2002
Thread 1 cannot allocate new log, sequence 1382
Checkpoint not complete
  Current log# 1 seq# 1381 mem# 0: E:\ORACLE\ORADATA\IKANPRD\REDO01.LOG
Thread 1 advanced to log sequence 1382
  Current log# 2 seq# 1382 mem# 0: C:\ORACLE\ORADATA\IKANPRD\REDO02.LOG

Should I resize all the online redo groups or just add one more group/member ?
Can we increase the checkpoint_interval, so that checkpoint will wait a little
more.

Is it possible that the above could be the cause of instance termination in
alert :

Shutting down instance (immediate)
License high water mark = 63
Thu May 30 15:40:01 2002
ARC1: terminating instance due to error 472

Regards,
Raman.

          Followup:

search for

cannot allocate new log checkpoint not complete

to see how to fix that.

No, it will not be the cause of that.  Please, please please -- contact support.
 There will be additional information already on your system that they will
request in order to diagnose this issue.

May 31, 2002 nasir -- Thanks for the question regarding
      "existence-based key and Oracle", version 8.1.7

      You Asked
we are designing a business application, which is going to be developed in
oracle & developer. if we use existence-based key concept in this design, would
it be applicable consedering our development tools (oracle & developer), or what
sort of problems are expected if we use this strategy?
Existence-baed concept is highly recommended in object oriented design, in this
concept we avoid composit keys, and instead of using one or more values/columns
of the table as a primary key, we add another column which uniqely identifies
the whole record. in this way we can add any field in a table at any time
without changing its key and its effects (foreign-keys) on other tables. its
proper definition (as i copied from some sight on internet is):

&#61623; Existence-based identity. You should add an object identifier attribute
to each class table and make it the primary key. The primary key for each
association table consists of the identifiers from one or more related classes.
Existence-based identifiers have the advantage of being a single attribute,
small, and uniform in size. Existence-based identifiers have no performance
disadvantage as long as your relational database management system (RDBMS) has
support. (Most RDBMSs provide sequence numbers for efficient allocation of
existence-based identifiers.) The only disadvantage is that existence-based
identifiers have no intrinsic meaning during maintenance.
&#61623; Value-based identity. Some combination of real-world attributes
identifies each object.
Value-based identity has different advantages. Primary keys have intrinsic
meaning to the user, facilitating de-bugging and maintenance of the database. On
the down side, value-based primary keys can be difficult to change. A change to
a primary key may require propagation to many foreign keys. Some objects do not
have natural real-world identifiers.
We recommend that you use existence-based identity for RDBMS applications with
more than 30 classes. Both existence-based and value-based identities are viable
options for all RDBMS applications.

now as we are planning to use developer at front-end, I have checked myself this
concept in relation with developer, I have found one thing that it makes our
'Form' a little bit heavy and a bit more complex logic is required, but I could
not found any other problem. so I want some expert's openion that if there could
be some other problems while using Developer with this existence-based key, or
is it recommended?

thanks
Nasir

      and we said...
Oh -- you mean "surrogate keys".  I've never heard anyone call this "existence
based key development" or existence base identity (and I've been doing this
database stuff for quite a while).

Surrogate keys are great, we use them all of the time.  Very natural, very
normal, very easy to do.  The tools work well in that environment.  The entire
Oracle data dictionary for example is built using that methodology.

May 31, 2002 NABENDU -- Thanks for the question regarding
      "User database transfer", version 8.1.7

      You Asked
Dear Tom,
Please tell me the steps involved in user database transfer from
a)  Oracle 8i  to  9i
b)  Oracle 8i  to  7  (I know how to transfer from 8i to 8. so,
you can tell me how to do either  from  8  to  7  or straightway from
8i to 7 if possible).
Waiting for an urgent feedback.

Regards,
NKG

Further clarification : "User database transfer" is nothing but application
database migration for a User from one version to another.
I mean data migration for a User which is normally done to transfer data from
one platform to another. I hope I could clarify the question.   Thanks.---- NKG

      and we said...
Oh, that is easy -- you only need to read:

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96530/toc.htm
The Oracle9i Database Migration guide.

It covers this for all releases of Oracle from 7.3.4 on up.  The mandatory steps
are all documented in there.
May 31,
      2002 Leo -- Thanks for the question
      regarding "Data Trigger that updates another table in another tablespace",
      version 9.0.1

      You Asked
Hi Tom,

Is there any trick that need to set to allow a database trigger to update
another table in another tablespace?

I have 2 applications (database are of 2 different tablespace and users) that
are only co-related by one table. What I would like to do is -- when a new entry
is inserted in tbs1.table1, it will be triggered to insert a row in tbs2.table2
as well.

Thank you!

      and we said...
There is no "trick", you just need the right privileges.

the schema TBS1 will need INSERT on TBS2.TABLE2 granted *directly* to them (not
via a role).  See

http://asktom.oracle.com/~tkyte/Misc/RolesAndProcedures.html
but beyond that, no tricks necessary.  (the tablespace by the way has nothing to
do with this.  That is pure "storage" and never affects privileges or your
ability to do something -- or not -- to an object.  I think you mean SCHEMA, not
TABLESPACE)
May 31, 2002 Dante -- Thanks for the question regarding "INDEX
      SKIP SCANS", version 9.0.1

      You Asked
How do i go about Oracle 9i Index Feature such as INDEX SKIP SCANS. I want to
use the features but it seems I can't find a way to use and enable it.

Thank you in advance.

      and we said...
read

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1156161921465
all you need to do to enable is:

a) install Oracle
b) use the CBO
c) have an index on a table where it makes sense to use it, the optimizer will
do the rest.
May 31, 2002 Ivan -- Thanks for the question regarding "Loader
      Performance Optimization", version 8.1.7

      You Asked
Presently we use following solution for load:
1. sql*loader loads flat files into Input Layer tables (ITL).
2. Immediately after, stored procedures are run to inspect data, transform and
insert/update records in Storage Layer Tables (STL)
During inspection, records with errors in number and date fields are recognized
and skipped, with appropriate massages in our log table.

In near future we have to set up huge DW in which we expect at list 300 GB
increase per year. Every day we will be supplied with amount of about 300.000
records via flat files.
Questions:
1.  Because, time is crucial for us, and for so massive amount of records, we
expect very bad time response from mentioned solution, do you have any idea how
we could resolve this bottleneck?
2.  I know that commit is stress for system but how long we could postpone
commit on so big amount of records.
3.  Can I somehow use (afterward mentioned) user-defined function in WHAN clause
of control file e.g.  WHEN conv_func(field1)=1 and conv_func(field3)=1 . . .

I supply you with few thoughts from our side. Please comment.

For your Information:
The today's loaders work in this way:
LOOP
      read next record from ILT into a structure SLT
      copy each field from SILT to a structure matching the SLT (let's say SSLT)
      INSERT SSLT, try to UPDATE in case of an error
      report the record, if an EXCEPTION is thrown while conversion or
insert/update
END LOOP
Commit;

To improve the loader performance, we considered loading in the following way:

INSERT INTO SLT ( field1, field2,...., fieldN)
SELECT   convert(field1), convert(field2),...., convert(fieldN)
FROM ILT;
COMMIT;

Doing this, in case of a field conversion, error the entire statement fails,
that is, NO data are transferred from ILT to STL because of , may be, one single
non-numeric data in a TO_NUMBER() conversion.
Additionally, we cannot get information of the buggy record/field to report it.

Also, if some records of the ILT might already exist in SLT, which causes an
error during the INSERT. So we have somehow to identify these records and mark
them in the ILT as "already exist". Afterward these records are updated, while
the non-existing records are copied using the INSERT/SELECT algorithm above.

Here is one idea with user-defined function:
INSERT INTO SLT ( field1, field2,...., fieldN)
SELECT  Convert(field1), convert(field2),...., convert(fieldN)
FROM ILT
WHERE   conv_func(field1, field2,....,  fieldN)= 1 ;
COMMIT;

Conv_funct is user-defined function (pl/sql), which returns 1, if conversion is
possible for record. Even more with this function could be used as bitmap
indexed on ILT (only values 0 ,1), and after insert of correct records is
finished, in additional pl/sql procedure could process incorrect records (with
function value 0) to precisely recognizes problems.
To be more exact:
When we search:
      select * from ILT where conv_func(fild1, fild2 …) = 1;
that query will never actually call the the function, rather it will take the
value 1 and goto the index and find the hits.
The bottleneck is pushed in direction of index  that’s mean during the SQLLoader
insert  in ILT, table will be also indexed from side of this function (with 1 –
ok , 0 – not ok).
Has this sense ?

Any idea appreciated

Thanks in advance

Ivan

      and we said...
In 9i there is a feature called the external table.

It lets you query directly from a flat file using sql.

So, you can take a file you would normally sqlldr and just "query it".  If you
define fields as numbers and dates in the CREATE TABLE organization external
statment -- they will automatically be logged to a BAD file for you (you need
not inspect them)

So, you can use the CREATE TABLE statement for the external statement to
"cleanse" your data (logging bad rows into the BAD file, along with an error
message in a LOG FILE).

Using another new feature, the MERGE command, you can merge this file with an
existing table -- letting us UPDATE existing rows when some criteria is matched,
INSERTing new when when it is not matched.

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1615330671789
for an example.  Additionally -- you can use a pipelined function if you need
some procedural conversions that are more sophisticated then a simple to_number
or to_date (which the LOADER should be doing)

Using a pipelined function we can:

flatfile -> external table (bad records are logged)
              -> plsql function to further cleanse each record
                    -> directly into the table in APPEND (direct path) mode

So, we can do it very efficiently in 9i.  Search for pipelined on this site for
examples.

I looked at your approach.  I believe you'll find it to not work so fast --
calling all of those plsql functions from SQL will have some overhead.  I would
give 9i some serious consideration here.  The new tools for doing this are
phenomenal.

Review &
      Followup

      Rating:
      5
Lucid  May 31, 2002
Reviewer:  Ivan
      Korac  from Ulm, Germany
Sounds to me: if you have some staff to cargo don’t use bicycle, use truck.

Please clarify:
When we migrate from dedicated server 8.1.7 to 9i, does it means that we have to
use OPS (RAC) by default. We do not expect any serious number of users, but is
OPS out of question, because of so huge amount of data ?

Regards

Ivan Korac

          Followup:

RAC is an option to the database, you use it only if and when you want to.  So
you do not have to use RAC, no.

May 31, 2002 Herbjorn -- Thanks for the question regarding
      "Passing resultsets from advanced searches to an application", version
      8i.X.X

      You Asked
Some customers would like to handle resultsets from database queries in their
applications using some sort of data objects. This is all fine, but when the
searches are very complex they would want to have a procedure to manage this.
But a PL*SQL procedure can not return a resultset, not even handle more than one
row of data without using a cursor.

I have a solution that could work. One could have a procedure that creates an
SQL string that later is executed in the database. The reason I do not like this
strategy is that the making of the SQL string might require some other database
operations, such as making queries to other tables or checking some criterias
that will make the outcome very different. So, if the procedure that creates the
SQL string is located in the application there is a risk that several database
calls are needed -- just to make the string... On the other hand if a PL*SQL
procedure makes the string then at least two database calls are necessary...

Do you have any strategies for implementing this kind of advanced searches that
requires less database calls, or that probably will work faster than my idea?

Thank you Tom for taking time to answer to many questions!

      and we said...
You've been reading too much SqlServer propaganda and not enough of the PLSQL
docs ;)

http://asktom.oracle.com/~tkyte/ResultSets/index.html
Both the statement:
a) plsql cannot return a resultset
b) not evenhandle more then one row of data without using a cursor

are 100% false.

Read about ref cursors and native dynamic sql in the PLSQL guides.  This is
*trivial*.

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1288401763279
for a sophisticated example

May 31, 2002 Allan -- Thanks for the question regarding
      "SQLCODE values", version 8.1.7

      You Asked
Hi Tom,
Our project is in the process of converting our Informix ESQL/C programs into
Pro*C applications.  As part of our testing the new code we have encountered
situations where following a database call (an insert in the case I have most
recently seen), we check the value of sqlca.sqlcode for being < 0 to signify the
call failed.  We are puzzled as to how we can have a situation where the sqlcode
value is 0, but the sqlca.sqlerrd[2] value is also 0 (no rows inserted).  We
have since discovered there was a flaw in the insert statement, but what I don't
understand is why such a flaw would not have generated an error.  Have we messed
up our precompiler options?

      and we said...
No, it is not an error to execute an insert (or update or delete) statment and
affect zero rows.  It is "successful" -- an insert need not insert ANY data in
order to succeed.

For example:

insert into t select * from t2 where 1=0;

that statement is 100% correct (if t and t2 have the same stuctures) but it'll
never insert any data.

It won't generate an error.
May 31,
      2002 OP -- Thanks for the question
      regarding "Fuction to replace a STRING !", version 8.1.6

      You Asked
Hi Tom,

suppose I have a string

"S1 should replace S2 in turn should replace S3"

and this string is stored in the column DATA of T1 table.

STRUCTURE OF T1 TABLE :-

T1

C1 number;
DATA VARCHAR2(2000);

Ques)  Is it possible to write a SELECT statement where
S1 can be replaced by A ,S2 by B and S3 by C .

Basically I am looking for a FUNCTION which can replace
a string by another,just like REPLACE is used to replace
a character.

Thanks and Regards
OP Lalwani

      and we said...
replace works on STRINGS (translate works character by character)

ops$tkyte@ORA8I.WORLD> select replace(
  2           replace(
  3  replace( 'S1 can be replaced by A ,S2 by B and S3 by C', 'S1', 'A' ),
  4               'S2', 'B'),
  5               'S3', 'C' ) from dual
  6  /

REPLACE(REPLACE(REPLACE('S1CANBEREPLACEDB
-----------------------------------------
A can be replaced by A ,B by B and C by C

ops$tkyte@ORA8I.WORLD>
Review & Followup

      Rating: 5
Great
      usage  February 07, 2002
Reviewer:  Peter
      Hung  from Vancouver, Canada
This helps a lot to my script.

      Rating:
      5
  May 31, 2002
Reviewer:
      A reader
Hi Tom,

i am passing country like 'usa;canada'

i want to get all state from this two country.
and i have table LISTING_STATE

column
state,state_no and country
ie
  NJ  10 US
  ONT 11 CANADA   etc..

Query
SELECT STATE,State_No  FROM LISTING_STATE WHERE UPPER(COUNTRY) in ('usa;canada')

should return all usa and canada state

 I am doing

SELECT STATE,State_No  FROM LISTING_STATE WHERE UPPER(COUNTRY) in
replace(upper('(''usa;can'')'),';',''',''')

But it is not working.

How can i achive that?

Thanks

          Followup:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:110612348061

      Rating: 5
Wow that was
      awesome  May 31, 2002
Reviewer:  reader

May 31, 2002 Ramasubbu -- Thanks for the question regarding
      "How to find out the number of rows processed for an active SQL of active
      session?.", version Oracle8i Enterprise Edition Release 8.1.6.3.0

      You Asked
Hi Tom,
    One of the cursor is often executing inside of stored procedure since it is
in the loop.I could see the sql text and rows processed from following query.

    select sql_text,rows_processed from v$sql
     where USERS_EXECUTING>0

  Actually what  is happening that rows_processed always accumlating the values
from previous session executed value.

For ex,
       I am executing the procedure generate_proc;

    devusr@oracle.WORLD> exec generate_proc;

    devusr@oracle.WORLD> select sql_text,rows_processed from v$sql
     where USERS_EXECUTING>0;

sql_text:
SELECT custic,balance,mnemonic  FROM BAL_table
 WHERE FILE_DATE = :b1  AND PROD BETWEEN 5
01 AND 600  AND
 ((LAST_MNT IS NULL ) OR (LAST_MNT BETWEEN :b2 AND :b1 )
  OR (NVL(EOMBAL,0) != 0 ))ORDER BY 2,1
Rows_processed:  1000

At end of generate_proc procedure it stored the rows_processed 1000 for the
above sql.

Logout the session.
devusr@oracle.WORLD> exit;

Aagain am login and executing the procedure

    devusr@oracle.WORLD> exec generate_proc;

    devusr@oracle.WORLD> select sql_text,rows_processed from v$sql
     where USERS_EXECUTING>0;

sql_text:
SELECT custic,balance,mnemonic  FROM BAL_table
 WHERE FILE_DATE = :b1  AND PROD BETWEEN 5
01 AND 600  AND
 ((LAST_MNT IS NULL ) OR (LAST_MNT BETWEEN :b2 AND :b1 )
  OR (NVL(EOMBAL,0) != 0 ))ORDER BY 2,1
Rows_processed:  1200

Here basically my expectation is rows_processed must be bewlow 1000. ie,200. But
it is accumulated with the previous session value.

 devusr@oracle.WORLD> select sql_text,rows_processed from v$sql
 where USERS_EXECUTING>0;

sql_text:
SELECT custic,balance,mnemonic  FROM BAL_table
 WHERE FILE_DATE = :b1  AND PROD BETWEEN 5
01 AND 600  AND
 ((LAST_MNT IS NULL ) OR (LAST_MNT BETWEEN :b2 AND :b1 )
  OR (NVL(EOMBAL,0) != 0 ))ORDER BY 2,1
Rows_processed:  1900

 devusr@oracle.WORLD> select sql_text,rows_processed from v$sql
 where USERS_EXECUTING>0;

sql_text:
SELECT custic,balance,mnemonic  FROM BAL_table
 WHERE FILE_DATE = :b1  AND PROD BETWEEN 5
01 AND 600  AND
 ((LAST_MNT IS NULL ) OR (LAST_MNT BETWEEN :b2 AND :b1 )
  OR (NVL(EOMBAL,0) != 0 ))ORDER BY 2,1
Rows_processed:  2000

Now procedure execution completed.

Now my question is
     How to find out the number of rows processed  for an active SQL of active
session?.

Thanx
muthu

      and we said...
v$sql is a view into the SHARED pool (not "your private pool").  The information
in there is by its very definition "shared".

You cannot get what you are after -- especially in a multi-user system.  We just
don't record the rowcounts for your individual queries.  The rows processed you
are seeing are an aggregate count not only across your many sessions but across
ALL sessions (all other user sessions).  If two people run your procedure at the
same time, they'll be adding counts to this v$sql entry as well.

If you are in a single user system -- just run the query before your procedure
and again after -- subtract and you'll have the new "total".  In a multi-user
system where others might be running that same query, this'll not work (perhaps
the stored procedure should be written with some dbms_output.put_lines to print
out relevant information?)  Additionally, you could enable sql_trace and use
tkprof
http://asktom.oracle.com/~tkyte/tkprof.html
to review the rows processed by all queries in the procedure for
your session.
Review & Followup

      Rating:
      4
DBMS_APPLICATION_INFO  May 31, 2002

Reviewer:  Suresh  from New York
Tom,
   Can't this be done using DBMS_APPLICATION_INFO package as per in your book
Page 1042? Thanks.

          Followup:

If you rewrite the code, adding this information for this query (like
dbms_output) yes.

If you have just 1 bit of data you want to convey in client_info, sure.  For
general purpose reporting -- dbms_output, sql_trace  will be the only way (and
sql_trace involves no extra work on your part, no changes)

      Rating:
      5
V$SQL  May 31, 2002
Reviewer:  Andre
       from Brazil
Would you briefly dscribe in your words these columns in V$SQL ?

- USERS_OPENING
- EXECUTIONS
- USER_EXECUTING
- PARSING_USER_ID
- PARSING_SCHEMA_ID

Are the latter two related to invoker's rights X definer's rights ?

          Followup:

users opening -- how many cursors are opened using it right now.

executions -- <quote>The number of executions that took place on this object
since it was brought into the library cache</quote> (the docs did a fine job on
that one)

users_executing <quote>The number of users executing the statement</quote>  Many
people might have open cursors to it -- but how many of those cursors are
executing RIGHT NOW, at this instant.

parsing user id -- what schema's authority was used to security check this
query.  Who parsed it (eg: if a definers rights procedure owned by SCOTT runs
"select * from emp" and the user GEORGE runs it, the parsing user id is SCOTT,
it was parsed using SCOTTS authority

parsing schema id -- what schema was used by default when parsing the query (eg:
what schema name would be stuck in to fully qualify object references that
didn't have a schema associated with them)

For the last two, an example:

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter system flush shared_pool;

System altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set current_schema=ops$tkyte;

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from emp where 1=0;

no rows selected

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set current_schema=scott;

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from emp where 1=0;

no rows selected

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select parsing_user_id, parsing_schema_id,
sql_text from v$sql
  2  where sql_text like 'select * from emp%';

PARSING_USER_ID PARSING_SCHEMA_ID
--------------- -----------------
SQL_TEXT
---------------------------------------------------------------------------------
--------------------------------------------------
            216               216
select * from emp where 1=0

            216               212
select * from emp where 1=0

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select user_id, username from dba_users where
username in ( user, 'SCOTT' );

   USER_ID USERNAME
---------- ------------------------------
       212 SCOTT
       216 OPS$TKYTE

Same query -- both were parsed by me (uid=216).  One was really executed as:

select * from OPS$TKYTE.emp where 1=0;  (parsing schema id = 216)

the other as:

select * from SCOTT.mp where 1=0;  (parsing schema id = 212)

      Rating: 5
Excellent
      !!!  May 31, 2002
Reviewer:  Andre
       from Brazil
Thanks again, Tom ! Great job !

May 31, 2002 Keith -- Thanks for the question regarding "what
      is the scope of pl/sql tables", version 8.1.7

      You Asked
I am working on some triggers to maintain summary information of parent records
in a self-referencing table.

To get around the mutating table problem I do the following:
1) Stage the parent record id in a pl/sql table within a package in a "for each
row" trigger.
2) In an "after statement" trigger call a procedure within the same package to
loop through and process the values in the pl/sql table.  This may cause an
additional parent record update to be triggered.  Only one level of this is
possible.

An initial problem with this is that the same record is getting processed
multiple times. And I get an
ORA-00036: maximum number of recursive SQL levels (50) exceeded

I have three workarounds
1) swap the pl/sql table into a local version, replace with an empty table, and
process the local version.
2) add a status column to the pl/sql table and logically omit records which have
been processed.
3) use a real table to stage the information and remove a row after processing.

Is one of these a better solution? Do either of the pl/sql table solutions have
issues if different records (in the same or different hierarchal branches) are
updated concurrently?

Last could you clairify the scope of pl/sql tables (global, transactional,
session,...)

      and we said...
Variables of type "PLSQL tables" have the same scope as any other variable in
PLSQL.

If you put them in a package outside of a procedure or function -- they "live"
for the life of the session.

If you put them in a procedure or function -- they "live" for the life of that
procedure or function.  Return from the procedure or function and they "go
away".

The problem you are getting, ora-00036, is not due to the same record getting
processed but rather more likely due to infinite recursion (the table is updated
-- you add a record to be processed into the plsql table, after the initial
update that fired you processes -- YOU yourself start updating the table which
in turn fires your trigger, adding more rows which are then processed in your
after trigger which in turn updates your table and so on and so on)

You need to avoid the recursion.  see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:469621337269
for example.
June 02, 2002 jamie -- Thanks for the question regarding
      "trigger insert clob field", version 9.0.1

      You Asked
Hi Tom,
Love your site I think it's great how you are willing to share your knowledge
and educate others.Anyway
I have a couple of questions and was hoping you could point me in the right
direction.First I am wanting to use
clob fields in our 9i database and reference them in triggers for an insert into
another table.According to the application developer guide fundamentals
on page 15-17 it says that you can do this.I wanted to do a small test so I
created a trigger and procedure as listed below
but the reference value in the trigger doesn't seem the have anything to insert
and I don't receive any error messages.
I have tried dbms_lob routine in the trigger to read the clob but still no
success.
Could you look at this and see where I am going wrong and possibly offer a
proper example.

Second can I use the utl_file package to read an ascii file find specific
portions of strings and modify them
and save the changes to the ascii file.Or is this something I need to do with
java.

Third I have been trying to run a query to select foreign key constraint
name,table it is on,column,
primary key table and column.Would you have any examples of this type of query

If I have listed too many questions could you please look at the first one
seeing it's the most important to me..
Any suggestions you have would be greatly appreciated.

Thanks ,
Jamie

select * from v$version;

BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.0.1.1.1 - Production
PL/SQL Release 9.0.1.1.1 - Production
CORE    9.0.1.1.1    Production

TNS for 32-bit Windows: Version 9.0.1.1.0 - Production
NLSRTL Version 9.0.1.1.1 - Production

create or replace PROCEDURE  COMMENT_ADD
(COMMENT_ IN CLOB)
AS
NEW_PKEY NUMBER;
NEW_COMMENT CLOB;

BEGIN

INSERT INTO TEMP_AUDIT(pkey,text,timestamp)
VALUES
(TMP_PKEY.NEXTVAL,EMPTY_CLOB(),SYSDATE)
RETURNING PKEY INTO NEW_PKEY;

SELECT TEXT INTO NEW_COMMENT FROM TEMP_AUDIT
WHERE PKEY = NEW_PKEY FOR UPDATE;

DBMS_LOB.COPY(NEW_COMMENT,COMMENT_,DBMS_LOB.GETLENGTH(COMMENT_));
COMMIT;

END;

CREATE OR REPLACE TRIGGER hist_insert
AFTER INSERT ON temp_audit
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE

BEGIN

  INSERT INTO HIST_AUDIT(pkey,text,timestamp)
  VALUES(HIST_PKEY.NEXTVAL,:new.text,SYSDATE);

END;
/

end result

jamie@J1.US.ORACLE.COM> col text format a20
jamie@J1.US.ORACLE.COM> select * from temp_audit;
pkey  text      timestamp
----- -------- ------------------
1      test     29-05-2002
2      test2    29-05-2002
3      test3    31-05-2002

jamie@J1.US.ORACLE.COM> select * from hist_audit;
pkey  text        timestamp
---- ------- -------------------
1             29-05-2002
2             29-05-2002
3             31-05-2002

jamie@J1.US.ORACLE.COM> spool off

      and we said...
Your trigger actually works -- WHEN it is fired!

The problem is -- you are NOT updating the table when you set the lob value.
You might be modifying the lob contents but -- and this is key -- you are NOT
modifying the lob locator in the table itself.  The row values of the table are
not changing, the trigger does NOT fire for the dbms_lob.copy (or write, or
trim, or append, or ...)

In short, you cannot fully achieve what you want via your method.  This shows
this works:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table temp_audit( pkey int, text clob,
timestamp date );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create sequence tmp_pkey;

Sequence created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table hist_audit( pkey int, text clob,
timestamp date );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create sequence hist_pkey;

Sequence created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> CREATE OR REPLACE TRIGGER hist_insert
  2  AFTER INSERT ON temp_audit
  3  REFERENCING NEW AS NEW OLD AS OLD
  4  FOR EACH ROW
  5  DECLARE
  6
  7  BEGIN
  8
  9    INSERT INTO HIST_AUDIT(pkey,text,timestamp)
 10    VALUES(HIST_PKEY.NEXTVAL,:new.text,SYSDATE);
 11
 12  END;
 13  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into temp_audit values (
tmp_pkey.nextval, 'hello world', sysdate );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from temp_audit;

      PKEY TEXT                 TIMESTAMP
---------- -------------------- ---------
         1 hello world          02-JUN-02

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from hist_audit;

      PKEY TEXT                 TIMESTAMP
---------- -------------------- ---------
         1 hello world          02-JUN-02

ops$tkyte@ORA817DEV.US.ORACLE.COM>

But it'll not work with the dbms_lob.copy as you have it coded -- when the
trigger fires, it gets an EMPTY_CLOB.  It does NOT get fired for the
dbms_lob.copy.

Your routine should just be:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace PROCEDURE
COMMENT_ADD(COMMENT_ IN CLOB)
  2  AS
  3  BEGIN
  4     INSERT INTO TEMP_AUDIT(pkey,text,timestamp)
  5     VALUES
  6     (TMP_PKEY.NEXTVAL,comment_,SYSDATE);
  7  END;
  8  /

Procedure created.

Anyway (terrible practice to commit in a procedure -- terrible)... In this case,
the trigger WILL get the value:

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          l_clob clob;
  3  begin
  4          dbms_lob.createTemporary( l_clob, true );
  5          dbms_lob.open( l_clob, dbms_lob.lob_readwrite );
  6
  7          dbms_lob.writeAppend( l_clob, length( 'this is some data' ), 'this
is some data' );
  8          comment_add( l_clob );
  9
 10          dbms_lob.close( l_clob );
 11  end;
 12  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from temp_audit;

      PKEY TEXT                 TIMESTAMP
---------- -------------------- ---------
         1 hello world          02-JUN-02
         2 this is some data    02-JUN-02

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from hist_audit;

      PKEY TEXT                 TIMESTAMP
---------- -------------------- ---------
         1 hello world          02-JUN-02
         2 this is some data    02-JUN-02

You just have to bear in mind that any "dbms_lob.write" type of activity will
NOT trigger the trigger -- ever.

June 02, 2002 Elumalai -- Thanks for the question regarding
      "SELECT QUERY", version 8.1.7

      You Asked
Hi Tom,
 Thanks for immediate reply for my previous query.
I had another doubt.,
I got two tables customer and supplier. Both are called as COMPANY.
In my application if the user select for company ,it should result both
customer_id and supplier_id as a single result,but both the table doesnt have
any relation so i am getting duplicates ie.,
CUST_ID    SUPPLIER_ID
---------- ---------
SC8010     7000011
4987654    7000011
4TSTAUR    7000011
4TSTAUR    7000011
4DFA013F   7000011
4DFA017F   7000011
4USD001F   7000011
4NIC001F   7000011
4DFA007F   7000011
4DFA012F   7000011

I need the result as .,

SC8010     -cust id
4987654    -cust id
4TSTAUR    -cust id
4TSTAUR    -cust id
4DFA013F   -cust id
7000011    -vendor_id
7000012    -vendor_id
7000013    -vendor_id

Could you please help on this.
Thanks in Advance
Sholan

      and we said...
That is a terrible design -- your system cannot easily answer the basic
questions you ask of it!

You need a new data model.

This will work but will be a horribly inefficient way to do it:

select distinct cust_id, 'cust-id' from t
union all
select distinct supplier_id, 'supp-id' from t;

June 02, 2002 Lon -- Thanks for the question regarding
      "Preserving the execution plan for a query on a table that varies
      greatly", version 8.1.7

      You Asked
Hi Tom,

We have a table that is queried quite often and the number of rows in this table
vary greatly. The number of rows can vary from a few hundred to hundreds of
thousands or rows.

The problem is that if the table is analyzed when there are only a few hundred
rows the optimizer does not use the indexes on the table. Since the table can
grow quite rapidly this causes full table scans of large amounts of data and
poor performance.

My question is:

Once we determine a performant execution plan for this table, can we use the
DBMS_STATS package to preserve the execution plan(s) for all subsequent queries
against this table no matter how many rows are in it?

Thanks,

Lon

      and we said...
Well, you won't use dbms_stats to "preserve the execution plans" (that would be
query plan stability, aka query outlines -- if you have my book -- there is a
full chapter on that).

If you want the optimizer to always think there are thousands of rows in the
table then either

a) analyze the table once when it has thousands of rows and never again.

b) use dbms_stats to force feed whatever stats you want to the optimizer

.
June 03, 2002 snebe -- Thanks for the question regarding "how
      to write to a array and send it back to jdbc", version 8.1.5

      You Asked
hi ,

i have attached the code,but not sure if this is the right thing to do,i have
return this to the jdbc call.
What i am trying to do is

for each record in the first cursor try to match something if matches then write
few fields from the first cursor and some from the second cursor to the type
table i have created and return the record set back.

help appreciated.

CREATE OR REPLACE PACKAGE asc
AS
type  result_matrix is record
(part_prefix   varchar2(20),--first cursor value should be written into this
field
part_suffix    varchar2(20),--first cursor value should be written into this
field
part_base      varchar2(20),--first cursor value should be written into this
field
feature_family varchar2(20),--second cursor value should be written into this
field
summ_element   varchar2(20),--second cursor value should be written into this
field
xory           varchar2(1));--based on whether matches or not i will set this
field to x or y
type matrix_rec is table of result_matrix index by binary_integer;

--type matrixarr is table of result_matrix;
procedure GET_matrix;
END asc;
/

CREATE OR REPLACE PACKAGE BODY asc
AS procedure GET_matrix is
matrix_rec1   matrix_rec;
BEGIN
matrix_rec1 := get_data();
END;

function get_data return matrix_rec
as

cursor part is (select a.ix_r ix,
          a.e_r se,
          a.x_r ix,
          b.e_f,
          b.t_c ,
          b.d_f  as es_ind,
          c.sc_x ec
   from wtable a,
          ctable b,
          dtable c
where a.k = 1
   and b.k(+) = a.k
   and c.k(+) = 1
   and c.y_c(+) = b.y_c
   and c.t_c(+) = b.t_c
UNION ALL
select a.x_r ix,
          a.e_r se,
          a.x_r ix,
          b.e_f,
          b.t_c ,
          'E' as es_ind,
          d.sc_x as ec
   from wtable a,
          ctable b,
         etable c,
          dtable d
where a.k = 1
   and b.k = a.k
   and b.d_f = 'S'
   and c.k(+) = 1
   and c.y_c(+) = b.y_c
   and c.t_c(+) = b.t_c
   and d.k(+) = 1
   and d.y_c(+) = c.y_c
   and d.t_c(+) = c.t_c
);
cursor feature is (select a.y_c ,
          a.c_x ,
          b.t_c ,
          'E'  as es_ind,
          b.c_x
from l_y a,
          dtable b
where a.k = 1
   and b.k = 1
   and b.y_c=a.y_c
UNION ALL
select a.svb019_feature_family_c feature_family,
          a.svb019_feature_family_desc_x family_desc,
          b.svb022_summary_c feature_code,
          'S'  as es_ind,
          b.c_x
from  atable a,
         btable b
where a.k = 1
   and b.k = 1
   and b.y_c=a.y_c
);

part_rec  part%ROWTYPE;
feature_rec feature%ROWTYPE;
matrix_tab matrix_rec;
i number;
begin
--dbms_output.put_line('inside the function');
FOR part_rec in part
  LOOP
   for feature_rec in feature
    loop
     if ((part_rec.feature_family=feature_rec.feature_family)
         and (part_rec.summ_element=feature_rec.feature_code)) then
       dbms_output.put_line (part_rec.feature_family);
       dbms_output.put_line (feature_rec.feature_code);
       matrix_tab(i).part_prefix:=part_rec.prefix;
     matrix_tab(i).part_prefix:=part_rec.prefix;
     matrix_tab(i).part_prefix:=part_rec.prefix;
      matrix_tab(i).summ_element:=feature_rec.feature_code;
      end if;
     i:=i+1;
    end loop;
  end loop;
return matrix_tab;
end;
end;

      and we said...
Couple of problems here:

o plsql table types cannot be returned to java until version 817, you need to
use a collection type prior to that

o You seem to be doing a join the really really *hard* way.  You loop over PART
and then for each row you find, you loop over EVERY record in FEATURE and find
"matches" -- this is what the DATABASE is supposed to do.

o you increment i for each FEATURE/PART but you only add records when you have a
FEATURE that matches.  If the first 5 part records don't have any matches and
you had 10 features, the first fifty array slots would be empty.

Now, the way I would do this is via a REF CURSOR (will goto a jdbc result set,
see

http://osi.oracle.com/~tkyte/ResultSets/index.html
for examples).

You will basically have:

create or replace package asc
as
   type rc is ref cursor;
   procedure get_matrix(  p_cursor IN OUT rc );
end;
/

create or replace package body asc
as

procedure get_matrix( p_cursor in out rc )
is
begin
   open p_cursor for
      select ...
        from ( part query from above goes here ) part,
             ( feature query from above goes here ) feature
       where part.feature_family = feature.feature_family
         and part.summ_element = feature.feature_code;
end;
end;
/

Review &
      Followup

      Rating: 3
how to pass
      array  August 21, 2001
Reviewer:  raji96
       from mi,u.s..
It was really good.

      Rating:
      5
Scalar?  May 31, 2002
Reviewer:
      A reader
Tom,
Is it possible to pass the pl/sql table of records to JAVA in 8.1.7 or should it
still be array SCALAR datatypes?
Regards,

          Followup:

You can pass a COLLECTION of OBJECTS

create or replace type myScalartype as object ( x int, y date, z varchar2 )
/
create or replace type myCollectionType as table of myScalarType
/

We can pass myCollectionType from Java to Plsql, Plsql to Java, whatever.

Tables of records -- (plsql records) no.

Tables of simple scalar types -- with the jdbc THICK 817 and up (oci) drivers --
yes (but I would suggest a collection type even here as they are supported with
the earlier drivers as well)

      Rating:
      5
Thanks  June 03, 2002
Reviewer:
      A reader
Dear Tom,
IF we have the third party PL/SQL APIs that provide arrays of record types, how
best to use them in JAVA? Should we write wrapper functions to change record
types to objects?
Regards,

          Followup:

You will *have* to write a wrapper function.  That is the right approach.

      Rating:
      5
Thanks  June 03, 2002
Reviewer:
      A reader
Thanks Tom.

June 03, 2002 Raman -- Thanks for the question regarding
      "Archive Log Cleaning", version 8.1.6

      You Asked
Hi Tom,

List of questions awaiting for you for a long time :

A)
Our production DB is running in Archive Log mode. Now, I want clean all archived
log from hard disk as its consuming lot of space. I don't want to keep the
archived log at any other place. What should I do :

1. Shutdown DB, cold backup and delete archived log. What kind of backup should
I take : RMAN, just copy OS files.

2. Don't shutdown DB, Switch logfile, move archive log and delete later, full
hot backup with RMAN and delete all archive logs.

and any other good strategy to save space (don't want to store archived logs for
future) you would like to suggest ?

B) How can I calculate the size of data stored in a LOB(CLOB and BLOB) column.
As its limit is 4GB in 816, but I need to know the actual size of contents.

C) How to calculate memory size consumed by a session.

D) My shared pool stat :

Obj mem: 109217843 bytes
Shared sql: 106493319 bytes
Cursors: 147250 bytes
Free memory: 7091468 bytes 6.76MB
Shared pool utilization (total): 247.03MB
Shared pool allocation (actual): 128M
Percentage Utilized: 202%

What should I do to tune the shared pool in MTS and Non MTS , both in archived
log.

Regards,
Raman.

      and we said...
You need to keep all of the archives for all of the backups you have.  Hopefully
you already now that you need AT LEAST two backups at all times.  You need all
of the archives available on tape or some other machine (remember -- it makes no
sense to keep the archives on the machine with the database, if the database
machine goes away -- so go your archives, you must get them off of there and
somewhere else).

So, that will dictate how many archives you need to keep and remember to keep
them elsewhere.

Do not shutdown the database. Do not do cold backups.  Read the backup and
recovery guide from cover to cover.  Keep at LEAST two full backups and all of
the archives from the time of the oldest backup.  Keep these archives OFF of the
database machine (on tape, on another machine -- have redundant copies).  Don't
skimp on backups and storage or you will lose data (not might, will)

b) a lob is stored in chunks.  When you create a lob column, you can specify the
chunksize.  If you use a chunksize of say 32k -- then every lob will be some
multiple of 32k in size.  You can see how large a given lob is with
dbms_lob.getlength.  Divide that by the chunksize -- round up -- that'll tell
you about how many chunks it takes.

A single chunk is used by ONE lob -- so if your chunksize is 32k and you put 33k
in there -- it'll consume 64k of storage for that row.

c) use v$sesstat joined to v$statname and look for values concerning "memory"
(where name like '%memory%';

d) recommend my book -- to get the details on process architecture, memory usage
and such.

You should use a LARGE_POOL with MTS.  Never never never use the shared pool for
MTS.  Your shared pool should probably be in the 80/100meg range and your
large_pool should be large enough to hold your UGA space.
Review &
      Followup

      Rating: 5
archive log
      cleaning  May 30, 2002
Reviewer:  ab
       from sargodha

      Rating: 4
Still a little
      puzzled  May 30, 2002
Reviewer:  Colin
      Davies  from Pasadena, CA
Why do you say "do not do cold backups"?

          Followup:

why shutdown just to backup?  It is not any harder really to do it hot and it is
not ANY easier to restore from cold then hot (not any easier at all, the restore
is identical)

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2876092892845
for another reason.  Most people try to do "unattended shutdown" and startups.
That is a recipe for disaster.  Leave it hot and it never goes down.

      Rating: 4
Hot Backups :
      How To ?  May 31, 2002
Reviewer:  Raman
      Pujani  from India
Hi Tom,

Thanks for the nice reply. I am archiving on the same machine, but different
Hard Drive. But as you adviced, I will keep copy of the archived log on tape and
keep on cleaning from hard drive.

A)
Now, System is running in archivelog, how to take full hot backup of DB, RMAN or
any other way ?
B)
Will this backup can be used, if in future the system crashes (media failure)
and I need to recover using THIS full backup and application of all archived
logs ?
C)
You said, I should use LARGE_POOL in MTS mode, how much should I allocate for
the same? I have allocated 128M to SHARED_POOL out of 512M of my machine. Should
I decrease it. Right now, my % of shared_pool used is 258M(200%, come to know
using some scripts provided at METALINK, told you in this thread previously).
D)
Will I achieve anything by shifting the server in dedicated mode from MTS as our
application already using connection pool ? If yes, What will be proper ratio
for SHARED_POOL and LARGE_POOL for the above scenario(refer to question C) ?

Thanks and Regards,
Raman.

          Followup:

a) i would recommend RMAN as there are constantly new features only available
with RMAN (eg: block level recovery in 9i for example -- the ability to recover
an individual corrupt block instead of an entire file)

b) yes

c) you need to have a large pool that is big enough to hold all of  your uga's

d) if your app uses a connection pool, using MTS is somewhat redundant -- sort
of like buffering a buffer.  I would tend towards dedicated server configuration
when using a middle tier connection pool.

      Rating: 4
Other than
      RMAN  June 03, 2002
Reviewer:  Raman
       from India.
Hi Tom,

Thanks, but little bit confusion :

Use of RMAN is good, but needs another DB server.
If I want to make hot backups, then I will be taking tablespaces in backup mode
and copying data files one by one. My confusion is, do I need to put all the
tablespaces in backup together or one by one. If one by one, then will it be a
consistent backup, I mean to say that one tablespace was backed up 10 minutes
back and other one is just 1 minute back. How will this backup would be used in
recovery as they are not taken a point-in-time(together) ? How this machenism
works ?

And regarding Checkpoing not completed problem, Can we just modify
checkpoint_timeout parameter instead of adding another log file ? Increase or
Decrease ? Will this help ?

Please let me know some URL regarding calculation of UGA to setup LARGE_POOL.

Thanks and Regards,
Raman.

          Followup:

a) use of RMAN is good but needs another DB server

that statement is false, to make 100% use of all rman features, you need a
catalog stored in a database, this is true.  However, rman works without a
catalog database as well (stores the data it needs in the control files)

b) if you use rman, you will NOT be placing the tablespaces into backup mode.
It is not necessary, not done.

If you are doing it by hand, you typically
   loop over tablespaces
      put tablespace N into backup mode
      copy files
      put tablespace N out of backup mode
   end loop

ALL HOT BACKUPS ARE INCONSISTENT BACKUPS (FUZZY) -- regardless of whether you
put all tablespaces into backup mode at the same time or not.  The datafiles are
continously written to during a hot backup, writes are not suspended -- a single
datafile is inconsistent with ITSELF even.

You can play with the other parameters however, you'll almost certainly need to
add more redo logs.

To calculate your UGA needs, just use v$sysstat to see what the average, min and
max UGA usage actually is.  There is no formula, it is based on what features
you use, what you actually do.

June 03, 2002 Ansuman -- Thanks for the question regarding
      "Uploading of data from a flat file", version 8.1.7

      You Asked
Hi,
   We've a requirement to upload huge files into the database a no of times in
our system. This is because we receive flat files from various partners and
cannot change this process right now. I'd like to know the possible ways of
doing this. I've used SQL LOAD and have read all the previous question posted on
this. As per my understanding SQL Loader will create insert statements for every
record read from the flat file and then insert the data into the database. Is
there any other way that a bulk upload of the data can be done without creating
all these insert statements as I really don't need them. From a performance
perspective what is the best method of achieving this requirement. Please note
that we might have to do some data integrity check but I think that I can do
after uploading the data.

Thanks
Ansuman

      and we said...
The insert statements that sqlldr generates are of the form:

insert into table_name ( column_name1, column_name2, ... column_nameN )
values ( :bv1, :bv2, :bv3, ... :bvN );

meaning

a) they are consistently generated
b) they use bind variables
c) they use the shared pool
d) they are more efficient then anything you could write yourself.

sqlldr is the best path for your needs.  In 9i, you might use an external table
(which is sqlldr in disguise)

Review &
      Followup

      Rating: 3
Are you using
      DIRECT path?  June 03, 2002
Reviewer:  Kenneth
      CHIK  from HK
In SQL*Loader you can choose CONVENTIONAL path or DIRECT path.  DIRECT path
works faster.

          Followup:

The statement "DIRECT path works faster" is not accurate.  direct path is
different from, direct path can be very (blindingly fast) but direct path is not
always faster (try it with 1 row and see for example).  Direct path implies many
things (things YOU MUST be aware of) besides "speed".

Direct path doesn't use SQL to process the data, hence, I would be using the
conventional path loader in this example -- to show that sqlldr won't "flood"
the shared pool with lots of unique sql as the original questioner implied.

      Rating: 4
Uploading of
      data from a flat file  June 03, 2002
Reviewer:
      Ansuman  from Milwaukee, WI USA
thanks a lot...I'll use the sqlldr path.

June 03, 2002 Eliot -- Thanks for the question regarding
      "Follow up question to "RAW datatypes and CRC32 "", version 9.0.1

      You Asked
I read in your book that dbms_utility.get_hash_value was subject to change
between versions, so I have been looking for a PL/SQL CRC function.  In your
answer to the "RAW datatypes and CRC32" question, you mentioned the MD5
procedure in dbms_obfuscation_toolkit. I have tried this, but it appears to
return an encrypted version of the checksum, and I cannot use this for
comparisons.  Would you have any other suggestions for what I could use to
produce a CRC from PL/SQL, or some way to make MD5 usable?
Thank you.
(Please do not use my name online.)
--
Here's some follow-up:  I saw that the package has procedures for raw and
varchar, and I was using the one for varchar.  The sort of values I got were
gibberish -- such as ¿¿aL§¶ ñHìFúHÙV¿

I contacted Oracle support and they told me the checksum is encrypted.  It
doesn't appear that I can store this checksum value and later use the same
procedure on the same data to match to the stored value, so it doesn't appear to
be useful for my purpose.  Any help would be appreciated.
Thank you for accepting my question.

      and we said...
It is not encrypted.  It is a checksum -- you should use the binary API's --
they are much safer.  The value you have in the varchar2 is just a binary value.
 It is NOT encrypted (i've no idea what put that thought into their head).  It
is "unsafe" in a varchar2 if the client has a different NLS_LANG from the server
(character set translation will kick in).  For that reason, I recommend using a
RAW only.

Use utl_raw.cast_to_raw to convert the string you want to checksum into a raw.
Then checksum that raw -- getting a raw back.  Save that raw checksum.  Later,
when you want to "compare them", simply checksum the raw string again and
compare the 16 byte raw checksum you just got to the 16 byte raw checksum you
stored.
June 03, 2002 Emily -- Thanks for the question regarding "Unix
      Back NT Restore", version 8.1.7

      You Asked
Is it possible to restore to NT from a Unix backup?  How about export Unix,
import NT?

What do you reccomend for a backup strategy?  Only warm backups?  Weekly cold
backups?

      and we said...
You cannot take a binary copy of the datafiles from Unix and restore them to NT.
 Word sizes, byte order and so on will be different.

You can use export/import.

We do nightly hot backups on my systems, keeping the last weeks worth of backups
available at all times.  I never do cold backups of a database in ARCHIVELOG
mode, that is a bad idea.  I run all but test databases in ARCHIVELOG mode and
test databases are generally easier to rebuild then to restore (so we tend not
to back them up -- or if we do it is a cold backup once a week or so).

I also do a full export once or twice a week, mostly to test the integrity of
the datafiles/data dictionary more then anything else.
June 03, 2002 Venkatasubramanian -- Thanks for the question
      regarding "migration, snapshot", version 8.1.7

      You Asked
Dear Tom,

Two questions .......

1. When we migrate from oracle 7.3 to 8i, are there issues related to rowid? If
so, do they  need to be addressed seperately? or the mig utility will take care
of this?

2. How to create a snapshot for the master table containing duplicate rows? When
we try the following way spcifying the schema name, still we get the error ORA-
12014. How to get over?

Example:

admin@CBOASIS817>create snapshot s2
  2  as select * from sitadmin.test@dblink1;
as select * from sitadmin.test@dblink1
          *
ERROR at line 2:
ORA-12014: table 'TEST' does not contain a primary key constraint

-sitadmin is the schema in remote database
-dblink name is dblink1
-test is the tablename in remote database with duplicate rows.

Thanks and Regards,
Venkat

      and we said...
1) you will want to read

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a86632/toc.htm
the rowid issues will be transparent to you UNLESS your application makes use of
rowids itself (looks at the value).  In 7.x, the rowid was "parsable" -- you
could find the block/file#/slot on block easily.  In 8.x and up, you need to use
the dbms_rowid package instead.

2)

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t  ( x int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create snapshot t_snap as select * from t;
create snapshot t_snap as select * from t
                                        *
ERROR at line 1:
ORA-12014: table 'T' does not contain a primary key constraint

ops$tkyte@ORA817DEV.US.ORACLE.COM> create snapshot t_snap refresh with rowid
as select * from t;

Materialized view created.

Tell it to use the rowid instead.
Review &
      Followup

      Rating:
      4
snapshot  June 04, 2002
Reviewer:
      Venkatasubramanian  from TN, India
Tom,

Thank you very much...

Regards
Venkat

June 03, 2002 -- Thanks for the question regarding "Index
      rebuild", version 8.1.7

      You Asked
Hi, Tom

Can you explain, why indexes degrade whith updates/inserts/deletes and how can I
control whether I need to rebuild my indexes.

Thanks in advance

Olaf

      and we said...
Well, you've come to the wrong guy.

I find that an index rebuild it a RARE thing to have to be done.
Others will swear up and down you need to do it constantly.

I believe that unless you can show ONE of the following:

a) a measurable significant increase in peformance
b) a measurable significant decrease in some resource usage (eg: less
   logical/physical IO)
c) a measurable, significant and LONG TERM savings in space

You are wasting your time.  (stress long term on that last one, most indexes --
after being rebuilt -- quick degenerate back to the way they were before the
rebuild, rebuilding them can actually negatively impact performance)

Some indexes, particularly bitmap indexes that are loaded into over time, will
benefit from a rebuild infinitely more then others.

Indexes that have have a high DEL_LF_ROWS as a percentage of over all rows may
benefit from a rebuild OR coalesce (use analyze index validate structure to
populate the index_stats view to gain this information).

In short, I spend zero time looking for indexes to rebuild.  When I discover a
performance issue, I will analyze it -- determine what the cause is -- and if it
was an index that needed to be rebuilt (again, bitmaps are the main culprit and
sometimes an index with lots of holes) -- we'll rebuild *and measure the
results*.  If we get no measurable, significant gain -- we'll never rebuild it
again -- else we might keep our eye on that one.

June 03, 2002 -- Thanks for the question regarding "Mutating
      trigger", version 901

      You Asked
Hi Tom
I have been thru the articles specified by you, but I dont know how to apply
them to my problem? I appreciate your patience with the length of this question

I have a table that accepts user defined columns for a set of base tables:
Example : Base Table "A"
id number, name varchar2(30)
"A" has a corresponding user defined table where users can add values that are
processed as physical "columns" of table A. Example:

"UDT"
Table_name varchar2(30), Column_Name varchar2(30), Data_type varchar2(80),
Data_Length number

Now a user can add the following data for this "UDT" table

A,price, number,2
A,SNum, number,4

I have a Procedure "GEN" that constructs a table "Auto_A"  with the column_names
price and snum .

desc AUTO_A

PRICE    NUMBER(2)
SNUM    NUMBER(2)

Now I want to automate the whole process by adding a trigger on the UDT table as
follows:

Create or Replace Trigger test
After Insert or Update on auto_a
Begin
GEN(:new.table_name);
end;

So when a user inserts a table_name value in the "UDT" table , this trigger gets
fired and executes the
"GEN" procedure that automatically creates the table "AUTO_A" using the name of
the new table_name inserted.

PROBLEM:

 insert into udt (table_name,Column_Name,Data_type)
 Values('cost','base','NUMBER');

ERROR at line 1:
ORA-04091: table XYZ.UDT is mutating, trigger/function may not see it
ORA-06512: at "XYZ.GEN", line 7
ORA-06512: at "XYZ.TEST", line 2
ORA-04088: error during execution of trigger 'XYZ.TEST'

How can I avoid this ? I went through your excellent article on avoiding
mutating problems but dont know how to apply to my problem.

Please let me know if I need to send the source code of the Gen procedure.

 Thanks

      and we said...
Ok, well doing DDL implies a COMMIT.  You cannot commit in a trigger.  Well, you
can but you must use an autonomous transaction.  Hence, if the create view
succeeds but the insert is rolled back -- you have a view that doesn't match
your data.

What you want to do is use dbms_job to SCHEDULE the view creation to take place
shortly AFTER the insert is COMMITTED.

Eg:

declare
   l_job number;
begin
   dbms_job.submit( l_job, 'GEN( ''' || :new.table_name || ''' );' );
end;

In that fashion, shortly after COMMITTING the insert, the view will be
recreated.  If you rollback the insert due to an error -- the job will be
"unqueued" as well and the create view won't happen.

You avoid the mutating table error since you do not attempt to touch it whilst
modifying it.

Make sure to set job_queue_processes/job_queue_interval!

June 03, 2002 alan -- Thanks for the question regarding "Create
      Table Nologging As Select * vs Insert /*+ append */ Into Table Select *",
      version 9.0.1

      You Asked
Hi Tom:

One of our fellow developer told as that it is faster to do a CREATE TABLE
NOLOGGING AS SELECT * FROM table_name than INSERT INTO table_name SELECT * FROM
table. Is this true? If Yes could you explain further why?

Is DIRECT LOAD, INSERT /*+ APPEND */ INTO table_name SELECT * FROM table is
equal/similar in terms of processing to a CREATE TABLE NOLOGGING AS SELECT *
FROM table_name. Please explain.

Thanks, and more power.

      and we said...
depends -- are you archivelog or noarchive log?

In noarchive log -- i would say *no*, no difference...  Consider this script:

drop table t;
drop table t2;
alter session set sql_trace=true;
create table T nologging as select * from all_objects;
create table t2 as select * from all_objects where 1=0;
insert /*+ append */ into t2 select * from all_objects;

in noarchivelog mode:

create table T nologging as select * from all_objects

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.04       0.04          0          0          0           0
Execute      1      5.90       6.46        493     140103         78       22433
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      5.94       6.50        493     140103         78       22433

insert /*+ append */ into t2 select * from all_objects

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.03       0.03          0          0          0           0
Execute      1      5.69       5.91        397     140097         56       22434
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      5.72       5.94        397     140097         56       22434

for all intents and purposes -- the SAME.

Now, in archivelog mode -- there might be somethere BUT -- they would be
comparing apples to toaster ovens.  The CTAS is done without logging -- but the
insert does logging.  You would alter the table to NOLOGGING before the
insert/append and then it would work just like the CTAS!  But even so -- on a
nicely tuned system - the insert append will be just as fast even though it logs
generally (as lgwr trickles the redo out during the insert over time)

I would ask them to prove it in a repeatable test case.


Subject | Article | Last Updated (East coast time)
execute immediate | article only | Jun 04, 2002 06:45 am
how to allow to drop only list of users | article only | Jun 04, 2002 06:48 am
Very strange Ora-1555. | article only | Jun 04, 2002 06:53 am
Why note set ORACLE_HOME under NT | article only | Jun 05, 2002 12:35 pm
SQLLDR and flat files VS Materialized Views | article only | Jun 05, 2002 01:23 pm
Backup -- do i have to do temp and rollback | article only | Jun 05, 2002 05:31 pm
OCI calls to oracle replace bind variables with literals before parsing | article only | Jun 05, 2002 05:52 pm
schema or new DB | article only | Jun 05, 2002 06:16 pm
Tuning the sql query | article only | Jun 06, 2002 10:58 am
Insert Performance | article only | Jun 06, 2002 10:59 am
Passing data from Java to PL/SQL | article only | Jun 06, 2002 04:27 pm
index use and rollback segment extends | article only | Jun 06, 2002 05:40 pm
Mized size of packages causes inefficiency? | article only | Jun 06, 2002 05:43 pm
Date Columns | article only | Jun 06, 2002 06:13 pm
Order Order ! | article only | Jun 07, 2002 11:27 am
optimizer_index_caching/optimizer_index_cost_adj | article only | Jun 08, 2002 05:07 pm
Windowing functions | article only | Jun 09, 2002 04:51 pm
row(s) 1 - 17 of 17
June 04, 2002 atul -- Thanks for the question regarding "execute immediate", version 8.0.4
You Asked
Sir,

1)How can i use execute immediate for oracle 8.0.4?

2)I am reading your book (Expert One on One)
where i find a script to know that am i using bind variables?

Should this be a strating point of any tuning excercise?
Can i impliment this script in 8.0.4?

3)Many times i find following is fast..(for table reorganisation)

1)export table say a (after deleting haif rows)
2)truncate table a
3)drop all the indexes from a including primary key(named primary key)
4)imp with indexes=n
5)recreate indexes other than pk as pk comes with imp..

This i find fast than doing without dropping indexes..
Is that ok?

Thanks.
atul
2)
and we said...
1) by upgrading it to 8i as that was the first release to support this new
feature called "native dynamic sql"

2) well, all code from the book is available on the wrox website.  This
particular script is here as well:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1163635055580
lower 100 to 10 in a test environment to find statements not using binds, yes,
it is part of my regular "tuning" to make sure the rules are being followed.

3) Did you ever keep statistics to show that re-orging the table achieved ANY of
the following:

a) a significant measurable increase in response time
b) a significant measurable decrease in some resource usage (eg: IO, physical or
logical)
c) a significent and LONG TERM reduction in space used?

If not, why did you reorg?  How do you justify the time, energy, downtime, and
expense of reorging to your manager without metrics like that?

I don't understand the comment:
This i find fast than doing without dropping indexes..

But anyway, I can reduce your multi-step process to fewer steps with the same
results:

exp
drop
imp
Review & Followup
Rating: 5
Table Re-org June 03, 2002
Reviewer: atul from India
Sir,

Thanks for your reply,

As per table re-org question..
Our production system has space constraints so i have to do that ..what i do i
keep the backup of deleted rows on tape and do exp/imp to get space only and not
speed..

again,i think if we delete haif rows from the table.
and drop and exp/imp will set high water mark much lower so i think we will get
good performance for select queries..

Is that ok?

On exp/imp.

say if a table has 5 indexes on them..
if we drop those indexes and do exp/imp.
imp will be faster because it will not have to write to indexes at the time of
import..

I have checked that..

Thanks.
atul

Followup:
Will you soon be reinserting new rows?  How long between rebuilds?  If it is
days or even just a few weeks -- not worth it (you aren't getting any long term
savings in space or speed).

As for

...
say if a table has 5 indexes on them..
if we drop those indexes and do exp/imp.
imp will be faster because it will not have to write to indexes at the time of
import..

I have checked that..
.....

of course imp would be faster -- but -- IS THE ENTIRE PROCESS faster?  You still
need to create those 5 indexes and now you are responsible for remembering to do
it.  If you just

exp/drop/imp

all of the bookeeping is done for you.  Unless you are building the indexes with
parallel/unrecoverable, just exp/drop/imp -- it'll take the same time. (think
about it -- you are doing no more, no less work in the end, you still need the 5
indexes)

June 04, 2002 Michal -- Thanks for the question regarding "how to allow to drop only list of users", version 8.1.7
You Asked
hi Tom,
I'm writing a DB part of an application.

The application includes user management - creating, altering, dropping, ...
Users are cretaed as DB users (not my own hardcoded user management). They are
also stored in table app_users(id, user_login, name, job_title, ...).

For adding new and dropping old users is responsible application administrator.
He has a granted roles create user, drop user.

How to allow him to drop only users, that are in table app_users ?

Thank You.
and we said...
Use a database BEFORE DROP trigger. Make sure to create this as some other user
(then the user doing the drop, else they can drop the trigger!)

ops$tkyte@ORA817DEV.US.ORACLE.COM> grant create session, create user, drop user
to a identified by a;

Grant succeeded.

ops$tkyte@ORA817DEV.US.ORACLE.COM> grant create session to b identified by b;
Grant succeeded.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table app_users ( username
varchar2(30) );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into app_users values ( 'B' );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger drop_user_trigger
  2  before drop on database
  3  when ( user = 'A' )
  4  declare
  5      l_cnt    number;
  6      l_name   varchar2(30);
  7  begin
  8      if ( ora_dict_obj_type = 'USER' )
  9      then
 10          l_name := ORA_DICT_OBJ_NAME;
 11          select count(*) into l_cnt
 12            from dual
 13           where exists ( select null
 14                            from app_users
 15                           where username = l_name );
 16          if ( l_cnt <> 1 )
 17          then
 18              raise_application_error( -20001, 'You cannot drop that user' );
 19          end if;
 20      end if;
 21  end;
 22  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> @connect a/a

a@ORA817DEV.US.ORACLE.COM> drop user scott;
drop user scott
*
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-20001: You cannot drop that user
ORA-06512: at line 15

a@ORA817DEV.US.ORACLE.COM> drop user b;

User dropped.

a@ORA817DEV.US.ORACLE.COM>
Review & Followup
Rating: 5
Excellant Answer June 03, 2002
Reviewer: A reader
This is a very good solution....

Rating: 4
How can I do the same for "Alter user" June 03, 2002
Reviewer: Anitha Madhur from CA,USA
Hi Tom,
         I have the same situation .I would like the application administer be
able to alter users ( lock ,unlock,password change) in the app_users table .
How can I do this ?

Thanks in advance,
Anitha

Followup:
You can check out the docs to see what sort of triggers are available (hey, if
there is a before drop, maybe there is a before ALTER, before CREATE -- hmmm)

See
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76939/adg14evt.htm#998000
program away....

Rating: 3
Package to add, drop, alter users June 04, 2002
Reviewer: Johan Snyman from JHB SA
Would it not be better to create a package with procedures for creating,
dropping and altering users. Create the package with definers rights (the
default) as a user with all the required privileges to create/alter users and
just grant the application administrator execute privilege on the package.

The drop procedure in the package can now check that only application users are
dropped, the create procedure can ensure that only the required privileges are
assigned to a new user and the alter procedure can be limited as required.

This just seem simpler than having to create several different triggers. Also,
in another post you indicated that alter user is a very powerful privilege which
can potentially open the entire database to the user.

Followup:
The trigger relieves the concern of opening the database up -- they can only
alter the set of users they are supposed to alter -- not users like SYS for
example.

The package would work just as well, very well.  It would require about the same
amount of code (3 procedures, 3 triggers).  I view it as 6 one way, 1/2 dozen
the other.  Sometimes it is nice to be able to do the DDL (create, alter, drop)
rather than call a procedure.

So yes, the package would be a very clean way to do this as well.  Now, you just
have to worry about the package OWNER having "alter user" ;)

June 04, 2002 kiril -- Thanks for the question regarding "Very strange Ora-1555.", version 8.1.6.1
You Asked
Hi Tom I am trying to execute :

declare
cursor cur is
select     /*+ USE_NL(t2 t1) */t1.art_grp_no,
    t1.amount+t1.disc_amount amnt,
    t2.invoice_date,
    t2.cust_no,
    t2.store_no
from invoice_line t1,invoice t2
where      t1.invoice_no=t2.invoice_no and
          t1.till_no = t2.till_no ;
retRow cur%ROWTYPE ;
cnt number := 0 ;
begin
  for retRow in cur loop
  begin
  cnt := cnt + 1 ;
    insert into TB_ART_GRP_CUST_STAT values(
    retRow.art_grp_no, retRow.amnt,
retRow.invoice_date,retRow.cust_no,retRow.store_no);
  exception
    when others then
    if sqlcode = -1 then
    update TB_ART_GRP_CUST_STAT set  TOTAL_SUM = TOTAL_SUM + retRow.amnt
    where     ART_GRP_NO = retRow.art_grp_no and
        INVOICE_DATE = retRow.invoice_date and
        CUST_NO = retRow.cust_no and
        STORE_NO = retRow.store_no ;
    end if ;
  end ;
  if mod(cnt , 1000) = 0 then
    commit ;
  end if ;
  end loop ;
end ;
/

And after while I have received 'ora-01555'
Why ???? If you can Please explain. I am not doing fetch across commit.
Can I find when and why I have received this error.
Some additional info:
During the execution of PL/SQL block there is no any DML or DDL on tables
Invoice and Invoice_line.
I am duing commit because there is no place on server for rollback info and disk
space for Temporary table is also very limited.

Info about Database parameters and tables:
oracle 8.1.6.1(dedicated ) in archivelog mode over 3 disks(separete indexes,data
and RBS and Temp). OS Win NT 4.0 SP 5 1Gb memory.
All DB is around 35 Gb.
There is Temporary tablespace 500Mb;
Trere are 7 rollback segment at same size 10mb (with optimal size);
RBS tablespace is 1Gb.
All tablespace are dictionary managed ( sorry but was migrated from 7.3).

sort_area_size = 50Mb.
db_block_buffers = 200mb.
shared_pool_size = 72914560.

Table Invoice_line has 12 range partitions (about 37 milion rows) and all
indexes on this table are local prefixed.
Table Invoice      has 12 range partitions (about 7 milion rows) and all indexes
on this table are local prefixed.
Table TB_ART_GRP_CUST_STAT has 4 range partitions (about 22 milion rows)

descr TB_ART_GRP_CUST_STAT
Name                            Null?    Type
------------------------------- -------- ----
ART_GRP_NO                      NOT NULL NUMBER(3)
TOTAL_SUM                                NUMBER(18,2)
INVOICE_DATE                    NOT NULL DATE
CUST_NO                         NOT NULL NUMBER(6)
STORE_NO                        NOT NULL NUMBER(2)

Trere is local PK using index on (cust_no,INVOICE_DATE,STORE_NO,ART_GRP_NO).

Thank in advance.
Kiro.
and we said...
You most certainly are doing a fetch across a commit.

you have:

  for x in cursor
  loop
      DML
      if (mod) then commit end if;
  end loop

you have a commit in your loop, you are fetching across that.  Here is an
example that shows what is happening to you:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:895410916429
there, we are reading from a READ ONLY tablespace and getting an ora-1555!  the
reason -- lots of small transactions wiping out transaction headers that we need
for read consistency.

Have you priced a 36gig drive recently?  About $200 usd.  You've probably spent
wwwwaaaayyyyy more then that trying to figure this out.  It would be more cost
effective to keep a closet full of these disks and just pull one out every time
you wanted to do this process and use the disk.  Just throw the disk away after
you run your update!  It would be cheaper.

You quite simply need to configure your system properly to do the jobs it
needs to do.  Period.

Let's take a further look at this code:

declare
cursor cur is
select     /*+ USE_NL(t2 t1) */t1.art_grp_no,
    t1.amount+t1.disc_amount amnt,
    t2.invoice_date,
    t2.cust_no,
    t2.store_no
from invoice_line t1,invoice t2
where      t1.invoice_no=t2.invoice_no and
          t1.till_no = t2.till_no ;
retRow cur%ROWTYPE ;
cnt number := 0 ;
begin
  for retRow in cur loop
  begin
  cnt := cnt + 1 ;
    insert into TB_ART_GRP_CUST_STAT values(
    retRow.art_grp_no, retRow.amnt,
retRow.invoice_date,retRow.cust_no,retRow.store_no);
  exception
    when others then
    if sqlcode = -1 then
    update TB_ART_GRP_CUST_STAT set  TOTAL_SUM = TOTAL_SUM + retRow.amnt
    where     ART_GRP_NO = retRow.art_grp_no and
        INVOICE_DATE = retRow.invoice_date and
        CUST_NO = retRow.cust_no and
        STORE_NO = retRow.store_no ;
    end if ;
  end ;
  if mod(cnt , 1000) = 0 then
    commit ;
  end if ;
  end loop ;
end ;
/

Hmm, couple of big problems I see:

1) it is not restartable.  When you hit the ora-1555 or ANY OTHER error -- what
then?  Well, you'll update the rows you just inserted adding the retRow.amnt in
erroneously.  Or, you'll update a row for the second time if it existed on the
first pass!  In other words  you will corrupt your data.  Even if you get
around the 1555 -- you have a TON more code to write to make this process safely
restartable -- so you process a row at least once and at most once (else the
integrity of your data -- GONZO)

2) you have a "WHEN OTHERS" that is not followed by a RAISE;  This is a bug --
100% of the time in my opinion.  You are looking for DUP_VAL_ON_INDEX (don't use
when others, use when dup_val_on_index and remove the sqlcode=-1 check).

What happens if the error is "out of space", "constraint xxxx violated", etc???
You silently ignore it.  Again, this would result in a total, utter and
complete loss of data integrity.

Bottom line -- in 8i this should be AT MOST two statements -- one to update
existing rows and one to insert the rows that did not exist yet.

In 9i, this will be ONE statement -- MERGE to do an update of existing and
insert of new.

In both cases -- you will configure sufficient disk space to do the work your
system needs to do.

Actually 3rd option.  If this table TB_ART_GRP_CUST_STAT is just a rollup of the
invoice and invoice_line table -- you should just:

create materialized view TB_ART_GRP_CUST_STAT
<mv options>
as
select t1.art_grp_no,
    t1.amount+t1.disc_amount amnt,
    t2.invoice_date,
    t2.cust_no,
    t2.store_no
from invoice_line t1,invoice t2
where      t1.invoice_no=t2.invoice_no and
          t1.till_no = t2.till_no ;

and refresh that as needed.  Let the system do the work for you (a full refresh
would be faster then update/insert every single row)
Review & Followup
Rating: 3
snapshot too old June 03, 2002
Reviewer: kiro from Bulgaria
Tom
Thanks for your response.
As I understood I hit ora-1555 for the reason of ROLLBACK TRANSACTION SLOT
OVERWRITTEN.

I think that TRANSACTION SLOT of "cursor Cur is..."
have been overwritten from some of subsequent transactions (insert ... ; update
... commit; ).

Am I right?
Please, make a short cooment.
Thanks
kiro.

Followup:
Yes, one of the rbs transaction slots for the segments:

from invoice_line t1,invoice t2

was overwritten by your frequent commits.  Again, this process should be:

a) one CTAS or materialized view if the table is rollup of the data

or

b) 2 sql statements followed by a commit afterwards (for speed, data integrity
and error free processing)

Rating: 4
transaction slot June 04, 2002
Reviewer: kiro from Bulgaria
Tom
Just to close picture how I can calculate number of transaction slots. Is there
any rule or it depends from size of rollback segments.
If you can make short comment it will be graet.
thanks

Followup:
If you have my book -- I go into this in some detail in chapter 5 on redo and
rollback.  Here is a list of things that must be true in order for this to
occur:

....
In order to receive the ORA-01555 from a delayed block cleanout, all of the
following conditions must be met:

o A modification is made and commits and the blocks are not cleaned out
automatically (for example, it modified more blocks then fit in 10% of the SGA
block buffer cache).

o Those blocks are not touched by another session and will not be touched until
our unfortunate query below hits it.

o A 'long running' query begins. This query will ultimately read some of those
blocks from above.  This query starts at SCN 't1'. That is the read consistent
SCN it must roll data back to in order to achieve read consistency. The
transaction entry for the modification transaction is still in the rollback
segment transaction table when we began.

o During the query, many commits are made in the system. These transactions do
not touch the blocks in question (if they did we wouldn’t have the impending
problem).

o The transaction tables in the rollback segments roll around and reuse slots
due to the high degree of commits. Most importantly, the transaction entry for
the original modification transaction is cycled over and reused.  In addition,
the system has reused rollback segment extents, so as to prevent a consistent
read on the rollback segment header block itself.

o Additionally, the lowest SCN recorded in the rollback segment now exceeds 't1'
 (it is higher than the read consistent SCN of the query) due to the large
amount of commits.
....................

so, it depends on the size of the rollback segments.  You should be aware that
in 9i, using undo tablespaces, you can set an undo retention period which makes
this a virtual non issue.

Rating: 4
June 04, 2002
Reviewer: kiro
thanks Tom
I am now reading your book.

Rating: 5
Reader June 05, 2002
Reviewer: A reader
From ixora.au.com

You should be able to calculate the number of slots for any database block size
and for any given version from V$TYPE_SIZE without dumping a header block. For
example for your 2K block size it goes like this:

      2048 bytes  db_block_size
    -   16 bytes  cache layer undo block header (KTUBH)
    -    4 bytes  cache layer tail
    -   72 bytes  extent control header (KTECH)
    -   44 bytes  extent map header (KTECT)
    -  960 bytes  extent table = extent entry * (maxextents - 1)
                  = 8 bytes (KTETB) * (db_block_size/16 - 8)
    -  104 bytes  transaction control header (KTUXC)
       =========
    =  848 bytes  for the transaction table
    /   40 bytes  per transaction table entry (KTUXE)
    =   21        transaction table slots!
        (8 bytes  unused)

June 05, 2002 Daryl -- Thanks for the question regarding "Why note set ORACLE_HOME under NT", version 8.1.x
You Asked
Metalink Note 77590.1 addresses the message "DRG-11101: failed to open file
\drgit3" that can occur when indexing binary documents with InterMedia under
Windows NT. As part of its explanation, they indicate that the lack of an
ORACLE_HOME environment variable setting under NT is the cause of the problem.
So the entire note explains how to set up a user-defined filter that invokes a
batch file which sets the ORACLE_HOME variable and then invokes ctxhx to convert
the binary document to plain text/html. The note says: " In NT, we DO NOT want
to set oracle_home in the NT environment. To get around this, we need to create
a user defined filter that incorporates the 'oracle_home' setting."

My question is: Why not? ORACLE_HOME is required to be set in
every other Oracle environment, so why not NT? I'm reluctant to set this up
because it complicates installation of my company's Oracle-based product at
customers. We would have to do something different based on the server operating
system, and it's hard enough adapting to all the variables at customer sites.

Thoughts?
and we said...
If you set an Oracle home on NT in the environment -- and you use multiple
oracle homes (eg: you using ias, oas, developer, etc etc) you will break those
other installations. There is only one environment these guys would share and
their oracle home would be set wrong.  NT != Unix.

It is only on platforms like Unix where using the environment is the technically
correct approach we do this (eg: on OS390 running MVS, there is no such thing as
an environment variable really).

Now, this bug to which you refer affected very very early releases of intermedia
under Oracle8i - it was fixed with patchset 8.1.5.1.  It does not exist in the
only supported release of Oracle8i -- 8.1.7.x -- hence it should not be an
issue.
Review & Followup
Rating: 4
Yes and no December 28, 2001
Reviewer: Daryl
Thanks for the reminder regarding environment variables under NT.

However, I'm not convinced that the issue has been fixed.

Oracle version (running under NT 4):
  Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production
  With the Partitioning option
  JServer Release 8.1.7.0.0 - Production

As CTXSYS:
  exec ctx_ddl.Drop_Preference( 'INSO_FILTER_PREF');
  exec ctx_ddl.Create_preference( 'INSO_FILTER_PREF', 'INSO_FILTER');

  exec ctx_ddl.Drop_preference('APP_INSO_FILTER_PREF');
  exec ctx_ddl.Create_preference('APP_INSO_FILTER_PREF', 'USER_FILTER');
  exec ctx_ddl.set_attribute('APP_INSO_FILTER_PREF','COMMAND','auto_filt.bat');

Contents of auto_filt.bat:
  set oracle_home=D:\oracle\ora81
  ctxhx.exe %1 %2 ascii8 iso8859-1

As user APP:
  SQL> create index app_index on app_binary ( file_attachment )
    2   indextype is ctxsys.context
    3   parameters ('storage CTXSYS.APP_SEARCH_STORE filter
         CTXSYS.INSO_FILTER_PREF datastore CTXSYS.APP_ATTACH_DS
          section group CTXSYS.HTML_SECTION_GROUP');

  Index created.

  SQL> select err_timestamp, err_textkey, err_text from ctx_user_index_errors;

  ERR_TIMESTAMP   ERR_TEXTKEY          ERR_TEXT
  --------------- -------------------- ------------------------------
  28-DEC-01       AAAgBtAATAAAA+CAAn   DRG-11101: failed to open file
                                        \drgit7

  SQL> drop index docfa_attach ;

  Index dropped.

  SQL> create index docfa_attach on doc_file_attachment ( file_attachment )
    2   indextype is ctxsys.context
    3   parameters ('storage CTXSYS.APP_SEARCH_STORE filter
         CTXSYS.APP_INSO_FILTER_PREF datastore CTXSYS.APP_ATTACH_DS
          section group CTXSYS.HTML_SECTION_GROUP');

  Index created.

  SQL> select err_timestamp, err_textkey, err_text from ctx_user_index_errors;

  no rows selected

The only difference between the two that I can see is the different filter
specification. Any other possibilities? Metalink isn't giving me much help on
this one.

Followup:
Please open a tar with support (also, this is a totally unpatched 817 instance -
you should apply the requisite patches).

Rating: 2
No patches for 8.1.7.0.0 June 05, 2002
Reviewer: Damien from Cambridge, MA USA
I am running 8.1.7.0.0. According to the patch matrix there are no patches for
this version of Oracle. Has there been any other resolutions.

Followup:
what do you mean -- there is 8171, 8172, 8173 and coming soon 8174 -- they are
all patch sets.

you should be 8173

Rating: 4
Another follow-up June 05, 2002
Reviewer: Daryl
Thus far, under Windows NT, I find that interMedia is extremely unreliable
without ORACLE_HOME set as a system-wide environment variable. I've not spend
tons of time figuring out what the possible reasons are (which is, sadly, what
Metalink will usually want me to do), since the NT machines I help set up are
generally hosting a single instance. That is to say, there aren't multiple
Oracle versions to have a conflict in the first place.

June 05, 2002 Tom -- Thanks for the question regarding "SQLLDR and flat files VS Materialized Views", version 8.1.7
You Asked
Tom:

As a general rule, what is the best approach to moving data between databases on
a regular basis?  I would think Materialized Views would be the best approach,
but I run into so many DBAs who shy away from them, and feel they have more
control over flat files, FTP, and then SQLLDRing them into the destination
database.

Thanks,
Tom
and we said...
Me, I like sqlplus COPY .  Interactive -- yet very efficient.

I cannot answer this question as it depends on too many variables.  Suffice it
to say -- there is no single "best" answer.  I tend to avoid flat files whenever
possible -- I use sqlplus copy, dblinks with insert into, and snapshots (aka
materialized views) when appropriate (avoiding flat files/ftp whenever possible)
June 05, 2002 Yu -- Thanks for the question regarding "Backup -- do i have to do temp and rollback", version 7.3.4
You Asked
Hi, Tom,

  In order to save space and time, when doing hot backup by copying datafiles,
can I avoid backup of temp and rollback datafiles (whenever a temp datafile is
lost, we can recreate it anyway)? Or in other words, in what conditions we have
to backup temp and rollback datafiles like other datafiles (since all changes
during hot backup would be saved in online redo logs)?

  Thank you!

  Yu
and we said...
if you use temporary tablespaces (true temporary ones) you can get away without
backing them up HOWEVER, practice restoring your database before you really have
to.  You'll have to learn how to offline drop these datafiles as part of the
recovery process and how to recover from not having them.

ROLLBACK DATA MUST BE BACKED UP!

the only thing different during a hot backup is that the datafile headers are
not updated in the datafiles -- the datafiles are constantly written to.  We
need rollback -- if you do not backup rollback -- you will lose your entire
database.  Part of recovery is rolling forward (thats redo) the other is rolling
back (thats rollback).  No rollback = no recovery.
Review & Followup
Rating: 4
Helpful answer June 06, 2001
Reviewer: Yu Dong from Ontario, Canada
Thank you Tom. It cleans up a thought of mine.
Yes if the temp datafile is lost, we need to take the temp datafile offline and
drop and recreate the TEMP tablespace.

Rating: 3
Backup Knowledge July 07, 2001
Reviewer: MSK from India
This is very good answer for hot backup

Rating: 4
Nadya June 04, 2002
Reviewer: Nadya from Russia
Tom, but redo logs contain all changes made to any database buffer including
data,index and also RBS, wheter the changes are commited or uncommited. After
crash when rolling forward the corresponding RBS are regenerated, using
information from redo logs. And then these RBS are used for rolling back. So I
can't fully understaned: if RBS are regenerated why we need to backup their
information? Is it important for Oracle simply to have these RBS or to have them
including information before crash, Oracle itself fills up RBS from redo. I feel
I can not understand this nuance, so I am confused.

Followup:
You need something to rollforward FROM don't you.

Think about it -- using your logic, you wouldn't have to backup the database at
all since all changes made to any database buffer including data, index.... are
logged -- why backup anything at all???  the answer: because we need a starting
point to rollforward from.

Rollback is an integral component of the database, it is not overhead, it is not
something to be minimized, it is an important structure.

Rating: 3
June 04, 2002
Reviewer: Rahul from NJ
Part of recovery is rolling forward (thats redo) the other is rolling
back (thats rollback).  No rollback = no recovery.

Tom if i loose my rollback how will oracle perform recovery...

Followup:
It won't.

Treat your rollback as you would system, or any important set of datafiles.

Rating: 4
Trial recovery in 9i June 04, 2002
Reviewer: Andre from Brazil
The docs say trial recovery is mainly intended for testing the redo log stream.
Why are they so emphatic about it, and why don't they treat datafiles the same
way?

Followup:
what do you think analzye table validate structure, dbv, dbms_repair are about?
They are all about checking datafiles.

We don't want you to lose data.  Files can become unreadable (corrupt) for many
reasons -- bad tapes, bad hardware, software failure, and so on.  Why take the
risk?  if your data is important to you that is...

Also, it makes sure you HAVE what you need to recover.  How many times have I
seen people in need of doing recovery suddenly discover "uh-oh, I don't have it
all"

Rating: 2
Trial recovery (cnt'd) June 05, 2002
Reviewer: Andre from Brazil
Sorry, possibily I didn't get across what I meant. What I really wish to know
is:

What is the real application of trial recover -- only testing redo logs or all
the database ?

In the docs and texts I've read they emphasize that trial recovery is intended
for testing the redo log stream. But I can't view it without imagining testing
all the database structure, at least as a consequence.

Followup:
The validation works for all of the necessary files.

I'm not sure where you are getting this "stress of redo logs" from.

Rating: 3
Database Backup (Hot) with out RBS01 ( Tablespace) June 05, 2002
Reviewer: Reddy from Bangalore, India
So for when ever taking a backup, I am taking system, and application users
tablespace backups only with out RBS. Is there any impact on this, when I really
want to recovery the database.

Reddy

Followup:
Try it - see what happens (or more specifically -- see what DOESN'T happen, your
restore and recovery!)

Don't know what was ambigous about

ROLLBACK DATA MUST BE BACKED UP!

Have  you ever TRIED to restore from your backups?????

June 05, 2002 Bob -- Thanks for the question regarding "OCI calls to oracle replace bind variables with literals before parsing", version 8.1.7
You Asked
Hi Tom,

We have a socket program that takes messages from our interface engine and
inserts the records into one table in the oracle database. I have found that
when the statements get to the oracle database, the bind variables in the C
program have been replaced with the literal values causing a parse and compile.
On initial startup of the socket program, it runs fast for a short time and then
slows considerably and stays slow. I'm suspecting that it slows because it has
to scan the sga area for the same statement (which has thousands of the same
statement with the literals) and then has to parse and compile every time.

Here is a section of the code that does two of the calls to oracle. Any ideas on
how to change it or if I'm even on the right track?

  /* do INSERT */

  sprintf(instmt,
    "INSERT INTO %s(LABHOLD_SEQ,RECEIVED_DATETIME,RAWDATA1) VALUES('%d' ,
SYSDATE, EMPTY_CLOB() ) ",tableName,labholdSeq);

  chaLogWrite(CHA_ERR_INFO, "INSERT_STATEMENT= %s\n", instmt);

  if (OCIStmtPrepare(stmthp, errhp, (text *) instmt, (ub4) strlen((char *)
instmt),
                    (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT))  {
    chaLogWrite(CHA_ERR_FAIL, "bInsertRecord() FAILED: OCIStmtPrepare()
insstmt\n");
    return OCI_ERROR;
  }
  if (OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4) 0,
               (CONST OCISnapshot *) 0, (OCISnapshot *) 0,
               (ub4) OCI_DEFAULT))  {
      (void) printf("FAILED: OCIStmtExecute() insstmt\n");
      chaLogWrite(CHA_ERR_FAIL, "FAILED: OCIStmtExecute() insstmt\n");
      return OCI_ERROR;
  }

 /* now do UPDATE */
  sprintf(upstmt,
    "SELECT RAWDATA1 FROM %s WHERE LABHOLD_SEQ=%d  FOR
UPDATE",tableName,labholdSeq);

  if (OCIStmtPrepare(stmthp, errhp, (text *) upstmt, (ub4) strlen((char *)
upstmt),
                    (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT))  {
    (void) printf("FAILED: OCIStmtPrepare() upstmt\n");
    chaLogWrite(CHA_ERR_FAIL, "FAILED: OCIStmtPrepare() upstmt\n");
    return OCI_ERROR;
  }

  if (OCIDefineByPos(stmthp, &defnp1, errhp, (ub4) 1,
                     (dvoid *) &clob, (sb4) -1, (ub2) SQLT_CLOB,
                     (dvoid *) 0, (ub2 *) 0, (ub2 *) 0, (ub4) OCI_DEFAULT) )
   {
    chaLogWrite(CHA_ERR_FAIL, "bInsertRecord() FAILED: OCIDefineByPos()\n");
    return OCI_ERROR;
  }
and we said...
you are not using bind variables ANYWHERE in this code.  (you are correct
however in your assumption about the slowdown, that'll definitely do it)

You are using sprintf to build totally unique statements.  If you were using
bind variables, it would look like:

  sprintf(instmt,
    "INSERT INTO %s(LABHOLD_SEQ,RECEIVED_DATETIME,RAWDATA1)
     VALUES( :bv1 , SYSDATE, EMPTY_CLOB() ) ",tableName);

and

  sprintf(upstmt,
    "SELECT RAWDATA1 FROM %s WHERE LABHOLD_SEQ=:bv  FOR UPDATE",tableName);

You are building 100% totally unique statements, You haven't "used" any bind
variables at all!

this statement:

"...the bind variables in the C
program have been replaced with the literal values..."

is sort of confusing to me -- as it is you doing the replacement with sprintf's!
 You haven't a single OCIBindByName or anything in there!!!!  It is not "magic",
it is your code:

  sprintf(instmt,
    "INSERT INTO %s(LABHOLD_SEQ,RECEIVED_DATETIME,RAWDATA1) VALUES('%d' ,
SYSDATE, EMPTY_CLOB() ) ",tableName,labholdSeq);

read
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76975/oci05bnd.htm#420697
many examples, shows you exactly what to do.
June 05, 2002 louis -- Thanks for the question regarding "schema or new DB", version 8.1.7
You Asked
Hi Tom,

Can you please help me calculate/suggest which is better for my next
production database. I would like to either create it into
an existing db, that is to say create a new schema or just
create a new database along with other 2 database currently
in the box. The server currently has 2 gig of ram and about
90 gig of Disk space.

Thanks,

Lou
and we said...
I am a big fan of "as few databases as possible".  I would go with a new schema.

It'll consume less disk resources.
It'll consume less ram.
It is easier to manage.
It is easier to backup and such.
June 06, 2002 Vijay -- Thanks for the question regarding "Tuning the sql query", version 8.1.7
You Asked
Hi,
I have been bogged down by the way the particular query which i have written, it
takes a long time if there are lots of records say may be around 0.5million
records .
There are 2 tables A and B. Table B(look up table around 800 records) has 10
columns out of which we have to select 3 columns of table B and some columns of
table A.
The matching criteria is like this.
Table B:
  PREMTAXKEY        NUMBER (10)   NOT NULL, (pRIMARY KEY)
  CYKEY             NUMBER (10),
  STKEY             NUMBER (10),
  CTYKEY            NUMBER (10),
  CITYKEY           NUMBER (10),
  PRLKEY            NUMBER (10),
  CVRKEY            NUMBER (10),
  TAXTYP            CHAR (1),
  TAXPCT            NUMBER (7,4),
  DESCR             VARCHAR2 (255)

Table A:(AROUND .5 MILLION RECORDS)
  PRMKEY1        NUMBER (10)   NOT NULL, (pRIMARY KEY)
  PRMKEY2        NUMBER (10)   NOT NULL, (pRIMARY KEY)
  PRMKEY3        NUMBER (10)   NOT NULL, (pRIMARY KEY)
  PRMKEY4        NUMBER (10)   NOT NULL, (pRIMARY KEY)
  CYKEY             NUMBER (10),
  STKEY             NUMBER (10),
  CTYKEY            NUMBER (10),
  CITYKEY           NUMBER (10),
  PRLKEY            NUMBER (10),
  CVRKEY            NUMBER (10),
  c1                CHAR (1),
  c2               NUMBER (7,4),
  c3               VARCHAR2 (255)

The look up table is so designed that we have to select the most appropriate
values from them and that criteria is in the order given below.
I will represent the values in the column as X and null as N in the criteria
below.(TABLE B)
   CYKEY   STKEY   CTYKEY   CITYKEY   PRLKEY   CVRKEY
1.   X       X       X        X         X         X
2.   X       X       X        N         X         X
3.   X       X       N        N         X         X
4.   X       N       N        N         X         X
5.   X       X       X        X         0         0
6.   X       X       X        N         0         0
7.   X       X       N        N         0         0
8.   X       N       N        N         0         0

We have to select the b.taxtyp,b,taxpct,b.descr based on the criteria given
above ie we will select the values if the the first criteria matches, if it
doesnt then we look for 2nd criteria likewise upto the 8th criteria and if none
matches then we have null values for them.
The last 4 crietrias have values as 0 for the columns prlkey and cvrkey which
are dummy numbers (these values are never found in the columns prlkey,cvrkey of
the table A).
I have written this query like this but it takes around 15mints to process.
SELECT A.PREMKEY1,A.PREMKEY2, A.PREMKEY3,A.PRLKEY,A.CVRKEY,

DECODE(B.TAXPCT,NULL,DECODE(C.TAXPCT,NULL,DECODE(D.TAXPCT,NULL,DECODE(E.TAXPCT,NU
LL,DECODE(F.TAXPCT,NULL,

DECODE(G.TAXPCT,NULL,DECODE(H.TAXPCT,NULL,I.TAXPCT,H.TAXPCT),G.TAXPCT),F.TAXPCT),
E.TAXPCT),D.TAXPCT),C.TAXPCT),
       B.TAXPCT)TAXPCT,

DECODE(B.DESCR,NULL,DECODE(C.DESCR,NULL,DECODE(D.DESCR,NULL,DECODE(E.DESCR,NULL,

DECODE(F.DESCR,NULL,DECODE(G.DESCR,NULL,DECODE(H.DESCR,NULL,I.DESCR,H.DESCR),G.DE
SCR),F.DESCR)
       ,E.DESCR),D.DESCR),C.DESCR),B.DESCR)TAXDESCR,

DECODE(B.TAXTYP,NULL,DECODE(C.TAXTYP,NULL,DECODE(D.TAXTYP,NULL,DECODE(E.TAXTYP,NU
LL,

DECODE(F.TAXTYP,NULL,DECODE(G.TAXTYP,NULL,DECODE(H.TAXTYP,NULL,I.TAXTYP,H.TAXTYP)
,G.TAXTYP),F.TAXTYP)
       ,E.TAXTYP),D.TAXTYP),C.TAXTYP),B.TAXTYP)TAXTYP,
         FROM A,(SELECT PREMTAXKEY, CYKEY,STKEY,CTYKEY,
                    CITYKEY,PRLKEY,CVRKEY,TAXTYP,TAXPCT,DESCR
                       FROM  B Z WHERE
                    Z.CYKEY IS NOT NULL AND
                    Z.STKEY IS NOT NULL AND
                    Z.CTYKEY IS NOT NULL AND
                    Z.CITYKEY IS NOT NULL)B,
            (SELECT PREMTAXKEY, CYKEY,STKEY,CTYKEY,
            CITYKEY,PRLKEY,CVRKEY,TAXTYP,TAXPCT,DESCR
             FROM  B Z WHERE
            Z.CYKEY IS NOT NULL AND
            Z.STKEY IS NOT NULL AND
            Z.CTYKEY IS NOT NULL AND
            Z.CITYKEY IS NULL)C,
            (SELECT PREMTAXKEY, CYKEY,STKEY,CTYKEY,
                CITYKEY,PRLKEY,CVRKEY,TAXTYP,TAXPCT,DESCR
                   FROM  B Z
                WHERE
                Z.CYKEY IS NOT NULL AND
                Z.STKEY IS NOT NULL AND
                Z.CTYKEY IS NULL AND
                Z.CITYKEY IS  NULL)D,
            (SELECT PREMTAXKEY, CYKEY,STKEY,CTYKEY,CITYKEY,
            PRLKEY,CVRKEY,TAXTYP,TAXPCT,DESCR
             FROM  B Z
            WHERE
            Z.CYKEY IS NOT NULL AND
            Z.STKEY IS  NULL AND
            Z.CTYKEY IS  NULL AND
            Z.CITYKEY IS NULL)E,
        (SELECT  CITYKEY,CTYKEY,STKEY,CYKEY,TAXPCT,DESCR,TAXTYP
                      FROM B Z WHERE
                         Z.PRLKEY = 0 AND
                         Z.CVRKEY = 0 AND
                         Z.CYKEY IS NOT NULL AND
                         Z.STKEY IS NOT NULL AND
                         Z.CTYKEY IS NOT NULL AND
                         Z.CITYKEY IS NOT NULL )F,
            (SELECT  CITYKEY,CTYKEY,STKEY,CYKEY,TAXPCT,DESCR,TAXTYP
                      FROM B Z WHERE
                         Z.PRLKEY = 0 AND
                         Z.CVRKEY = 0 AND
                         Z.CYKEY IS NOT NULL AND
                         Z.STKEY IS NOT NULL AND
                         Z.CTYKEY IS NOT NULL AND
                         Z.CITYKEY IS NULL)G,
            (SELECT  CITYKEY,CTYKEY,STKEY,CYKEY,TAXPCT,DESCR,TAXTYP
                      FROM B Z WHERE
                         Z.PRLKEY = 0 AND
                         Z.CVRKEY = 0 AND
                         Z.CYKEY IS NOT NULL AND
                         Z.STKEY IS NOT NULL AND
                         Z.CTYKEY IS NULL AND
                         Z.CITYKEY IS  NULL)H,
            (SELECT  CITYKEY,CTYKEY,STKEY,CYKEY,TAXPCT,DESCR,TAXTYP
                      FROM B Z WHERE
                         Z.PRLKEY = 0 AND
                         Z.CVRKEY = 0 AND
                         Z.CYKEY IS NOT NULL AND
                         Z.STKEY IS  NULL AND
                         Z.CTYKEY IS  NULL AND
                         Z.CITYKEY IS NULL)I
        WHERE   A.PRLKEY = B.PRLKEY(+) AND
            A.CVRKEY = B.CVRKEY(+) AND
            A.CITYKEY = B.CITYKEY(+) AND
            A.CTYKEY = B.CTYKEY(+) AND
            A.STKEY = B.STKEY(+) AND
            A.CYKEY = B.CYKEY(+) AND
            A.PRLKEY = C.PRLKEY(+) AND
            A.CVRKEY = C.CVRKEY(+) AND
            A.CYKEY = C.CYKEY(+) AND
            A.STKEY = C.STKEY(+) AND
            A.CITYKEY = C.CITYKEY(+) AND
            A.PRLKEY = D.PRLKEY(+) AND
            A.CVRKEY = D.CVRKEY(+) AND
            A.CYKEY = D.CYKEY(+) AND
            A.STKEY = D.STKEY(+) AND
            A.PRLKEY = E.PRLKEY(+) AND
            A.CVRKEY = E.CVRKEY(+) AND
            A.CYKEY = E.CYKEY(+) AND
            A.CYKEY = F.CYKEY(+) AND
            A.STKEY = F.STKEY(+) AND
            A.CTYKEY = F.CTYKEY(+) AND
            A.CITYKEY = F.CITYKEY(+) AND
            A.CYKEY = G.CYKEY(+) AND
            A.STKEY = G.STKEY(+) AND
            A.CITYKEY = G.CITYKEY(+) AND
            A.CYKEY = H.CYKEY(+) AND
            A.STKEY = H.STKEY(+) AND
            A.CYKEY = I.CYKEY(+)
Can you help me out in tuning this query.

Thanks
and we said...
Talk about designing for bad performance, yuck.  I don't know how you came up
with this but -- yuck.

No promises (i'd rather take this one out behind the barn and shoot it really --
looks like "redesign time" to me) but you can try:

SELECT A.PRMKEY1,A.PRMKEY2, A.PRMKEY3,A.PRLKEY,A.CVRKEY,
       nvl( (select b.taxpct from b b
              where A.PRLKEY = B.PRLKEY AND A.CVRKEY = B.CVRKEY AND
                    A.CITYKEY = B.CITYKEY AND A.CTYKEY = B.CTYKEY AND
                    A.STKEY = B.STKEY AND A.CYKEY = B.CYKEY ),
         nvl( (select c.taxpct from b c
                where A.PRLKEY = C.PRLKEY AND A.CVRKEY = C.CVRKEY AND
                      A.CYKEY = C.CYKEY AND A.STKEY = C.STKEY AND
                      A.CITYKEY = C.CITYKEY),
          nvl( (select d.taxpct from b d
                 where A.PRLKEY = D.PRLKEY AND A.CVRKEY = D.CVRKEY AND
                       A.CYKEY = D.CYKEY AND A.STKEY = D.STKEY),
           nvl( (select e.taxpct from b e
                  where A.PRLKEY = E.PRLKEY AND A.CVRKEY = E.CVRKEY AND
                        A.CYKEY = E.CYKEY),
            nvl( (select f.taxpct from b f
                   where A.CYKEY = F.CYKEY AND A.STKEY = F.STKEY AND
                         A.CTYKEY = F.CTYKEY AND A.CITYKEY = F.CITYKEY ),
             nvl( (select g.taxpct from b g
                    where A.CYKEY = G.CYKEY AND A.STKEY = G.STKEY AND
                          A.CITYKEY = G.CITYKEY ),
              nvl( (select h.taxpct from b h
                     where A.CYKEY = H.CYKEY AND A.STKEY = H.STKEY ),
               nvl( (select i.taxpct from b i
                      where A.CYKEY = I.CYKEY ), NULL )))))))) taxpct,
       nvl( (select b.descr from b b
              where A.PRLKEY = B.PRLKEY AND A.CVRKEY = B.CVRKEY AND
                    A.CITYKEY = B.CITYKEY AND A.CTYKEY = B.CTYKEY AND
                    A.STKEY = B.STKEY AND A.CYKEY = B.CYKEY ),
         nvl( (select c.descr from b c
                where A.PRLKEY = C.PRLKEY AND A.CVRKEY = C.CVRKEY AND
                      A.CYKEY = C.CYKEY AND A.STKEY = C.STKEY AND
                      A.CITYKEY = C.CITYKEY),
          nvl( (select d.descr from b d
                 where A.PRLKEY = D.PRLKEY AND A.CVRKEY = D.CVRKEY AND
                       A.CYKEY = D.CYKEY AND A.STKEY = D.STKEY),
           nvl( (select e.descr from b e
                  where A.PRLKEY = E.PRLKEY AND A.CVRKEY = E.CVRKEY AND
                        A.CYKEY = E.CYKEY),
            nvl( (select f.descr from b f
                   where A.CYKEY = F.CYKEY AND A.STKEY = F.STKEY AND
                         A.CTYKEY = F.CTYKEY AND A.CITYKEY = F.CITYKEY ),
             nvl( (select g.descr from b g
                    where A.CYKEY = G.CYKEY AND A.STKEY = G.STKEY AND
                          A.CITYKEY = G.CITYKEY ),
              nvl( (select h.descr from b h
                     where A.CYKEY = H.CYKEY AND A.STKEY = H.STKEY ),
               nvl( (select i.descr from b i
                      where A.CYKEY = I.CYKEY ), NULL )))))))) descr,
       nvl( (select b.taxtyp from b b
              where A.PRLKEY = B.PRLKEY AND A.CVRKEY = B.CVRKEY AND
                    A.CITYKEY = B.CITYKEY AND A.CTYKEY = B.CTYKEY AND
                    A.STKEY = B.STKEY AND A.CYKEY = B.CYKEY ),
         nvl( (select c.taxtyp from b c
                where A.PRLKEY = C.PRLKEY AND A.CVRKEY = C.CVRKEY AND
                      A.CYKEY = C.CYKEY AND A.STKEY = C.STKEY AND
                      A.CITYKEY = C.CITYKEY),
          nvl( (select d.taxtyp from b d
                 where A.PRLKEY = D.PRLKEY AND A.CVRKEY = D.CVRKEY AND
                       A.CYKEY = D.CYKEY AND A.STKEY = D.STKEY),
           nvl( (select e.taxtyp from b e
                  where A.PRLKEY = E.PRLKEY AND A.CVRKEY = E.CVRKEY AND
                        A.CYKEY = E.CYKEY),
            nvl( (select f.taxtyp from b f
                   where A.CYKEY = F.CYKEY AND A.STKEY = F.STKEY AND
                         A.CTYKEY = F.CTYKEY AND A.CITYKEY = F.CITYKEY ),
             nvl( (select g.taxtyp from b g
                    where A.CYKEY = G.CYKEY AND A.STKEY = G.STKEY AND
                          A.CITYKEY = G.CITYKEY ),
              nvl( (select h.taxtyp from b h
                     where A.CYKEY = H.CYKEY AND A.STKEY = H.STKEY ),
               nvl( (select i.taxtyp from b i
                      where A.CYKEY = I.CYKEY ), NULL )))))))) taxtyp
  from a
/

My first guess on indexing would be:

b(prlkey,cvrkey,cykey,stkey)

that would be "good" for:

       nvl( (select b.taxpct from b b
              where A.PRLKEY = B.PRLKEY AND A.CVRKEY = B.CVRKEY AND
                    A.CITYKEY = B.CITYKEY AND A.CTYKEY = B.CTYKEY AND
                    A.STKEY = B.STKEY AND A.CYKEY = B.CYKEY ),
         nvl( (select c.taxpct from b c
                where A.PRLKEY = C.PRLKEY AND A.CVRKEY = C.CVRKEY AND
                      A.CYKEY = C.CYKEY AND A.STKEY = C.STKEY AND
                      A.CITYKEY = C.CITYKEY),
          nvl( (select d.taxpct from b d
                 where A.PRLKEY = D.PRLKEY AND A.CVRKEY = D.CVRKEY AND
                       A.CYKEY = D.CYKEY AND A.STKEY = D.STKEY),
           nvl( (select e.taxpct from b e
                  where A.PRLKEY = E.PRLKEY AND A.CVRKEY = E.CVRKEY AND
                        A.CYKEY = E.CYKEY),

and then another on

b(cykey,stkey,citykey,ctykey)

to cover:
            nvl( (select f.taxpct from b f
                   where A.CYKEY = F.CYKEY AND A.STKEY = F.STKEY AND
                         A.CTYKEY = F.CTYKEY AND A.CITYKEY = F.CITYKEY ),
             nvl( (select g.taxpct from b g
                    where A.CYKEY = G.CYKEY AND A.STKEY = G.STKEY AND
                          A.CITYKEY = G.CITYKEY ),
              nvl( (select h.taxpct from b h
                     where A.CYKEY = H.CYKEY AND A.STKEY = H.STKEY ),
               nvl( (select i.taxpct from b i
                      where A.CYKEY = I.CYKEY ), NULL )
Review & Followup
Rating: 4
June 06, 2002
Reviewer: A reader

Rating: 2
June 06, 2002
Reviewer: A reader
Since table A has a composite primary key (of 4 columns), these subqueries can
return more than 1 row, then this wont work??

Followup:
But then you are creating a cartesian product in your query -- you are in effect
multiplying the result set (making it larger then it is).

is that what YOU want?

If these subqueries return >1 row, which one of those rows do you want?

(and remember, this is a design I would take out behind the barn.... not
promising you anything here, I'm thinking this is beyond reasonable hope
personally.  Even if this is "faster", I'm not sure by how much -- it'll have to
run all of these subqueries 3 times for each of the 1/2 million rows.

June 06, 2002 Lon -- Thanks for the question regarding "Insert Performance", version 8.1.7
You Asked
Hi Tom,

I have a general question on INSERT statement performance.

Is a multi-row insert using the insert into ... select col1,col2 from table
ALWAYS more performant than doing multiple distinct inserts.

We have a set of AFTER triggers that currently perform multiple inserts into
another table and my task is to find ways to increase performance of these
triggers. Currently these triggers perform at least three inserts (in some cases
there are hundreds of inserts each time the trigger fires) into the other table
from inside a loop. I have found a way to reduce this to two insert statements.

One is like

insert into table (col1,col2,...) values(val1, val2,...);

the other is like

insert into table select /*+ index(a) index(b) index(c) */
a.c1,b.c2,b.c3,c.c4,... from
t1 a, t2 b, t3 c
where a.col = b.col and a.c1 = c.c1 and
exists (select /*+ index(d) */ 1 from t4 d where d.c2 = b.c2 and
col5 = 'constant');

This last insert has a triple join and a sub-select. I really like this idea of
insert ... select from ... but I have some concerns about performance. I would
appreciate your comments.

And yes I am using bind variables ;)

Thanks,

Lon
and we said...
If you can avoid selecting the data BACK to the client (even if the client is a
trigger) and can do it as an insert select -- it is better.  My mantra
(documented in my book over and over) is:

1) if you can do it in a single sql statement -- do it.

Time and time and time again, I find that by packing everything I can into a
single sql statement -- it goes faster.  There are *rare* occasions where a
procedural solution is better but in general.....

(personally, i would avoid the hints unless you are 100% certain you need them)
Review & Followup
Rating: 5
Excellent June 03, 2002
Reviewer: A reader
Dear Tom,
Hints are curious. As CBO keeps getting powerful, what are the Hints that we
still need?
Regards,

Followup:
Hopefully none, on the other hand all.

If you *need* a hint, you'll need one of the many we have -- there is no set of
"don't ever use these and always use these" (if there were, we'd just drop the
"don't use these" category)

Rating: 3
why multitable insert into one table is faster than usual single table insert? June 06, 2002
Reviewer: john from ukraine
Tom,

insert
    into t1 select n from t1m

took 35 seconds, while

insert first
       when 1=1 then into t1
       when 1=0 then into t2
    select n from t1m

took only 28 seconds

on 4 million records.

Do we have to rewrite all our insert statements using new multitable insert even
for a SINGLE table insert?

Thanks

Followup:
Umm, no.

Elapsed time isn't very reliable.  It is like flipping a coin.  If it landed on
heads 5 times in a row -- could you say "coins land on heads"?

Run those inserts over and over and over -- you'll find the runtimes to be the
same -- when averaged over many runs.

Better yet, use TKPROF and you'll find they are comparable.  Elapsed time isn't
reliable unless you have a dust free lab environment with infinitely fast disks
and no other users and you own the entire network and all of the cpu's.

insert into t select * from all_objects

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.03       0.03          0          0          0           0
Execute      1      6.17       6.86       3091     140534       1777       22437
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      6.20       6.89       3091     140534       1777       22437

********************************************************************************

insert first
  when 1=1 then into t
  when 1=0 then into t2
select * from all_objects

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.04       0.03          0          0          0           0
Execute      1      6.31       6.97       3436     140519       1777       22437
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      6.35       7.00       3436     140519       1777       22437

Shows that for all intents and purposes, they executed the "same" relatively
speaking, script was:

alter session set sql_trace=true;
drop table t;
drop table t2;

create table t as select * from all_objects where 1=0;
create table t2 as select * from all_objects where 1=0;

set timing on
insert into t select * from all_objects;

drop table t;
create table t as select * from all_objects where 1=0;

insert first
  when 1=1 then into t
  when 1=0 then into t2
select * from all_objects;

When run a couple of times without sql_trace

single table                 multi-table
-----------------            ----------------------
6.12                         6.38
6.15                         6.44
6.24                         6.55
6.32                         6.27

so, for all intents and purposes -- pretty much "the same".  Your first insert
may have

o hit waits getting the dictionary cached warmed up for "t1" (don't know how you
did the test... cannot say)

o hit waits imposed by other users in the system

o hit waits imposed by disk IO (io the second insert didn't have to do)

o and so on and so on and so on.  Use TKPROF, always run many times, always make
sure each run is "equal" (eg: I dropped T between inserts -- that equalized
them)

Rating: 5
Error ORA-00925 June 06, 2002
Reviewer: A reader from USA
When I do the test using the scripts, I get the error.

SQL> insert first
  2    when 1=1 then into t
  3    when 1=0 then into t2
  4  select * from all_objects;
insert first
       *
ERROR at line 1:
ORA-00925: missing INTO keyword.

What should I do?

Thank you.

Followup:
upgrade to 9i ;) where this feature is first available (multi-table inserts)

Rating: 4
But then.... June 06, 2002
Reviewer: Kishan from Singapore
Hi Tom:
Just to highlight...the original questioner gives the version of database as
8.1.7. which is fine but then the followup question refers to version 9i. Don't
you think that may mislead some as it obviously has already if you look at the
title of the question and read through? A nice thing it would be if the reviewer
mentions the database version but then world is not full of Tom Kytes. So it
would be useful if you could highlight the version of the database or any such
thing, if it differs from the original question....

Thanks for your time and effort.

June 06, 2002 -- Thanks for the question regarding "Passing data from Java to PL/SQL", version 8.1.7
You Asked
Tom,

We have a web application that uses java as the development language. In
addition, we have pl/sql that will handle the database inserts,updates, and
selects.

On one page, the user will be entering one or more rows of
data such as

Product Code          Amount
-------------         -------------
xyz                      20.40
abc                      44.00
rst                      25.00

where the user will be keying in the amount fields. Currently,
the plsql procedure has two input parameters and requires
the java program to place a delimiter between each value
and then the pl/sql parses out the values to be updated.

For example  PROCEDURE saveProduct(pProductCode IN VARCHAR2,
                                   pAmount      IN VARCHAR2)

where pProductCode will look like  xyz|abc|rst|
and   pAmount      will look like  20.40|44.00|25.00|

Is there a way to pass this data as an array from java to plsql
so I don't have to parse this information out?  This seems
a lot of extra work to have java create the delimited string
and then have pl/sql parse it out.

Thanks
and we said...
see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:712625135727
it works from "regular" java in addition to the java stored procedure I used to
demonstrate with.
Review & Followup
Rating: 4
Thanks June 06, 2002
Reviewer: A reader
You have really helped me solve this problem.

P.S. I noticed you have a spell checker for
     the textarea.  Can you explain how you
     did this?

Followup:
it is a plsql based spell checker.  See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:854500633682

June 06, 2002 -- Thanks for the question regarding "index use and rollback segment extends", version 8.1.7
You Asked
Hi Tom,

I created an index like this(compressed since the first 2 columns are the most
non-selective, table has over 10 million records):
    create unique index case_address_idx on case_address
    (
    ad_def_no,
    ad_type,
    ad_case_no,
    ad_date,
    ad_time
    )
    storage (initial 512M next 512M pctincrease 0)
    tablespace indx2
    pctfree 5
    compress 2
    nologging

Now I have a query in an old procedure like this:
    select * into addr from case_address where
    ad_case_no=12345678 and ad_type=1 and ad_def_no=1 and rownum<2 order by
ad_date desc, ad_time desc;

Since columns in the predicate is not in the same order as those in the index, I
think it won't be able to use the index and was about to change the query in
procedure, but I found this in trace:
    SELECT *
    FROM
     CASE_ADDRESS  WHERE AD_CASE_NO = :b1  AND AD_TYPE = 1  AND AD_DEF_NO = 1
      AND ROWNUM < 2  ORDER BY AD_DATE DESC,AD_TIME DESC

    call     count       cpu    elapsed       disk      query    current
rows
    ------- ------  -------- ---------- ---------- ---------- ----------
----------
    Parse        1      0.03       0.03          0          0          0
  0
    Execute    149      4.48       4.76          0          0          0
  0
    Fetch      149      1.88       2.98        213        573          0
126
    ------- ------  -------- ---------- ---------- ---------- ----------
----------
    total      299      6.39       7.77        213        573          0
126

    Misses in library cache during parse: 0
    Optimizer goal: CHOOSE
    Parsing user id: 35     (recursive depth: 1)

    Rows     Row Source Operation
    -------  ---------------------------------------------------
    195  COUNT STOPKEY
        195   TABLE ACCESS BY INDEX ROWID CASE_ADDRESS
        228    INDEX RANGE SCAN DESCENDING (object id 26895)

It is even index range scan, not fast index scan. Unbelievable. Can you explain
why this would happen?

Also if you see a huge number of extends for the rollback segment in the
previous day, is there anything you can do to find out what caused it except
pulling your hair to recall and bugging every user?

Thanks.
and we said...
Your index is:

create unique index case_address_idx on case_address
( ad_def_no, ad_type, ad_case_no,  ad_date,  ad_time )

your predicate is:

AD_CASE_NO = :b1  AND AD_TYPE = 1  AND AD_DEF_NO = 1

which is the same as:

AD_DEF_NO = 1 and
AD_TYPE = 1   and
AD_CASE_NO = :b1

so, the three things in the predicate are exactly the first three things in the
index -- it is a perfect use of the index.  I don't see anything extraordinary
or exceptional here myself?

Now, I must say you are getting very very extremely lucky here!!!   YOU HAVE A
BUG WAITING TO HAPPEN

o You want to get the record from case address
o for a given ad_def_no, ad_type, ad_case_no
o you want to order by ad_date and ad_time DESC
o you want the first one from that set (the newest ad_date and ad_time

Your query does not ask for that -- you are getting it BY 100% sheer luck (the
plan is accidently perfect for you).

ROWNUM is assigned and evaluated first, then the order by is applied.  You want
to query:

select *
  from ( select * from case_address
          WHERE AD_CASE_NO = :b1  AND AD_TYPE = 1  AND AD_DEF_NO = 1
          ORDER BY AD_DATE DESC,AD_TIME DESC )
 where rownum < 2;

In order to assured of getting the row you want.  If you do not -- you WILL get
the WRONG ANSWER (as far as you are concerned) after some upgrade -- or even
just a change in the mind of the optimizer.

Your current query -- as you wrote it -- does this:

o You want to get the record from case address
o for a given ad_def_no, ad_type, ad_case_no
o you want the first one from that set
o you want to order that SINGLE, RANDOM record by ad_date and ad_time DESC

the way you have it coded, we are free to find ANY record that matches your
ad_def_no, ad_type, ad_case_no -- and then sort it.

You want to find them all, sort them, then take the first -- big difference!
Review & Followup
Rating: 4
So the order of columns in predicate doesn't have to match those in the index? June 06, 2002
Reviewer: Pat from MD
That is a big misconception on my part. So if you have concatenated index
(a,b,c,d,e), and if your query predicate is like "... where c=xx and b=xx and
a=xx", as long as there are the first several columns in the index, the index
would be used? I thought it has to be "where a=xx and b=xx and c=xx " for the
index to be used. So these are the only 5 cases in which this index would be
used:
1. where a=xx
2. where a=xx and b=xx
3. where a=xx and b=xx and c=xx
4. where a=xx and b=xx and c=xx and d=xx
5. where a=xx and b=xx and c=xx and d=xx and e=xx
AND ORDER OF THOSE COLUMNS WON'T MATTER?

I did not write the query and so did not think about the ROWNUM and order by
clause, but you are absolutely right.

About the 2nd part of my question(related to rollback segment extends), I
suspect it is because I did sth like "alter table xxx modify col_a char(8)"
(col_a used to be varchar2(8)). So the alter table clause is actually doing the
updating and caused the rbs to extend. But is there anyway of confirming what
happened to rbs after the transaction finished?

Your help is most appreciated.

Followup:
There are many more cases where the index can be used -- we can use it for b=xx
even -- see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1156161921465
for an index skip scan example.

The order of the predicate matters not in this case (rbo and cbo -- doesn't
matter, both will see the index can be used)

There is no way to really see what caused the rbs to extend.  In 9i the resource
manager has been extended in such a way that you are able to setup constraints
on how much undo a given segment of users may generate -- you can use that to
CONTROL it but you'll still not really be able to tell what "did" it.  You can
use log miner to see what was going on -- but it'll be really hard to pinpoint
what did it.

me, I never worry about it -- i setup rollback to be upto X m/gbytes in size and
if it gets that big -- so be it (i must have needed it).

June 06, 2002 Owen -- Thanks for the question regarding "Mized size of packages causes inefficiency?", version 8.1.7
You Asked
Hi

Until recently I worked at a company where the policy was to have packages
matched for size, that is, as far as possible all packages were to be as close
as possible to the same, maximum size. This was alleged to improve performance
by reducing memory fragmentation. (Note that a consequence of this was that
routines were essentially grouped randomly throughout the set of packages, and
not by any idea of common function or such-like.)

I couldn't find any evidence to support this policy, nor was any ever provided,
but it was the policy nonetheless. Could you shed some light on it for me? Is it
a sensible but little-known technique, or just misguided?

Thanks in advance

Owen Phelps
and we said...
Sounds like a new myth to me.  I've never heard of such a thing.

Besides, a 1,000 line package might compile to be less p-code then a 100 line
package.

The fact is -- we page in code in 4k'ish chunks, regardless of size.  Once upon
a time, many many versions ago, we paged in entire packages/procedures -- for
many years now, that has not been the case.

So, it is a myth with a kernel of (old) truth behind it -- but an "unachievable"
myth.  Were the developers supposed to sit there and add/remove code until the
compiled code_size was "right on"???  Impossible, totally impossible.

Totally "non-sensible" and hopefully an almost unknown 'technique'

Reminds me of my Ada programming days.  Someone tried to make me ALPHABETIZE my
subroutines -- declare them in order alphabetically, define them in order --
alphabetically.  They didn't like to read code on "screen", they wanted to print
it out to read it and it would help them find stuff (since they couldn't
"search" the paper).  The end result - I wrote a script to rename all of my
routines to:

   aa_some_function;
   ab_another_function;
   ac_......

and so on (and used a big "pragma renames" at the bottom so I didn't have to
hack the rest of the code, sort of like a synonym for a procedure).  Met the
letter of the law, but didn't do anything for anyone.

Here is a query you can use to see that this is totally futile:

  1  select trunc(source_size/1024) src_size_k,
  2        min(code_size),
  3        max(code_size),
  4        max(code_size)-min(code_size) diff,
  5        count(code_size) cnt
  6    from dba_object_size
  7   where source_size > 0
  8     and type in ( 'PROCEDURE', 'FUNCTION', 'PACKAGE BODY' )
  9   group by trunc(source_size/1024)
 10  having max(code_size)-min(code_size) > 0
 11*  order by 4
ops$tkyte@ORA817DEV.US.ORACLE.COM> /

SRC_SIZE_K MIN(CODE_SIZE) MAX(CODE_SIZE)       DIFF        CNT
---------- -------------- -------------- ---------- ----------
        68          16006          16492        486          2
        21           6187           6722        535          3
        24           6120           6738        618          2
        12           1867           2627        760          3
        28           5684           6994       1310          2
        74          17689          19007       1318          3
        20           4954           6555       1601          2
        26           4135           6074       1939          3
        18           3170           5187       2017          3
       150          30077          32203       2126          2
         0            149           2383       2234         65
         1              0           2239       2239         19
        67          15850          18342       2492          2
         9           1450           4582       3132          5
        10           3191           6597       3406          3
        31           6554           9987       3433          3
        16           1469           5034       3565          7
        27           5006           8935       3929          2
         2            331           4375       4044          9
         4            268           4549       4281          4
         3           1502           5919       4417          2
       132          34193          38985       4792          2
         5            594           5520       4926          8
       122          25256          31011       5755          2
         6           1604           8053       6449          5
        47           6113          12649       6536          2
        11           2736           9588       6852          3
         7           1135           9943       8808          4
        15           4680          16379      11699          2
        13           3321          15039      11718          4
        22           3289          16350      13061          7
        32          21828          35886      14058          2
        54          15473          30590      15117          2
         8           2671          17999      15328          5
        25           4652          21423      16771          3
        23           5379          22723      17344          3
        38           8972          26923      17951          2
        34           6352          24784      18432          2
        17           3951          24080      20129          3
        45          10423          44512      34089          3
        71           8215          57543      49328          3

41 rows selected.

So, for example, there is some code out there that is 71k in size.  The smallest
results in about 8k of p-code, the largest in about 57k.  Hmmm, what would they
do then???
Review & Followup
Rating: 5
I love those demystifications !!! June 06, 2002
Reviewer: Andre from Brazil
I get "mystified" at how people think up such stuff. Have I told you about a
former (luckily) boss of mine, a "senior" DBA, who asked me to reduce the number
of free-lists because they were taking up too much space ? Yep, believe me, it's
the naked truth...

Followup:
Well, there is a nuggest of truth in there as well.

A session is associated with a freelist.  If you have 10 freelists and a single
session doing inserts -- it'll only use one of the freelists -- the others will
not be used.  We'll grab a new extent without considering the existing freespace
that is there.

So the freelists don't take up space really -- but they can lead to "less
efficient space utilization" if you have way more then you really need.  (stress
CAN -- eg might -- eg don't worry about it 99.999% of the time)

Rating: 5
Great, thanks. June 06, 2002
Reviewer: Owen from UK
That's much what I had expected, but it's great to have it confirmed. Thanks
very much!

Rating: 5
continued June 07, 2002
Reviewer: Andre from Brazil
Thanks for the feedback on the freelist stuff. In fact I had heard of it and it
makes sense, but surely it wasn't the case with my boss. As a matter of fact he
had no idea what a free list was all about...

June 06, 2002 David -- Thanks for the question regarding "Date Columns", version 8.1.6
You Asked
We saved a SYSDATE value with the following query...

Insert into My_Table (Name, MY_Date)
Value ('David', SYSDATE )

That worked fine.
Now we want to query My_Table like this...

Select MY_Date
From My_Table
Where MY_Date = '2002-06-04 18:22:31.0'

This gives the following error....

ERROR at line 1:
ORA-01861: literal does not match format string

What's up with that?
and we said...
well, what is your default date format, typically it is by default:

dd-mon-rr

that means 2002-06-04 18:22:31.0 cannot be converted by dd-mon-rr.

so, you want to be explicit (my preference with dates ALWAYS as the default can
change from db to db)

where my_date = to_date( '2002-06-04 18:22:31', 'yyyy-mm-dd hh24:mi:ss' );

what is up with that .0 -- until 9i, there is no fractional second support....
Review & Followup
Rating: 5
Date Columns June 06, 2002
Reviewer: David Schwartz from USA
>>what is up with that .0 -- until 9i, there is no fractional second support....

That's part of the problem. When we use SYSDATE it's being inserted into the
column.

Any ideas?

Followup:
no it is not.

sysdate inserts a 7 byte binary piece of data that includes

the century
the year
the month
the day
the hour
the minute
the second

A byte per element.  Show me an example with the ".0" on it and I'll tell you
whats going on.

Rating: 4
To_char To_Date June 06, 2002
Reviewer: Mal from Redwood City, CA
I agree.  Explict date conversions reduce future errors, increase readability
and reliability.  I wish more folks would take that good advice to heart.  It is
not a lot more work to code, and make your code more portable.

On a similar note, for comparision purposes use MM not MON as this can vary
depending on language.  MM is a simple numeric so does not vary from language to
language.

Rating: 5
Date Columns June 06, 2002
Reviewer: David Schwartz from New York
You're correct!
The .0 is comming from the Java program where the Insert statement is being
executed.

We're looking into it.

Any ideas why that's happening?

Followup:
Evil Java at work I suppose ;)

Must be the default Java "to_string" functionality -- their date/times support
fractional seconds.

Hopefully you are using BIND VARIABLES in your inserts right?

Rating: 3
Great ! July 18, 2002
Reviewer: Christyna from Planet Acid Jazz
I just want to say that I had the same problem and by reading this, it is sloved
now ... and can continue and do my job fast !!!!!!!!!!!

Thx Tom !!!!!!!!

June 07, 2002 -- Thanks for the question regarding "Order Order !", version 816
You Asked
Hi,
The scripts are as follows
{
DROP TABLE ORDER_TABLE CASCADE CONSTRAINTS ;

CREATE TABLE ORDER_TABLE (
  SNO         NUMBER (5)    NOT NULL,
  DESCP       VARCHAR2 (10),
  PARENT_SNO  NUMBER (5),
  CONSTRAINT PK_SNO
  PRIMARY KEY ( SNO )
    USING INDEX
     TABLESPACE SYSTEM PCTFREE 10
     STORAGE ( INITIAL 12288 NEXT 12288 PCTINCREASE 50 ))
   TABLESPACE SYSTEM
   PCTFREE 10
   PCTUSED 40
   INITRANS 1
   MAXTRANS 255
  STORAGE (
   INITIAL 12288
   NEXT 12288
   PCTINCREASE 50
   MINEXTENTS 1
   MAXEXTENTS 249
   FREELISTS 1 FREELIST GROUPS 1 )
   NOCACHE;

ALTER TABLE ORDER_TABLE ADD  CONSTRAINT FK_PSNO
 FOREIGN KEY (PARENT_SNO)
  REFERENCES ORDER_TABLE (SNO) ;

INSERT INTO ORDER_TABLE ( SNO, DESCP, PARENT_SNO ) VALUES (
1, 'a', NULL);
INSERT INTO ORDER_TABLE ( SNO, DESCP, PARENT_SNO ) VALUES (
2, 'b', 1);
INSERT INTO ORDER_TABLE ( SNO, DESCP, PARENT_SNO ) VALUES (
3, 'a', 1);
INSERT INTO ORDER_TABLE ( SNO, DESCP, PARENT_SNO ) VALUES (
4, 'a', NULL);
INSERT INTO ORDER_TABLE ( SNO, DESCP, PARENT_SNO ) VALUES (
5, 's', 4);
INSERT INTO ORDER_TABLE ( SNO, DESCP, PARENT_SNO ) VALUES (
6, 'q', 4);
INSERT INTO ORDER_TABLE ( SNO, DESCP, PARENT_SNO ) VALUES (
7, 'a', 4);
INSERT INTO ORDER_TABLE ( SNO, DESCP, PARENT_SNO ) VALUES (
8, 's', NULL);
 insert into order_table values( 11,'a',null) ;
COMMIT;
}

SQL> select * from order_table
  2  /

       SNO DESCP      PARENT_SNO
---------- ---------- ----------
         1 a
         2 b                   1
         3 a                   1
         4 a
         5 s                   4
         6 q                   4
         7 a                   4
         8 s
        11 a
9 rows selected.
As u see the data
   1,4,11 are parent sno
   2,3,5,6,7 are child sno.

I please Want
1)   rownumbers listed for children only in the following order
     parent
       child-1
       child-2
     parent
       child-1
       child-2
I want to concatenate the rownumber returned with hyphen
    example
       1
         2-1
         3-2
       4
         5-1
         6-2
         7-3
       8
 -- My attempts
for the Q1) was
attempt 1)

  1  select sno||'-'||r
  2           from ( select sno,  parent_sno,
  3                       row_number() over ( partition by parent_sno order by
sno ) r
  4*                  from order_table )
  5  /

SNO||'-'||R
---------------------------------------------------------------------------------

2-1
3-2
5-1
6-2
7-3
1-1
4-2
8-3
11-4

9 rows selected.

As u see here it did the correct numbering except for I wanted
parent sno with no hyphen also  the order is not as required.
SO i changed
SQL>   select decode(parent_sno,null,to_char(sno),sno||'-'||r) the_value
  2          from ( select sno,  parent_sno,
  3                          row_number() over ( partition by parent_sno order
by sno ) r
  4                   from order_table )
  5   order by the_value
  6  /

THE_VALUE
---------------------------------------------------------------------------------

1
11
2-1
3-2
4
5-1
6-2
7-3
8

9 rows selected.
Here the record with sno = 11 is messing up obviously because of acending
order.How to club the parent and child together.

Also how to push the child records a bit inwards ?

1A) How to ge the above requirement without using analytic functions.
Is it possible using some stored functions may be in sql.

2) rownumbers listed for parents as well as children starting with
   1 for each parent
    parent-1
      child-2
      child-3
 for examp
      1-1
        2-2
        3-3
      4-1
        5-2
        6-3
        7-4
      8-1
     11-1
Thanx.
and we said...
1a) why would you preclude analytics?  Basically the answer will be "no, not in
sql, not without analytics".  We need to get a rownum within a partition -- the
only way in sql to do that is with analytics.  You will be writing procedural
code to do it any other way (not calling plsql from sql -- but rather doing it
all in plsql and having plsql return a collection)

2)

ops$tkyte@ORA817DEV.US.ORACLE.COM> select decode( rn, 1, null, '         ' ) ||
                                           sno || '-' || rn
  2    from (
  3  select sno,
  4             decode(parent_sno,null,sno,parent_sno) order_by,
  5             row_number() over
                     ( partition by decode(parent_sno,null,sno,parent_sno)
  6                                                     order by sno ) rn
  7    from order_table
  8             )
  9   order by order_by, rn
 10  /

DECODE(RN,1,NULL,'')||SNO||'-'||RN
---------------------------------------------------------------------------------
----
1-1
    2-2
    3-3
4-1
    5-2
    6-3
    7-4
8-1
11-1

9 rows selected.

does that.....
Review & Followup
Rating: 5
That's Great BUT June 07, 2002
Reviewer: A reader
Thanx for anserwing Q2).Can u please also answer if possible Q1) using analytics
which is row numbering only for child not parent but clubbed together.I gave you
my attempt for this but i failed.
I want
      1
         2-1
         3-2
       4
         5-1
         6-2
         7-3
       8
      11

Followup:
It is called a variation on a theme.  A look at what I did, a tweak to the
select:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select
       decode( rn, 1, to_char(sno), '         ' || sno || '-' || (rn-1) )
  2    from (
  3  select sno,
  4             decode(parent_sno,null,sno,parent_sno) order_by,
  5             row_number() over ( partition by
decode(parent_sno,null,sno,parent_sno)
  6                                                     order by sno ) rn
  7    from order_table
  8             )
  9   order by order_by, rn
 10  /

DECODE(RN,1,TO_CHAR(SNO),''||SNO||'-'||(RN-1))
---------------------------------------------------------------------------------
---------
1
         2-1
         3-2
4
         5-1
         6-2
         7-3
8
11

9 rows selected.

and you get what you want (hey, the heavy lifting was all done.....)

Rating: 5
Wonderful June 07, 2002
Reviewer: A reader
Thanx much for your answers.I was struggling to derive the answer from your
answer to q2.Well all are not like TOM.
Well I promise I will look at your solution only after i get mine taking hints
from your earlier answer. Trying since late morning still no success.
  Its really thought provoking whenever I see your answers
I pinch myself  sadly for not having thought your way.
I will start eating Almonds as they say in my native country .
Thanx again.

June 08, 2002 Shan -- Thanks for the question regarding "optimizer_index_caching/optimizer_index_cost_adj", version 8.1.6
You Asked
Hi tom,

We are running a database with a hybrid of OLTP and DSS system and couple of
days back, we analyzed the both oltp/dss schema's through dbms_stats and started
to see lots of full table scans in DSS system as opposed to table fetch by
rowid.

here is statspack report for 1 hr interval :

table fetch by rowid                     7,755,169
table scan blocks gotten                   882,828
table scan rows gotten                 185,471,858
table scans (direct read)                        0
table scans (long tables)                      167
table scans (rowid ranges)                       0
table scans (short tables)                  48,997

so i thought of setting optimizer_index_caching=75 and
optimizer_index_cost_adj=50 in a session and ran the same queries that went for
full table scan. Absolutely i got all the queries to use the index rather than a
full table scan, but I don't seem to gain any time in terms of executing via
indexes.

Also, but setting _caching=75 and _adj=50, it makes all the queries to take
index path and there are some queries that are faster in full table scan than
index. so how to strike a balance between these ?

Thanks in Advance,
Shan
and we said...
Beautiful.

I'll paraphrase your question:

a) we analyzed
b) the optimizer chose a certain path because we believe indexes are like
setting "fast=true"
c) we forced the optimizer to choose another path
d) we didn't see any difference

"Absolutely i got all the queries to use the index rather than a
full table scan, but I don't seem to gain any time in terms of executing via
indexes."

e) by making all queries use the index -- we actually decreased performance.

I have a question for you -- why are full table scans so frightening?
Especially in a DSS query?  Full Table scans are not evil, they are not bad --
they are many times the fastest way to the data (as you've seen).

My suggestion, since you saw no measurable performance gain based on your
actions -- in fact have measured a performance decrease -- would be to UNSET
those parameters, let the optimizer do as it was (full scanning).  It seems that
it was in fact doing the right thing before.

Indexes do not imply, infer, or gaurantee "faster performance".  The can in fact
mean "slower".
Review & Followup
Rating: 5
Try Partitions, not indexes June 07, 2002
Reviewer: Rajesh from New York
Tom,
  Since it is a DSS system and doing FTS, try partitioning if you have not
already done so to increase performance.

Followup:
It is a hybrid system -- be careful there.

Partitioning does not imply fast=true (not any more then indexes do).  It is a
tool you can use but in a HYBRID system -- you must be very careful not to kill
the one side at the advantage of the other.  It can be tricky.

Rating: 4
what is disadvantage of partitioning in OLTP systems? June 07, 2002
Reviewer: A reader
Hi Tom

What can go wrong if we partition tables in an OLTP system? Not range because I
doubt it would benefit if our queries is not based on the partition key but if
we hash partition wouldnt we improve performance by spreading I/O?

Followup:
Think "index access".

Suppose you hash on X, but you index read on

o X
o Y
o Z

Well, Y and Z better be GLOBAL indexes or else you will force multiple index
scans (one each for each hash partition) on your OLTP system.  Instead of doing
a single index range scan, it could be doing 8 of them (or 16 or whatever).

I didn't say there is a disadvantage -- I said "be careful".  OLTP and DSS are
sometimes at odds with eachother. You must balance what you do between them and
be aware of all of the ramifications.  Partitioning is not a silver bullet, just
a tool.  It does not imply fast=true out of the box any more then an index does.

Rating: 5
June 07, 2002
Reviewer: Shankar from CA, USA
Hi Tom,

Here is the reason for me, against full table scans (Not for all tables, few
tables is okay)

my rationale of fts is, basically the blocks are read into buffers and its puts
them in LRU end of LRU list which has a high probability of aging out sooner and
its not good for reducing IO on subsequent access to the table. so how do i
strike a balance of these parameters (_caching/_cost_adj) in a hybrid system
(oltp/dss).

Thanks,
Shankar

Followup:
but you said "it didn't go any faster".

Many times, we just checkpoint the objects and bypass the buffer cache,
preferring disk over searching the buffer cache anyway.  A full scan is not
something that makes good use of the buffer cache in any case.

full scans are not evil.

The algorithms surrounding the caching of blocks has been thought about for a
long time, don't work so hard to defeat them.

If it didn't go faster, in fact slower response times are observed -- that
should re-enforce the previous two points.

Indexes are not the best thing in the world....

Cache hit ratios are WAY OVERRATED....

Rating: 5
Cache Hit Ratios June 07, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
Hopefully Tom won't mind a plug for another site (by a former Oracle
employee)...

I have found Cary Millsap's articles/whitepapers (available at www.hotsos.com)
to be lucid and grounded in solid fundamentals.  They are worth checking out -
especially the one on why a 99.9% buffer cache hit ratio is not a good thing.

Anyway, $0.02 of unsolicited input...

- Mark

Followup:
No -- that's great.  (i actually would like people to go out and find more info
on their own!)

Jonathan Lewis has great things to say about cache hit ratios as well in his
lectures. (things about why they are totally not the thing to consider in many
cases -- and that the blind striving for good cache hits is counter productive)

Rating: 4
a bit confused June 07, 2002
Reviewer: A reader
Suppose you hash on X, but you index read on
o X
o Y
o Z
Well, Y and Z better be GLOBAL indexes or else you will force multiple index
scans (one each for each hash partition) on your OLTP system.  Instead of doing
a single index range scan, it could be doing 8 of them (or 16 or whatever).

I dont understand very well this paragraph. Do you mean in my predicate I am
using 3 columns and all 3 is indexed individually? Or compositely indexed?

May you illustrate with a small example, if possible. If it´s going to be
complicated then dont worry

Thank you

Followup:
I'm saying you frequently run these queries in your oltp system:

select * from t where x = :x;

select * from t where y = :y;

select * from t where z = :z;

(pretend (x,y,z) are (cust_id,phone_number,last_name) and the table T is
customer -- you are doing a customer lookup)

If you hashed on cust_id (good choice for the hash column, almost unique), then
the indexes you have on Y and Z (if local) will be partitioned by X -- you'll
have to range scan EACH of them (each of the partitions)....

Hope that makes more sense.

Rating: 5
Interesting June 08, 2002
Reviewer: Doug from Southbury, CT USA
fast=true  .. that's funny LOL..   So many people think I can just make the
database run 2x faster by putting in a secret command.

June 09, 2002 Jim -- Thanks for the question regarding "Windowing functions", version 9i
You Asked
Hi Tom,

Lets say I have a table
DAILY_STOCK_PRICES_DSP
----------------------
DSP_DATE       DATE
DSP_OPEN       NUMBER(6,2)
DSP_HIGH       NUMBER(6,2)
DSP_LOW        NUMBER(6,2)
DSP_CLOSE      NUMBER(6,2)

Is there a windowing function which will let me Select
the date and price action where the day has the smallest
range (DSP_HIGH - DSP_LOW) for the last 4 days and is the
last day in the series of 4 days.

Eg

say
  Dt          O      H       L      C      Range
2-MAY-2002  2.00   2.10    2.00   2.10      .10
3-MAY-2002  2.10   2.15    2.10   2.13      .05
6-MAY-2002  2.05   2.05    1.96   2.00      .09
7-MAY-2002  2.00   2.01    1.98   2.00      .03

So the 7-MAY-2002 has the smallest range in a series
of 4 days and the last in the 4 days so we want to
SELECT it.

IF the prices of the 7-MAY-MAY and 3-MAY-2002 were swapped
  Dt          O      H       L      C      Range
2-MAY-2002  2.00   2.10    2.00   2.10      .10
3-MAY-2002  2.00   2.01    1.98   2.00      .03
6-MAY-2002  2.05   2.05    1.96   2.00      .09
7-MAY-2002  2.10   2.15    2.10   2.13      .05
Then we would not want the 7th May since it's not the final
smallest range of the 4 days and we would not want
3rd May since it is not the last day of a series of 4 days.
Of course if the 2 trading days prior 2-MAY-2002 had larger
ranges then the 3rd of May then we would want it.

Thanks for your time
Jim

====================== TOM Asks ===================
  Dt          O      H       L      C      Range
2-MAY-2002  2.00   2.10    2.00   2.10      .10
3-MAY-2002  2.10   2.15    2.10   2.13      .05
6-MAY-2002  2.05   2.05    1.96   2.00      .09
7-MAY-2002  2.00   2.01    1.98   2.00      .03
8-MAY-2002  2.00   2.01    1.99   2.00      .02

Now, here, May 7th is still at the end of a 4 day range.  Would you still want
to see May-7th?  And May-8th is at the end of a 4 day range -- would the answer
be both May-7th and May-8th?

More simply put -- do you want the rows such that

a) there exist 3 rows "in front of it"
b) it has the smallest range
=========================================================

Good question, Yes it must satisfy both a) and b) above
so both the 7th of May and the 8th of May satisfy the
requirements.

So the processing would be
1: get the 7th of May
2: compare it to the next trading day and see if a trade opportunity
is there.

3:Get the next row that meets our criteria (8th of May) and
compare it to the next trading day and see if a trade opportunity
is there.

If possible we would want to eliminate/filter out days that
are followed by an "inside day" that is,
to say in your example, the 7th of May meets our 4 day series
and smallest range criteria however the following day is an inside
day because it's high is <= to the 7th of May and it's low is
>= the 7th of May so disregard the 7th of May

If the following was the case
  Dt          O      H       L      C      Range
2-MAY-2002  2.00   2.10    2.00   2.10      .10
3-MAY-2002  2.10   2.15    2.10   2.13      .05
6-MAY-2002  2.05   2.05    1.96   2.00      .09
7-MAY-2002  2.00   2.01    1.98   2.00      .03
8-MAY-2002  2.00   2.02    1.90   2.00      .02

Then we would want both the 7th May and 8th of May since
the 8th of May is nolonger "inside" the high and low of the
7th of May.

Regards
Jim
and we said...
Say the data was:

  Dt          O      H       L      C      Range
2-MAY-2002  2.00   2.10    2.00   2.10      .10
3-MAY-2002  2.10   2.15    2.10   2.13      .05
6-MAY-2002  2.05   2.05    1.96   2.00      .09
7-MAY-2002  2.00   2.01    1.98   2.00      .03
8-MAY-2002  2.00   2.01    1.99   2.00      .02

Now, here, May 7th is still at the end of a 4 day range.  Would you still want
to see May-7th?  And May-8th is at the end of a 4 day range -- would the answer
be both May-7th and May-8th?

More simply put -- do you want the rows such that

a) there exist 3 rows "in front of it"
b) it has the smallest range

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

Ok, here is the query that returns May 7/8 -- Hopefully, it shows enough
constructs that you can do whatever filtering you need -- I used the lead
function to put may 8th along with the may 7th data -- you might be able to use
that to say "only keep the ones where the lead doesn't exist" or something:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t
  2  /

DT                 O          H          L          C      RANGE
--------- ---------- ---------- ---------- ---------- ----------
02-MAY-02          2        2.1          2        2.1         .1
03-MAY-02        2.1       2.15        2.1       2.13        .05
06-MAY-02       2.05       2.05       1.96          2        .09
07-MAY-02          2       2.01       1.98          2        .03
08-MAY-02          2       2.01       1.99          2        .02

ops$tkyte@ORA817DEV.US.ORACLE.COM> select t.*,
  2             lead( range ) over ( order by dt ),
  3             lead( dt )    over ( order by dt )
  4    from ( select dt,
  5                  range,
  6                  count(*) over ( order by dt rows 3 preceding ) cnt,
  7                  min(range) over ( order by dt rows 3 preceding)  min_range
  8             from t
  9             ) t
 10   where cnt = 4
 11     and range = min_range
 12  /

DT             RANGE        CNT  MIN_RANGE LEAD(RANGE)OVER(ORDERBYDT) LEAD(DT)O
--------- ---------- ---------- ---------- -------------------------- ---------
07-MAY-02        .03          4        .03                        .02 08-MAY-02
08-MAY-02        .02          4        .02
Review & Followup
Rating: 5
Very Nice June 09, 2002
Reviewer: Jim from Australia
Thanks Very Much Tom,
Thats exactly what I wanted, like you said I can
work on adding my own filters.
Will consult the "magic" Oracle expert so I can better
understand how the windowing functions are working.
Regards
Jim

Rating: 5
Very Nice June 09, 2002
Reviewer: Jim from Australia
Thanks Very Much Tom,
Thats exactly what I wanted, like you said I can
work on adding my own filters.
Will consult the "magic" expert 1 on 1 Oracle  book so I can better understand
how the windowing functions are working in your solution
Regards
Jim

Rating: 5
June 09, 2002
Reviewer: A reader
Tom

I am using personal Oracle 8.1.7 and i am getting this errors.

  1  select t.*,
  2                 lead( range ) over ( order by dt ),
  3                 lead( dt )    over ( order by dt )
  4        from ( select dt,
  5                      range,
  6                      count(*) over ( order by dt rows 3 preceding ) cnt,
  7                      min(range) over ( order by dt rows 3 preceding)
min_range
  8                 from t
  9                 ) t
 10      where cnt = 4
 11*       and range = min_range
 12  /
select t.*,
*
ERROR at line 1:
ORA-00439: feature not enabled: OLAP Window Functions

Any comment.

Thanks

Followup:
It is an EE function...


Subject | Article | Last Updated (East coast time)
Slow queries on a 100,000 row table even though indexed. | article only | Jun 11, 2002 10:16 am
Dropping Tables and Application Security | article only | Jun 11, 2002 10:35 am
Copying the Tables | article only | Jun 11, 2002 10:36 am
PL/SQL questions | article only | Jun 11, 2002 10:37 am
Collection of questions | article only | Jun 11, 2002 01:46 pm
Delete and Rollback | article only | Jun 11, 2002 02:41 pm
Scheduling JOBS, | article only | Jun 11, 2002 06:45 pm
Using triggers to avoid ora-01401 (inserted value too large) | article only | Jun 12, 2002 06:47 am
Passing Array from VB and Updating a table | article only | Jun 12, 2002 08:47 am
Update failure | article only | Jun 12, 2002 08:55 am
problem with connection - reg | article only | Jun 12, 2002 06:17 pm
Random values | article only | Jun 13, 2002 07:26 am
Create snapshot on View | article only | Jun 13, 2002 07:40 am
Schema Object Naming Restrictions | article only | Jun 13, 2002 07:42 am
Summarizing data over time - by time interval | article only | Jun 13, 2002 08:37 am
Multi row table from Oracle (Like Dual) | article only | Jun 13, 2002 09:13 am
Decode Function | article only | Jun 13, 2002 09:39 am
hints | article only | Jun 13, 2002 09:54 am
Availability Options | article only | Jun 13, 2002 10:15 am
Summarizing Data over Sunday's but only for sunday's of project's existence | article only | Jun 13, 2002 12:17 pm
partition key | article only | Jun 13, 2002 12:35 pm
statistics | article only | Jun 13, 2002 01:06 pm
Imports | article only | Jun 13, 2002 02:09 pm
Dependency | article only | Jun 13, 2002 02:14 pm
Using date column as unique key | article only | Jun 13, 2002 02:30 pm
Using Ref Cursor as parameter between Stored Procedures | article only | Jun 14, 2002 07:24 am
LONG Data type & exp7 for 815 | article only | Jun 14, 2002 09:03 am
ORA-24761 | article only | Jun 14, 2002 05:02 pm
Generating Data into csv (Excel Sheet) format | article only | Jun 17, 2002 07:13 am
9i | article only | Jun 17, 2002 07:24 am
How to find End Hit of table in After update of table level trigger | article only | Jun 17, 2002 07:42 am
Cursor or plain select | article only | Jun 17, 2002 08:38 am
How to return a Resultset from Java Stored Procedure | article only | Jun 17, 2002 10:57 am
Migrate applications to Oracle 8 | article only | Jun 17, 2002 12:58 pm
problem running rsh command from Java Stored Procedure | article only | Jun 17, 2002 08:47 pm
Using Memory Windows to get around HP-UX memory constrait | article only | Jun 17, 2002 08:54 pm
row(s) 1 - 36 of 36
June 11, 2002 mike -- Thanks for the question regarding "Slow queries on a 100,000 row table even though indexed.", version Oracle 8.1.5
You Asked
Hi.

Here's what I've got.

I have a news table of 100,000 rows.

Here is the description:

create table news (
news_id integer,
primary key (news_id),
transmission_time number
);

create index news_tt on news(transmission_time);

My query:

select news_id from news order by transmission_time desc;

takes a very long time.  Is there a better way to do this?
and we said...
Well, in that particular one -- there is 0% chance of ever using
an index since transmission time is nullable and totally null
index entries are never made (bitmap indexes being an
exception).  So, the index could never be used to order the data
in the above since rows with null transmission_time values would
be missing.

In any case, even if I made transmission_time not null or added
"where transmission_time is not null" to the query -- it would
tend to want to full scan.  Anytime you are accessing the entire
table -- it is going to want to do that.  In the absence of a
predicate to reduce the number of rows to less then about 20% of
the table -- its going to full scan.

You might very well find out that using an index is SLOWER to
get 100,000 rows then full scanning/sorting and retrieiving.
All of the 100,000's scattered IO's you need to do (index
read/block read/index read/block read and so on) takes a long
time.

Here  is how you can get it to use your index though:

tkyte@ORACLE8I> create table news (
  2  news_id integer,
  3  primary key (news_id),
  4  transmission_time number
  5  );

Table created.

Elapsed: 00:00:00.47
tkyte@ORACLE8I>
tkyte@ORACLE8I> insert into news select object_id, rownum from
all_objects
  2  /

2730 rows created.

Elapsed: 00:00:06.36
tkyte@ORACLE8I>
tkyte@ORACLE8I> create index news_idx on news(transmission_time)
  2  /

Index created.

Elapsed: 00:00:00.69
tkyte@ORACLE8I>
tkyte@ORACLE8I> set autotrace traceonly explain
tkyte@ORACLE8I> select * from news where transmission_time is
not null order by transmission_time desc
  2  /
Elapsed: 00:00:00.01

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (ORDER BY)
   2    1     TABLE ACCESS (FULL) OF 'NEWS'

As you can see -- full scan even with the predicate so lets
analyze the table

tkyte@ORACLE8I>
tkyte@ORACLE8I> analyze table news compute statistics;

Table analyzed.

Elapsed: 00:00:01.33
tkyte@ORACLE8I>

and hint it - telling it we would really like it to use the
index

tkyte@ORACLE8I> select /*+ index_desc( news, news_idx ) */ *
  2    from news
  3   where transmission_time is not null
  4   order by transmission_time desc
  5  /
Elapsed: 00:00:00.02

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=12 Card=2730
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'NEWS' (Cost=12
   2    1     INDEX (FULL SCAN DESCENDING) OF 'NEWS_IDX' (NON-UN

and it does.  But note that its not faster...

tkyte@ORACLE8I> set autotrace off
Review & Followup
Rating: 4
Why does DESC in after ORDER BY much slower than just ORDER BY? June 10, 2002
Reviewer: A Reader from USA
We have a query that return about a millon rows.  If we do ORDER BY
a_indexed_field, it takes less than 1 second or faster.  However, if we do ORDER
BY a_indexed_field DESC, it would be much much slower (say, 40 seconds).  We
tried to build the index in DESC order, or tried using hint INDEX_DESC, or
different combinations, the query is still very slow.  But we do need the order
in DESC order in the application.  What should we do to make the DESC order
faster?
Thank you very much in advance.

Followup:
The table/indexes are analyzed correct?

Let's see the autotrace output AND the querys (set autotrace traceonly) for:

a) select with order by a_indexed_field
b) select with order by a_indexed_field desc
c) select /+ index desc hinted query */

Rating: 5
Why does DESC in after ORDER BY much slower than just ORDER BY? June 11, 2002
Reviewer: A Reader from USA
Thank you very much Tom.  Yes, the table was analyzed.
I finally find a solution, even though I am not sure why, by using an in line
view (I got the idea from searching this site). Like the following:
select * from
(select col1,col2,col3,col4 from t where col1=somthing order by
the_indexed_filed DESC)
It's very fast and takes only 20 ms.

June 11, 2002 Munzer -- Thanks for the question regarding "Dropping Tables and Application Security", version 8.1.7
You Asked
Tom:

1.  I want to clean up a database from a lot of unused tables.  Would you just
use "Drop table tablename".

Is there anything you would do to clean up or defrag afterward to improve
performance?

2.  Let us say you setup security for an application in the database by creating
several accounts and assigning prviliges at the account level.  "SALES" get
acces to sales package, "Accounting" gets access to accounting packages and
forms and so on.

In order for this to work every user will logging as his account
connect sales/sales@database  or connect acct/acct@database and then he only
have access per his assgined privileges.

But in the main html form(web application) or oracle form (client/server) how do
you determine which userid/password to use.

You have to log in user first to know what group he belongs to.  SO do you
bascially log in with a generic login/userid and then determine which group he
belongs to, disconnects and re-log with the group userid he belongs to.

In case of the web I will be creating DADs per each account in the application
server.

THank you,
and we said...
1) yes, no.

yes I would drop tables.
no i would not do anything special afterwards.

2) Me, I don't do it that way.  I have people log in AS THEM SELVES and use
ROLES "sales", "Accounting" and so on -- granting these roles to the users.

You, if you want to do that that way, will ask the user for their "username",
"password" and "role they want to assume".

Your application will see the role and connect as that user (using some
relatively secure method for obtaining the password for that role).  Your
application will then verify the username/password is OK against your own tables
and that the user is allowed to have that role.
Review & Followup
Rating: 5
Security June 09, 2002
Reviewer: Munz from Reston, USA
Tom:

1.  According to your way, you let user login as himself and he will be using
his role privileges.  Trying to envision this in PL/SQL application:

On a web page, you display an html index page (before connecting to database
yet) that user types in scott/tiger and then you take these parameters and you
use the scott/tiger DAD.  DO you basically use variables in your connect string
and replace it with what the user logged in.

But how  do I submit my index page.  I mean I am not connected to database yet
and I can not run the PL/SQL procedure to connect.

2.  DO you have an example of implementing this in a real  application?

Followup:
1) Your DAD needs to connect as some public user -- an "open" url.

It'll redirect to a URL that uses the DAD requested by the end user.  So,
suppose you have a login page like:

http: //hostname/public_user/plsql/login

When you submit that page (with a username, password and "role") it'll validate
the username, password and role and after SUCCESSFULLY doing that, use
owa_util.redirect_url to send you to:

 'http: //hostname/' || THE_ROLE_THEY_WANT || '/plsql/home_page'

With the encrypted cookie/whatnot that identifies the user.

Rating: 5
Application Security June 10, 2002
Reviewer: Munz from Reston, VA
Tom:

1.  Do you do the same thing fo rclient/server environment.  Connect as a public
user, find the role and re-direct with a new connection string.

2.  When you initally connect with the public account do you give it very
limited role so that user  cannot mark the hyperlink and try to break it by
typing procedure names?

Followup:
1) *I* do not.  *I* let people be themselves.

If you want to -- you could in a client server environment, you would not
"redirect".  you would disconnect/reconnect

2) of course.  but that is true of ALL PUBLIC DAD's

June 11, 2002 Deepa -- Thanks for the question regarding "Copying the Tables", version Oracle (V7.3/8.0)
You Asked
How do we copy the table with contents from one Database to
another Database.

Eg. I have one database Ah/ah@dev_ie
    and another is Ahuja/ah@stage_ie.

  I want to copy all tables from stage_ie to Dev_ie.

Thankx
  Tom.
and we said...
There are several methods:

1) you can EXP from stage_ie and IMP into DEV_IE.  This is nice
especially if you have not created the tables ahead of time as
exp will get the DDL (tables, indexes, constraints, triggers,
code, etc) and copy it as well.

2) a Database link can be used.  "create database link dev_ie
using 'dev_ie'" and then "insert into T@dev_id select * from T".
 Tables must already exist.

3) the sqlplus copy command.  This is nice as you can set commit
counts (copy X records and commit) and you need no intermediate
file to move around as you would with exp/imp.  Tables must
already exist.  See
http://osi.oracle.com/~tkyte/Misc/MoveLongs.html
for a quick copy example.
Review & Followup
Rating: 4
Copy tables between two accounts June 06, 2002
Reviewer: Jeff Yuan from AZ, USA
Tom,

What's the best ways to copy tables from a schema (Production) to another schema
(Test) in same database? We need to refresh all tables in Test by using tables
in Production.  Some tables are huge.

Besides the three methods, do you recommend "truncate table xxx;" and then
"insert into test.xxx select * from prod.xxx;"?

We use V8.1.7

Thanks a lot.

Followup:
In the same database?

drop table xxx;
Create table as select with the unrecoverable and parallel options...
then index in parallel with unrecoverable...

then backup...

but -- test and production in the same instance?  bad bad idea don't you think?

Rating: 4
thanks...great help June 10, 2002
Reviewer: mandavi from india
but what do u we do if u have a stand alone
windows 98 running? how do u copy then?

Followup:
You can use dblinks -- just setup the listener.  it works standalone

June 11, 2002 Munzer -- Thanks for the question regarding "PL/SQL questions", version 8.1.7
You Asked
Tom:

1  WHen You use
for x in (Select * from table1, table2, table 3 where
table1.a=table2.a(+) and table1.a=table3.a(+)  )
LOOP
...
END LOOP

Can you use any kind of select statement. for example have a 6 table join with
all kinds of filters and bind variables.

2.  It seems that my program does not work when I do

i:=5;
For x in i+1..i+10
LOOP
..
END LOOP

Is this invalid statement where the starting and ending number is a calculation

3.  WHen you define a cursor in memory in a PL/SQL program is it possible to
change the set of records or redefine the cursor in the program again.

4.  IF you have 5 columns in a table that make a primary key, does oracle
automatically create an indesx for all those columns so that every time you
insert it is updating the index.  Would that slow things up.

Thank you,
and we said...
1) yes.

2) its fine, you did something else wrong in the test:

ops$tkyte@ORA8I.WORLD> declare
  2          i number;
  3  begin
  4          i := 5;
  5          for x in i+1 .. i+10
  6          loop
  7                  dbms_output.put_line( x );
  8          end loop;
  9  end;
 10  /
6
7
8
9
10
11
12
13
14
15

PL/SQL procedure successfully completed.

3) does not make sense to me.  don't know what you are trying to say there.

4) Oracle will either create or use an existing index on those 5 columns.  As to
whether it would "slow things down" -- it is an UNAVOIDABLE price to pay to have
a primary key.  Its the price you pay.  There is no other way to do it.. Even if
you choose not to use these 5 columns as your primary key -- they are UNIQUE (by
your data model) and there will be an index on them in all cases.
Review & Followup
Rating: 5
Cursor June 07, 2002
Reviewer: munz from Reston, USA
Tom:

1.  I tried to use this since I do not have a primary key in EMP

  For x in (select emp.*,rownum r from emp)
  LOOP
   .....
    update emp set empid=x.lname||x.firstname where rownum=x.r
END LOOP

END;

The update did not work.  It was updating the whole table with the last value.
ANy idea why?

2.  I used ROWID instead of ROWNUM and it worked fine.

3.  IF rowid is the physical address of the record in the data file what is the
rownum?

Thank you,

Followup:
well, this has absolutely nothing to do with the original question???

anyway, read
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4061088733083
(your description of what it does is wrong as well.  it will NOT update the
entire table, in fact, only the first update will update s single row -- the
other updates do nothing -- since "where rownum = X" where X > 1 is never
true....  If you read and understand the above link, you'll know why.

rowid works cause rowid is a primary key.

rownum is a made up number assigned to a row after it is fetched from a table.

Rating: 5
Rownum June 08, 2002
Reviewer: Monz from Reston, USA
TOM:

Oh ok i think I understand it.

When you open the cursor yo are fetching one record a  time and every time you
fetch the rownum is 1.

I thought the when you open cursor it loads all result set in memory which
includes rownum 1..20 and then you will be accessing the rownum of each record.

Am I correct?

Followup:
Read:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4061088733083
I think is explains this rather nicely.  rownum is assigned to rows as they
happen to be fetched during a query (or the search part of an
insert/update/delete).

Rating: 5
Outer join June 08, 2002
Reviewer: Suresh from New York
Tom,
  Regarding the 1st question in this thread, could you please let me know how
many tables can be outer joined in a single SQL statement? Thanks.

Followup:
There is no limit I am aware of.

Here is a "big one"

  1  declare
  2     l_query long;
  3     l_cnt   number;
  4  begin
  5     l_query := 'select count(*) from dual d1';
  6     for i in 2 .. 255
  7     loop
  8             l_query := l_query || ', dual d'||i;
  9     end loop;
 10     l_query := l_query || ' where 1=1 ';
 11     for i in 2 .. 255
 12     loop
 13             l_query := l_query  || ' and d1.dummy = d' || i || '.dummy(+) ';
 14     end loop;
 15     execute immediate l_query into l_cnt;
 16     dbms_output.put_line( 'and the answer is ' || l_cnt );
 17* end;
 18  /
and the answer is 1

PL/SQL procedure successfully completed.

Rating: 5
June 09, 2002
Reviewer: Suresh
Tom,
  Thanks for your response regarding the maximum number of tables that can be
outer joined in a SQL statement. The reason I asked you is that I got this
following error sometime back while trying to do more than one outer join in a
statement.

ORA-01417: a table may be outer joined to at most one other table

   So, from your anwer, I understand that there can be many tables outer joined
in a single SQL statement and from the above mentioned error, it is clear that a
table cannot be outer joined with more than one table in a statement.

  Please let me know if this is right.

Followup:
What that is saying that if some table T is outer joined to some other table T2,
then T may not be outer joined to T3.  In the following, D1 = T, D2 = T2, D3 =
T3:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from dual d1, dual d2, dual d3
  2  where d1.dummy (+) = d2.dummy and d1.dummy(+) = d3.dummy;
where d1.dummy (+) = d2.dummy and d1.dummy(+) = d3.dummy
                   *
ERROR at line 2:
ORA-01417: a table may be outer joined to at most one other table

That is the error you are getting.  You can workaround this issue with inline
views:

  1  select *
  2    from ( select d1.dummy d1_dummy, d2.dummy d2_dummy
  3                from dual d1, dual d2
  4               where d1.dummy(+) = d2.dummy ) d_temp,
  5        dual d3
  6*  where d_temp.d1_dummy(+) = d3.dummy
ops$tkyte@ORA817DEV.US.ORACLE.COM> /

D D D
- - -
X X X

That will physically make "d_temp" exist for a moment and then outer join that
temporary set to d3.  The effect is the same (and it is allowed)

Rating: 5
Tom, Thanks a lot. As always you are the best. June 09, 2002
Reviewer: Suresh

Rating: 3
ANSI multiple outer join avoiding ORA-01417 June 11, 2002
Reviewer: David Penington from Melbourne Australia
This is where I like the power of the ANSI join notation. You can unambiguously
specify the behaviour while outer-joining to multiple tables. The example which
needed an inline view becomes:
select d1.dummy d1_dummy, d2.dummy, d3.dummy
from dual d3 cross join dual d2 left outer join dual d1 on
 (d1.dummy = d2.dummy AND d1.dummy = d3.dummy)
/
A more meaningful example:
select xxx.x1, yyy.empid, emp.empname
from xxx join yyy on (xxx.stateid = yyy.stateid)
left outer join emp on (emp.empid = yyy.empid and emp.empname = xxx.x1)
This outer joins two tables to one while avoiding ORA-01417: a table may be
outer joined to at most one other table

Followup:
did not mention that as question was for 817.

Rating: 5
January 07, 2004
Reviewer: A reader

June 11, 2002 Rukshan -- Thanks for the question regarding "Collection of questions", version 8.1.7
You Asked
1) Should the DR$indexname$I/K/N/R tables be analyzed for performance ?
2) Should the ctxsys schema be analyzed ?
3) Can you please suggest a method / script to query the RMAN repository to
check whether the overnight RMAN backups worked successfully or not ?
4) How can I find out whether my intermedia indexes needs optimising or not ?
5) How can I tell whether my java_pool_size is appropriately sized. Is there a
hit ratio that can be queried via v$ tables

Thanks & Rgds

Rukshan
and we said...
1) An extract from note

Article-ID:         <Note:139979.1>

....
The question arises, should these, the DR$ tables,  be analyzed?

The answer:

No, these DR$...$ tables do not need to be analyzed.

The reason:

The queries we issue on the DR$ tables are very
simple.  It is usually a select by index key prefix, we
do not do joins or anything fancy on them.  There is no benefit
to having cost-based-optimization of those queries because the
rule-based query plan is ALWAYS optimal.
.........

You see -- these queries are not part of your query -- the are part of the
recursive sql.  For now (9i and before) you should not analyze them.

2) you can, but it is not necessary.

3) use the validate command

<quote>
validate

Syntax (pretty picture not included in this text interface of mine)

Purpose
To examine a backup set and report whether it can be restored. RMAN scans all of
the backup pieces in the specified backup sets and looks at the checksums to
verify that the contents are intact so that the backup can be successfully
restored if necessary.
</quote>

4) Currently -- that is something you have to estimate yourself, based on how
you manage your indexes.  In 9iR2 there is a ctxsys package to report back on
the status of an index that will help you decide whether you need an optimize or
not.

If you do like I do -- index documents as they are added, one by one, you will
need to optimize more frequently then if you do it in batch.  This is due to the
"bitmap" like structure they use in the index.

What you should do is collect metrics about the performance of the index over
time (sample queries -- collect the performance).  Then optimize and re-collect.
 Now, optimize on a short and then a longer time frame -- finding the time frame
that in your environment gives you the best payoff (time/energy spent optimizing
versus measurable increased performance).

I myself do it very infrequently -- maybe once or twice a year.  then again, I
add 18 documents a day on average to my index.  your mileage will vary.

5) if you get no errors, it is big enough.  if you use statspack, you can watch
the free memory in it to see if it is too big.
Review & Followup
Rating: 3
RMAN - VALIDATE June 10, 2002
Reviewer: Rukshan Soza from England
Hi Tom,

Thanks for the answers and suggestions. The problem with RMAN restore validate
command is that Oracle recommends that this be done when database is mounted and
not open. So, this is really not practical for us. I was wondering whether there
was a way within the RMAN repository which can be queried via SQL to check
whether the last backup completed successfully or not.

Followup:
Where do you see that?
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76990/preparer.htm#435378

Rating: 2
validate rman restores June 11, 2002
Reviewer: Rukshan Soza from England
Check note:
http://support.oracle.co.uk/metalink/plsql/ml2_documents.showFrameDocument?p_database_id=NOT&p_id=125303.1
Here's an extract from the above note:

7. Validating the Restore of Backups and Copies

A restore validation executes a restore test run without actually restoring the
files.
Test the restore of the entire database or individual tablespaces, datafiles, or

control files. The restore ... validate and validate backupset commands test
whether you can restore backups or copies. You should use:

- restore ... validate when you want RMAN to choose which backups or copies
should be tested.
- validate backupset when you want to specify which backup sets should be
tested.

To let RMAN choose which backup sets or copies to validate:

7.1 If you want to validate the whole database, close it. If you want to
validate
individual files, then either close the database or take the individual
datafiles
offline. This example checks the status of the database, aborts the instance,
and then mounts it:
...
....

Followup:
Just do the validate backupset, not a restore.  Database can be open.  Checkout
the link I posted.  If you page up to the beginning of that chapter, it says:

...
Because you are not using a recovery catalog, RMAN must obtain the information
it
needs from the target database control file. Consequently, the database must
be
mounted or open. For these examples, we will mount the database but not open
it.
......

it can be open for a BACKUP and VALIDATE.

btw: the contents of the note you posted are simply an extract of the chapter I
sent you to.

June 11, 2002 shyam -- Thanks for the question regarding "Delete and Rollback", version 9i
You Asked
I always wonder whether Oracle can give a version of Delete with out rollback or
a Truncate with a where clause.

This will help lot of batch jobs to process fast . The situation is we need to
delete millions of rows and insert new data.

I am not sure if it is possible or it is a major change in Oracle server
architecture.

Thanks
Shyam
and we said...
It would be a major change to ALL TRANSACTIONAL SYSTEMS.

You do not want what you ask for (trust me, you don't).

Suppose the delete "failed" halfway through (constraint violation, system
failure, whatever).  Now what do you have?  Total, 100% loss of data integrity
-- that is what.

In order to do what you want (sort of), I generally recommand:

create table as select <the rows you want to KEEP> -- using paralle and
unrecoveral.

direct path load or insert /*+ append */ (same thing basically) the new data

index in parallel, with unrecoverable as well

drop the old table
rename the new table

BACKUP

There you go -- very fast, very efficient -- results in "compact data" (whereas
a delete would leave lots of free space).  No logging, no undo -- just what you
think you need.
Review & Followup
Rating: 4
Reference to parallel June 10, 2002
Reviewer: Randy from TX
You suggest parallel operations alot.  I haven't found a definitive source that
describes exactly what the prerequisites are.

Are there init. parameters, storage clauses, etc. that allow it?  For example,
is it enough to set parallelism on the table, or would you first have to set a
parameter?  These dependencies are not apparent in the docs or postings I've
read.  Thanks.

Followup:
There is this totally AWESOME document.

Better then that -- it's free as well...

It is on the web.

It is called the Oracle Server Concepts Guide and everyone out there should read
it AT LEAST once (with every major version of Oracle)

One of the chapters in there:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c22paral.htm#365
<quote>
 Parallel Execution of SQL Statements

This chapter describes the parallel execution of SQL statements. The topics in
this chapter include:

    * Introduction to Parallel Execution of SQL Statements

    * Process Architecture for Parallel Execution

    * Setting the Degree of Parallelism

    * Parallel Query

    * Parallel DDL

    * Parallel DML

    * Parallel Execution of Functions

    * Affinity

* Other Types of Parallelism

</quote>

covers these questions and much much more..

Rating: 4
Delete and Rollback June 11, 2002
Reviewer: Shyam Reddy from London
What you mentioned is true.

Suppose we are deleting a 500Million row table and the incremental table is
around 2 Million rows. Can we do a CTAS, rebuild indexes, gather stats every day
in a short period of time. We have 3-4 hour window.

Rating: 3
Seen it June 11, 2002
Reviewer: Randy from TX
I do read the manual because I completely agree on usefulness of the concepts
manual in particular (loved the sarcasm BTW) :).  I know it seems like *no one*
reads the docs, but some of do!!  But it seems to cover this topic haphazardly
with regard to what effect certain settings will have.

Thanks.  I'll dig around some more.

Followup:
I don't know, this:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c22paral.htm#3177
seems to hit your points -- "what parallelizes a query"...

June 11, 2002 Reddy -- Thanks for the question regarding "Scheduling JOBS, ", version 9.0.1
You Asked
I could not figureout how to schedule job's daily at 2:15 AM.
First day it worked at 2:15AM. From the next day, the job's were executed at
12:00AM.
Also wanted to schedule every sunday 9AM (in addition to the daily 2.15AM). This
is another schedular job.

Here is the scheduled info.

declare
  v_job_number number;
BEGIN

dbms_job.submit(v_job_number, 'call_backupcall.exec_proc;;',TO_DATE('26-MAY-2002
2:15:00', 'DD-MON-YYYY HH24:MI:SS'), 'trunc(SYSDATE+1)', null );

  COMMIT;
EXCEPTION
WHEN OTHERS THEN
  dbms_output.put_line(SubStr('Error '||TO_CHAR(SQLCODE)||': '||SQLERRM, 1,
255));
RAISE;
END;

I don't understand how to calculate next schedule example : trunc(SYSDATE+1)
???.

I want to stop above jobs on Nov. 10, 2002.
Is it possible to include end date info. in the dbms_job or do I have to stop
the job's manually.

I would greatly appreciate your help on this one.

Thanks
Reddy
and we said...
The interval could be:

case when trunc(sysdate)+1+2/24+15/24/60 > to_date('11/10/2002','mm/dd/yyyy')
     then NULL
     else trunc(sysdate)+1+2/24+15/24/60
end

That returns

NULL when the next date is past the one you want to stop on, this will
"unschedule" the job.

Tomorrow at 2:15am otherwise.
Review & Followup
Rating: 5
DBMS_JOB time calculation June 13, 2002
Reviewer: reddy ks from Atlanta, GA USA
Thank you so much.
Your response helped me a lot.

Have a nice day
Reddy

June 12, 2002 Mohit -- Thanks for the question regarding "Using triggers to avoid ora-01401 (inserted value too large)", version 8.1.7
You Asked
Hi Tom,

Got a chance to ask a question after quite a while... your site is always so
busy! ]:-) Anyway, here goes...

I have a situation where there are tables in my database receiving data from
numerous external sources. Quite a few of these tables have comment-type
freetext fields in them, and there have been instances where the "comments" sent
in by one system have gone beyond the size of db columns allocated to hold them
resulting in error.

The easy (and most obvious) way to fix this is to make the size of these db
columns the same as maximum expected size of the "comments" data. However, the
no. of interfacing systems will constantly ramp up over time, and I do not want
to continually change the column length whenever a system with larger "comments"
field comes in.

I would much rather do this through triggers or such-like, where the comments
data is checked to be <= db column size, and truncated appropriately when it
exceeds this size.

However, when I try and create a before-insert trigger to implement this, I get
a "inserted value too large" error. It seems Oracle is checking to ensure the
data can fit into destination column BEFORE firing the trigger, thus
circumventing my check-and-fix logic!

A simple case to illustrate this is given below...

--------------------------------------------------------------
SQL> drop table mytest;

Table dropped.

SQL> create table mytest (col1 number(10), col2 varchar2(10));

Table created.

SQL> create or replace trigger fmtTrig
  2  before insert on mytest
  3  for each row
  4  declare
  5      maxColSize   number(4);
  6      tmpBuffer    varchar2(4000);
  7  begin
  8      select data_length
  9      into   maxColSize
 10      from   user_tab_columns
 11      where  column_name = 'COL2'
 12      and    table_name  = 'MYTEST';
 13
 14      tmpBuffer := substr(:new.col2,1,maxColSize - 1);
 15      :new.col2 := tmpBuffer;
 16      dbms_output.PUT_LINE('inserted values are: '||tmpBuffer);
 17  end;
 18  /

Trigger created.

SQL> insert into mytest values (1, 'this goes');

1 row created.

SQL> insert into mytest values (2, 'this is truncated');
insert into mytest values (2, 'this is truncated')
            *
ERROR at line 1:
ORA-01401: inserted value too large for column

SQL> rollback;

Rollback complete.
-------------------------------------------------------------

All help on this matter will be highly appreciated.

Many thanks for taking the time out day in and day out to answer queries from
people like myself!

Best Regards,

Mohit.
and we said...
Well, we cannot do it directly on the TABLE itself -- as you have seen, when the
data "don't fit", we don't let it happen.  Same thing would happen if you tried
to insert "hello" into the number field -- the data doesn't fit, we don't do it.

You can use a View and an insert of trigger however.  Here is a sample of what
you could do.  I made it pretty generic -- so this procedure can regenerate the
trigger/view whenever we need (more columns, less columns, etc).  You could
expand this to do whatever edits you wanted on dates, numbers, etc.  Load into
this instead of the table and you'll achieve what you want.  Also, you can
replace the dbms_output with a better logging facility (like an insert into
another "log table" showing the truncated values and all)

Owner of the procedure MUST have

create view
create trigger

granted directly to them -- not via a ROLE

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( col1 number(10), col2
varchar2(10) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure gen_trigger_view(
p_tname in varchar2 )
  2  as
  3      l_stmt  long;
  4  begin
  5      l_stmt := 'create or replace view ' || p_tname || '_v as select ';
  6      for x in ( select column_name
  7                   from user_tab_columns
  8                  where table_name = upper(p_tname)
  9                  order by column_id )
 10      loop
 11          l_stmt := l_stmt || ' cast( ' || x.column_name || ' as
varchar2(4000) )' || x.column_name || ',';
 12      end loop;
 13      l_stmt := rtrim( l_stmt, ',' ) || ' from ' || p_tname;
 14
 15      execute immediate l_stmt;
 16
 17      l_stmt := 'create trigger ' || p_tname || '_v instead of INSERT on ' ||
p_tname || '_v
 18                 begin';
 19
 20      for x in ( select column_name, data_length
 21                   from user_tab_columns
 22                  where table_name = upper(p_tname)
 23                    and data_type = 'VARCHAR2'
 24                  order by column_id )
 25      loop
 26          l_stmt := l_stmt || '
 27           if :new.' || x.column_name || ' <> substr(:new.' || x.column_name
|| ',1,' || x.data_length || ') then
 28              dbms_output.put_line( ''Field ' || x.column_name || '
truncated'' );
 29           end if;';
 30      end loop;
 31
 32      l_stmt := l_stmt || '
 33      insert into ' || p_tname || '( ' ;
 34
 35      for x in ( select column_name, data_length
 36                   from user_tab_columns
 37                  where table_name = upper(p_tname)
 38                  order by column_id )
 39      loop
 40          l_stmt := l_stmt || x.column_name || ',';
 41      end loop;
 42      l_stmt := rtrim(l_stmt,',') || ') values (';
 43
 44      for x in ( select column_name, data_type, data_length
 45                   from user_tab_columns
 46                  where table_name = upper(p_tname)
 47                  order by column_id )
 48      loop
 49          if ( x.data_type = 'VARCHAR2' )
 50          then
 51              l_stmt := l_stmt || 'substr( :new.' || x.column_name || ',1,'
|| x.data_length || '),';
 52          else
 53              l_stmt := l_stmt || ':new.' || x.column_name || ',';
 54          end if;
 55      end loop;
 56      l_stmt := rtrim(l_stmt,',') || ');
 57      end;';
 58
 59      execute immediate l_stmt;
 60  end;
 61  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec gen_trigger_view( 't' );

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> desc t_v
 Name                                                                    Null?
 Type
 -----------------------------------------------------------------------
-------- -------------------------------------------------
 COL1
 VARCHAR2(4000)
 COL2
 VARCHAR2(4000)

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t_v values ( 10, 'helloworld' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t_v values ( 11, 'hello world' );
Field COL2 truncated

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

      COL1 COL2
---------- ----------
        10 helloworld
        11 hello worl

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review & Followup
Rating: 5
Brilliant, thanks Tom June 11, 2002
Reviewer: Marc from Switzerland

Rating: 5
June 11, 2002
Reviewer: Chen Sun from HK
Hi, Tom
  Would you explain the function of CAST, and when we use it
? As I have never used it during programming....
  Thanks

Followup:
It is used to change datatypes.  I used it here to make a view full of
varchar2(4000)'s -- that should be able to hold most things with the exception
of object types and lobs.

You can use it to cast a column from one datatype to another.

Rating: 4
Thanks for the solution, Tom! June 12, 2002
Reviewer: Mohit

June 12, 2002 Pranav -- Thanks for the question regarding "Passing Array from VB and Updating a table", version 8.1.7.3
You Asked
Hi,
  Thanks a lot for helping everybody.  You really are a great source.  I am
running in to a bit of a problem, but I am not sure what is the right path to
take.  I am trying to pass an array from VB to a Package.  The array has 10
digit numbers as elements.  I used Note:46329.1 (
http://metalink.oracle.com/metalink/plsql/showdoc?db=NOT&id=46329.1
 from metalink to
accomplish this task.  The type is defined in the package as:
  type vbArray is table of VARCHAR2(10) index by BINARY_INTEGER;

  Now I am trying to write a dynamic statement and do an UPDATE to a different
user every time. The user name is sent as a different variable.  I tried using
cast and putting the statement as:

  v_SQL := 'begin
              UPDATE privph' || v_User || '.Table
              SET usercat1 = ''Y''
              WHERE famnum IN
              ( select * from TABLE( CAST (:arr_UserCats as vbArray) ) );
            end;';
  execute immediate v_SQL USING in arr_UserCats;

But Note: 1007755.6 (
http://metalink.oracle.com/metalink/plsql/ml2_documents.showFrameDocument?p_database_id=NOT&p_id=1007755.6
 says that Array index by BINARY integer is not a SQL
TYPE and thus it cannot be used in the USING clause.  I tried writing the query
with USING and that would not even compile.  I also tried FORALL that would not
compile either.  Can you please help me out and tell me what would be the best
way to handle this.  Also I have to keep my Array as NINARY INTEGER in order to
receive an Array from VB.
  Thanks again for all your help.
Sincerely,
Pranav
and we said...
easiest approach:

  for i in 1 .. arr_userCats.count
  loop
      execute immediate 'update privph' || v_user ||
                        '.table set usercatl = ''Y'' where famnum = :x'
      using arr_userCats(i);
  end loop;

In 9i you can do dynamic sql with a for all -- but not in 8i.

You can be more sophisticated and use an array update with DBMS_SQL (this is an
array INSERT but an array update would be much the same -- this example is from
my book expert one on one - the dynamic sql chapter)

  4  procedure dbmssql_array( p_tname     in varchar2,
  5                           p_arraysize in number default 100,
  6                           p_rows      in number default 500 )
  7  is
  8      l_stmt      long;
  9      l_theCursor integer;
 10      l_status    number;
 11      l_col1      dbms_sql.number_table;
 12      l_col2      dbms_sql.date_table;
 13      l_col3      dbms_sql.varchar2_table;
 14      l_cnt       number default 0;
 15  begin
 16      l_stmt := 'insert into ' || p_tname ||
 17                ' q1 ( a, b, c ) values ( :a, :b, :c )';
 18
 19      l_theCursor := dbms_sql.open_cursor;
 20      dbms_sql.parse(l_theCursor, l_stmt, dbms_sql.native);
 21          /*
 22           * we will make up data here.  When we've made up arraysize
 23           * rows, we'll bulk insert them.  At the end of the loop,
 24           * if any rows remain, we'll insert them as well
 25           */
 26      for i in 1 .. p_rows
 27      loop
 28          l_cnt := l_cnt+1;
 29          l_col1( l_cnt ) := i;
 30          l_col2( l_cnt ) := sysdate+i;
 31          l_col3( l_cnt ) := to_char(i);
 32
 33          if (l_cnt = p_arraysize)
 34          then
 35             dbms_sql.bind_array( l_theCursor, ':a', l_col1, 1, l_cnt );
 36             dbms_sql.bind_array( l_theCursor, ':b', l_col2, 1, l_cnt );
 37             dbms_sql.bind_array( l_theCursor, ':c', l_col3, 1, l_cnt );
 38             l_status := dbms_sql.execute( l_theCursor );
 39             l_cnt := 0;
 40          end if;
 41      end loop;
 42      if (l_cnt > 0 )
 43      then
 44          dbms_sql.bind_array( l_theCursor, ':a', l_col1, 1, l_cnt );
 45          dbms_sql.bind_array( l_theCursor, ':b', l_col2, 1, l_cnt );
 46          dbms_sql.bind_array( l_theCursor, ':c', l_col3, 1, l_cnt );
 47          l_status := dbms_sql.execute( l_theCursor );
 48      end if;
 49      dbms_sql.close_cursor( l_theCursor );
 50  end;
Review & Followup
Rating: 4
Thanks a lot July 03, 2002
Reviewer: Pranav from CT, USA
I tried it, its a great solution.  It also gave me an idea to use cast and do a
table cast and got the results much better.

 arrTemp := myArray();
  FOR i IN 1..a_UserCat.count LOOP
      arrTemp.extend;
      arrTemp(i) := a_UserCat(i);
  END LOOP;

  -- Update Table
  v_SQL := 'UPDATE privph' || v_PhNumber || '.table1
            SET usercat1 = ''Y''
            WHERE famnum IN
            ( select * from TABLE( CAST (:arrTemp as myArray) ) )';
  execute immediate v_SQL USING in arrTemp;

Here myArrray is varchar2 Table Type.

June 12, 2002 Pranav -- Thanks for the question regarding "Update failure", version 8.1.7.3
You Asked
Hi,
  I am updating a value in Table1 based on the value in Table2 based on three
joins.  What I want to accomplish is if the join is unsuccesful for Table1 then
I want to insert the particular record from Table2 to Table1.  So far all I have
seen for acomplishing this is using Cursors.  Is there a better way??
Sincerely,
Pranav
and we said...
In 9i -- this would be called a MERGE and can easily be done in a single
command:

merge into EMP e1
using EXTERNAL_TABLE e2
on ( e2.empno = e1.empno )
when matched then
    update set e1.sal = e2.sal
when not matched then
    insert ( empno, ename, job, mgr, hiredate, sal, comm, deptno )
    values ( e2.empno, e2.ename, e2.job, e2.mgr, e2.hiredate, e2.sal, e2.comm,
e2.deptno )
/

In 8i - this takes two commands:

update ( select t1.a, t2.b
           from t1, t2
          where t1.pk1 = t2.pk1 and t1.pk2 = t2.pk2 and t1.pk3 = t2.pk3 )
   set a = b;

insert into t1
select * from t2
where not exists ( select null from t1 where
t1.pk1 = t2.pk1 and t1.pk2 = t2.pk2 and t1.pk3 = t2.pk3 );

no client side processing needed (or desired in most cases)
Review & Followup
Rating: 5
June 12, 2002
Reviewer: A reader

Rating: 5
A Good Update June 13, 2002
Reviewer: M.Balaji from Singapore
It's very usefull.

June 12, 2002 -- Thanks for the question regarding "problem with connection - reg", version 8.1.7.0.0
You Asked
Hi Tom

create or replace and compile java source named "testoracle"
as
import java.sql.*;
import java.util.*;
import java.lang.*;
import oracle.jdbc.driver.*;
import oracle.sql.*;
import java.io.*;
public class testoracle
{
  public static String test()throws SQLException
  {
    int i=0;
     String s1,s2,s3;

    try
    {
      DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());

      s1="jdbc:oracle:thin:@192.168.3.19:1522:java1";
      s2="system";
      s3="manager";
      Connection con = DriverManager.getConnection(s1,s2,s3);
      \\ at this line we are getting runtime error
      Statement st= con.createStatement();
      ResultSet rs=st.executeQuery("select * from sm_employee");
      while (rs.next())
      {
       i++;
      }
    } catch (Exception e)
    {
     return(e.toString());
    }
   return("Success");
  }
}

--FUNCTION IN ORACLE---

CREATE OR REPLACE FUNCTION testoracle RETURN
Varchar2 IS LANGUAGE JAVA NAME
'testoracle.test() return
java.lang.String';

----

THERE IS NO COMPILATION ERROR BUT WHEN THE FUNCTION CREATED IN ORACLE IS
EXECUTED THE FOLLOWING ERROR OCCURED.

SELECT TESTORACLE() FROM DUAL

java.security.AccessControlException: the Permission (java.net.SocketPermission
192.168.3.19:1522 connect,resolve) has not been granted by dbms_java.grant_permi
ssion to SchemaProtectionDomain(SYSTEM|PolicyTableProxy(SYSTEM))

Any Suggestion will be appreciated

Regards
and we said...
I really hope you are not using SYS or SYSTEM to play with?  Please use an
account you create -- sys and system should be considered reserved accounts.

When you get the access control exception, you need to use dbms_java to grant
the priv -- the list of what you need is listed right in the error mesage:

begin
        dbms_java.grant_permission
        ('USERNAME',
         'java.io.SocketPermission',
         '192.168.3.19:1522',
         'connect,resolve');

         commit;
end;
/

followup to comment

Never use SYS for any testing.  SYS is even more special then SYSTEM.  SYS
cannot create triggers on its tables, read only transactions *aren't* for SYS
and so on.  SYS should only be used for special administrative purposes.

SYSTEM is Oracle's account, its where we put stuff.  It is a DBA account, it is
were we store various bits and pieces of stuff.  Consider it an "off limits"
account.

Since you are already using SYS and SYSTEM -- just use them once more, to create
your OWN testing account.  Get out of the habit of using them.
Review & Followup
Rating: 5
Why not system/sys - reg July 10, 2001
Reviewer: KP from India
Quote

"I really hope you are not using SYS or SYSTEM to play with"

If i create a table  like
"create tabe x(x varchar2(40)) tablespace users" under the user system what sort
of a problem am i going to face.

We are just experimenting with dummy database for java stored procedures. Is
ther going to be any security/connection problem while connected to sys/system.
Or is it related to performance problems.

Can u pls elaborate.

Regards

Rating: 1
yr reply to comment 1 - reg July 11, 2001
Reviewer: KP from India
Repeat

What sort of problem we are going to face if use r going
to use system account to create tables. Pl specify. Pl elaborate.

Or is it just a rule we have to we have to follow.

Rating: 3
July 11, 2001
Reviewer: Wim
kp,

Consider SYS an SYSTEM as a kind of 'reserved' account. Do not use them for
testing purposes.
regards

Rating: 2
I still have a problem... June 12, 2002
Reviewer: Tom
Tom:

I ran the script that you recommended, but I received the following error:

begin
*
ERROR at line 1:
ORA-29532: Java call terminated by uncaught Java exception:
java.lang.ClassNotFoundException: java/io/SocketPermission
ORA-06512: at "SYS.DBMS_JAVA", line 0
ORA-06512: at line 2

Would you help me to resolve this error?  Please advise.

Thanks,
Tom

Followup:
do you have java installed in database.  can you create and run a simple java
stored procedure.

Rating: 3
should be java.net.SocketPermission February 28, 2003
Reviewer: Richard Ji from NYC, NY
It's java.net.SocketPermission not java.io.SocketPermission.
Minor error, just want to point it out.

June 13, 2002 Sima -- Thanks for the question regarding "Random values", version 8i
You Asked
Hi Tom,

I have a question, I was hoping you can help me out here.

The question goes like this:

If I have a table emp with say 10 rows. I need to randomnly
select say 3 rows. Is there a way of doing this? Can I use the
sample function here? If so, could you please explain how I can
use this function for this purpose?

Thanks in advance,

Simak
and we said...
Sample won't assure you of 3 rows.  It'll give you a random
percentage.

So, on a 10 row table, you would expect results like this:

  1* create table emp as select empno from scott.emp where
rownum  <=10
ops$tkyte@ORA8I.WORLD> /

Table created.

ops$tkyte@ORA8I.WORLD> select * from emp sample(30);

     EMPNO
----------
      7782

ops$tkyte@ORA8I.WORLD> /

     EMPNO
----------
      7499
      7566
      7654
      7782

ops$tkyte@ORA8I.WORLD> /

     EMPNO
----------
      7782
      7788
      7839

ops$tkyte@ORA8I.WORLD> /

     EMPNO
----------
      7566
      7698
      7782
      7788
      7844

ops$tkyte@ORA8I.WORLD>

Basically, what it does is goes to each row, generates a random
number between 0 and 1, if the random number * 100 is less then
your pct, you get the row.

Each row has a 30% chance of appearing.  The sample(30) on a 10
row table will return anywhere from 0 to 10 rows.  The pct works
well on very large tables since over time, the laws of averages
kicks in and you get about 30% of the data.

Now, if you have a REASONABLE sized data set (in the hundreds or
less number of rows), you can:

  1  select * from
  2  ( select empno, random.rand
  3  from emp
  4  order by 2 )
  5* where rownum <= 3
ops$tkyte@ORA8I.WORLD> /

     EMPNO       RAND
---------- ----------
      7654       7503
      7521       7509
      7844       7614

ops$tkyte@ORA8I.WORLD> /

     EMPNO       RAND
---------- ----------
      7839       6181
      7369       7432
      7782      10317

ops$tkyte@ORA8I.WORLD> /

     EMPNO       RAND
---------- ----------
      7369        165
      7844       6330
      7499       7215

that'll get you a random three rows in Oracle8i and up.  You can
pick up random from
http://osi.oracle.com/~tkyte/Misc/Random.html
it is a PLSQL version of the C-Runtime rand() function.
Review & Followup
Rating: 4
How about selecting random ROW from table with 1mil + rows! June 10, 2002
Reviewer: Ian Matyssik from Japan
The answer abouve was really good! But what if I want to do that on a table with
many(1mil+) rows. Nested query will consume too much resources on a big tables.
Is there any way to do it more optimized?

Followup:
Use the sample then.

Rating: 4
What if your table starts from 100 rows and grows into 1mil+ within one year. June 12, 2002
Reviewer: A reader
So what you say is good for more likely static tables, but in my case I have a
table which is more dynamic and can grow from 10 to 0.5mil rows in 1 month. In
that case I would have to adjust sample rate all the time to get more optimized
result and never endup with 0 rows selected.

So far I came up with that query but as I mentiond before it will be vulnarable
to a quikly growing table.

SELECT C1,C2 FROM
( SELECT C1,DBMS_RANDOM.VALUE,C2 FROM ( SELECT C1,C2 FROM T1
SAMPLE BLOCK (0.1) ) ORDER BY 2 ) WHERE ROWNUM <= 1

If it is possible, could you comment on it please.

Followup:
Look -- in order to get random values from a rapidly growing table PERIOD will
be "quirky and slow" at best.

What is your goal here -- why do you want a single random row?  what are you
trying to accomplish.  Perhaps there is a better way.

Rating: 5
About single random row! June 12, 2002
Reviewer: Ian Matyssik from Japan
What I am trying to do is to have a table be queried by an web aplication to
choose rundom image from the avaliable onse. It is really comon and I saw it in
many places.
So let's say we have table with ids of that images and my web aply will select
next random one every then and now. If it does not worth answering please don't
but if it can help some one out there not only me would be greate to hear opinin
on that.

Thank you in advance.

Followup:
Suppose the table has:

create table images ( id number primary key,
                      .....

If you do this:

   select max(id) into l_id from images;  -- fast, uses the index to find max
   l_random_id := dbms_random.value( 1, l_id );
   select * into l_rec from images where id >= l_random_id and rownum = 1;

you'll get a random image fast.  Just use a sequence (can contain gaps) to
populate ID.

(when we start with a question -- how do i do this.... it is much much easier to
give a reasonable solution to the problem ;)

Rating: 5
Random images June 13, 2002
Reviewer: Paul from England
Why not number all your images sequentially starting from a particular number.
Then get the highest number from the table. Use these two numbers as your range
and choose random numbers within that range. Then display the images matching
the chosen numbers.

Rating: 5
Thank you for a good and constractive solution. June 13, 2002
Reviewer: Ian Matyssik from Japan
That is exectly what I wanted to know.

June 13, 2002 Valeriy -- Thanks for the question regarding "Create snapshot on View", version 8.0.5
You Asked
I created a view in Oracle 8.0 that requred some time to
retrieve date - about 1 min.
I have to run this view pretty often and I dont want to
wait each time when I do it. It's enough for me read not
up-to-date data, for instance, the yestterday data will be
good for me too.
So, My question is:
Is it possible to create snapshote on View?
And setup refresh term equal 1 day to do it each night.
Thank you in advance.
and we said...
Instead of creating a snapshot on a view, just create a snapshot
as CURRENT_VIEW_TEXT. That is, if you have a view like:

create or replace view my_view
as
select * from t1, t2, t3, .....
where .....

Just

create snapshop my_view
as
select * from t1, t2, t3, .....
where .....

which is really a materialized view (which is what you are
looking for)....  You do not need a snapshot of a view, you need
a snapshot (which gives you the view for free)...

For example:

scott@DEV8I.WORLD> l
  1  create snapshot emp_view
  2  refresh complete
  3  start with sysdate
  4  next trunc(sysdate)+1+3/24
  5  as
  6  select e.empno, e.ename, d.dname from emp e, dept d
  7* where e.deptno = d.deptno
scott@DEV8I.WORLD> /

Materialized view created.

That would create a view that was refreshed every night at 3am
server time.  Its a full refresh -- I would just query emp_view
the next day (or with materialized views and query rewrite -- i
can actually query the BASE tables and if possible the optimizer
will rewrite the query to use the materialized view)....
Review & Followup
Rating: 4
How about view on views May 09, 2002
Reviewer: Jim R from Seq/Kings, CA
Tom,
What if I have a view written against (many)other views and tables,  can I
create a MV on this view?  This particular view was written against many
"layers' of views and tables and I don't want to recreate all the sql (would end
up joining about 20 or more tables).  Is this possible.  Thanks in advance.

p.s. we inherited a number of these views and know it is a bad design, but we
don't have the resources currently to reengineer.

Followup:
It is not a bad design -- it happens often.

Is it performing poorly?

Rating: 4
snapshot on a view June 12, 2002
Reviewer: john
i have a synonym on a view of a remote data base.
this view is created on that db on so many tables.

my application joins this synonym with other tables in the current database
which is very slow.

1.now can i create a snapshot at my database side on the view of the other
database.

if yes, i will make my application to point to  this snapshot, which should be
faster..

2.am i right saying the above

Followup:
1) yes.  maybe (probably, most likely)

2) not sure what that means...

June 13, 2002 Chip -- Thanks for the question regarding "Schema Object Naming Restrictions", version 9.0.1
You Asked
Tom,

In chapter 2 of the Oracle9i SQL Reference manual in rule 5 of the Schema Object
Naming Rules, "Oracle strongly discourages you from using $ and #."  Why should
$ and # be avoided for object names ?

Thanks, Chip :)
and we said...
Because we use them heavily.  It is simply in order to avoid name clashes with
system objects down the road that we might introduce.

If you don't use them, you'll not hit a namespace clash with us on a database
table name or column name.
Review & Followup
Rating: 4
Schema Object Naming Follow-Up November 13, 2001
Reviewer: Chip Briggs from Denver, CO USA
Further on rule 9 states "Within a namespace, no two
objects can have the same name."

If a bad boy was creating their own objects in the SYS
schema (or other Oracle defined schemas: eg SYSTEM, OUTLN,
etc), then namespace clashes with # and $ would be expected
because $ and # are heavily used by Oracle.

Since the schema is a namespace boundary, I am wondering
how the use of # and $ in a created schema (eg EXAMPLE)
could cause a namespace clash.

Followup:
We create objects on your behalf, in your schema.  I'm not talking about tables
like that (in SYS/SYSTEM), I'm talking about LOB$ segments and such.

Rating: 5
Thank You November 13, 2001
Reviewer: Chip Briggs from Denver, CO USA
The reason to avoid # and $ is sound.

Since Sybase uses #<tablename> for temporary tables, I was toying with the idea
of using a #<tablename> naming convention for some global temporary table names.
 I still like the idea of a naming convention to readily identify global
temporary tables, but now plan to use a prefix which does not have # and $.  I
noticed you used TEMP_ as a prefix in chapter 6 of your Expert One-on-One Oracle
book.

Followup:
Well, it wouldn't work anyway since you cannot start with "#" unless you use
"#tname" everywhere and that gets ugly.

TEMP_  or _TEMP is pretty universal -- leading or trailing.

Rating: 5
What about blanks in a column name? June 12, 2002
Reviewer: Beth from USA
You can create a column name with a blank space in it, by surrounding the column
name in quotes, during the table creation statement.

Is it ever appropriate or useful to do this?

Followup:
Appropriate -- that is 100% in the eye of the beholder.

Useful -- not in my opinion.  Dangerous -- yes, Error prone -- yes, breaks many
generic tools -- yes, good idea -- i don't believe so.

June 13, 2002 Peter -- Thanks for the question regarding "Summarizing data over time - by time interval", version 8.1
You Asked
Hello

I have an application that gathers and stores data over time. Because of the
applications reliance on the network and other functions the
data is gathered at irregular intervals.

example table

TimeStamp   Object    Value
---------   ------    -----
12:01       Size      1.3
12:02       Size      1.7
12:04       Size      2.0
12:08       Size      1.8
12:11       Size      1.1
12:12       Size      2.0

I would like to be able to sumarize this data by averaging the value
of an Object (size above) at regular intervals. So if I choose an
interval of 5 minutes the output would be

TimeStamp   Object    Value
---------   ------    -----
12:00       Size      1.66
12:05       Size      1.8
12:10       Size      1.55

and if the interval was 10 minutes

TimeStamp   Object    Value
---------   ------    -----
12:00       Size      1.77
12:10       Size      1.55

Can I do this with one SQL command using GROUP BY or will I need
a PL/SQL procedure?

Thanks for any help
and we said...
There is more then one way to do this -- this was the first that came to mind:

ops$tkyte@ORA815.US.ORACLE.COM> select trunc(dt,'hh24') +
(trunc(to_char(dt,'mi')/:n)*:n)/24/60, avg(value)
  2    from t
  3   group by  trunc(dt,'hh24') + (trunc(to_char(dt,'mi')/:n)*:n)/24/60;

TRUNC AVG(VALUE)
----- ----------
12:00 1.66666667
12:05        1.8
12:10       1.55

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> exec :n := 10;

PL/SQL procedure successfully completed.

ops$tkyte@ORA815.US.ORACLE.COM> select trunc(dt,'hh24') +
(trunc(to_char(dt,'mi')/:n)*:n)/24/60, avg(value)
  2    from t
  3   group by  trunc(dt,'hh24') + (trunc(to_char(dt,'mi')/:n)*:n)/24/60;

TRUNC AVG(VALUE)
----- ----------
12:00        1.7
12:10       1.55

ops$tkyte@ORA815.US.ORACLE.COM>
Review & Followup
Rating: 5
Summarizing data over time - by time interval June 12, 2002
Reviewer: Peter Jones from CERN Geneva Switzerland
Thanks again this is very helpful. One question though.
There are occasions when no data is taken for while. So if ther are no data for
a particular interval then I would like this to show up as zero so that it shows
up on the plot. So if i'm taking averages every 10 minutes and
between 12:30 and 13:00 there is no data stored in the
table how can I get results like

Time  Ave
12:00 1.2
12:10 3.0
12:20 4.0
12:30 0
12:40 0
12:50 0
13:00 1.2

Thanks again for you help

Followup:
just need to outer join to a set of times you want to see.  Use of inline view
makes this pretty easy:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select trunc(the_dt,'hh24') +
(trunc(to_char(the_dt,'mi')/:n)*:n)/24/60, avg(value)
  2    from (select (trunc(dt,'hh24') + (trunc(to_char(dt,'mi')/:n)*:n)/24/60)
dt, value from t ) t,
  3    ( select to_date(:start_dt,'hh24:mi')+(rownum-1)*:n/24/60 the_dt
  4        from all_objects
  5       where rownum <=
(to_date(:end_dt,'hh24:mi')-to_date(:start_dt,'hh24:mi'))*24*60/:n + 2)t2
  6   where t.dt (+) = t2.the_dt
  7   group by  trunc(the_dt,'hh24') +
(trunc(to_char(the_dt,'mi')/:n)*:n)/24/60;

TRUNC AVG(VALUE)
----- ----------
12:00 1.66666667
12:05        1.8
12:10       1.55
12:15
12:20
12:25
12:30

7 rows selected.

Rating: 3
Further Question June 12, 2002
Reviewer: A reader
You need a driving set of data which contains all the time intervals you
require, you can then outer join it to your result set. If I have done anything
like this in the past it has been implemented via a calendar object containing
all the required time intervals... but Tom will probably have a cuter answer !!

Rating: 4
Can we use INTERVAL? June 13, 2002
Reviewer: Andy Tsang from Hong Kong
I see in 9iR2, there is an INTERVAL expression. Can we use it for this purpose?

Followup:
It is there in 9iR1 actually and yes you could use the interval type for some of
these calculations in 9i

Rating: 5
Summarizing data over time - by time interval June 13, 2002
Reviewer: Rich from NJ
Tom,

Thanks for this tip, it came in handy. I was able to modify it a little and get
the pivot table type report I needed using your code, decode and the rollup
grouping functions.

TIME                       Accepts   Declined   Canceled     Totals
----------------------- ---------- ---------- ---------- ----------
06/12/2002 02:00:00 am           1          0          0          1
06/12/2002 03:00:00 am           1          0          0          1
06/12/2002 08:00:00 am           3          0          0          3
06/12/2002 09:00:00 am           3          0          0          3
06/12/2002 10:00:00 am           6          0          0          6
06/12/2002 11:00:00 am           6          1          3         10
06/12/2002 12:00:00 pm           2          1          0          3
06/12/2002 01:00:00 pm           2          2          0          4
06/12/2002 02:00:00 pm           2          0          0          2
06/12/2002 03:00:00 pm           4          0          1          5
06/12/2002 04:00:00 pm           6          0          1          7
All Times                       36          4          5         45

12 rows selected.

select decode(grouping(trunc(o.offer_status_date,'hh24') +
(trunc(to_char(o.offer_status_date,'mi')/60)*60)/24/60), 1, 'All Times',
trunc(o.offer_status_date,'hh24') +
(trunc(to_char(o.offer_status_date,'mi')/60)*60)/24/60) TIME,
sum(decode(o.offer_status_code, 'A', 1, 0)) "Accepts",
sum(decode(o.offer_status_code, 'D', 1, 0)) "Declined",
sum(decode(o.offer_status_code, 'C', 1, 0)) "Canceled",
count(o.offer_status_code) "Totals"
from offer o
where o.offer_status_date between to_date( '12-jun-2002 00:00:00', 'dd-mon-yyyy
hh24:mi:ss' )
                 and to_date( '13-jun-2002 23:59:59', 'dd-mon-yyyy hh24:mi:ss')
  and o.offer_status_code in ('A', 'D', 'C')
group by rollup(trunc(o.offer_status_date,'hh24') +
(trunc(to_char(o.offer_status_date,'mi')/60)*60)/24/60)

June 13, 2002 Arunasalam -- Thanks for the question regarding "Multi row table from Oracle (Like Dual)", version 8.1.7
You Asked
Hi,
Good Day!. I would like to know whether oracle provides any  table like dual
which returns many number of rows. (the number of rows should be able to
restricted by the application developer).

Scenario:
For example, we need to find the weeks between two dates (in WWRRRR format) and
to link it with the actual data table in outer join fashion.
Right now we are using a stored procedure to populate the global temporary table
with all the weeks and then outer join it with the actual table. The result is
shown in graphs.

Question:
As we need to avoid using the temporary table, I would like to know whether
oracle provides any table (like dual) which returns "n" number of rows).

Thanks in Advance for your help
Arun KarthiK
and we said...
Sure, here is how to do it in 8i and a better (more memory efficient) way in 9i:

ops$tkyte@ORA9I.WORLD> create or replace type myTabType as table of number
  2  /

Type created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> create or replace function virtual_table( p_nrows in
number ) return myTabType
  2  as
  3       l_data myTabType := myTabType();
  4  begin
  5       l_data.extend(p_nrows);
  6       return l_data;
  7  end;
  8  /

Function created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> variable n number
ops$tkyte@ORA9I.WORLD> exec :n := 5;

PL/SQL procedure successfully completed.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> select *
  2    from TABLE( cast(virtual_table(:n) as myTabType) )
  3  /

COLUMN_VALUE
------------

5 rows selected.

that'll go in 8i and this will go in 9i:

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> create or replace function virtual_table( p_nrows in
number ) return myTabType
  2  PIPELINED
  3  as
  4  begin
  5       for i in 1 .. p_nrows
  6       loop
  7            pipe row (null);
  8       end loop;
  9       return;
 10  end;
 11  /

Function created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> select *
  2    from TABLE( cast(virtual_table(:n) as myTabType) )
  3  /

COLUMN_VALUE
------------

5 rows selected.

of course, you can fill myTabType with some values if you like in 8i and in 9i,
you can pipe row (whatever value you want)
June 13, 2002 Betty -- Thanks for the question regarding "Decode Function", version 8.1.7.4
You Asked
I have a table:
Test_Table
key    number(10),
aflags2    char(1)

aflags2 uses all eight bits for flags.  I can select each bit as follows:
decode(bitand( ascii(aflags2),1),1, 'P', ' ') ,
decode(bitand( ascii(aflags2),2),2, 'PH', ' '),
and so on for all 8 bits

I have a create view for this table:
Create view Test_view
as
    select
           key as Test_Key,
           decode(bitand( ascii(aflags2),1),1, 'PL', ' ') as Panic_Low,
           decode(bitand( ascii(aflags2),2),2, 'PH', ' ') as Panic_High
from Test_Table;

Now I need to write a report that has the following conditions:
If the bit flags are set (in aflags2)
if bit 1 and bit 2 are set then make value 'P'
else
if bit 1 only make value 'PL'
else
if bit 2 only make value 'PH'
if not set value is 'space '

Can I do this with a decode in the create view or do I need to use PL/SQL
if-then-else?
Thanks
and we said...
You can decode on AFLAGS and compare that to a bitand of aflags to see if they
are the same:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( aflags char(1) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select chr(rownum) from
all_objects where rownum < 256;

255 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select ascii(aflags),
  2         decode( bitand(ascii(aflags),3),
  3                                    1, 'PL',
  4                                    2, 'PH',
  5                                    3, 'P',
  6                                           ' ' )
  7    from t
  8  /

ASCII(AFLAGS) DE
------------- --
            1 PL
            2 PH
            3 P
            4
            5 PL
            6 PH
            7 P
            8
            9 PL
           10 PH
           11 P
           12
           13 PL
           14 PH
           15 P
           16

Now, I masked off the low two bits using:

bitand(ascii(aflags),3)

so it was only looking at bits 1 and 2 -- if that is not what you wanted (eg:
you wanted to make sure bits 3, 4, 5, 6, 7, 8 were zeros as well) just remove
the bitand part of that -- just use ascii.

Once we have the bitand(ascii) -- or just ascii -- we can just compare to
numbers again -- like you did.
June 13, 2002 Suresh -- Thanks for the question regarding "hints", version 8.1.7
You Asked
Tom,
   Recently, I was working with hints in Oracle and in one of the cases when I
wrote a SQL statement with an invalid hint, Oracle did not come up with any
error. Why is Oracle accepting an invalid hint without coming up with an error
and is there a reason why Oracle is not validating it?

Select /*+ kjklfgjdskj */ count(*) from emp;
COUNT(*)
-------
14

Thanks,
Suresh
and we said...
because that is quite simply "a comment"

A comment that HAPPENS to be in the format of a hint will be used as such.

A comment that is not in the format of a hint will not be used as such.
June 13, 2002 Doug -- Thanks for the question regarding "Availability Options", version 8.1.7
You Asked
Tom.. I have this bookmark to a post you had about the different options for
24x7 with Oracle:
http://www.oracle.com/oramag/oracle/00-May/index.html?o30tom.html
I wondered if it deserved some futher comments in light of Data Guard and RACs.
 Would you comment on the horizontal and verticle capabilities of RACs?  What's
the largest number of nodes running RACs that you know of?

Thanks.
and we said...
probably the only thing I would change would be to remove:

*  You have to design your application to work with OPS. You have to understand
OPS and be aware of the potential performance bottlenecks. (Oracle8i Release
8.1.6 has a special two-node-cluster mode of OPS. If you use this mode, 100
percent of the performance issues are nonissues. Both nodes are used to read
data, but only one node is used to write data. The second node takes over for
the first node in the event of a failure and starts writing as well.)

that is not true with RAC anymore.  Data Guard is a better standby with more
features.  To have a truly available system -- it takes two things in my
opinion:

o RAC in a room -- to make sure that server system never goes down.  Scalable
and available

o data guard over the wan -- to make sure that if the room with RAC "goes away"
for whatever reason, you have upto date information in a failover site.

9iR2 makes data guard even more appealing with the introduction of a logical
standby database.

Most RAC clusters are in the 2-4-8 range.  Those are the ones I see frequently.
I know of a data warehouse running 40-50 nodes (might be more by now) on an SP2.
Review & Followup
Rating: 5
Great Tom - thanks for the update June 18, 2002
Reviewer: Doug C from Danbury, CT USA
Thanks Tom.  I've always liked that post in oramag for an outline of the
available options.

June 13, 2002 Mark -- Thanks for the question regarding "Summarizing Data over Sunday's but only for sunday's of project's existence", version 9i
You Asked
Hi Tom,

In the following question asked previous
http://asktom.oracle.com/pls/ask/f?p=4950:8:926329::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:4222062043865,
You showed how to summarize data over 5 or 10 minute increments. I want to take
this a step further.

We have four tables that are equi-joined. They are budgets for projects. Each
project has detail records that start on different dates and end at different
dates.

I want to summarize the data to each Sunday that the project exists, but not
before it started or after it ended. Now the dates in the table is a Sunday, but
they sometimes skip sunday's.

The definition of a project ending is in the project table. There is a closed
date, if it is null the job is still open and needs to continue getting result
set records, even if there hasn't been a budget entered for months. But if there
is a closed date in the project table, then the "Weekly Sunday" records go up
until that date.

Todays date is 6/13/02 so these jobs won't have a 6/16/record yet.
But there are some records out there that have been entered for future dates(bad
data entered) and needs to be in the result set.

Oh, one last thing before the examples, speed is an issue. Meaning if it takes
over two hour to create this table, which we are making a Materialized View of,
then it might be too long. Currently there are 180,000 records in the detail
table, the materialized view is expected to return 5 - 10 million records.

As an example

Project    Close Date
1           NULL
2           NULL
3           1/28/02

Project    Week_date    Labor Hours Budgeted
1           5/5/02             40
1           5/12/02            30
1           5/26/02            10
1           6/9/02             20
2           5/12/02            50
2           5/26/02            50
3           1/6/02             10
3           1/20/02            10

So the result set expected will show

Project    Week Date   Labor Hours Budgeted    Labor Hours To Date
1          5/5/02             40                      40
1          5/12/02            30                      70
1          5/19/02             0                      70
1          5/26/02            10                      80
1          6/2/02              0                      80
1          6/9/02             20                     100
2          5/12/02            50                      50
2          5/19/02             0                      50
2          5/26/02            50                     100
2          6/2/02              0                     100
2          6/9/02              0                     100
3          1/6/02             10                      10
3          1/13/02             0                      10
3          1/20/02            10                      20
3          1/27/02             0                      20

Thank you very much for your time.

Mark
and we said...
ops$tkyte@ORA9I.WORLD> break on project skip 1
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> select t3.project, t3.week_date, nvl(t2.lhb,0),
  2         sum(t2.lhb) over ( partition by t3.project order by t3.week_date )
lhtd
  3    from t2,
  4         ( select project, week_date
  5             from ( select next_day( trunc(sysdate,'year')-1, 'sun'
)+(7*(rownum-1)) week_date
  6                      from all_objects
  7                     where rownum <= (sysdate-trunc(sysdate,'year'))/7 ),
  8                  t1
  9            where week_date <= nvl(close_date,sysdate)
 10              and week_date >= ( select min(week_date)
 11                                   from t2
 12                                  where t2.project = t1.project )
 13         ) t3
 14   where t2.project(+) = t3.project
 15     and t2.week_date (+) = t3.week_date
 16   order by t3.project, t3.week_date
 17  ;

   PROJECT WEEK_DAT NVL(T2.LHB,0)       LHTD
---------- -------- ------------- ----------
         1 05/05/02            40         40
           05/12/02            30         70
           05/19/02             0         70
           05/26/02            10         80
           06/02/02             0         80
           06/09/02            20        100

         2 05/12/02            50         50
           05/19/02             0         50
           05/26/02            50        100
           06/02/02             0        100
           06/09/02             0        100

         3 01/06/02            10         10
           01/13/02             0         10
           01/20/02            10         20
           01/27/02             0         20

15 rows selected.

Now, for an explanation of what it does.

First -- i needed a set of all PROJECT's + weeks that we should report.  I
started by generating all of the weeks in this year so far (you would need to
generate the set you want to support -- the trunc(sysdate,'year') might be
(select min(week_date) from t2 instead)

ops$tkyte@ORA9I.WORLD> select next_day( trunc(sysdate,'year')-1, 'sun'
)+(7*(rownum-1)) week_date
  2    from all_objects
  3   where rownum <= (sysdate-trunc(sysdate,'year'))/7
  4  /

WEEK_DAT
--------
01/06/02
01/13/02
......

Now, once I had that, I did a non-equi join with T1 to synthesize all
project+week_date ranges of interest:

ops$tkyte@ORA9I.WORLD>        ( select project, week_date
  2             from ( select next_day( trunc(sysdate,'year')-1, 'sun'
)+(7*(rownum-1)) week_date
  3                      from all_objects
  4                     where rownum <= (sysdate-trunc(sysdate,'year'))/7 ),
  5                  t1
  6            where week_date <= nvl(close_date,sysdate)
  7              and week_date >= ( select min(week_date)
  8                                   from t2
  9                                  where t2.project = t1.project )
 10         )
 11  /

   PROJECT WEEK_DAT
---------- --------
         1 05/05/02
           05/12/02
           05/19/02
           05/26/02
           06/02/02
           06/09/02

         2 05/12/02
           05/19/02
           05/26/02
           06/02/02
           06/09/02

         3 01/06/02
           01/13/02
           01/20/02
           01/27/02

15 rows selected.

Now, that query shouldn't take too long to materialize - even with lots of
projects and lots of dates....

Then, we just outer joined to that by project+week_date -- and used the analytic
functions to create the "to date" field (since we are joining on week_date and
project -- and we partition by project/order by week_date and sort by
project/week_date -- it'll be efficient to use that in the analytic functions)
Review & Followup
Rating: 5
To Dates are ready to go. June 13, 2002
Reviewer: Mark Spritzler from Santa Ana, CA
Thanks Tom,

Again you have responded quickly and accurately.

We very much appreciate you time and effort.

Rating: 3
Great query that should have helped. Probably wrong on our end June 17, 2002
Reviewer: Mark Spritzler from Santa Ana, CA
We tried to use your query in our systems. Unfortunately when we ran it, it took
42 hours to complete.

Here is our query:

create materialized view test_jc_bd_t_todate_mv_lim2 nologging as
select
    t3.PROJECT_ID,
    t3.JOB_ID,
    t3.FLOOR_ID,
    t3.AREA_ID,
    t3.TASK_ID,
    t3.WEEKLY_DATE,
    nvl(t2.SUM_LABOR_HOURS,0),
    nvl(sum(t2.SUM_LABOR_HOURS) over ( partition by t3.PROJECT_ID,
                                                    t3.JOB_ID,
                                                    t3.FLOOR_ID,
                                                    t3.AREA_ID,
                                                    t3.TASK_ID
                                       order by t3.WEEKLY_DATE ),0)
SUM_LABOR_HOURS,
    nvl(t2.SUM_SUBS_DOLLARS,0),
    nvl(sum(t2.SUM_SUBS_DOLLARS) over ( partition by t3.PROJECT_ID,
                                                    t3.JOB_ID,
                                                    t3.FLOOR_ID,
                                                    t3.AREA_ID,
                                                    t3.TASK_ID
                                       order by t3.WEEKLY_DATE ),0)
SUM_SUBS_DOLLARS,
    nvl(t2.SUM_MATERIAL_DOLLARS,0),
    nvl(sum(t2.SUM_MATERIAL_DOLLARS) over ( partition by t3.PROJECT_ID,
                                                    t3.JOB_ID,
                                                    t3.FLOOR_ID,
                                                    t3.AREA_ID,
                                                    t3.TASK_ID
                                       order by t3.WEEKLY_DATE ),0)
SUM_MATERIAL_DOLLARS
from
    aw_bd_task_weekly_mv t2,
    (
        select t1.PROJECT_ID,
           t1.JOB_ID,
           t1.FLOOR_ID,
           t1.AREA_ID,
           t1.TASK_ID,
           t4.WEEKLY_DATE
    from   TEST_SUNDAY_MV t4,
           (
           select a.project_id,
                  a.job_id,
                  a.floor_id,
                  a.area_id,
                  a.task_id,
                  a.weekly_date,
                  b.complete_date
           from   aw_bd_task_weekly_mv a,
                  project@sales_dw b
           where  a.project_id(+) = b.project_id
           ) t1
    where
           t4.weekly_date <= nvl(t1.complete_date,sysdate)
           and t4.weekly_date >= (select min(t5.WEEKLY_DATE) WEEKLY_DATE
                                  from   (
                                         select a.project_id,
                                                a.job_id,
                                                a.floor_id,
                                                a.area_id,
                                                a.task_id,
                                                a.weekly_date,
                                                b.complete_date
                                           from aw_bd_task_weekly_mv a,
                                                project@sales_dw b
                                           where  a.project_id(+) = b.project_id
                                           ) t5
                                  where t1.PROJECT_ID = t5.project_id
                                        and t1.JOB_ID = t5.JOB_id
                                        and t1.FLOOR_ID = t5.FLOOR_id
                                        and t1.AREA_ID = t5.AREA_id
                                        and t1.TASK_ID = t5.TASK_id
                                 )
    ) t3
where
    t3.PROJECT_ID(+) = t2.PROJECT_ID
    and t3.JOB_ID(+) = t2.JOB_ID
    and t3.FLOOR_ID(+) = t2.FLOOR_ID
    and t3.AREA_ID(+) = t2.AREA_ID
    and t3.TASK_ID(+) = t2.TASK_ID
    and t3.WEEKLY_DATE(+) = t2.WEEKLY_DATE

June 13, 2002 Senthil -- Thanks for the question regarding "partition key", version 8.1.5
You Asked
hi thomas,

a question about partition key.

i would like to know the limitations and options for creating partion keys. That
is, I know its possible to create partions on number, character or date
column.But,
(i) is it possible to create partitions on the value derived from the other
columns.
(ii) is it possible to create partition on the month(ie, 12 partitions) of the
given 'date' column.

please let me know.

thanks & regards,
senthil
and we said...
(i) only if the value is permanently stored.  So I can use a trigger to derive a
column based on other column values/other tables and partition on that.

(ii) if you create a column "MM" and have a trigger maintain:

   :new.mm := to_char( date_column, 'MM' );

but then you will have to use that in queries as well. (cannot use "where
date_column = sydate" and expect partition elimination to occur, would need to
use "where date_column = sysdate and to_char(sysdate,'mm') = MM")

If you are trying to just distribute data evenly aross 12 partitions -- you want
HASH partition.

If you want to keep 12 months of data online, you want to use a sliding window
and just partition naturally on the DATE_COLUMN field.
Review & Followup
Rating: 3
June 05, 2001
Reviewer: A reader
That's great

Rating: 4
Sliding Window / date column partition? June 12, 2002
Reviewer: Eric Raskin from Pleasantville, NY USA
Great article, but I'm not quite clear on the "sliding window" you mentioned.  I
have many tables where partitioning by date and keeping last 24 months would be
useful.  Can you give me an example?

Followup:
If you have my book -- I describe this in detail in the chapter on partitioning.

Basically -- you would partition by month.  Each month in a partition.

When you get a new month (on the last day of a month -- before data for the new
month comes in), you would SPLIT the last partition into two -- one that holds
this current month and one for next month.  Now you have 25 months online.

Tomorrow, when the oldest month is no longer needed, you can drop that
partition.  You have 24 months again.

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:928294572145
for an online example

Rating: 5
split vs add June 13, 2002
Reviewer: Connor from UK
A number of texts advise 'split last partition into two' versus others which
advise 'add new partition' as a means of keeping the partition window "up to
date".  Besides possible differences in the validation of incoming data (ie
whether data above the current highest partition is permitted or rejected), is
there any advantage of one over the other?

Cheers
Connor

Followup:
Well, the split last partition works nicely if the last partition is empty (or
nearly so).  The example here:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:928294572145
does a split last partition -- but I used a partition called the_rest which I
expect to be empty.  BUT -- since its upper bound is MAX_VALUE, it will catch
everything at the high end.  Now, splitting an empty or almost empty partition
is very fast -- so this technique is useful if you expect that there might be a
couple of "early entries" and you don't want them to fail.

The add partition technique would have the upper partition have a hard limit on
the values inserted into the table (not max_value, some other hard stop value).
In this case, if there were a couple of "early" entries -- they would be
rejected.

It is a matter of need in this case.  I sort of like the "the_rest" concept
personally as I find it tells me right off the bat -- I'm doing a window here.

Rating: 5
Split Partition Comment June 13, 2002
Reviewer: George Spears from Dayton, OH
Just a general comment as relates to split partition vs. add partition:

Split partition WILL invalidate the indexes, whereas add partition will NOT
invalidate indexes.  This can have a substantial time ramification (since the
index(es) will have to be rebuilt).... as well as a performance ramification
since invalidated indexes are not usable, therefore execution plans will switch
to "partition scans" as opposed to index lookups.

I know 9iR2 has done some optimization with the Split Partition, something
called a fast split.  It may correct for this issue....

Followup:
Good point -- if you have global indexes, this can be a real bother (until 9i
when you can split with index maintenance).

Rating: 5
Split Indexes June 13, 2002
Reviewer: George Spears from Dayton, OH
Split partition will also invalidate local indexes...  at least in 9i R1.

Followup:
Yes, but it'll only invalidate the local index partitions on the partition you
are splitting -- and in keeping with

<quote>
does a split last partition -- but I used a partition called the_rest which I
expect to be empty.  BUT -- since its upper bound is MAX_VALUE, it will catch
everything at the high end.  Now, splitting an empty or almost empty partition

is very fast -- so this technique is useful if you expect that there might be
a
couple of "early entries" and you don't want them to fail.
</quote>

we need only rebuild the local index partitions on the partition we split and
since I "expect" them to be empty or near empty -- that'll be a trivial task.

ops$tkyte@ORA9I.WORLD> CREATE TABLE t
  2  (
  3    COLLECTION_YEAR     int,
  4    COMPOUND_NUMBER     number
  5  )
  6  PARTITION BY RANGE (COLLECTION_YEAR) (
  7    PARTITION PART_95 VALUES LESS THAN (1996) ,
  8    PARTITION PART_96 VALUES LESS THAN (1997) ,
  9    PARTITION PART_97 VALUES LESS THAN (1998) ,
 10    PARTITION PART_98 VALUES LESS THAN (1999) ,
 11    PARTITION PART_99 VALUES LESS THAN (2000) ,
 12    PARTITION PART_00 VALUES LESS THAN (2001) ,
 13    PARTITION PART_01 VALUES LESS THAN (MAXVALUE)
 14  )
 15  ;

Table created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> create index t_idx1 on t(collection_year) local;

Index created.

ops$tkyte@ORA9I.WORLD> create index t_idx2 on t(compound_number) local;

Index created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> begin
  2     for i in 1995..2005
  3     loop
  4          insert into t values ( i, i );
  5     end loop;
  6  end;
  7  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> select index_name, status from user_indexes where
index_name like 'T_IDX_';

INDEX_NAME                     STATUS
------------------------------ --------
T_IDX1                         N/A
T_IDX2                         N/A

ops$tkyte@ORA9I.WORLD> select index_name, partition_name, status from
user_ind_partitions where index_name like 'T_IDX_';

INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
T_IDX1                         PART_96                        USABLE
T_IDX1                         PART_97                        USABLE
T_IDX1                         PART_98                        USABLE
T_IDX1                         PART_99                        USABLE
T_IDX1                         PART_00                        USABLE
T_IDX1                         PART_01                        USABLE
T_IDX2                         PART_95                        USABLE
T_IDX2                         PART_96                        USABLE
T_IDX2                         PART_97                        USABLE
T_IDX2                         PART_98                        USABLE
T_IDX2                         PART_99                        USABLE
T_IDX2                         PART_00                        USABLE
T_IDX2                         PART_01                        USABLE
T_IDX1                         PART_95                        USABLE

14 rows selected.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> alter table t
  2  split partition part_01
  3  at( 2002 ) into ( partition part_01, partition part_02 )
  4  /

Table altered.

ops$tkyte@ORA9I.WORLD> select index_name, status from user_indexes where
index_name like 'T_IDX_';

INDEX_NAME                     STATUS
------------------------------ --------
T_IDX1                         N/A
T_IDX2                         N/A

ops$tkyte@ORA9I.WORLD> select index_name, partition_name, status from
user_ind_partitions where index_name like 'T_IDX_';

INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
T_IDX1                         PART_96                        USABLE
T_IDX1                         PART_97                        USABLE
T_IDX1                         PART_98                        USABLE
T_IDX1                         PART_99                        USABLE
T_IDX1                         PART_00                        USABLE
T_IDX2                         PART_95                        USABLE
T_IDX2                         PART_96                        USABLE
T_IDX2                         PART_97                        USABLE
T_IDX2                         PART_98                        USABLE
T_IDX2                         PART_99                        USABLE
T_IDX2                         PART_00                        USABLE
T_IDX2                         PART_01                        UNUSABLE
T_IDX1                         PART_95                        USABLE
T_IDX2                         PART_02                        UNUSABLE
T_IDX1                         PART_01                        UNUSABLE
T_IDX1                         PART_02                        UNUSABLE

16 rows selected.

those are the only ones we need rebuild and -- if they are empty/near empty --
it'll be a breeze (so I really don't consider it).  I would NOT recommand
splitting a large, FULL partition as a regular standard "operating procedure".

Rating: 5
Partition Split June 13, 2002
Reviewer: George Spears from Dayton, OH
Fully in agreement.  I just brought up the point since this bit me in a recent
benchmark, and I had over a billion rows in the partition.... (one month of
data), and it had 7 indexes....  Ugly situation.... <grin>

In my situation, I definitely wanted to "add partition" instead of "split
partition".  Unfortunately, I had to find this out the hard way.

Good feedback Tom!

June 13, 2002 Venkateswara Reddy -- Thanks for the question regarding "statistics", version 8.1.5
You Asked
Hi Tom

Here one more query.

I have a table like

dir_name varchar2(100)
file_name varchar2(50)
extn varchar2(10);

I have some data inthis table. This contains one directory and exisiting files
with extnsion like .doc,.xls,.sql on  a particular directory. Here I want query
like

                 Total Files  *.docfls  *.xlsFiles *.sqlFiles *.fmb'Fl
c:\hrms\docs    20               10        5            5
c:\hrms\forms   22                                                22

some thing like this, If system found new extenstion in a particular directory
the query has form like above contain format.

How can I do that. Any help highly appreciated.

Thanks in  Advance

Reddy
and we said...
You need to dynamically generate that query unless you have a fixed set of file
extensions.

The query you will generate will look like this:

select dir_name,
       count(*),
       sum( decode( extn, 'doc', 1, 0 ) ) docfls,
       sum( decode( extn, 'xls', 1, 0 ) ) xlsfls,
       sum( decode( extn, 'sql', 1, 0 ) ) sqlfls,
       .....
       sum( decode( extn, 'fmb', 1, 0 ) ) fmbfls
  from t
 group by dir_name
/

A stored procedure that will do this is:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( dir_name varchar2(100),
file_name varchar2(50), extn varchar2(10) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      type array is table of varchar2(50);
  3
  4      l_dir_names  array := array( 'c:\hrms\docs', 'c:\hrms\forms' );
  5      l_dir_cnt    number default l_dir_names.count;
  6      l_extns      array := array( 'doc', 'xls', 'sql', 'fmb', 'txt' );
  7      l_extn_cnt   number default l_extns.count;
  8  begin
  9      for i in 1 .. 1000
 10      loop
 11          insert into t values
 12          ( l_dir_names(dbms_random.value( 1, l_dir_cnt ) ),
 13            null,
 14            l_extns( dbms_random.value( 1, l_extn_cnt ) ) );
 15      end loop;
 16      commit;
 17  end;
 18  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package demo_pkg
  2  as
  3      type rc is ref cursor;
  4
  5      procedure get_cursor( p_cursor in out rc );
  6  end;
  7  /

Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package body demo_pkg
  2  as
  3
  4
  5  procedure get_cursor( p_cursor in out rc )
  6  is
  7      l_query long := 'select dir_name, count(*)';
  8  begin
  9      for x in ( select distinct extn from t )
 10      loop
 11          l_query := l_query || ',sum(decode(extn,'''||x.extn||''',1,0)) ' ||
x.extn || 'fls';
 12      end loop;
 13      l_query := l_query || ' from t group by dir_name';
 14
 15      open p_cursor for l_query;
 16  end;
 17
 18  end;
 19  /

Package body created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autoprint on
ops$tkyte@ORA817DEV.US.ORACLE.COM> variable x refcursor
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec demo_pkg.get_cursor( :x );

PL/SQL procedure successfully completed.

DIR_NAME        COUNT(*)     DOCFLS     FMBFLS     SQLFLS     TXTFLS     XLSFLS
------------- ---------- ---------- ---------- ---------- ---------- ----------
c:\hrms\docs         504         72        113        117         70        132
c:\hrms\forms        496         64        122        118         58        134

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review & Followup
Rating: 4
Statistics June 06, 2002
Reviewer: Reddy from Bangalore, India
Nice  Solution. But, In my table N no of extensions may there. In a particular
directory contains many extensions. The query should give when i execute that N
no of count of files. I donot want to specify the Extension name such as
.XLS,.DOC,.FMB. But above specfied format should come.Here if any new extension
found in that table like .DLL, .qry or .vbp the query shoudl take care.

Thanks in advance

Reddy

Followup:
I don't believe you read the answer.  Read it again.

There is a static query that shows the ONLY way to do this in sql (by hardcoding
the values) followed by a stored procedure that dynamically generates the query
with any and all of the values it finds in the table -- "if any new extension is
found" -- the stored procedure returning a ref cursor will supply it.

There isn't another way to do this with rows and columns -- period.

Rating: 4
Statistics June 06, 2002
Reviewer: Reddy from Bangalore, India
Nice  Solution. But, In my table N no of extensions may there. In a particular
directory contains many extensions. The query should give when i execute that N
no of count of files. I donot want to specify the Extension name such as
.XLS,.DOC,.FMB. But above specfied format should come.Here if any new extension
found in that table like .DLL, .qry or .vbp the query shoudl take care.

Thanks in advance

Reddy

Rating: 4
statistics June 08, 2002
Reviewer: Reddy from Bangalore, India
Thank you Mr. Tom

I have created successfully package and package body. But when I execute the
following error occured.

Error at line No : 1
ORA-06512 : Invalid Number
ORA-06512 : at line number 12

I have changed instead of Long Data type to Varchar2(2000), then it says Numeric
or Value arror at line number 12.

My database is 8.1.5 Standard Edition.

Thanks in advance

Reddy

Followup:
why would you change the length of a string in response to "invalid number"????

Anyway, my line 12 is a "end loop".  what is YOUR line 12 (after putting the
string BACK to long so as to get back to the original error, instead of the self
induced error of "string too small" you have).

What modifications to my code did you make?  perhaps you should post a complete
example we can all run (in the manner of my example)

did you run my example "as is" -- this block:

ops$tkyte@ORA815.US.ORACLE.COM> declare
  2      type array is table of varchar2(50);
  3
  4      l_dir_names  array := array( 'c:\hrms\docs', 'c:\hrms\forms' );
  5      l_dir_cnt    number default l_dir_names.count;
  6      l_extns      array := array( 'doc', 'xls', 'sql', 'fmb', 'txt' );
  7      l_extn_cnt   number default l_extns.count;
  8  begin
  9      for i in 1 .. 1000
 10      loop
 11          insert into t values
 12          ( l_dir_names(mod( i, l_dir_cnt ) +1 ),
 13            null,
 14            l_extns( mod(i, l_extn_cnt ) +1 ) );
 15      end loop;
 16      commit;
 17  end;
 18  /

will generate test data for you in 815 (i ran this test in 815, my code works
fine, must be some modification you added to my code, or some difference in the
reality of your tables and the tables you discussed in the question)

Rating: 5
Tuning scripts June 10, 2002
Reviewer: Andre from Brazil
Do you have some personal collection of scripts you might make available to us ?
My main interest is tuning scripts reflecting much of what is discussed on this
site.

Followup:
for general tuning I use statspack.

For tuning tuning -- well, I typically start from scratch.  Yes -- from scratch.

I find most issues are "unique" (if they are not, statspack picks up on it for
me).  I develop scripts for the site I'm at -- and find they are not generally
reusable.

So -- sorry, I don't have a directory full of tuning scripts.  utllockt,
statsback (estat/bstat before that) and tkprof are my main tools.

Rating: 4
STATISTICS June 12, 2002
Reviewer: Reddy from Bangalore, India
Hi tom
Please look up this code and tell me where exactly wrong is there.

scott@PSI1> ED
Wrote file afiedt.buf

  1  Create or Replace package demo_pkg
  2   as
  3       type rc is ref cursor;
  4       procedure get_cursor( p_cursor in out rc );
  5*  end;
scott@PSI1> /

Package created.

scott@PSI1> ed
Wrote file afiedt.buf

  1  Create or Replace package body demo_pkg
  2  as
  3  procedure get_cursor( p_cursor in out rc )
  4  is
  5      l_query LONG := 'select directory_name, count(*)';
  6  begin
  7      for x in ( select distinct extn from VC_DIR_STS_TBL )
  8     loop
  9  l_query := l_query || ',SUM(decode(extn,'''||x.extn||''',1,0)) ' || x.extn
|| 'fls';
 10  end loop;
 11  l_query := l_query || ' from VC_DIR_STS_TBL group by directory_name';
 12        open p_cursor for l_query;
 13    end;
 14*   end;
 15  /

Package body created.

scott@PSI1> desc VC_DIR_STS_TBL
 Name                            Null?    Type
 ------------------------------- -------- ----
 DIRECTORY_NAME                           VARCHAR2(500)
 FILE_NAME                                VARCHAR2(1000)
 EXTN                                     VARCHAR2(10)

scott@PSI1> serveroutput OFF
serveroutput OFF
linesize 80
set autoprint on
scott@PSI1> variable x refcursor
scott@PSI1> exec demo_pkg.get_cursor(:x);
begin demo_pkg.get_cursor(:x); end;

*
ERROR at line 1:
ORA-01722: invalid number
ORA-06512: at "SCOTT.DEMO_PKG", line 12
ORA-06512: at line 1

scott@PSI1> spool off
Thanks in advance

Reddy

Followup:
time for you to do some debugging,  use a procedure like:

create or replace procedure p ( p_str in varchar2 )
is
   l_str   long := p_str;
begin
   loop
      exit when l_str is null;
      dbms_output.put_line( substr( l_str, 1, 250 ) );
      l_str := substr( l_str, 251 );
   end loop;
end;
/

to print out the query and run the query standalone in sqlplus.  I cannot
reproduce.  I just:

ops$tkyte@ORA815.US.ORACLE.COM> create table vc_dir_sts_tbl (directory_name
varchar2(10), file_name varchar2(10), extn varchar2(10) );

Table created.

ops$tkyte@ORA815.US.ORACLE.COM> insert into vc_dir_sts_tbl select
substr(username,1,10), substr(username,1,10), substr(username,1,3) from
all_users;

14 rows created.

ops$tkyte@ORA815.US.ORACLE.COM> Create or Replace package demo_pkg
  2   as
  3       type rc is ref cursor;
  4       procedure get_cursor( p_cursor in out rc );
  5   end;
  6  /

Package created.

ops$tkyte@ORA815.US.ORACLE.COM> Create or Replace package body demo_pkg
  2  as
  3  procedure get_cursor( p_cursor in out rc )
  4  is
  5      l_query LONG := 'select directory_name, count(*)';
  6  begin
  7      for x in ( select distinct extn from VC_DIR_STS_TBL )
  8     loop
  9        l_query := l_query || ',SUM(decode(extn,'''||x.extn||''',1,0)) ' ||
x.extn || 'fls';
 10     end loop;
 11     l_query := l_query || ' from VC_DIR_STS_TBL group by directory_name';
 12     p(l_query);
 13     open p_cursor for l_query;
 14  end;
 15  end;
 16  /

Package body created.

ops$tkyte@ORA815.US.ORACLE.COM>
ops$tkyte@ORA815.US.ORACLE.COM> variable x refcursor
ops$tkyte@ORA815.US.ORACLE.COM> exec demo_pkg.get_cursor(:x);
select directory_name, count(*),SUM(decode(extn,'A',1,0))
Afls,SUM(decode(extn,'AUR',1,0)) AURfls,SUM(decode(extn,'B',1,0))
Bfls,SUM(decode(extn,'DBS',1,0)) DBSfls,SUM(decode(extn,'DEV',1,0))
DEVfls,SUM(decode(extn,'GSC',1,0)) GSCfls,SUM(decode(extn,
'OPS',1,0)) OPSfls,SUM(decode(extn,'OUT',1,0))
OUTfls,SUM(decode(extn,'SCO',1,0)) SCOfls,SUM(decode(extn,'SYS',1,0))
SYSfls,SUM(decode(extn,'USE',1,0)) USEfls from VC_DIR_STS_TBL group by
directory_name

PL/SQL procedure successfully completed.

ops$tkyte@ORA815.US.ORACLE.COM> print x

DIRECTORY_   COUNT(*)       AFLS     AURFLS       BFLS     DBSFLS     DEVFLS
GSCFLS     OPSFLS     OUTFLS     SCOFLS     SYSFLS
---------- ---------- ---------- ---------- ---------- ---------- ----------
---------- ---------- ---------- ---------- ----------
    USEFLS
----------
A                   1          1          0          0          0          0
     0          0          0          0          0
         0         0          0          0          0          0
         0

Take the query produced in text and run it and see what happens.  This method

a) works
b) works well
c) works in 815

update on this one... just figured it out.  You have an extn that isn't a
valid identifier in Oracle...

consider:

ops$tkyte@ORA815.US.ORACLE.COM> insert into vc_dir_sts_tbl values ( 3, 3, 3 );

1 row created.

ops$tkyte@ORA815.US.ORACLE.COM> variable x refcursor
ops$tkyte@ORA815.US.ORACLE.COM> exec demo_pkg.get_cursor(:x);
select directory_name, count(*),SUM(decode(extn,'3',1,0))
3fls,SUM(decode(extn,'A',1,0)) Afls,SUM(decode(extn,'AUR',1,0))
AURfls,SUM(decode(extn,'B',1,0)) Bfls,SUM(decode(extn,'DBS',1,0))
DBSfls,SUM(decode(extn,'DEV',1,0)) DEVfls,SUM(decode(extn,'GSC
',1,0)) GSCfls,SUM(decode(extn,'OPS',1,0)) OPSfls,SUM(decode(extn,'OUT',1,0))
OUTfls,SUM(decode(extn,'SCO',1,0))
SCOfls,SUM(decode(extn,'SYS',1,0)) SYSfls,SUM(decode(extn,'USE',1,0)) USEfls
from VC_DIR_STS_TBL group by directory_name
BEGIN demo_pkg.get_cursor(:x); END;

*
ERROR at line 1:
ORA-01722: invalid number
ORA-06512: at "OPS$TKYTE.DEMO_PKG", line 13
ORA-06512: at line 1

there is your error , it is caused by this "SUM(decode(extn,'3',1,0)) 3fls"

solution:

  9        l_query := l_query || ',SUM(decode(extn,'''||x.extn||''',1,0)) "' ||
x.extn || 'fls"';

use QUOTED identifiers (with double quotes).   that'll do it.

Rating: 4
Statistics June 13, 2002
Reviewer: R.V.Reddy from Bangalore, India
Dear Tom

I have taken same code and executed, but again failed says that 'P'Must be
declared. I am
not able debug. I show my development team also, they are unable to find what is
wrong. Here
what is the reason to add ' P(l_query) '. Can you explain plese?

scott@PSI1> Create or Replace package demo_pkg
  2       as
  3           type rc is ref cursor;
  4           procedure get_cursor( p_cursor in out rc );
  5       end;
  6  /

Package created.

scott@PSI1>  Create or Replace package body demo_pkg
  2      as
  3      procedure get_cursor( p_cursor in out rc )
  4      is
  5          l_query LONG := 'select directory_name, count(*)';
  6      begin
  7          for x in ( select distinct extn from VC_DIR_STS_TBL )
  8         loop
  9    l_query := l_query || ',SUM(decode(extn,'''||x.extn||''',1,0)) "' ||
 10  x.extn || 'fls"';
 11        end loop;
 12        l_query := l_query || ' from VC_DIR_STS_TBL group by directory_name';
 13        p(l_query);
 14        open p_cursor for l_query;
 15     end;
 16*    end;
 17  /

Warning: Package Body created with compilation errors.

scott@PSI1> SHOW ERRORS
No errors.
scott@PSI1> SELECT TEXT,LINE,POSITION FROM USER_ERRORS WHERE NAME='DEMO_PKG';

TEXT
--------------------------------------------------------------------------------
      LINE   POSITION
---------- ----------
PLS-00201: identifier 'P' must be declared
        13          7

PL/SQL: Statement ignored
        13          7

scott@PSI1> SPOOL OFF
Thanks in advance

Reddy

Followup:
reread the followup right above this one

a) p is a procedure I wrote and supplied for you there.
b) I found out what the error was and came back and updated the followup.  I
debugged it for you. Answer supplied above.

Rating: 5
July 09, 2002
Reviewer: A reader
Reddy,

    Better quit job...go home and study Oracle Manual

June 13, 2002 J -- Thanks for the question regarding "Imports", version 8.0.5
You Asked
Tom,

Can you export/import LONG RAW columns?? If so, what is the best way?

Does import only allow for bulk loading not selective loading?

Thanks

J
and we said...
yes, you can import/export them.  Just make sure to set buffer= to be BIGGER
then the max long raw you expect to have to deal with.

import either

o loads all rows
o no rows

In 8i, you can use a "query=" parameter to export to selectively export the rows
you want.
Review & Followup
Rating: 3
Converting Long Raw datatype to Varchar2 June 13, 2002
Reviewer: Reddy from Bangalore, India
Hi Tom

Is it possible to convert Long Raw data type to Varchar2 without deleting the
data in a table.

Reddy

Followup:
well -- to a varchar2 -- not really since a long raw holds BINARY large data,
and a varchar2 holds 4000 bytes of CHARACTER (text).

We can convert it to a blob. using TO_LOB in 8i and above.

June 13, 2002 Nag -- Thanks for the question regarding "Dependency", version 8.1.7
You Asked
Tom

Do you have a sql which will give child tables for a given Parent table.
or
A sql which will give all the related tables for a given table.

And  finally, how can I find out with a sql all the tables involved in a view.

Thank you
and we said...
Look at *_DEPENDENCIES, where * = user, all and dba.

For constraints, look at *_constraints.

For example, given a parent table T you can:

select table_name
  from user_constraints
 where (r_constraint_name) in ( select constraint_name
                                  from user_constraints
                                 where table_name = 'T'
                                   and constraint_type in ( 'P', 'U' ) );

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table p ( x int primary key, y int
unique );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table c1 ( x references p );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table c2 ( y int references p(y) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select table_name
  2    from user_constraints
  3   where (r_constraint_name) in ( select constraint_name
  4                                    from user_constraints
  5                                   where table_name = 'P'
  6                                     and constraint_type in ( 'P', 'U' ) );

TABLE_NAME
------------------------------
C1
C2
Review & Followup
Rating: 4
March 29, 2002
Reviewer: A reader

Rating: 3
Need more info June 13, 2002
Reviewer: Gerry Abreu from CA, USA
Hello,
The SQL is fine though I would need SQL for many more levels of dependencies not
just two.
If you would please email me at gerry.abreu@eds.com if you have more info.
Thanks.

Followup:
I would make a copy of user_dependencies then (for performance reasons mostly)
and use connect by ( a sql clause -- see the sql language manual).

Just watch out for recursive dependencies ;)

June 13, 2002 Pat -- Thanks for the question regarding "Using date column as unique key", version 8.1.7
You Asked
Hi Tom,

I'm gathering statistical data using the date column as a unique key.  I've
added a unique constraint on the column + I'm using the trunc(sysdate) to ensure
that there's no duplicates.  The column also has a default "trunc(sysdate)".

How can I prevent anyone inserting duplicates if their SQL is not using the
trunc function?

Thanks.
and we said...
put a trigger that does:

create trigger xxx
before insert on t
for each row
begin
   :new.date_field := trunc(:new.date_field);
end;
Review & Followup
Rating: 4
CHECK contraint June 14, 2002
Reviewer: Michael from Germany
Good solution!

But perhaps a CHECK constraint is another idea for the problem - it prevents all
un-truncted inserts:

create table t (field1 date);
alter table t add (check (field1 = trunc(field1));

insert into t values (sysdate);
ORA-02290: check constraint (SCOTT.SYS_C007898) violated

June 14, 2002 Cezar -- Thanks for the question regarding "Using Ref Cursor as parameter between Stored Procedures", version 8.1.7
You Asked
Hi Tom,

First, congratulations for yours excellent answers. They are really good!

I need to call a package.procedure that returns a Ref Cursor.

How do I manipulate the Ref Cursor in the caller package.procedure?

It is opened called procedure and I don't know "fetch" the return.

Is it possible?

Thanks,

Cezar Santos
and we said...
Just like it was ANY OTHER cursor.  Not any different:

scott@ORA817DEV.US.ORACLE.COM> create or replace package demo_pkg1
  2  as
  3     type rc is ref cursor;
  4
  5     procedure p( x in out rc );
  6  end;
  7  /

Package created.

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> create or replace package demo_pkg2
  2  as
  3     procedure p;
  4  end;
  5  /

Package created.

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> create or replace package body demo_pkg1
  2  as
  3     procedure p( x in out rc )
  4     is
  5     begin
  6      open x for select * from dual;
  7     end;
  8  end;
  9  /

Package body created.

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> create or replace package body demo_pkg2
  2  as
  3     procedure p
  4     is
  5      l_cursor demo_pkg1.rc;
  6      l_dummy varchar2(20);
  7     begin
  8      demo_pkg1.p(l_cursor);
  9
 10      loop
 11         fetch l_cursor into l_dummy;
 12         exit when l_cursor%notfound;
 13         dbms_output.put_line( 'hey, i got ' || l_dummy );
 14      end loop;
 15      close l_cursor;
 16     end;
 17  end;
 18  /

Package body created.

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> exec demo_pkg2.p;
hey, i got X

PL/SQL procedure successfully completed.
Review & Followup
Rating: 4
How can I display a cursor in pl/sql June 14, 2002
Reviewer: A reader
Of course, when I know the structure and the attributes of a certain cursor I
can do a dbms_output.put_line for each attribute. In SQL*Plus I can use print
for display any cursor. How can I do a "print" of a cursor within pl/sql without
knowing the structure of the cursor. Many thanks for a good idea.

Followup:
You cannot -- there are no constructs for dynamically accessing a ref cursor --
it is 100% a compile time thing.

You would be looking to use DBMS_SQL to dynamically process any query.  Search
this site for dump_csv for example.  That shows plsql taking any query and
creating a delimited file from it.  Without knowing the number and types of
columns until runtime.

Rating: 5
One more time!!! June 14, 2002
Reviewer: Cezar A Santos from S?o Paulo - Brazil
Again you solve the problems!!!!

Thanks!

June 14, 2002 Johnson -- Thanks for the question regarding "LONG Data type & exp7 for 815", version 8.1.5
You Asked
You have always been a source for my problem.

I have two questions.

1. I have a table which has a Long Data Type Column.
   If I try to duplicate the table using the following command, it   doesn't
allow me :

create table just as select * from test where 2=3;

What could be the problem?
With Long column in a table, is it not possible?

Kindly help.

2. I wish to know whether oracle 8i (8.1.5) has got a export utility for
exporting data in Oracle 7.0 format.

Thank you.
and we said...
Sorry -- if I've been a source of problems for you, I try to be a source of
solutions !!! ;)

1) see
http://asktom.oracle.com/~tkyte/Misc/MoveLongs.html
2) No, the 815 install doesn't have the 7.x export however, if you have 7.x
somewhere (you must, you want to export to it), you can use that EXP tool from
v7 over sqlnet to the 8i instance.  Just run catexp7 found in the 8i
$ORACLE_HOME/rdbms/admin directory first -- so the necessary v7 export views are
there.
Review & Followup
Rating: 4
LONG Data type & exp7 for 815 June 15, 2002
Reviewer: A reader
Thank you very much for the information.

June 14, 2002 Jim -- Thanks for the question regarding "ORA-24761", version 9.0.1
You Asked
Hi Tom,

Could you provide an explanation as to what this error is trying to tell me.
The error messages manual says "Cause: The application tried to commit a
transaction and the server rolled it back." and
"Action: No action required" is kind of vague.  Why would a transaction be
rolled back with this message?

TIA
and we said...
It is a message you would get with a global transaction for example.

You can start a transaction (a "named" transaction) and have other sessions join
it and participate in it.  If one of the sessions rolls it back however, and
another session tries to use it -- they'll get "ora-24761", the transaction was
rolled back.

see
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76975/oci08sca.htm#425180
you can get a similar message with deferrable constraints:

ops$tkyte@ORA9I.WORLD> create table t ( x int, constraint check_x check ( x > 0
) deferrable initially deferred );

Table created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> insert into t values ( -1 );

1 row created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> commit;
commit
*
ERROR at line 1:
ORA-02091: transaction rolled back
ORA-02290: check constraint (OPS$TKYTE.CHECK_X) violated

as well....
June 17, 2002 Satyendu -- Thanks for the question regarding "Generating Data into csv (Excel Sheet) format", version 8.1.7/8.0.6
You Asked
Hi Tom,

I have got the following doubt.

1. One of my shell script will generate some data from our Database and I want
to send those data in csv format (Excel Sheet) as an attachment through our
email server. As of now I am sending those data as cvs format or spooling into a
file and then sending those data as an attachment using uuencode.

Can you please let me know how to convert into a csv format and send this as an
attachment through mail. Our database is running under AIX4.2 and version used
is 8.0.6/8.1.7.

Regds,
SP
and we said...
Well, you would have to learn all about mime types, how to encode attachments in
email and the like.

Or, you could use java and the javamail API.

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:255615160805
for example - it shows how to use javamail to send an email with an attachment.
You would not have to do a java stored procedure (with 806 you cannot), rather
use the CONCEPT to create a java program outside of the database that does what
you need.
Review & Followup
Rating: 4
converting to CSV file format May 26, 2002
Reviewer: Satyendu from London
Very nice suggestion.

Rating: 1
Can u please throw some more light on this June 15, 2002
Reviewer: A reader
My requirement is to convert the content of a spool file/text file into a Excel
Speread sheet. That too the data contains hierarchical and mathemetical
calculation too.

Followup:
search this site for

owa_sylk

June 17, 2002 Mary -- Thanks for the question regarding "9i", version 8i
You Asked
Hello tom

We are planning to upgrade the system to 9i so I want to know the new features
of 9i.
I have DBA on my company and always the system down and he said that he did know
why. every thing is OK. Could you please advice me about the way to check the
Oracle server 8i I know it is silly question but please I need your help.
and we said...
See
http://download-west.oracle.com/otndoc/oracle9i/901_doc/nav/docindex.htm#index-NEW
thats the new features guide.

Get a new DBA.  Everything is OBVIOUSLY not OK.  There is insufficient
information here -- about the nature of the "down system".  It could be anything
from OS related (the OS failed) to hardware (the network was down) to database
(it crashed for some reason).  I cannot be more specific then that.
Review & Followup
Rating: 5
links give error June 15, 2002
Reviewer: P from LA
tom
some how link doesnot work.
I am thinking of taking of my upgrade 8i to 9i OCP test.
which book do you suggest - or link to new features guide -------which works ;)
there is this book by oracle place
oracle 9i new features - how is it?
thank you for all your advice
P

Followup:
The link works -- sometimes, if you use the search, the hilight routine "breaks"
the link.  If that happens just hit the "bookmark here" link to get a "clean"
page.

You could also just goto

otn -> documentation -> 9i -> 9i new features

June 17, 2002 Kannan -- Thanks for the question regarding "How to find End Hit of table in After update of table level trigger", version 8.1.7
You Asked
Hi,
   By 'selecting' latest updated information in table, i need to do some
calculation (eg.salary). Based on caluclated values i've raised error like
salary execeed..
For that i'm inserting data into temp table which are required for calculation
in before update trigger of each row. In after update trigger of table level i
read data from temp table.

    Suppose i made updation in two rows, after update trigger(table level) fired
2 times.
so that my calculation migth be wrong at first time itself and error thrown. If
we get 2nd row data the calculation may be correct. Can we idendtify end of
table updation hit in after table level trigger.

  Eg. i having 2 rows like

         Name     Salary
         EMP1     2000
         EMP2     2000

  My constraint is, Salary should not exceed 4000.

  Suppose i'm changing 1st row salary as 3000 & 2nd row salary as 1000. The
error should not fire. But at first time of after table level trigger if we sum
salary .. it will be 3000+2000 = 5000. so error message raised. pls reply asap.

thanks and regards
kannan
and we said...
This is exactly why we have a mutating table constraint!

The easiest approach will be to maintain another table "emp aggregate".  Suppose
this constraint of 5,000 was to be enforce by DEPTNO.  You can do this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table emp as select empno, deptno,
trunc(sal/3) sal from scott.emp;

Table created.

that is our emp table...

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table emp_agg
  2  ( deptno number primary key,
  3    sal       number,
  4    constraint sal_cons
  5    check ( sal <= 5000 )
  6    deferrable initially deferred
  7  )
  8  /

Table created.

note how the constraint is DEFERRABLE and initially DEFERRED.  That means, it
can go "out of wack" during our transaction -- it can be temporarily violated --
but before we can commit -- it must be satisfied

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into emp_agg select deptno,
nvl(sum(sal),0) from emp group by deptno
  2  /

3 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from emp_agg;

    DEPTNO        SAL
---------- ----------
        10       2915
        20       3623
        30       3131

Now, we need a trigger to maintain this for us:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger emp_trigger
  2  before insert or update or delete on emp
  3  for each row
  4  begin
  5      if ((updating or deleting) AND :old.sal is not null )
  6      then
  7          update emp_agg set sal = sal-:old.sal where deptno = :old.deptno;
  8      end if;
  9
 10      if ((updating or inserting) AND :new.sal is not null )
 11      then
 12          update emp_agg set sal = sal+:new.sal where deptno = :new.deptno;
 13      end if;
 14  end;
 15  /
Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select sum(sal) from emp where deptno = 20;

  SUM(SAL)
----------
      3623

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> update emp set sal = 2500 where empno = 7902;

1 row updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select sum(sal) from emp where deptno = 20;

  SUM(SAL)
----------
      5123

we have violated our rule but it is OK.  We can either try to commit (which
would fail) or we can check to see if we can commit:

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set constraint sal_cons immediate;
set constraint sal_cons immediate
*
ERROR at line 1:
ORA-02290: check constraint (OPS$TKYTE.SAL_CONS) violated

that tells us "no, you cannot commit -- you have a violation".  Lets correct
that:

ops$tkyte@ORA817DEV.US.ORACLE.COM> update emp set sal = 500 where empno = 7788;

1 row updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set constraint sal_cons immediate;

Constraint set.

and now we know we can commit -- put the constraint back to deferred (for our
next transaction)

ops$tkyte@ORA817DEV.US.ORACLE.COM> set constraint sal_cons deferred;

Constraint set.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select sum(sal) from emp where deptno = 20;

  SUM(SAL)
----------
      4623

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.
and all is well...
Review & Followup
Rating: 5
If the row is not in emp June 13, 2002
Reviewer: pasko from HH,Germany
Hi Tom
Thanks for a Great answer to the aboveQuestion

In the Trigger above:

if ((updating or inserting) AND :new.sal is not null )
     then
    update emp_agg set sal = sal+:new.sal where deptno = :new.deptno;
end if;

Shouldn't we first check to see if a row for a particular deptno is already in
the emp_agg  OR that is  not necessary.

Regards

Followup:
DOH -- forgot about that !!!!

should be:

if ((updating or inserting) AND :new.sal is not null )
then
    update emp_agg set sal = sal+:new.sal where deptno = :new.deptno;
    if (sql%rowcount = 0 )
    then
       insert into emp_agg (deptno,sal) values (:new.deptno,:new.sal);
    end if;
end if;

thanks!!

Rating: 2
Constraint is not constant, It's Dynamic June 14, 2002
Reviewer: kannan from INDIA
  Thanks for ur answer. But Constraint is not constant for us. It will be
retrived from some other entity. So i can't rectify problem as u say.
   Actually i'm trying to commit both records from forms 6i.
Initially i'm having data
          1st row    2000
          2nd row    2000
After i'm changing
          1st row    3000
          2nd row    1000

Now after update of table level trigger fired for Ist row..
      In trigger i'm summing data in table. it's 5000. i'm comparing this with
data 4000 which is reteived from some other table. It exceeds than 4000. So
error raised..
     But if it is fired for 2nd row.... the sum will 4000. so error won't come.
Before that error fired... so we can't change data as per our requirement.
      If we edit data as
          1st row    1000
          2nd row    3000
It will works.  So at the time of updation if we find end hit.. we can avoid at
first time.

pls reply

Followup:
Is this a SINGLE UPDATE that changed two rows

or

TWO updates issued one after the other that change two rows.

Rating: 2
How to Find End Hit of updations June 17, 2002
Reviewer: kannan from INDIA
 TWO updates issued one after the other that change two rows.
  At end of updations in two rows the result will be correct. But at the time of
first row itself it raise error.

Followup:
then a deferrable constraint is the only way as you need the logic to be
validated at commit time, not at statement execution time.  Meaning, you cannot
achieve your desired end goal.

Rating: 5
June 17, 2002
Reviewer: David Penington from Melbourne Australia
Re: "But Constraint is not constant for us. It will be
retrived from some other entity. So i can't rectify problem as u say."
Tom's solution can still be used. In the trigger, instead of just calculating a
summary value, calculate a summary of the calculated constraint evaluation too,
and store 'OK' or 'FAIL' or similar. Then the deferred constraint becomes CHECK
(cons_result = 'OK')deferrable initially deferred.

June 17, 2002 Adam -- Thanks for the question regarding "Cursor or plain select", version 8.1.7
You Asked
I have to overhaul a large amount of legacy which I think is poorly written.

In a main loop it does a VERY large amount of OPEN, FETCH, CLOSE for single row
selects.  I need to cache some of these results however..

   open foo_cur;
   fetch foo_cur INTO foo_rec;
   close foo_cur;

I believe that these should be a single select with a no_data_found exception?
I believe that open and closing cursors would have a slight overhead. A single
row SELECT with an INTO would be preferable, especially since this program has
to be as effecient as possible?

SELECT foo INTO x from bar;

Tests in SQLPLUS do not show any real gain, however in this BATCH process I
believe I would see a significant gain due to sheer amount of iterations and
single selects using cursors.

Is my theory correct?

Many Thanks
and we said...
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1544606261686
yes, your theory is correct.
Review &
      Followup

      Rating: 4
Many
      Thanks  June 17, 2002
Reviewer:
      A reader
Excellent answer and a very quick response!

Many Thanks

June 17, 2002 Marcin -- Thanks for the question regarding "How
      to return a Resultset from Java Stored Procedure", version 9.0.1

      You Asked
I am developing Java Stored Procedure which I intend to call from PL/SQL or
ASP.NET (OLEDB). I would like the procedure to return Resultset as an output
parameter or return value.
I have read in Oracle 9.0.1 docs that returning resultsets from Java Stored
Procedure is supported. However, I haven't found any information on how to
achieve this - could you provide me a complete example including a proper "Call
spec" (which I believe is the key to success here) for publishing procedure in
Oracle?

Thank You very much

Marcin Belczewski
marcin.belczewski@globalintech.pl
GlobalIntech, Poland

      and we said...
The only way I am aware to do this is

o Java returns a STRING that is the query to be opened
o the PLSQL layer (which you MUST have, you need that wrapper layer) opens a
  ref cursor that is that string.

So, you have two tiny plsql procedures -- one is the "wrapper", the other is
something like

is
   l_query long;
begin
   java_stored_procedure( l_query );
   execute immediate 'alter session set cursor_sharing=force';
   open p_cursor for l_query;
   execute immediate 'alter session set cursor_sharing=exact';
end;

So, the java stored procedure -- instead of binding and opening the query --
will just construct it.  The PLSQL layer does the open.
Review &
      Followup

      Rating: 3
Why Oracle docs
      say sth else?  June 17, 2002
Reviewer:  Marcin
      Belczewski  from Poland
Ok Thank You!

As I can see Oracle docs - for example: "Oracle 9i New Features Summary" which
on page 13 says: "...enhancement to Java Stored procedures includes support for
returning multiple rows (REFCURSORS)..." are wrong?!?!?!

Therefore it is not possible to have a proc like this

public void resultSetToClient(ResultSet[] rs)

where rs is a ResultSet output???

          Followup:

The docs are written at the same time as the software.  Rarely, although it does
happen, they document a feature that was supposed to be there -- but did not
quite make it.

I have not discovered a way to do this

June 17, 2002 Carlos Mario -- Thanks for the question regarding
      "Migrate applications to Oracle 8 ", version 8.1.5

      You Asked
Hello Tom,
My name is Carlos Mario, and I work in an Oracle consulting
company in Colombia
Our clients are migrating their databases from Oracle 7.3 to
oracle 8.0 and we are interested to know some strategies to
migrate applications to Oracle 8 in order to improve
performance.

We have the following questions:.
Could you recommend us any strategy in order to migrate our old
Oracle 7.3 applications to oracle 8.0?
It is possible to enjoy their new features without changing our
applications code?
Which features are possible to use with old applications, and
which ones are only possible to use with new ones?
How big is the impact on the applications and what do you
recommend?

      and we said...
Well hopefully you are moving to Oracle8i release 8.1 -- not
8.0...

A simple migration of an application from 7.3 to 8i probably
will not see an immediate performance improvement.  You'll need
a "port" of the application from 7.3 to 8i to see that in most
cases.

There are some cases where you can take advantage of new
features without changing anything.  For example, one of the
easiest ways to speed up a pro*c application is to employ array
processing, especially on SELECTS.  Unfortunately, this used to
involve heavily changing the logic in the client.  For example
the simple code:

   exec sql open c;
   for( i=0;;i++ )
   {
      EXEC SQL WHENEVER NOTFOUND do break;
      EXEC SQL FETCH C INTO :value;
      printf( "row %d = %.*s\n", i, value.len, value.arr };
   }
   EXEC SQL WHENEVER NOTFOUND CONTINUE;
   EXEC SQL CLOSE C;

becomes:

   EXEC SQL OPEN C;
   for( rows_fetched = 0, done = 0;
        !done;
        rows_fetched=sqlca.sqlerrd[2] )
    {
        EXEC SQL FETCH C INTO :value;
        done=(sqlca.sqlcode==1403);

        for( i = 0; i < sqlca.sqlerrd[2] - rows_fetched; i++ )
        {
            printf( "row %d is '%.*s'\n",
                     rows_fetched+i+1,
                     value[i].len, value[i].arr );
        }
    }
    EXEC SQL CLOSE C;

a series of somewhat complicate loops and arrays.  In Oracle8i,
rather then change my code from the simple to the complex -- I
can simply recompile with:

   $ proc .... PREFETCH=200 .....

and now pro*c will implement array fetching for me,
transparently.  When I ask for 1 row -- it'll get 200 and save
them for me.  The next 199 times I ask for a row -- it'll just
get it from memory -- no network.  I get all of the advantages
of array fetching but none of the pain.

Other features, like the ability to do array processing in
PLSQL, will require a modification to your code.  For example, I
can take a simple routine like:

ops$tkyte@8i> begin
  2      for j in 1 .. 5000 loop
  3          insert into insert_into_table values ( j );
  4      end loop;
  5      commit;
  6  end;
  7  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.72

and recode it as:

ops$tkyte@8i> declare
  2   type numTab is table of number(4) index by binary_integer;
  3   data numTab;
  4  begin
  5      for j in 1 .. 5000 loop
  6          data(j) := j;
  7      end loop;
  8
  9      forall i in 1 .. 5000
 10          insert into insert_into_table values( data(i) );
 11      commit;
 12  end;
 13  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.10

They to the same thing, the second one just does it much faster
(there is an array fetch in plsql as well now, BULK COLLECT.  It
will speed up queries in a similar fashion).

Other new features, such as the NOCOPY feature on PLSQL
parameters -- making parameter passing of large
structures/tables much faster -- are very simple to implement.
Just add one word to the definition of the stored procedure and
away you go.

Another example of a feature you can take advantage of without
changing anything is function based indexes.  Say your
application has tons of queries with:

   select * from T where upper(some_column) = :x;

In the past, you could not use any indexes on some_column to
speed up that query.  Now you can -- see

http://osi.oracle.com/~tkyte/article1/index.html
for a write up on that feature.

As Oracle8.0 and 8.1 added hundreds of features, I cannot
possibly list out each one you could or could not use out of the
box without changing your code (I don't know what you code looks
like ;)

I can suggest you take a look at the document:

http://technet.oracle.com/doc/oracle8i_816/server.816/a76962/toc.htm
It lists the new features in 8.0, 8.1.5 and 8.1.6 and gives a
brief overview of each with pointers to more in depth
documentation.  In that fashion, you'll be able to see many of
the new features and decide for yourself which apply and which
do not.  Some, like partitioning, you can slide in "under" the
application without change.  Others, like BULK COLLECT in PLSQL,
require modification to take advantage of it...

Review &
      Followup

      Rating: 4
prefetch,
      fetch  June 15, 2002
Reviewer:  Kapil
       from india
Hi Tom,
Does the first fetch (using the prefetch option) gets the records and store it
in SGA as well as PGA  while the the first fetch (without using the prefetch
option) gets the records only in the SGA?
Can the prefetch option be specified dynamically ie not hard coding the value?

          Followup:

prefetch fetches into the client.

The SGA does not store the records.  The PGA doesn't either (not really, there
are times when using the sort_area_retained_size they are there).  The records
are retrieved as needed from the buffer cache (they do not exist as "rows" in
there, just blocks in a buffer cache) or disk.

We do not pre-assemble the result set and then return stuff.  we return stuff as
you ask for it in general.

so the prefetch controls how many rows the client pulls with each FETCH call.
the client will then cache these rows.  These rows are not also cached on the
server.

The prefetch option is specified during precompile time on the command line or
inline using exec oracle option.  it is not a runtime option.

      Rating: 4
prefetch,
      fetch  June 15, 2002
Reviewer:  Kapil
       from india
Hi Tom,
This is with reference to PRO*C on UNIX.
Do you mean the server process(started after connecting to ORACLE) when you say
client?
Also will the complete data be loaded as oracle blocks in the buffer cache at
the first fetch irrespective of the prefetch option?

          Followup:

No, i mean the client -- the piece of software you wrote, not the dedicated
server process that is running on the server.

The complete data MAY NEVER be loaded as Oracle blocks simultaneously.  Consider
a query that is:

   select * from one_hundred_billion_row_table;

and each row is say 100 varchar2(4000) columns.  You have a machine with 1gig of
ram.  It won't fit -- but the query will run.

We fetch data *as needed*, on demand.  We tend not to "pre answer" the query,
but get the data as we need it, as you fetch it.

      Rating: 4
What are all
      the changs  June 16, 2002
Reviewer:  Reddy
       from Bangalore, India
Hi Tom

I would like to know If i migrate from Oracle 7.3.4 to 8i,
actually what are all the changes will come new version from the old version. I
know the rowid will changes. Any thing new changs is there.

thanks in advance

Reddy

          Followup:

read the getting to know Oracle8i guide:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76962/toc.htm
but why in the year 2002 when Oracle9i Release 2 (two VERSIONS after 8i) is
production, you would be going to 8i.... beyond me...

      Rating: 4
host
      array  June 17, 2002
Reviewer:  Kapil

Hi Tom,
I have used the prefetch option with host arrays as well as otherwise.
But I seem to get a better performance in case of host arrays which is why i
wanted to confirm if the data is stored in the client process or the dedicated
server process.
So is it still better to use host arrays even with the prefetch option?
Also as you mentioned for larger queries the complete data cannot be fetched in
one shot. Then how many blocks are fetched at one time in such cases?

          Followup:

I think it is better to use prefetch.  Great performance, less code.  No muss,
no fuss.

Not only for LARGE queries but small ones as well.  we just do enough work to
answer the current client request.

June 17, 2002 Yves -- Thanks for the question regarding
      "problem running rsh command from Java Stored Procedure", version 8.1.7

      You Asked
Hi There.
I have written a very small Java Stored procedure that runs an OS command and
captures standard output and standard error to two seperate files. It runs
great exec for when I try to do an rsh to a remote host. I suspect that this
is a permission problem from within Oracle but I can't figure out which
permission I need. I have granted myself the following permissions:
call dbms_java.grant_permission('VNUSER','SYS:java.net.NetPermission', '*',
null);
call
dbms_java.grant_permission('VNUSER','SYS:java.security.SecurityPermission','*',
null);
call dbms_java.grant_permission('VNUSER','SYS:java.util.PropertyPermission',
'*','write');
call
dbms_java.grant_permission('VNUSER','SYS:java.lang.reflect.ReflectPermission',
'*', null);
call dbms_java.grant_permission('VNUSER','SYS:java.lang.RuntimePermission',
'*',null);
call dbms_java.grant_permission
'VNUSER','java.io.FilePermission','*','execute');
call
dbms_java.grant_permission('VNUSER','java.lang.RuntimePermission','*','writeFile

Descriptor' );
It is still not working.
Here is my java code:
//***********************************************
import java.util.*;
import java.io.*;
public class Exec
{
public static void ExecDB(String runthis, String outfile, String errfile)
{
try
{

Runtime rt = Runtime.getRuntime();
String stdout = runthis + " 1> " + outfile;
String stderr = " 2> " + errfile;
String[] command = new String[4];
command[0] = "cmd.exe";
command[1] = "/C";
command[2] = stdout;
command[3] = stderr;
Process proc = rt.exec(command);
// any error???
int exitVal = proc.waitFor();
System.out.println("ExitValue: " + exitVal);
} catch (Throwable t)
{
t.printStackTrace();
}
}
}
//************************************************
Here is the PL/SQL that calls the java
CREATE OR REPLACE PACKAGE HOST_CALL AS
PROCEDURE ExecDB (command IN VARCHAR2, outfile IN VARCHAR2, errfile IN
VARCHAR2);
END;
/
show err
CREATE OR REPLACE PACKAGE BODY HOST_CALL AS
PROCEDURE ExecDB (command IN VARCHAR2, outfile IN VARCHAR2, errfile IN
VARCHAR2)
AS LANGUAGE JAVA NAME 'Exec.ExecDB (java.lang.String, java.lang.String,
java.lang.String)';
END;
/
show err
Note that my java code works fine from a Dos command window.
Please help as this is urgent!

      and we said...
The Oracle database runs as a very under privileged user -- no network access by
default.  We install in a secure fashion -- to avoid security related issues (I
know, that might be counter intuitive given the windows platform you are working
on where most stuff is installed in "insecure" mode).

In any case, your issue is much like what is discussed here:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:241814624807
(this is why is works from your DOS command prompt but not from inside the
database -- you are running as two totally different users)

Now, you could change your oracle database to run as a different user -- or you
could look to see if there were a way to avoid using the inheritly non-secure
rsh utility (preferred -- there has to be a better way).
Review &
      Followup

      Rating: 5
What other
      alternative to rsh is there?  November 24, 2001

Reviewer:  Yves  from Washington, D.C.
My goal with the above code was to start a script on another machine, a SUN box,
and capture its standard output and standard error.  If you know a better
solution than the rsh one, please let me know.  I could not think of one.

          Followup:

Is there a database on that machine.

      Rating: 5
follow
      up  November 24, 2001
Reviewer:  Yves

No there is not database on that machine

          Followup:

How about a webserver?  Is there a webserver/appserver on that box?

      Rating: 5
follow
      up  November 24, 2001
Reviewer:  Yves
       from Washington, D.C.
There is a web/apache server on another SUN machine that is networked with the
one the program is on.

I changed the logon account of the database service and I am able to run the rsh
command.  But somehow I can't see the standard output and standard error in
their respective files.  This is my command in sqlplus:
SQL> call host_call.execdb('rsh remotehost echo
abc123','d:\out.txt','d:\err.txt');

This does however work from a doc Command window outside of Oracle.  I have also
set serveroutput on and ran dbms_java.set_output.

          Followup:

Look in the user_dump_destination to see if there are any trace files generated.
 If we don't see any output -- we don't really know that it is running --
perhaps instead of "echo abc123", try "touch /tmp/testing.dat" and see if that
file appears on the remote host.

      Rating: 5
follow
      up  November 24, 2001
Reviewer:  Yves
       from Washington, D.C.
I tried your "rsh remotehost touch /tmp/testing.dat" suggestion and it does
appear on the remote host.  However I am not able to capture the standard output
or standard error with a different command.

      Rating: 5
Follow
      up  November 24, 2001
Reviewer:  Yves
       from Washington, D.C.
I forgot to mention that there are not trace files in the user_dump_dest
directory after I run my command.

          Followup:

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:952229840241
for an alternative way of running a host command and capturing the output - one
that I know works...

      Rating: 1
follow
      up  November 24, 2001
Reviewer:  Yves Azie

I can capture output for any other command run locally with not problem.  It is
the rsh output that somehow I can't get a hold of.

I looked at the code you pointed me to.  It is sending the output to the
console, and not capturing for further parsing by other programs.

          Followup:

Umm, I thought you might be able to take its METHOD of getting stdout and seeing
if that WORKS.  If it works, we can then put that output anywhere we like (as
easy as I wrote it to the "console" using system.out.println, you can write it
to the file).

Can you at least try it?  Just put the data where ever you want to ultimately -
-try it and SEE if we can capture the output to the console -- if we can --
obviously you could then put it in a file.

Under the covers, we do nothing different for rsh then for any other command, if
this is working for other commands, there must be something unique in the way
your rsh is writing to stdout and stderr.

Given that I don't have the facilities to test this, the only thing I can do is
try to have you try different things.

      Rating: 2
Follow
      up  November 24, 2001
Reviewer:  Yves

I have compiled the code you pointed me to in the the database and tested it.
The procedure runs the rsh command without problems and returns the output to
the sqlplus window OK.  That left me puzzled!  My code must be doing something
unsupported.

Anyway, I am now pondering on what modification I need to make to capture
standard output and standard error in two seperate files or variables.

I am not sure if you can help with that.

Thanks a million already for all you have done!!!

      Rating: 5
Its now
      working  November 24, 2001
Reviewer:  Yves
       from washington, D.C.
Hi Tom.
I have successfully modified the code you had refered me to, to now output
STDOUT and STDERR in seperate parsable streams.

Thanks, for your help.  You saved the day.

Cheers.

          Followup:

Care to share?  I think others (including me) would be interested in seeing the
final result....

      Rating: 5
Final
      Code  November 24, 2001
Reviewer:  Yves
       from Washington, D.C.
Here is the modified code.  Note that for my purpose I need to append to the
output and error files during the same database session, that is why I have a
parameter of "true" in the second argument of FileOutputStream.

//
create or replace and compile
java source named "Util"
as
import java.util.*;
import java.io.*;
import java.lang.*;

public class Util extends Object
{

 public static int RunThis(String command, String outdest, String errdest)
 {
 Runtime rt = Runtime.getRuntime();
 int rc = -1;

 try
 {
    FileOutputStream fos = new FileOutputStream(outdest,true);
    FileOutputStream fes = new FileOutputStream(errdest,true);

    PrintWriter outfile = new PrintWriter(fos);
    PrintWriter errfile = new PrintWriter(fes);

    Process p = rt.exec(command);

    InputStreamReader isr = new InputStreamReader(p.getInputStream());
    InputStreamReader esr = new InputStreamReader(p.getErrorStream());

    BufferedReader obr = new BufferedReader(isr);
    BufferedReader ebr = new BufferedReader(esr);

    String outputline=null;
    String errorline=null;

    while ( (outputline = obr.readLine()) != null)
       {
         if (outfile != null)
         outfile.println(outputline);
         System.out.println(outputline);
       }

    while ( (errorline = ebr.readLine()) != null)
       {
         if (errfile != null)
         errfile.println(errorline);
         System.out.println(errorline);
       }

    if (outfile != null)
        outfile.flush();
    outfile.close();

    if (errfile != null)
        errfile.flush();
    errfile.close();

    rc = p.waitFor();
 }
 catch (Exception e)
 {
    e.printStackTrace();
    rc = -1;
 }
 finally
 {
    return rc;
 }
 }
}
/

create or replace
function RUN_CMD( p_cmd  in varchar2, outdest in varchar2, errdest in varchar2)
return number
as
language java
name 'Util.RunThis(java.lang.String, java.lang.String, java.lang.String) return
integer';
/

create or replace procedure RC(p_cmd in varchar2, outdest in varchar2, errdest
in varchar2)
as
  x number;
begin
  x := run_cmd(p_cmd, outdest, errdest);
end;
/

      Rating: 2
... you could
      change ...user  June 17, 2002
Reviewer:
      George  from Chester Springs, PA USA
You have aluded to this ability elsewhere as well, but
I cannot find any documentation on how to do this.

Our situation is that we will be writing either JAVA
procedures or C-external procedures to execute OS commands,
but we would prefer that those commands be executed by
some other OS (UNIX) user than Oracle.  Please clarify
which OS user normally runs JAVA and external procedures,
and how we can modify the default behavior.

Thanks...

          Followup:

java stored procedures - run in the dedicated/shared server process space -- run
as the user that started the database.

external procedures, written in c -- run under the userid of the TNS listener
that "forks" them off.  If you start the extproc listener when logged in as
"nobody" -- that'll be who they run as.  If you start the extproc listener when
logged in as "oracle" -- that'll be who they run as.

June 17, 2002 Andrew -- Thanks for the question regarding
      "Using Memory Windows to get around HP-UX memory constrait", version 8.0.5

      You Asked
Under HP-UX 11.00 there is a memory limit for 32bit apps of 1.75 Gig.  I
understand that.  Also, I know we should upgrade to 64bit Oracle to eliminate
this problem.  For a quick (interim) fix, does 32bit oracle 8.0 support HP-UX
Memory Windows?  If so, how do I assign different memory windows to each
instance I start?

Thanks,
aja

      and we said...
The reqirements for memory window support are:

32-bit or 64-bit HP-UX 11.0 installed
Oracle 8.0.6, 8.1.5, or later
HP-UX patches PHKL-17091 and PHCO-16795

So, with 8.0.5 as you currently have -- no, with 8.0.6 (which is the currently
supported 8.0 release of Oracle) -- yes.

How does Oracle work with Memory Windows?

Oracle must be running on an HP system that has Memory Windows enabled. Contact
your HP representative to find out if your machine can support Memory Windows,
and how to set it up. Once Memory Windows is enabled, you need to increase the
number of Memory Windows on the machine by tuning the HP-UX kernel parameter
max_mem_window to a value at least as large as the number of windows you'll
eventually need.

Once the HP and Oracle Memory Windows features are installed, the administrator
make entries in the /etc/services.window file. This file maps the Oracle
instance IDs to Window keys, the same way the /etc/services file maps service
names to port numbers. The system automatically reads the /etc/services.window
file start the Oracle processes in the correct Window.

Implementing Oracle with Memory Windows

The procedure is simple. For every Oracle instance, place its name in the first
column of the /etc/services.window file, and the Window key in the second
column. For example, if you are running two Oracle instances, one named sales
and the other named dev, this is how your /etc/services.window file might look:

        sales        5
        dev          6

The oracle binary uses the $ORACLE_SID environment variable plus the
/etc/services.window entry to determine which Window an Oracle process needs to
be running in.

You can use any Window key except for 0, which the system reserves for the
"global window". IF you accidentally use the Window key 0, the oracle instance
will start up, but it will not be executing in its own Window, and as a result
you will not see any benefits of Memory Windows.

Limitations of Oracle with Memory Windows

Memory Windows does provide Oracle instances with more memory. However, there is
a limitation. Processes running in one Window cannot share memory or
memory-mapped files with processes running in another Window. This is not an
issue with Oracle itself, because distinct instances do not communicate via
shared objects. However, it is possible that a custom or third-party tool
monitors Oracle by looking at its shared memory segments. If this is the case,
the tool also has to be mapped to a Memory Window.

Review &
      Followup

      Rating: 2
Great so
      far...  March 28, 2001
Reviewer:  Andrew
       from Troy, MI USA
Now how do I start various Oracle instances in seperate memory windows.  Do I
user setmimwindow <options> svrmgrl so that when I start the database the
various Oracle processes will inheret the memory window?  Or is there some other
way I shouild be doing this?

      Rating: 5
Great
      Help.  March 30, 2001
Reviewer:  AJ Allen
       from Troy, MI USA
The detail realy helps me understand how to use this facility. Thanks.

      Rating: 5
Key
      values  June 17, 2002
Reviewer:
      A reader
Hi,

From your notes -

For example, if you are running two Oracle instances, one named sales and the
other named dev, this is how your /etc/services.window file might look:

        sales        5
        dev          6

The oracle binary uses the $ORACLE_SID environment variable plus the
/etc/services.window entry to determine which Window an Oracle process needs to
be running in.

You can use any Window key except for 0, which the system reserves for the
"global window". IF you accidentally use the Window key 0, the oracle instance
will start up, but it will not be executing in its own Window, and as a result
you will not see any benefits of Memory Windows
--

Just needed to know, how to derive the "Window key" ?

Example, I have 6 instances on HP-UX 11.0, then can i have

db1 5
db2 6
db3 7
db4 8
db5 9
db6 10

Or is there any significance of the "Window key" value?

Thanks

          Followup:

Not that I am aware of (not being an HP/UX expert tho - i'll have to defer to
them, you might contact your HP person and see what they say...)


Subject | Article | Last Updated (East coast time)
transferring data | article only | Jun 18, 2002 06:58 am
What happened to the question from "P from LA" | article only | Jun 18, 2002 07:00 am
Upper limit of a container (say Varray ) depends on what Parameter | article only | Jun 18, 2002 02:16 pm
query | article only | Jun 18, 2002 02:44 pm
Sql plus does not display data type and constraints when describing a table - ? | article only | Jun 18, 2002 02:46 pm
Import after table mod | article only | Jun 18, 2002 04:23 pm
Performance ramifications of setting BLANK_TRIMMING in init.ora to TRUE | article only | Jun 19, 2002 05:36 pm
CURSOR_SHARE | article only | Jun 19, 2002 05:38 pm
Can you explain this code so that I can understand it before using it? | article only | Jun 20, 2002 07:20 am
Alternative to ref cursor and overloading procedures | article only | Jun 20, 2002 07:33 am
Status of objects like procedures, triggers and functions | article only | Jun 20, 2002 08:07 am
Java and Oracle | article only | Jun 20, 2002 12:12 pm
Creating OBJECT TYPES | article only | Jun 20, 2002 01:11 pm
Base tables on views | article only | Jun 20, 2002 08:31 pm
db link | article only | Jun 20, 2002 08:35 pm
Restricton on view and outer join | article only | Jun 20, 2002 08:41 pm
Migration from 9.01 to 9.2 | article only | Jun 21, 2002 07:08 am
Customised Database Creation | article only | Jun 21, 2002 07:11 am
Netscape "Bounce" | article only | Jun 21, 2002 07:20 am
JDBC | article only | Jun 21, 2002 07:23 am
BITMAP Indexes | article only | Jun 21, 2002 01:18 pm
Use of AFTER ROW trigger | article only | Jun 21, 2002 02:24 pm
How to Return Result Sets from Oracle Stored Procedures | article only | Jun 22, 2002 09:31 am
Concurrency :Only one at a Time | article only | Jun 22, 2002 10:17 am
SAP DBA | article only | Jun 22, 2002 01:36 pm
ORA-01001 | article only | Jun 22, 2002 01:39 pm
Function execution and 9i Security | article only | Jun 22, 2002 05:45 pm
Categories of stored outlines - what is the point? | article only | Jun 22, 2002 05:48 pm
Finding how many of a particular day there have been this year so far | article only | Jun 23, 2002 10:42 am
is this possible .... | article only | Jun 23, 2002 10:45 am
dbms_job question | article only | Jun 23, 2002 10:48 am
Creating a table from few tables | article only | Jun 23, 2002 10:55 am
SQL OBJECTS | article only | Jun 23, 2002 11:56 am
Installing our Own Developer Application | article only | Jun 23, 2002 11:58 am
9i features | article only | Jun 23, 2002 02:13 pm
External tables | article only | Jun 23, 2002 02:57 pm
Uncomplete execution plan? | article only | Jun 24, 2002 07:43 am
Adding a hidden column in a table. | article only | Jun 24, 2002 07:46 am
Batch and OLTP in 24*7 | article only | Jun 24, 2002 08:09 am
row(s) 1 - 39 of 39
June 18, 2002 Charlie -- Thanks for the question regarding "transferring data", version 8.1.7
You Asked
Hi Tom,

I transferred data from windows to linux system:

from:
Windows2000 SP2 (NTFS)
Oracle Enterprise Edition 8.1.7.0.0
data size: 170m
tablespace: extent management local - automatic allocation

to:
Redhat Linux 6.2
Oracle Enterprise Edition 8.1.7.0.1
data size 1.3G
tablespace: extent management local - uniform size 10m

One thing I noticed is data size changed from 170M to 1.3G.  Why this happened?
Is this because:
1. data file on windows is compressed using NTFS, and uncompressed on linux
2. difference between "extent management local - automatic allocation" and
"extent management local - uniform size 10m"

I am not sure which one? Or something I did is wrong. :)

Thanks,
and we said...
With extent management local, if you create 1,024 empty tables, you'll have
allocated 1,024 * 64k = 64mb of space.

In that LMT with 10m exents, you will have 1,024 * 10m = 10,240mb....

you have lots of small segments in your database.  10m is very very very large
for an extent ( i would only put objects that would be between 100mb to 5,000mb
in such a tablespace.

You go exactly what you asked for.  Every segment was created with a 10mb
initial extent -- even if it was empty.
Review & Followup
Rating: 4
followup June 17, 2002
Reviewer: Charlie
Ok. I see.  I am going to have around 200 tables in this database.  Can you give
me some suggestion
about how to size it properly.  I have the following ideas:

- extent management local - uniform size 64K (64K is default value,right?)
- extent management local - automatic allocation

Which one is better?   I do have 5 or 6 big tables(each of them have 1 million
records), so what should
I do?

Thanks,

Followup:
Unless you know how big the tables are going to be, use automatic.  It'll use a
sizing algorithm that, as the tables get larger -- so do the extent sizes.

If you know the sizes of your tables (normal, in most developed systems) put
them in a "small", "medium", or "large" tablespace -- where each tablespace is a
uniform extent size (of small, med and large)

1 million records can be 10meg or it can be 10gig -- cannot answer based on
record counts.  Your goal: size the extents so that a table goes into about 500
or less extents in the perfect world but -- and an important but -- don't lose
sleep if it goes into 1,000's of extents (its ok).

Rating: 4
Extents and partitioned tables June 17, 2002
Reviewer: Rob from Pittsburgh, PA
Tom:

In your followup you mention 500 or less extents for the table. If it were a
partitioned table would the "ideal" still be 500 extents for the table or 500
extents per partition?

Followup:
500 per segment would be more clear I suppose.  Each partition being a segment.

June 18, 2002 Arup -- Thanks for the question regarding "What happened to the question from "P from LA"", version 8.1.7
You Asked
Tom,

You don't have to answer this question if time is critical. But I was wondering
what happened to the questioner "P from LA" whose insert /*+ append */ was
generating a large amount of redo? The last I heard of the topic was from you at
NYOUG meeting earlier this month where you were supposed to telnet into the
server and get to the bottom of it. I am curious from an educational point of
view and I am sure a lot of your fans will be.

Thanks a lot in advance.

Arup
and we said...
The question was:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:421219463156
I got in touch with him.  Had him do a sql_trace -- turns out it was due to the
sheer MASSIVE amount of data they were doing -- the recursive sql for the
sequence turned out the be the culprit.  By upping the cache size to 50 -- they
did not hit the problem again (so, it was a matter of me not testing to scale
here!  I did 10's of thousands of rows, but needed to do 10's of millions maybe.

So, the very little rollback needed for their recursive sql updates to seq$
killed them (with only 20 smallish extents).  Solution in their case, set the
cache to at least 50 (so we do a recursive update every 50 rows instead of 20).
Actually, I suggested that whilst they do the update, they set the cache size in
the thousands to avoid the hit on the recursive sql even more.
Review & Followup
Rating: 5
What a catch! June 17, 2002
Reviewer: Arup Nanda from Norwalk, CT, USA
Tom,

Thanks a lot for the update. Who would have thought about the seq$ update
creating the rollback problems! As I mentioned before, this is definitely
educational for me, to look for the not so ubiquitous recursive sqls; and I hope
the same goes for the rest of the users in this forum.

Thanks again.

Arup

Rating: 4
to_lob will generate any redo? June 17, 2002
Reviewer: P from LA
tom,
thanks for all your help
will this generate any redo/undo. DB is noarchivelog
this time there are around 10000 records
------------------------------
SQL> INSERT /*+APPEND*/ INTO NCDBMEL.IST_EXTENSION_DETAIL
  2  (MEL_SEQ ,
  3  NCDB_SEQ   ,
  4  CREATE_USER     ,
  5  CREATE_DATE    ,
  6  MODIFIED_USER    ,
  7  MODIFIED_DATE    ,
  8  EQUIPMENT_ID      ,
  9  EXTENSION_TYPE   ,
 10  EXTENSION             ,
 11  SAFE_SHUTDOWN   ,
 12  ACC_MITIGATION      ,
 13  PIV                    ,
 14  RELIEF_REQUEST   ,
 15  OPERATING_POSITION ,
 16  SAFETY_POSITION     ,
 17  CATEGORY             ,
 18  DESIGN_STROKE
 19  )
 20  (
 21  SELECT
 22   B.NCDB_SEQ,
 23   B.NCDB_SEQ   ,
 24  'LOAD DATA'   ,
 25  SYSDATE ,
 26  NULL ,
 27  A.LAST_UPDATE ,
 28   A.EQUIPMENT_ID  ,
 29   A.EXTENSION_TYPE          ,
 30   to_lob(A.EXTENSION)               ,
 31   A.SAFE_SHUTDOWN       ,
 32   A.ACC_MITIGATION         ,
 33   A.PIV                     ,
 34   A.RELIEF_REQUEST        ,
 35   A.OPERATING_POSITION  ,
 36   A.SAFETY_POSITION        ,
 37   A.CATEGORY                ,
 38   A.DESIGN_STROKE
 39  FROM NCDB.IST_EXTENSION a, NCDB.IST_MAIN_VIEW B)
 40
SQL> /
INSERT /*+APPEND*/ INTO NCDBMEL.IST_EXTENSION_DETAIL
                                *
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-01562: failed to extend rollback segment number 4
ORA-01628: max # extents (20) reached for rollback segment RB3

Followup:
Well, yes it does.  It goes back to the "index" concept.

A lob is represented as three things in the database

a) a lob locator in the table (like a pointer)
b) a lob INDEX <<<===== (that index thing) that points to the lob chunks
c) the lob chunks

there is an index there.

Rating: 4
How to find the recursive SQL? June 17, 2002
Reviewer: Anthony from Philippines
Hi Tom,

I'm just wondering if how can we conclude that a recursive SQL is  the culprit??

tnx ..
NOTNA

Followup:
it was the only activity in the trace file.

setting the cache size to 50 from the default of 20 made it so they did not fill
up the rollback.

setting back to 20 (and hence more then doubling the number of times they
updated the sequence entry) re-introduced the issue.

Rating: 5
so how do we get around this one? June 17, 2002
Reviewer: P from LA
tom,
if index is causing this error....is there any other way around

Followup:
Here, I would say (as I've been known to say frequently):

you have sized your rollback segments insufficiently for the amount of work you
are doing.  Rollback is not a precious resource to be minimized at all costs.
It is is integral data structure in the database -- needed to support many
operations.  In order for you to do your job efficiently -- you will need more.

Now, I don't know how big your extents are but I can say that 20 extents is
generally where I START my rollback segment (not max them out).  You quite
simply have a way undersized configuration.  If this is a one time operation,
create a really truly big rbs (say on /tmp if you want), use the dreaded "set
transaction use rollback segment" command and do your operation.  Then, drop it.

Rating: 5
doing same thing as you said June 18, 2002
Reviewer: P from LA
tom,
we are doing same thing as you said....big rollback seg. because this is a one
time operation

June 18, 2002 chaitanya -- Thanks for the question regarding "Upper limit of a container (say Varray ) depends on what Parameter", version 8.1.7
You Asked
Hi Tom,

Thanks for your response on rowpointer in a refcursor.  It cleared my doubts.

I have a problem like this :
1. I have a Varray as below :

CREATE OR REPLACE TYPE str_test AS object(
    item_NAME         VARCHAR2(40),
    sale_price      NUMBER(15,6));

CREATE OR REPLACE TYPE pra_test IS VARRAY(10000) OF str_test;

2.  I wrote a anonymous block to load data into the structure like this :

DECLARE
la_pra_Test           pra_test := pra_test();   -- holds array of ln_indx number
:= 1;
begin
FOR ln_indx in 1..10001 LOOP
la_pra_test.EXTEND();
--dbms_output.put_line('ln_indx ='||ln_indx);
la_pra_test(ln_indx) :=
    str_test('ITEM'||ln_indx,
                 ln_indx||01.00);

END LOOP;
end;

here the problem is if ln_indx = 10000, the block executes fine, but if it is
10001 it gives ora-06532 subscript outside of limit.  Could you please let me
know how is the limit defined ?

The other day i had a bigger structure and i was populating data from a table
and it failed at ln_indx = 500,  is it anything to do with memory defined in
oracle ?

3. One more related problem is :
   i would like to select all employees whose name is in a table type:
   i have read the earlier questions, it works fine if used in explicit sql
statements, but my requirement is , the sql statement is a dynamic one and i am
executing using execute immediate it is not working ?

if query is like this :
table_type_variable is loaded with the empno's

select * from emp where
   empno in (select * from THE(select CAST(table_type_vaiable)
               from dual)

if my variable is like this:

var1 := 'insert into emp_test select * from emp where
        empno in (select * from THE (select '|| CAST(table_type_variable) '|| '
from dual)';
execute immediate var1;

your answers will help me to know more fundamentals as allways.
Thanks
Chaitanya
and we said...
Umm, you put the limit on there:

CREATE OR REPLACE TYPE pra_test IS VARRAY(10000) OF str_test;

you told us 10,000 was the upper limit.  Hence, 10001 is definitely out of
bounds.

Nothing to do with memory -- everything to do with your definition of the type
itself.

In dynamic sql you must bind the variables.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace type myTableType as table
of number
  2  /

Type created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          l_data myTableType := myTableType( 1,2,3,4 );
  3  begin
  4          execute immediate
  5          'begin insert into t select column_value from TABLE( cast(:x as
myTableType) ); end;'
  6          USING l_data;
  7  end;
  8  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

         X
----------
         1
         2
         3
         4

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review & Followup
Rating: 5
June 18, 2002
Reviewer: chaitanya from Phoenix
Hey tom,
Thanks for your reply.
I don't know how i overlooked the definition of varray and asked the question.

June 18, 2002 Charlie -- Thanks for the question regarding "query", version 8.1.7
You Asked
Hi Tom,

I have a table as follows:

SQL> desc t;
 Name                                                  Null?    Type
 ----------------------------------------------------- -------- ---------------
 NAME                                                           VARCHAR2(20)
 TIME_SHIFT                                                     NUMBER(4)
 AUTO_ID                                                        NUMBER
 STATE                                                          VARCHAR2(3)

SQL> select * from t;

NAME                 TIME_SHIFT    AUTO_ID STATE
-------------------- ---------- ---------- -----
a                            12          1 A
a                            13          2 B
a                          13.4          3 C
a                            18          4 A
a                            19          5 B
a                            22          6 A
a                            22          7 B
b                             8          8 C
b                             9          9 D
c                             1         10 C
c                             2         11 C
c                             3         12 C
c                             4         13 C

13 rows selected.

I'd like to generate a report based on the following conditions:
- same name
- different state
- time shift range +/- 1 (time_shift-1 <= value <= time_shift+1) among rows

If the result matches these conditions, I called it a "GROUP".  The desired
output
will look like this:

auto_id_group  avg_time_shift  name
-------------  --------------  ----
1,2,3          12.8            a
4,5            18.5            a
6,7            22              a
8,9            8.5             b

where,
auto_id_group  - comma seperated sorted auto_id within "GROUP"
avg_time_shift - average of time_shift within "GROUP"

if the data were:

NAME                 TIME_SHIFT    AUTO_ID STATE
-------------------- ---------- ---------- -----
a                            12          1 A
a                            13          2 B
a                          13.4          3 C
a                          14.4          4 A
...

14.4 row WILL NOT be in the 1,2,3 group since state in the "GROUP" has
to be different.

if the data were:

NAME                 TIME_SHIFT    AUTO_ID STATE
-------------------- ---------- ---------- -----
a                            12          1 A
a                            13          2 B
a                          13.4          3 C
a                          14.4          4 D
...

would that 14.4 row be in the 1,2,3 group -- even though the spread from the
first is 2.4?

>>>
Sorry about confusion, Tom.

There will be no 1,2,3 group but 1,2,3,4 group since they have:
- same name
- different state
- time shift range +/- 1 (PREVIOUS_ROW_time_shift-1 <= CURRENT_ROW_SHIFT <=
PREVIOUS_ROW_time_shift+1) among rows.  For example, 13-12=1, 13.4-13 <1,
14.4-13.4=1

Thanks,
and we said...
Cannot think of a "pure sql" approach to this particular one.  I can think of a
procedural one that works good in 9i using pipelined functions (had to change
your number(4) to number(4,2) to do the math)

ops$tkyte@ORA9I.WORLD> create or replace type myScalarType as object
  2  ( auto_id_group  varchar2(40),
  3    avg_time_shift number,
  4    name           varchar2(5)
  5  )
  6  /

Type created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> create or replace type myTableType as table of
myScalarType
  2  /

Type created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> create or replace function func( p_cursor in
SYS_REFCURSOR )
  2  return myTableType
  3  PIPELINED
  4  as
  5      l_rec    t%rowtype;
  6
  7      l_last_name        varchar2(5);
  8      l_last_time_shift  number;
  9      l_last_states      varchar2(4000);
 10      l_auto_group_id    varchar2(4000);
 11      l_sum_time_shift   number;
 12      l_cnt_time_shift   number;
 13  begin
 14      loop
 15          fetch p_cursor into l_rec;
 16          exit when p_cursor%notfound;
 17
 18          if ( l_rec.name <> l_last_name OR
 19               instr( '/' || l_rec.state || '/', l_last_states ) > 0 OR
 20               abs( l_rec.time_shift-l_last_time_shift ) > 1 OR
 21               l_auto_group_id is null )
 22          then
 23              if ( l_auto_group_id is not null )
 24              then
 25                  pipe row (myScalarType( l_auto_group_id,
 26                       l_sum_time_shift/l_cnt_time_shift, l_last_name));
 27              end if;
 28
 29              l_auto_group_id := l_rec.auto_id;
 30              l_sum_time_shift := l_rec.time_shift;
 31              l_cnt_time_shift := 1;
 32              l_last_name      := l_rec.name;
 33              l_last_states    := '/' || l_rec.state || '/';
 34              l_last_time_shift:= l_rec.time_shift;
 35          else
 36              l_auto_group_id := l_auto_group_id || ',' || l_rec.auto_id;
 37              l_sum_time_shift := l_sum_time_shift + l_rec.time_shift;
 38              l_cnt_time_shift := l_cnt_time_shift + 1;
 39              l_last_states    := l_last_states || l_rec.state || '/';
 40              l_last_time_shift:= l_rec.time_shift;
 41          end if;
 42      end loop;
 43      if ( l_auto_group_id is not null )
 44      then
 45          pipe row (myScalarType( l_auto_group_id,
 46                 l_sum_time_shift/l_cnt_time_shift, l_last_name));
 47      end if;
 48
 49      return;
 50  end;
 51  /

Function created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> select *
  2    from TABLE( func( CURSOR (select * from t order by name, time_shift) ) )
  3  /

AUTO_ID_GROUP        AVG_TIME_SHIFT NAME
-------------------- -------------- ------------------------------
1,2,3                          12.8 a
4,5                            18.5 a
6,7                              22 a
8,9                             8.5 b
10                                1 c
11                                2 c
12                                3 c
13                                4 c

8 rows selected.

In 8i, this could be:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function func( p_query in
varchar2 )
  2  return myTableType
  3  as
  4          type rc is ref cursor;
  5          l_cursor rc;
  6      l_rec    t%rowtype;
  7
  8      l_last_name        varchar2(5);
  9      l_last_time_shift  number;
 10      l_last_states      varchar2(4000);
 11      l_auto_group_id    varchar2(4000);
 12      l_sum_time_shift   number;
 13      l_cnt_time_shift   number;
 14
 15      l_data   myTableType := myTableType();
 16  begin
 17          open l_cursor for p_query;
 18      loop
 19          fetch l_cursor into l_rec;
 20          exit when l_cursor%notfound;
 21
 22          if ( l_rec.name <> l_last_name OR
 23               instr( '/' || l_rec.state || '/', l_last_states ) > 0 OR
 24               abs( l_rec.time_shift-l_last_time_shift ) > 1 OR
 25               l_auto_group_id is null )
 26          then
 27              if ( l_auto_group_id is not null )
 28              then
 29                  l_data.extend;
 30                  l_data(l_data.count) :=
 31                     myScalarType( l_auto_group_id,
 32                       l_sum_time_shift/l_cnt_time_shift, l_last_name);
 33              end if;
 34
 35              l_auto_group_id := l_rec.auto_id;
 36              l_sum_time_shift := l_rec.time_shift;
 37              l_cnt_time_shift := 1;
 38              l_last_name      := l_rec.name;
 39              l_last_states    := '/' || l_rec.state || '/';
 40              l_last_time_shift:= l_rec.time_shift;
 41          else
 42              l_auto_group_id := l_auto_group_id || ',' || l_rec.auto_id;
 43              l_sum_time_shift := l_sum_time_shift + l_rec.time_shift;
 44              l_cnt_time_shift := l_cnt_time_shift + 1;
 45              l_last_states    := l_last_states || l_rec.state || '/';
 46              l_last_time_shift:= l_rec.time_shift;
 47          end if;
 48      end loop;
 49          close l_cursor;
 50      if ( l_auto_group_id is not null )
 51      then
 52          l_data.extend;
 53          l_data(l_data.count) :=
 54                   myScalarType( l_auto_group_id,
 55                     l_sum_time_shift/l_cnt_time_shift, l_last_name);
 56      end if;
 57
 58      return l_data;
 59  end;
 60  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select *
  2    from TABLE( cast( func('select * from t order by name, time_shift')
  3                                          as myTabletype ) )
  4  /

AUTO_ID_GROUP                            AVG_TIME_SHIFT NAME
---------------------------------------- --------------
------------------------------
1,2,3                                              12.8 a
4,5                                                18.5 a
6,7                                                  22 a
8,9                                                 8.5 b
10                                                    1 c
11                                                    2 c
12                                                    3 c
13                                                    4 c

8 rows selected.

sorry -- no really "cool" answer for this one.
Review & Followup
Rating: 4
followup June 18, 2002
Reviewer: Charlie
Thank you very much for your quick response!  I tried your approach for 8i but
it finds auto_id 3, which seems not right.

SQL> select * from t;

NAME                 TIME_SHIFT    AUTO_ID STATE
-------------------- ---------- ---------- -----
a                            12          1 A
a                            13          2 B
a                            14          3 A  <- same state as auto_id 1, which
should be excluded
a                            18          4 A
a                            19          5 B
b                             8          6 C
b                             9          7 C
aa                            1          8 C
aa                            2          9 C
aa                            3         10 C
aa                            4         11 C

11 rows selected.

Elapsed: 00:00:00.10
SQL> select * from TABLE(cast( func('select * from t order by name, time_shift')
as myTabletype ) );

AUTO_ID_GROUP                            AVG_TIME_SHIFT NAME
---------------------------------------- -------------- -----
1,2,3                                                13 a
4,5                                                18.5 a
8                                                     1 aa
9                                                     2 aa
10                                                    3 aa
11                                                    4 aa
6                                                     8 b
7                                                     9 b

8 rows selected.

I'm thinking is there any way we can use powerful analytic function(lag, window
range/rows) and combine
with max(decode(...)) technique to flatten auto_ids as 1,2,3,4... or somthing
like that.

Thanks,

Followup:
sorry -- had the instr backwards:

instr( l_last_states, '/' || l_rec.state || '/' ) > 0 OR

is what it should be.  That'll put 3 into a group of its own when 3 has a state
of 'A'.  Now, if you just want to IGNORE it, you'll need to supply appropriate
logic on your own to get that result (the concept is here, you'll just need to
add the right code to make it perfect for your requirements)

June 18, 2002 Nag -- Thanks for the question regarding "Sql plus does not display data type and constraints when describing a table - ?", version 8.1.7
You Asked
SQL> DESC EMP
 Name

---------------------------------------------------------------------------------
------------------
 EMPNO

 ENAME

 JOB

 MGR

 HIREDATE

 SAL

 COMM

DEPTNO

                                                                      1.Why does
this happen.
2.How can I change the font size and type in sqlplus.

Btw: What kind of client tools do you use.
and we said...
1) set linesize 80
   desc emp

looks like your linesize is set really wide and the remaing information is way
off to the right somewhere

2) see
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/win.817/a82954/apa.htm#423093
you can do this in 817 and up sqlplus only.

my client tool - I use command line (character mode) sqlplus exclusively
(sqlplus.exe on windoze and sqlplus on unix).  I use imp/exp/sqlldr when
appropriate.
Review & Followup
Rating: 4
Set linesize or other settings do not work ... why June 18, 2002
Reviewer: Vasaku
Tom

I'm trying run the following script, and create a table in the first example
and select

something in the second example...

I also want to use set settings along with my dml or ddl statements for which
I'm writing

script...

But each time running into errors... can you show me the right way to this?

SQL> ed script

SQL>
  1  set define off
  2  Create table t(x varchar2(20));
  3* set define on
  4  /
set define off
    *
ERROR at line 1:
ORA-00922: missing or invalid option

SQL> ed
Wrote file afiedt.buf

  1  set define off
  2  set linesize 2000
  3  select * from emp
  4* set define on
SQL> /
set define off
    *
ERROR at line 1:
ORA-00922: missing or invalid option

Followup:
you are just running stuff "wrong".

put this into a file called "foo.sql":

set define off
create table t ( x varchar2(20) );
set define on

Then, in sqlplus run:

SQL> @foo

that'll run each command, one at a time.  You are editing the single command
buffer and putting multiple commands into it -- that is what is causing your
error (when you edit using "ed" -- you can put but ONE command in the buffer).
You want to use a script.

June 18, 2002 Angelo -- Thanks for the question regarding "Import after table mod", version 9.0.1
You Asked
Tom,

Can I export data from a table, add a column to that table and then use the
exported data in an import with the utilitie screaming at me?
and we said...
I assume you mean "withOUT" the utility screaming at me ;)

Yes, exp creates inserts with all of the columns in the statement -- hence,
it'll work with added columns.  It is when you DROP a column, change the
datatype -- etc -- that you might run into an issue (for which I have methods
around in my book if you are intertested)
Review & Followup
Rating: 3
June 19, 2002
Reviewer: A reader
Thanks,

Yes I'm interested in the book and will purchase it soon

June 19, 2002 Krassimir -- Thanks for the question regarding "Performance ramifications of setting BLANK_TRIMMING in init.ora to TRUE", version 8.0.5
You Asked
Tom,

I am about to set init.ora's parameter BLANK_TRIMMING to TRUE in a development
database.
Is this going to cause performance problems?

I want to do this to allow for tracing of complex call stacks.
For example, currently if we want to see what PL/SQL programs are invoked when a
user of our application performs an action we spend a long time to manually
follow through the code and build an error-prone callstack picture by hand.

My understanding is that setting BLANK_TRIMMING to TRUE will allow for automatic
callstack picture generation (TOAD is supposed to do the generation) with a
display of the line number of the executed code inside each program.

So if I step into procedure B from line 5 of procedure A, then I want to see a
picture of the call stack that looks like this:

PROCEDURE B(1)
PROCEDURE A(5)

Thank you
and we said...
what makes you think blank_trimming would do anything remotely similar to that?

where did you read that?

Have you looked at the very simple plsql routine:

  dbms_utility.format_call_stack

??

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure p1
  2  as
  3  begin
  4          dbms_output.put_line( 'Hello' );
  5          dbms_output.put_line( dbms_utility.format_call_stack );
  6  end;
  7  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure p2
  2  as
  3  begin
  4          null;
  5          null;
  6          null;
  7          null;
  8          null;
  9          p1;
 10  end;
 11  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure p3
  2  as
  3  begin
  4          null;
  5          null;
  6          p2;
  7  end;
  8  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec p3
Hello
----- PL/SQL Call Stack -----
  object      line  object
  handle    number  name
81703834         5  procedure OPS$TKYTE.P1
81716be0         9  procedure OPS$TKYTE.P2
8170c1dc         6  procedure OPS$TKYTE.P3
817065d8         1  anonymous block

PL/SQL procedure successfully completed.

that shows that p3 called p2 from line 6, p2 called p1 from line 9 and on line 5
of p1 -- we asked for the call stack....
Review & Followup
Rating: 4
Call stack June 24, 2002
Reviewer: Krassimir Georgiev from Boston, MA USA
Tom,

I am going to use dbms_utility.format_call_stack to display the call stacks in
our application. Thanks for the suggestion.

To answer your question about what made me think that blank_trimming has
anything to do with call stack tracing at all, here is an excerpt from the Help
system of a GUI tool called TOAD:

" The Call Stack window displays the chain of functions and procedures as they
are called, in the order they are called, with the most recent function or
procedure listed on the top.
...

NOTE: If you are using Oracle 8.0.5 or earlier, you must set the BLANK_TRIMMING
value to TRUE in the init.ora file in order to see the Call Stack tab. If this
is not done, the Call Stack menu option will be grayed out, and the tab will not
display on the Breakpoints, Watches, Call Stack & DBMS Output window. "

June 19, 2002 juan -- Thanks for the question regarding "CURSOR_SHARE", version 9.01
You Asked
Hi a question please
As I understand and before modifing all the parameter files I have.

I want to confirm this:
In almost any case where was advisable to user CURSOR_SHARE=EXACT
is now better to use CURSOR_SHARE=SIMILAR in Oracle 9.01?

Thankyou.
and we said...
NO, short answer -- there are side effects to having constants turned into bind
variables blindly.

Longer answer -- see chapter 10 of my book -- i detail these side effects.

cursor_sharing force/similar in my humble opinion is a band-aid a system would
use temporarily whilst you beat the developers who didn't use bind variables
properly in their code about the head.  You fix the bug your applications and
then go back to cursor_sharing=exact.

It has limited application in my opinion.  Sort of a last resort.
Review & Followup
Rating: 5
June 21, 2002
Reviewer: A reader
Thanks.

June 20, 2002 Brett -- Thanks for the question regarding "Can you explain this code so that I can understand it before using it?", version 8.1.7
You Asked
In another question, you provided the following code to count business days
between two dates:

select to_char(sysdate-rownum+1,'DD-MON Day'),
       trunc(((trunc(sysdate-rownum+1))-trunc(sysdate,'year'))/7)+1
from all_objects
where rownum <= 7
/

1).  In the first line, why do you use "rownum+1"
2).  Can you explain the second line?

It's a great solution, I just want to know what it means before I use it.

Thanks,

Brett
and we said...
1) because i wanted to subtract 0, 1, 2, 3, 4, 5, 6 days to get today, yesterday
the day before.  rownum starts at 1 so.... -rownum+1 adds 0, 1, 2, 3, ... and so
on.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select -rownum+1 days_to_add,
  2         to_char(sysdate-rownum+1,'DD-MON Day') the_date,
  3         trunc(((trunc(sysdate-rownum+1))-trunc(sysdate,'year'))/7)+1
number_of_days
  4  from all_objects
  5  where rownum <= 7
  6  /

DAYS_TO_ADD THE_DATE         NUMBER_OF_DAYS
----------- ---------------- --------------
          0 20-JUN Thursday              25
         -1 19-JUN Wednesday             25
         -2 18-JUN Tuesday               25
         -3 17-JUN Monday                24
         -4 16-JUN Sunday                24
         -5 15-JUN Saturday              24
         -6 14-JUN Friday                24

7 rows selected.

2) the second line is some math.

trunc(sysdate-rownum+1))  is the day we are computing FROM.  Take the first
line in the above output with days_to_add=0.  trunc(sysdate) is what we are
looking at.  We just stripped the time component off there.  So, today on the
20th of June, trunc(sysdate-rownum+1)) is 20-jun-2002 00:00:00

trunc(sysdate,'year') is just the first day of this year:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select trunc(sysdate,'year') from dual;

TRUNC(SYS
---------
01-JAN-02

(trunc(sysdate-rownum+1))-trunc(sysdate,'year') is the number of days in the
year so far (subtract a date from a date you get number of days)

ops$tkyte@ORA817DEV.US.ORACLE.COM> select
(trunc(sysdate-rownum+1))-trunc(sysdate,'year') from dual;

(TRUNC(SYSDATE-ROWNUM+1))-TRUNC(SYSDATE,'YEAR')
-----------------------------------------------
                                            170

trunc(((trunc(sysdate-rownum+1))-trunc(sysdate,'year'))/7)+1 is the number
of full weeks in the year todate (with respect to the DATE we pass in --
"sysdate-rownum+1" in my example):

ops$tkyte@ORA817DEV.US.ORACLE.COM> select
trunc(((trunc(sysdate-rownum+1))-trunc(sysdate,'year'))/7)+1
  2  from dual;

TRUNC(((TRUNC(SYSDATE-ROWNUM+1))-TRUNC(SYSDATE,'YEAR'))/7)+1
------------------------------------------------------------
                                                          25

Now, the number of weeks must be the number of times that day occurred.
Review & Followup
Rating: 5
Very good explanation. June 20, 2002
Reviewer: A reader

June 20, 2002 -- Thanks for the question regarding "Alternative to ref cursor and overloading procedures", version 8.1.7
You Asked
Hi Tom,

I am trying to write a procedure which accepts 4 in  parameters:

p1,p2,p3,p4   for which the occurence of the passage of parameters would be like

1. Either p1 or (p2,p3,p4) are sent in - in which p3,p4 are again optional.

The question is:

     instead of writing

       if (p1 != null) then

               select * from table where col1=p1

        else

           if (p3 = null and p4 != null) then

               select * from table where col2=p2 and col3=p4

            end if

           else

            if (p3 != null and p4 = null)

                select * from table where col2=p2 and col4=p3

              else

                 select * from table where col2=p2 and col3=p4 and col4=p3;
             end if;

            .......

ie:

p2   p3    p4     where clause

Y    N     N        where col2 = p2
Y    Y     N        where col2 = p2 and col3=p3
Y    N     Y        where col2=p2 and col4=p4.

If there a way which i can write this in a single sql statement.

Regards,
and we said...
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1288401763279
shows how to do this.
June 20,
      2002 Sateesh -- Thanks for the question
      regarding "Status of objects like procedures, triggers and functions",
      version 8.1.5

      You Asked
Hi,
  If any changes are made to a table like adding new columns.
The dependant objects (procedures, functions and triggers)
become invalid. This is a common scenario and we use the
alter procedure recompile command to recompile them.

Is there any chance of the above mentioned objects becoming
invalid other than the above mentioned case.

Because what i have experienced is once i change the table
structure , i recompile all the prodcedures , functions and
triggers. But when the database is shutdown and brought back the
objects become invalid again. Not all the objects become invalid
only few of them become invalid.

I am looking at those procedures and triggers which become
invalid, they look normal like any other procedure.

Any explinations please...

Thanks & Regards
SAteesh

      and we said...
Revocation of grants will cause a procedure to go invalid.

The compilation of a dependent procedure will cause them to go
invalid (eg: if b calls a and both a and b went invalid due to
the change and you compiled B and (which would compile A) and
then compiled A again -- B would end up being invalid).

Shutting down the database and restarting will not invalidate
anything.  I believe what might have happened is something like
the above -- you found that A and B were invalid and compiled
both in the order B and then A.  The compilation of A
invalidated B.

You should use packages to avoid this totally.  Packages break
the dependency chain and dramatically decrease the number of
potential invalidations.

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:286816015990
for further info on that.  You also do understand that it is
perfectly OK for them to be invalid, that they will
automagically recompile themselves the next time they are run.
 You need not manually compile them.

Review &
      Followup

      Rating:
      4
  May 31, 2001
Reviewer:  Valar
      Govarthanaraj  from Littleton, MA USA
This was exactly the kind of article which I was looking for, as my stored
procedures were invalidated. Thanks  a lot !!

      Rating: 4
Mysterious
      invalidation of SYS objects  June 20, 2002

Reviewer:  A P Clarke  from London, England

Tom

This morning our users were not able to log on.  The immediate cause was the
massive invalidation of a number of SYS-owned packages (DBMS_ALERT, DBMS_OUTPUT,
DBMS_APPLICATION_INFO, etc.).  The DBA was able to manually recompile all those
packages and the users could log on.  Hurrah!

I have two questions:

(1)  The DBA swears blind no DDL was run last night, so have you come across
anything that could cause this phenomenom?

(2)  Why didn't the packages "automagically recompile themselves" when they were
called?  Is it due to them being owned by SYS?

TIA, APC

          Followup:

did someone revoke something from "public".  That'll invalidate most all of the
database.

What error did users get (that is always helpful).  How did the dba log in when
others could not (what was different there).

June 20, 2002 Frank -- Thanks for the question regarding "Java
      and Oracle", version 8.1.7

      You Asked
Tom,
Personally i've never used JAVA features of oracle8i in REAL world product. I
dont know the advantages of JAVA features in the oracle server side comparing to
the plsql. For example, why do we need to use java stored proc. instead of plsql
stored proc? It seems those features are just for catching up with the fashion,
doesnt it? Could you explain it and have u ever seen any REAL products which
widely use JAVA features of oracle8i in the SERVER SIDE?
Thanks!

      and we said...
Java is just a language, its a choice -- not a fashion statement.

If you've ever used UTL_SMTP to send mail with PLSQL, well, I'm sorry to say
that you have in fact used Java in the database.

If you've ever wanted to get the time down to the millisecond or discover the
server timezone and you are not on 9i -- you'll probably be using Java.

If you've ever wanted to get a list of files in a directory so you can use
UTL_FILE to read them with PLSQL -- you'll be looking at Java.

Java can do things PLSQL cannot do (or cannot do easily, I'd like to think PLSQL
can do it all personally ;)  -- but it cannot).

PLSQL can do things Java cannot do easily (work with data from tables -- very
very natural in PLSQL).

If you are a plsql programmer, java stored procs might hold nothing more then a
passing interest.  If you are a java programmer that doesn't know PLSQL and has
no desire to learn, Java stored procedures might be very enticing.

Real world examples of Java being used:

o asktom.oracle.com uses it to send mail, lots of mail
o I use it on my web site to send email with attachments (html, pdf, whatever)
o I use it as a better/easier UTL_HTTP to integrate data, for example, my
website internally "borrows" stock quotes from yahoo every 5 minutes -- a java
procedure grabs the data, PLSQL processes it.

search for "create replace java source compile" for other exmaple of java
procedures people have asked for.

I view it as yet another tool to be used.  Nothing more, nothing less.  You can
do some cool things easily that were not possible before.
Review &
      Followup

      Rating: 5
Buy Tom's
      Book  November 26, 2001
Reviewer:  Jim

This question is covered extremely well
by Tom's book.
Too many people treat Java and J2EE as a fashion statement
if you ask them why they are using Java many can't give you a sound technical
reason.

      Rating: 5
Real-world
      use  November 27, 2001
Reviewer:  Matthew
      Cromer  from Raleigh, NC
We have written a real-world system using java stored procedures.  As Tom will
tell you, PL/SQL is blindingly fast for procedural code in the database.
However, we find the Java to be blisteringly fast inside the database as well,
just not quite as fast at PL/SQL.  We also like to be able to run and test java
code in the debugger, and inside and outside the database.

Our team is a lot more experienced with java than PL/SQL and so we decided to
use java to write the business logic modules.  There are bits and pieces of
PL/SQL (triggers, a few SPs, etc.) but the bulk of the code is java.  We load
our entire java code base as a jar using Oracle's LoadJava tool.

      Rating: 5
Great
      Answer!  May 07, 2002
Reviewer:  Robert Wood
       from Memphis, TN
Thanks for your solid, to-the-point answer.

      Rating: 3
DDL in Java or
      stored procedures  June 20, 2002
Reviewer:  G.
      Gushue  from Toronto, ON, Canada
The answer is still not clear.  If you are designing an application, should you
use packages with procedures or have the DDL in the Java code.

The argument that we have here is where to separate the business rules.  Should
they be in the application tier or the database tier?

          Followup:

If you ask me, it is my 100% confirmed -- affirmed and very firm opinion that a
well written java database program would never have:

select
insert
update
delete
merge

coded in it -- it would have lots of CALL (or, as i code, BEGIN/END; blocks)
statements.

Reasons (just some) --

I can tune the sql easily -- from the database.

I can help enforce the usage of bind variables in the SQL (i still have to worry
about them binding to the CALL -- but if I hide 10 sql statements in each
procedure i'll have 1/10 of the statements to fix in the java code)

I can control security in a fashion I like much better.

I keep the database logic out of the java logic.

I find that it is generally faster (sure, there are counter cases)

I can let database savy people write the database stuff and let the cool gui
programming upto the java people...

and so on.

That's my opinion, based on experience.  How many times I've been called in to
fix the Oracle database (too slow, taking too much in the shared pool, etc) and
then have to wind my way through thousands of lines of java to find the sql that
needs fixing and writing the code to fix it and so on....

Now, I'm sure alot of the java coders out there (and hey, I'm  one of them --
I've been there) will be all over me....

      Rating: 4
Java vs
      Packaged Procedures  June 25, 2002
Reviewer:  G.
      Gushue  from Toronto, ON, Canada
Thanks for the response to my earlier question.  However, the battle still
continues of where to put the business logic....especially when it concerns DML
(a correction from my earlier posting - not DDL).

I have tried looking for articles about this on the web and only found a few
things.  I found that the article on the asktom site was the most useful.
Thanks for providing this service.

Regards,
GG

      Rating:
      5
  February 21, 2003
Reviewer:  Helena
      Marková  from Bratislava, Slovakia

June 20, 2002 -- Thanks for the question regarding "Creating
      OBJECT TYPES", version 8.1.7

      You Asked
Tom,

I am doing some dev work,and would like to pass back an array to Java.

Now I decided to use the following:

CREATE OR REPLACE TYPE per_qualfs_typ AS OBJECT
(qt_sys_id    qualf_types.qt_sys_id%TYPE
,qualf_typ_cd    qualf_types.qualf_typ_cd%TYPE
)

and get the following

LINE/COL ERROR
-------- -----------------------------------------------------------------
0/0      PL/SQL: Compilation unit analysis terminated
3/15     PLS-00201: identifier 'QUALF_TYPES.QUALF_TYP_CD' must be declared

it only works if I do something like qt_sys_id integer , qualf_typ_cd
Varchar2(1).

Is the above not acceptable?
regards
Femi

      and we said...
You cannot use the %type with object types.  You are only allowed to use SQL
types in object types (they are not dependent on plsql)

June 20, 2002 Ambarish -- Thanks for the question regarding
      "Base tables on views", version 8.1.5

      You Asked
Hi Tom,

Given a view, how do i find out all the base tables on which it
exists. Furthermore, is it possible to determine whether the
view is updatable and/or key preserving ?

Thanks
Chitnis

      and we said...
Sure, *_Dependencies (* = all, user, dba) and
*_updatable_columns.  Consider (emp has a pk, dept has a pk and
there is an fk from emp to dept):

ops$tkyte@ORA8I.WORLD> create view
  2  emp_dept_view
  3  as
  4  select emp.ename, dept.dname
  5  from emp, dept
  6  where emp.deptno = dept.deptno;

View created.

ops$tkyte@ORA8I.WORLD> select referenced_name, referenced_type
  2  from user_dependencies
  3  where name = 'EMP_DEPT_VIEW'
  4  /

REFERENCED_NAME                     REFERENCED_T
----------------------------------- ------------
DEPT                                TABLE
EMP                                 TABLE

ops$tkyte@ORA8I.WORLD> l
  1  select column_name, updatable,
  2   insertable, deletable
  3  from USER_UPDATABLE_COLUMNS
  4* where table_name = 'EMP_DEPT_VIEW'
ops$tkyte@ORA8I.WORLD> /

COLUMN_NAME                    UPD INS DEL
------------------------------ --- --- ---
ENAME                          YES YES YES
DNAME                          NO  NO  NO

In this case, EMP is the key preserved table.
Review &
      Followup

      Rating: 5
Is there a way
      to find "base" columns?  June 20, 2002
Reviewer:
      Paul Druker  from Fairfax, VA USA
So, user_dependencies view tells me that emp_dept_view is using DEPT and EMP
base tables.

I am wondering if it's possible to go one step further and find out that column
ENAME in a view is coming, in fact, from the EMP table. Could not find any view
that provides me with this kind of information...

          Followup:

No, we do not keep it to that level of detail.

June 20, 2002 --
      Thanks for the question regarding "db link", version 816.3

      You Asked
hi tom
1)i have 2 databases with the same name but each resides on diff.
servers and i connect from a diff machine to wichever server i want
but my connect string is different my question is while i am at
sqlplus i want to know wich coneect string i used to connect how can i find this
out

2)i created a db link to a remote database so when i select from a table in that
database i see my connection is still there only when i exit from my local
session then it also disconnect from the remote session how can i disconnect
from the remote but still stay connected to the local database

thank you

      and we said...
1) you cannot -- that is translated on the client to a real connect string.  It
is not visible in the database, the database has NO idea what tnsname you used.

You can use

ops$tkyte@ORA8I.WORLD> select instance_name || '@' || host_name from v$instance;

INSTANCE_NAME||'@'||HOST_NAME
----------------------------------------------------------------------------
ora8i@aria

to determine which database you are in.  that'll be SID@HOST

2) alter session close database link dblink-name;
Review &
      Followup

      Rating: 4
How can we
      specify database link in a stored procedure?  June 20, 2002

Reviewer:  A reader  from USA
Hi Tom,
We have a stored procedure that is used to update tables with the same name but
on different servers.  Can we pass the database link as a in parameter (which
does not work in my case) or do we have a right way to do it?  We need to do
that in a application.
Example:
create or replace PROCEDURE Pro_test
  (link_name in varchar2,in_number in number)
as
BEGIN
  update table_name@link_name set col2='test_test' where col1=in_number;
END;

where the link_name is what we are trying passed in.  I know the way I write is
not right, I just don't know the right way and here just show what we need.
Thank you very much in advance.

          Followup:

begin
   execute immediate
    'update table_name@' || link_name ||
     ' set col2 = ''test_test'' where col1 = :y'
   using in_number;
end;

      Rating: 5
whoa!! Bind
      variables @ 50%  June 20, 2002
Reviewer:
      John  from MA
Hi Tom,

I just finished re-reading :

http://asktom.oracle.com/pls/ask/f?p=4950:8:952871::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:528893984337,
(and enjoyed it all over again)
about bind vars and had to reply here.

begin
   execute immediate
    'update table_name@' || link_name ||
     ' set col2 = :X where col1 = :y'
   using 'test_test', in_number;
end;

I understand link_name but, why not X??

          Followup:

because bind variables only need to be used (only should be used) where the
values change.

I took the example:

BEGIN
  update table_name@link_name set col2='test_test' where col1=in_number;
END;

and seeing that test_test is constant (eg: maybe they are setting a flag to yes
for a specific columns) -- that does not demand a bind variable.  It is only
when the values change that you need bind variables.

Constants are not evil -- only when used improperly.  My first cut on the answer
was yours but I retracted it -- if the constant is in fact constant, it should
be constant.  If it varies, then by all means -- it should be a bind.

June 20, 2002 Wil
      -- Thanks for the question regarding "Restricton on view and outer join",
      version 8.1.7

      You Asked
  Hi Tom,

  I cannot find nor can I make up examples for the following
  restrictions mentioned in Oracle documentation.

 1. Select query for a view cannot have any reference to currval or nextval.
Whatever I try seems to work

  2. Outer join cannot have "OR"  , "IN" operators

   While it is easy to makeup an example for "OR" restriction, I
   can't find a good example of "IN" restriction.

  Can you please provide  one or two examples showing how select
  statements run into error because of these restrictions mentioned  above.

Regards

Wil

      and we said...
scott@ORA817DEV.US.ORACLE.COM> create or replace view v
  2  as
  3  select seq.nextval x from dual;
select seq.nextval x from dual
           *
ERROR at line 3:
ORA-02287: sequence number not allowed here

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> select * from dual where dummy(+) in (1,2,3);
select * from dual where dummy(+) in (1,2,3)
                                  *
ERROR at line 1:
ORA-01719: outer join operator (+) not allowed in operand of OR or IN
Review &
      Followup

      Rating: 5
sequence in
      views  June 13, 2002
Reviewer:  Suresh
       from New York
Tom,
   Any specific reason why sequences are not allowed in Views? Thanks.

          Followup:

don't know why the restriction exists.

      Rating: 5
why cant we
      ...  June 20, 2002
Reviewer:  bole taro
      dolbaje
what about the below restriction..
ORA-01719: outer join operator (+) not allowed in operand of OR or IN

why so ?

          Followup:

sometimes -- just because.  It is the way things are.

Same answer as the answer to the followup right above.

I can say in 9i, the restriction is removed:

scott@ORA9I.WORLD> select ename, dname
  2  from emp left outer join dept on ( emp.deptno = dept.deptno OR emp.empno =
55 );

ENAME      DNAME
---------- --------------
SMITH      RESEARCH
....

June
      21, 2002 jery -- Thanks for the
      question regarding "Migration from 9.01 to 9.2", version 9.01

      You Asked
Now I´m working in 9.01
I have read about some improvements, but It's not clear.

What do you think about the migration from 9.01 to 9.2

A)If you want
b) A good idea
c) A really good idea
d) you must to

And why?

Thanks

      and we said...
are you in production or development?

in production, probably not (i'm in production after all)

in development -- depends on when production is ;)

in early development -- 100%, I would go for it.

in late development, have gone through many cycles of testing -- maybe not,
might wait for the first "update" to my app to upgrade.

I would make sure I had the latest 9iR1 patches installed, particularly security
related ones.
Review & Followup

      Rating: 5
8i/9.0 =>
      9.2  June 20, 2002
Reviewer:  Connor
       from UK
There's a metalink note out there somewhere about possible corruption when going
from 8=>9=>9.2 (I think).  Can't remember the specifics :-(

          Followup:

Article-ID:         <Note:197737.1>
Circulation:        PUBLISHED (EXTERNAL)

...
Applying the patch at the time of upgrade to 9.2.0.1 is the preferred method of
resolution. The patch eliminates the need to rebuild freelists and will prevent
the ORA-600 errors and data corruptions from occurring.
.....

      Rating: 5
8.1.7, 9.0.1 or
      9.2  June 21, 2002
Reviewer:  Johan Snyman
       from JHB SA
We have a production system currently running 8.1.6.2.0 on Tru64 Unix 5.1

Due to an issue with hot backups, we initially wanted to upgrade to 8.1.6.3.0,
but the patch have been retracted for Tru64 unix.

We now have to decide between:

Upgrade to 8.1.7.3.0
or
Upgrade to 9.0.1
or
Upgrade to 9.2

I would like to get as much "bang for the buck" as possible, since regression
testing is quite intensive (it is a quite complicated application). There are
also some 9i features we could make use of in future.

I am therefore inclined to go 9.x.x, but the question is 9.0.1 or 9.2 ?

From my experience with the 8.1.x versions, the newer versions introduced both
fixes and additional functionality, but I have also read that patches for older
versions sometimes only make it to the latest version at a later stage.

What would you recommend, 9.0.1 or 9.2 ?

We also use Oracle 9iAS 9.0.1 (on SUN Solaris), and the first release of Oracle
9iDS at the moment and would like to upgrade both to 9.2 as well. Any
recommendations in that regard ? Especially in terms of
Appserver/Database/9iDSR2 compatibility.

Regards

          Followup:

I would personally be tempted to goto either of 8174 (most ports have an 8174)
or 9iR2 as that is the current production release.  9iR1 would be out of the
question at this point for the simple reason that 9iR2 exists.

As for whether I went with 8i or 9i -- that would mostly be based on how soon I
would be introducing the new features into my environment.  If it was to be
soon, I would tend to at least try 9i.

June 21, 2002 Abhijeet -- Thanks for the question regarding
      "Customised Database Creation", version Oracle 8i

      You Asked
I would like to know the detailed steps to create a new database
on Windows NT Platform using the Oracle 8i version. Also how to
associate a new password file to the newly created database.
   What about the SYS and SYSTEM users for the new database.

      and we said...
The steps to create a database are in

http://technet.oracle.com/doc/oracle8i_816/nt.816/a73008/ch5.htm#1014550
I suggest taking the dbassist path..

The steps for managing (and creating) the password file is in

http://tomkyte:eefm3KCj@technet.oracle.com/doc/oracle8i_816/nt.816/a73008/ch6.htm#1028152
What about the SYS and SYSTEM users??
Review &
      Followup

      Rating: 3
steps
      required  June 21, 2002
Reviewer:  Rahul
      Sharma  from India
Hi Tom,

I have just installed the oracle server without creating the
database and now I want to create it manually by "CREATE DATABASE..." command
means without using wizard utility.
It would be great help if you provide me each and every commands step by step
and also give me the detail if any OS level change is require.

Thanks.

          Followup:

Hey, you don't need to hear it from me -- you can get it straight from the docs.

Since you don't supply anything truly useful such as:

o database version
o operating system (even in light of asking for OS level changes!!!)

I can only show you an example document.  I'll pick 9iR2 on Windows:

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/win.920/a95491/create.htm#1005961
that is a chapter in the Windows NT admin guide entitled appropriately enough
"Creating a Database Manually"

June 21, 2002 Mark -- Thanks for the question regarding
      "Netscape "Bounce"", version 9.0.1

      You Asked
Tom:

Probably far off topic, but I know you are a Netscape user.  OK, well, I think
you are a Netscape user.  Anyway, I have created several mod_plsql pages, but
the pages "bounce" or are redrawn in rapid succession in Netscape (4.79 anyway).
 Yet, your site does not bounce in the browser.  Have you seen this behavior?  I
found a site that said it is because there is an http-equiv setting the charset,
and Netscape draws the page, checks the http-equiv and then redraws the page.
So, I have removed all the http-equiv's from my pages, but still can't get the
page to display without the "bounce" or "double draw".  Just curious if you have
seen this and if you have a handy solution.

Thanks,

Mark

      and we said...
I use mod_plsql here.

I am a netscape user -- but a netscape v7.x user (I love having email, IM and
browser all in one and it even works on Linux which I am in the processing of
switching all of my pc's over to).

I've never seen the "bounce" but then again, I've not used Netscape 4.7 in
years.

I'll make this "public" to see if anyone else out there has any input.
June 21, 2002 Pawan -- Thanks for the question regarding
      "JDBC", version 8.1.7

      You Asked
What is the best way to become competent in JDBC. I don't know Java but I am
competent with Oracle backend development. How should I proceed to start using
JDBC and become proficient. What books / manuals do you suggest?

Thanks

      and we said...
I would suggest learning JAVA first.

As a C programmer, I learned Java just by getting the first "learn java" book I
found.  Worked through the examples and went from there.  Learning jdbc is
trivial if you know

o the database and how it works (you got that)
o java (you don't got that)

June 21, 2002 Steven -- Thanks for the question regarding
      "BITMAP Indexes", version 7.3.4

      You Asked
Tom,

I am trying to use a bitmap index on a column in one of our tables, but I
thought that I would first test that a bitmap index would be worth while.  Here
is my test case, can you please exaplain why my index is ignored.

Thanks

Steven

coos02@Live> create table t (
  2   x  int,
  3   yes_no varchar2(1)
  4  )
  5  /

Table created.

coos02@Live>
coos02@Live>
coos02@Live> BEGIN
  2    FOR X IN 1 .. 1000
  3    LOOP
  4      INSERT INTO t
  5      VALUES ( x,DECODE ( MOD ( X,2 ) ,1,'Y','N' ) );
  6    END LOOP;
  7  END;
  8  /

PL/SQL procedure successfully completed.

coos02@Live>
coos02@Live> CREATE BITMAP INDEX t_idx ON t(yes_no)
  2  /

Index created.

coos02@Live>
coos02@Live> set autotrace traceonly
coos02@Live>
coos02@Live> SELECT  x,yes_no
  2  FROM t
  3  WHERE yes_no = 'N'
  4  /

500 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'T'

Statistics
----------------------------------------------------------
         10  recursive calls
          2  db block gets
         38  consistent gets
          0  physical reads
          0  redo size
       9754  bytes sent via SQL*Net to client
        971  bytes received via SQL*Net from client
         44  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
        500  rows processed

      and we said...
well, in order to use bitmaps you must use the CBO, that is the first error.
You need to analyze the objects.

Second issue will most likely be that for 1,000 measley little rows, the index
path will still not be choosen.  Even if you have one billion rows, we'll likely
bypass it for this query.  You are asking to goto an index and retrieve 50% of
the rows from the table.  so, lots of random IO on the table to get 1/2 of the
rows.  Much better just to full scan it (we'll end up reading EVERY block in any
case won't we -- if every other row is a Y or N.  Why both with the index at
all)

Review &
      Followup

      Rating: 4
Bitmap
      Indexes  June 19, 2002
Reviewer:  Steven
      Cooper  from UK
Tom,

The table was purely for demonstration purposes the actual table is a lot bigger
and as a few more values, my main issue was that I couldn't provoke the
optimizer to use the index and I didn't know why ,but now that you've told me I
need to use the CBO I now know the error of my ways :).  My only problem now is
that if I analyse the table it will make all of our existing queries on the
table change (potentially), and whilst that will suit my queries, it might not
suit the 3rd party software that uses this table.  I have read that you can drop
statistics from tables how do you do this?  I think I might try analysing it and
if it mucks things up just drop the stats.  can you see any probs I might get by
doing this??

Thanks in Advance
Steven

          Followup:

well, as I said -- your "sample" query would read 50% of the rows from the table
via the index.  That is not a case for using an index (even a bitmap).  Since
you have to go to the table for 50% of the rows -- full scan = RIGHT ANSWER.

Want to force it, use hints.  don't need to analyze.  it'll be slower tho.

      Rating: 5
Delete
      Statistics  June 19, 2002
Reviewer:  Farid
      Uddin  from Toronto, Canada
Hi,

Try using
Alter Table table_name Delete Statistics;

      Rating: 5
Delete
      Statistics  June 19, 2002
Reviewer:  Steven
      Cooper  from UK
Farid,

Thanks for the advice, but it doesn't seem to work.

coos02@Live> desc t
 Name                            Null?    Type
 ------------------------------- -------- ----
 X                                        NUMBER(38)
 YES_NO                                   VARCHAR2(1)

coos02@Live> Alter Table t Delete Statistics;
Alter Table t Delete Statistics
              *
ERROR at line 1:
ORA-01735: invalid ALTER TABLE option

Thanks anyway

      Rating: 5
Deleting
      Statistics  June 19, 2002
Reviewer:  Steven
      Cooper  from UK
Thought I would share my findings.....can't believe I didn't try this earlier
Doh!......I must be having a bad day..

analyze table t delete statistics

      Rating: 5
ANALYZE TABLE t
      delete statistics  June 19, 2002
Reviewer:
      Pasko  from HH,Germany
Hey Steve,

It's

ANALYZE  TABLE  t  delete  statistics

      Rating: 3
Delete
      statistics  June 19, 2002
Reviewer:  Marc
       from Switzerland
Try this:

analyze table <table_name> delete statistics;

or

exec dbms_stats.delete_table_stats ('<schema>','<table_name>')

      Rating: 3
Yes, but stop
      willy-nilly deleting them.   June 20, 2002

Reviewer:  Chris Slattery  from Dublin, Ireland.

All the replies here have said 'Delete the statistics'

Yes, but why not  store them first. Create the stats table in the schema to hold
them.
dbms_stats.create_stat_table
then
dbms_stats.export_table_stats  (pick your statid)

and THEN delete them. Isn't that a better way - since then  you can put the old
statistics back in immediately with import_table_stats if you get a plan
degradation ?

      Rating: 3
They must be
      deleted  June 20, 2002
Reviewer:  Steven
      Cooper  from Uk
From my understaning if you have stats on a table the CBO will use them. The
problem we have is we don't analyse any of our tables Full Stop we use RBO
(Don't ask me way we just do) if I start analysing table willy nilly the queries
that use them will use the CBO, and whilst (as  I mentioned above) this would be
fine for my quries the rest of the queries i.e. written by the 3rd party
software house I could not vouch for, thats why I needed to know how to delete
the stats so If I muck things up using the CBO .. I can always revert to RBO.
Chances are I'm just being paranoid and every thing would work fine or even
better, but I can't take the risk..

      Rating: 5
Still a good
      thread.   June 21, 2002
Reviewer:  Chris S
       from Dublin, Ireland
Isn't this the one of the very reasons Stored Outlines were developed - to give
plan stability when moving from RBO to CBO ?

Check it out in the Concepts manual - as Mr. K rightly points out it's a manual
that's nearly always the dustiest on the stack but should be the most used.

          Followup:

question was about version 7.3.4..... (query outlines are 8i)

Yes, in 8i, you could use stored outlines if you

a) collected all of them..
b) had your code issue the alter session command to use them..

here, to test it out, i would be tempted to use the hints just to see how it
goes.

June 21, 2002 Logo
      -- Thanks for the question regarding "Use of AFTER ROW trigger", version
      8.1.6

      You Asked
Tom,

I know how we could use the BEFORE ROW level triggers. e.g We can use it set the
PK of a table (select my_pk_s.nextval into :new.pk_col from dual) or set the
value of a column (select substr(:new.col1, 1, 10) into :new.col1 from dual)
etc.

But how are the AFTER ROW level triggers used? What are they typically used for?
I can't think of any except may be capturing the rowid information into another
table.

Would appreciate if you could explain its use with some examples.

Thanks

      and we said...
An after row trigger would be used for data validation.

You would use an after row trigger to verify the data is correct -- you cannot
do this reliably in a BEFORE row trigger as the data is subject to being changed
by subsequent BEFORE row triggers!  The after row trigger sees the data as it
will exist prior to putting the data in the table -- the row is "stable".

You would use an after row trigger to audit data for example.

An after row trigger will generate less redo then a before row trigger will
during its firing -- so if you need not modify the :new record - just need to
fire some process (eg: send an email on the update of a row) you would use the
after trigger.

There are literally hundreds of uses -- the above three (data verification,
auditing, notification) being just the tip of the iceberg.
Review &
      Followup

      Rating:
      5
  June 15, 2001
Reviewer:
      A reader
After-row triggers are also faster for a validation check than would be the case
with before-row triggers (assuming the check could be done in either)

      Rating: 5
after row
      trigger  June 20, 2002
Reviewer:  Munz
       from Reston, USA
Tom:

I have the two different databases. In database A, emp table gets updated by
either code in database A or database B.  Since I do not have control over the
second database and I want to alert updates to the original creator I created a
column "PBA_update_Flag".  I told developer for other database every time he
sends an update statement to set this flag to "Y" so I know it is coming from
them.

CREATE OR REPLACE TRIGGER pba_update
  AFTER UPDATE on emp
  FOR EACH ROW
  WHEN (new.pba_update_flag='Y')

DECLARE

  v_email_conf       user.email_conf%type;
  v_pv_email         contact.pv_email@link%type;

BEGIN

   SELECT a.email_conf,b.pv_email into v_email_conf,v_pv_email
      FROM user a, contact@pic_link b
      WHERE a.contactid=b.contactid and
 a.emp_id=:old.emp_id;

  IF (v_email_conf='Y' and :new.pba_update_flag='Y') THEN

  mail_pkg.send(
       p_sender_email=>'my_email@yahoo.com',
       p_from=>null,
       p_to=>mail_pkg.array(v_pv_email),
       p_cc=>mail_pkg.array(),
       p_bcc=>mail_pkg.array(),
       p_subject => 'PBA Change from Database B' );
  END IF;

END;

1.  The problem I had, is that if the old value of pba_update_flag is set to 'Y'
the trigger will fire, even though I specified it should only run when a new
value of 'Y' is coming in.  What do you think happened?

2.  I was thinking of setting this flag to 'N' always when I run update from
database A so it will never fire the trigger.  Is that a goog workaround?

3.  Should I use schedule job instead of sending the email directly, because
this will send email before database B commits transaction.  Would the rollback
kill the job if it was scheduled?

4.  Can I detect the actual columns in the table that database B changed and
report those to the user.  Do I have to compare old.col1 with new.col1 for each
column to conclude the ones that have changed.

Thank you,

          Followup:

1)  AFTER UPDATE of pba_update_flag on emp

will fire the trigger only when you actually update that value, else it always
fires the trigger after update (when ...)

2) try #1 first...

3) that is my documented recommendation (in the book, on this site, etc).  I
always send mail with dbms_job because

  o smtp is slow sometimes
  o smtp is not transactional and dbms_job will make it transactional

4) You need to compare.

      Rating: 5
After row
      tigger  June 20, 2002
Reviewer:  Munz
       from Reston, USA
Tom:

1.  You say you always schedule a job because smtp is low sometimes.  Does that
slow down the procedure too?  Do you schedule to be sent an hour later?

2.  Also, if the user in the other database rollback the update, would the job
still be scheduled?  How do you know he rolled back so you can kill it?

3.  Can you use commit in new triggers with new versions or you have to do it
externally?

          Followup:

1) what procedure?  I schedule to happen in the background as soon as possible
after the commit (eg: Just send parameters for JOB and WHAT, no time)

2) The job "queueing" is piggy backing on your transaction -- if you roll back
your transaction, the request to run something after you commit is rolled back
as well.  that is exactly what makes this transactional.

3) for the EXCESSIVELY RARE (and I mean really really excessively rare)
occasions where you might be tempted to commit in a trigger, we have autonomous
transactions.  The only really valid use of which I've seen is for auditing.
They do not apply here, you do not want or need them for this.  For details --
see my book.  I spent a whole chapter on this topic (as well as another chapter
on this email topic, as well as another chapter on this dbms_job topic and so
on)

      Rating:
      4
example  June 21, 2002
Reviewer:
      A reader
Hi, TOm,

Would you mind give us an example to show how to send email
via smtp using background process "DBMS_JOB" after update
a specific column in the table?

I just can not get it without trigger how we can accomplish
this task by scheduling a job after certain modification
happened on one of the column in the table?

Thanks,

          Followup:

you have to have a trigger -- the trigger will call dbms_job is all.

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:255615160805
for some utl_smtp/email stuff.  To get the info about dbms_job and such -- you
can either just call dbms_job from the trigger, or if you want to see it step by
step -- my book has it (the above link is a short extract from that book).

June 22, 2002 Balaji -- Thanks for the question regarding "How
      to Return Result Sets from Oracle Stored Procedures", version 8.1.7

      You Asked
Thank you for accepting my question.

We are in the process of converting DB2 Stored Procedures to Oracle.  In DB2,
along with the Procedure specification, clause "RESULT SETS 1" is defined.  The
result sets are returned primarily through a "Open Cursor" Statement. Apart from
this the procedure also has Return Statements (and these return statements also
contain expression).  These DB2 stored procedures are invoked from C++ programs
and the result sets are used in the C++ programs for manipulation.

My Problem: How to handle this in Oracle Stored Procedures?
Issue 1 - Return Statement Cannot have expression in procedures (though in
functions this is allowed).
Issue 2 - Result Set has to be returned from same function along with return
statement with expression.(More than One Value cannot be returned in function).

      and we said...
http://asktom.oracle.com/~tkyte/ResultSets/index.html

You will use a procedure or function and the procedure will have IN OUT
variables of ref cursor types and the function can either return a ref cursor OR
have parameters of IN OUT types that are ref cursors.

Examples in the link above.
Review &
      Followup

      Rating: 3
Result Sets
      from Oracle Stored Procedures.  June 22, 2002

Reviewer:  Balaji T  from Chennai, India
Thank you for the clarification.
The example given narrates how to return result sets using functions.
But the stored procedures we are working on have result sets as well as a return
statement with expression.  Functions cannot anyway return more than one value
(Result Set + Return Value).
Can we have a method to handle this in Stored Procedures (instead of functions)?

          Followup:

create or replace function f( x in number,
                              result1 in OUT types.rc,
                              result2 in OUT types.rc ) return number

there is a function that takes as INPUT a number X, returns two result sets
result1 and result2 and finally returns a number as a value.

Functions, like procedures, accept IN, IN OUT, or OUT parameters.  A function is
nothing more then a procedure that returns a values.

A stored procedure would look like:

create or replace procedure p( result in out types.rc )

and when you called it, instead of executing:

  begin :x := f(); end;

you would:

  begin  p(:x); end;

is all -- they both can do this as handily as the next.  Also, read the PLSQL
documentation, it has alot to say about ref cursors.

      Rating: 4
Returning
      Result Sets through Oracle   June 24, 2002

Reviewer:  Balaji T  from Chennai, India
Yes.  The suggestion was very very useful and we are able to solve the problems
now.

June 22, 2002 -- Thanks for the question regarding "
      Concurrency :Only one at a Time ", version 816

      You Asked
Sorry for the inconvenience I if I am not able to communicate my problem.

Simple Please tell me how to let different users execute a package procedure in
a sequential manner.
If user A is using it, User B cannot.Has to wait then Retry.

My attempts user dbms_application_info and set value of client info
to some string like 'work in progress'. In the end I send message
'finished'.

If user B wants to run proc it can only run if in V$session it finds
'finished'.
How to enhance this. Is there a better way ?

Thanx

      and we said...
Oh, OK, easy enough.

You are looking at the wrong package, you want to look at DBMS_LOCK.  Using
this, you can create your own lock -- run the code, release the lock.  Others
that try to run the code will serialize on getting the lock.

search for dbms_lock on this site or if you have my book, I have examples in
there as well
-----------------------------------------------------------------------

have you looked at SEQUENCES in the database?  they are highly concurrent,
unique number generators.  They will NOT be gap free but they will be

a) unique
b) fast
c) highly concurrent

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

i'm afraid I don't understand.  If user a is using "p_proc", when p_proc
completes they will be "done" using p_proc.

I'll really need a clearer problem definition.  I don't see what the issue is.

Is it that you are trying to maintain a STATEFUL connection in a STATELESS
environment or what?

Review &
      Followup

      Rating: 5
Thanx
      Sir  June 23, 2002
Reviewer:  A reader

Were you So Intelligent by birth or have trained yourself into it ?

June 22, 2002 PARTHA -- Thanks for the question regarding "SAP
      DBA", version 8.1.7

      You Asked
Tom,

Can you Please Tell me What are the duties for SAP Database Administrator.

Is it the same as Normal database Administrator or is there any different.

Please Explain in Detail.

Thanks for your Cooperation.

      and we said...
Not any different then for being the DBA for "developed application X" or "off
the shelf product Y".

You are probably expected to be competent with the SAP installation,
configuration and tuning processes (as you would be for developed application X,
or off the shelf product Y).

You still have to backup, tune, recover, analyze issues and such just as normal.
 But you are probably also expected at some level to admin the SAP software
itself.
Review
      & Followup

      Rating: 3
DBA
      Review  June 23, 2002
Reviewer:
      A reader

June 22, 2002 Pratap -- Thanks for the question regarding
      "ORA-01001", version 9.0.0

      You Asked
Hi Tom

I created a packaged procedure returning a ref cursor in user_1 in database 1.
Created a synonym for the package in user_2 in database 2 over a database link.
But when I access the package in user_2 then it gives a ORA-01001.

Again am i hitting a bug here?

Best Regards

Pratap

      and we said...
documented restriction number 2 in this list:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a77069/05_ora.htm#1802
is what you are encountering.
June
      22, 2002 Chetan -- Thanks for the
      question regarding "Function execution and 9i Security", version 9.0.1

      You Asked
Tom,

I have two questions.

1. Herez the query,

   select func1(),func2() from table

   how are the functions in the query execute. Will these
   functions be executed in parallel. Does it matter, if both
   the function make selection from same table.

2. in 9iDB there is a row level security available
   (Virtual Private Database - VPD). I tried running its SA_xxx
    e.g SA_session in 8i i think it is called DBMS_RLS
    ( I am not sure )
    packages, however I could not find it. Is it installed
    by default or I have to run some script, what script will
    I have to run.

 Thanks
Chetan

      and we said...
1) they will be executed serially (func1 and func2) however -- if table is big
and you have parallel query AND the functions are parallel enabled -- func1 may
be parallelized as may be func2 (you might have 8 func1 executions happening at
the same time if you had parallel 8).

2) Actually, that is Oracle8i release 1 and up (815).

What you are referring to with the SA_SESSION package would be Oracle Label
Security -- OLS (and this too was an Oracle8i option) -- a specific
implemetation of row level security.  DBMS_RLS is still alive and well -- you
can still use that.  OLS is a separately licensed option to the database (EE
database).  If you did not license it, you will not have it.
June 22, 2002 Doug -- Thanks for the question regarding
      "Categories of stored outlines - what is the point?", version 8.1.7

      You Asked
Tom - in the top of query stabalization, I can store outlines of execution paths
and force the database to use a particular plan. However, in the syntax, I am
able to do this:
ALTER SESSION SET CREATE_STORED_OUTLINES = TRUE | FALSE | <category>
With a CATEGORY.   In other words, sometimes it seems to me it is suggested that
I might what certain execution paths used for batch at night and some during the
day for OLTP. Am I misunderstanding something?  Isn't the best execution path
the fastest no matter what I am doing day or night?  What is a compelling reason
to have 2 different plans for the same query?

      and we said...
There are reasons you want different plans for the same exact query.

Suppose you are developing an interactive application that allows pagination up
and down through a result set.  You would like to get the first rows from
"select * from t order by x" ASAP (well, the end users do).  Here, if X is NOT
NULL and there is an index on X, the best plan would be to use the index and do
table access by rowid.

Now, assume you are a batch program.  You run the SAME exact query.  You would
now really want to be able to get the LAST row of this result set ASAP -- if you
have to wait 5 minutes for the first row you don't care (you are just software,
you have infinite patience).  You are concerned with overall throughput, not
apparent response time.

Hence, for this query, you want two different plans to be used -- depending on
WHO runs it (OLTP or BATCH).
Review &
      Followup

      Rating: 5
Fair
      enough  June 22, 2002
Reviewer:  Doug
       from Danbury, CT USA
Ok. Thanks for clearing that up.

June 23, 2002 Barry -- Thanks for the question regarding
      "Finding how many of a particular day there have been this year so far",
      version 8.1.7

      You Asked
Is there a way to figure out how many of a certain day (Mon, Tues, Fri,
etc)there have been so far this year.  I know that there are several date
functions that can be used to pull day of week, day of year, etc.  But in
running a query from DUAL, how can I get it to count and tell me that there have
been 22 Fridays or Mondays, etc., this year.

Example of why I need this number:  I know that my technicians have done 500
work orders on Fridays so far this year.  But how many work orders on average do
we do on any given Friday, or any other day for that matter.  To do this
average, I need to know the number of Fridays (or other days) so far this year,
to divide the 500 work orders by to calculate my average.

Looking for a short easy query I can run,probably against the Dual table to tell
me in 7 rows, this is how many of each day there has been so far this year.  I
dont want to use the week of year function since depending on what day of the
week the year started on, we may have had 22 Fridays, but only 21 Thursdays,
etc.

Desired output
SUNDAY  21
MONDAY  21
TUESDAY 21
WEDNESDAY 21
THURSDAY  21
FRIDAY  22
SATURDAY  22

Thank you very much for your assistance.
Barry

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> select to_char(sysdate-rownum+1,'DD-MON
Day'),
  2             trunc(((trunc(sysdate-rownum+1))-trunc(sysdate,'year'))/7)+1
  3    from all_objects
  4   where rownum <= 7
  5  /

TO_CHAR(SYSDATE- TRUNC(((TRUNC(SYSDATE-ROWNUM+1))-TRUNC(SYSDATE,'YEAR'))/7)+1
---------------- ------------------------------------------------------------
18-JUN Tuesday                                                             25
17-JUN Monday                                                              24
16-JUN Sunday                                                              24
15-JUN Saturday                                                            24
14-JUN Friday                                                              24
13-JUN Thursday                                                            24
12-JUN Wednesday                                                           24

7 rows selected.

does that....
Review & Followup

      Rating: 5
Finding how
      many of a particular day there have been this year so
      far  June 22, 2002
Reviewer:  Barry Moody
       from Bellevue, Nebraska (USA)
Super Response!!  I took the info you gave me, modified it slightly so just the
day and number of days show. It's exactly what I wanted.  I enjoy reading the
questions and responses.  You do a great job.
Keep up the great work.

      Rating: 5
finding how
      many of a particular day there have been so far this
      year  June 22, 2002
Reviewer:  Barry
       from Bellevue, Nebraska, USA
Quick followup.  Can this query be done from a table other than ALL_OBJECTS.
Some clients may not be permissioned for the ALL_OBJECTS table.  Thinking of
using DUAL if possible?
Thanks again.

          Followup:

If the client doesn't have access to ALL_OBJECTS -- they are using an
unsupported database.  ALL clients have access to ALL_OBJECTS in all cases.

Yes, you could use dual (but access to dual is no more or less assured then
access to all_objects).  You would do this:

....
from ( select 1 from dual union all
       select 2 from dual union all
       ....
       select 7 from dual )
.....

June 23, 2002 Vasaku -- Thanks for the question regarding "is
      this possible ....", version 8.1.7

      You Asked
Tom

one of my users sends me a text file of data which is like following..

aso,bso,zso
tro,rro,bro

this data mapes to the first 3 columns of the data from table t.....

column1    column2   column3  column5  column6

aso          bso      zso     somevalue somevalue
tro          rro      bro     somevalue somevalue

Now I need to concatanate each row in the text file and check for the existence
of that value in table t( by concatanating the values of first 3 columns of the
table t) and then delete the respective rows in table t.

I visualize that we need to load up an array with concatanated values from the
text file , loop through it and then delete from the table.

Can you guide me please...

Thanks

      and we said...
I would use sqlldr to load up some temp table with the data.

Then, issue:

delete from T where (column1,column2,column3) in ( select * from temp_table );

Review &
      Followup

      Rating: 5
I
      agree....  June 22, 2002
Reviewer:  Vasaku

with you...

1.Canyou show me how to write a simple sqlldr script for this purpose

2.I'm not allowed to use sqlldr for this purpose, how can do this
procedurally...

pls respond

          Followup:

cd $ORACLE_HOME/rdbms/demo and look at ul*.ctl -- lots of examples

NOT ALLOWED TO??? Why?? does someone at your company want to waste money on
purpose?  geez, I though the economy was tight -- guess not, your company has
plenty to burn.  I feel sorry for you.  The dumb things I see people put through
for no technically valid reason.  Oh well.

Guess the "fastest" way then is:

o edit the file (i'll assume vi, you figure out the way to do it in your editor
;)

o :1,$s/^/insert into temp_table values ( '/

  that adds "insert into temp table values ( '" to the front of each line
  File now has:

  insert into temp_table values ( 'abc,def,ghi

  in it..

o :1,$s/,/','/g

  that changed each comma into ','.  File now has:

  insert into temp_table values ( 'abc','def','ghi

  in it..

o :1,$s/$/');/

  that added '); to the end of each line, file is now:

  insert into temp_table values ( 'abc','def','ghi' );

o add a line at the top that says:

  alter session set cursor_sharing=force;

and run the script in sqlplus.

      Rating:
      5
  June 22, 2002
Reviewer:
      A reader
Another fast way of doing the same (particularly if you are not on Unix) would
be to use MS Excel. Just convert the text file to an excel worksheet (rename
.txt to .xls) and insert/manipulate the field values...

      Rating:
      3
other  June 23, 2002
Reviewer:  ashraf
      gamal
hi

if i havn't yet table in the database and i have text file insted of creating
table can sqlldr create the table

          Followup:

sqlldr does not create tables, no.

June 23, 2002 prashant -- Thanks for the question regarding
      "dbms_job question", version

      You Asked
Usually I run Oracle Procedure as a job by DBMS_job
package.Can u tell me how run simple PL/SDQL code (not procedure

or function)a job thru DBMS_job package becoz when I give the
following syntax,I get an error for ":" in "c:\".
*************************************
variable jobno number;
begin
dbms_job.submit(:jobno,'c:\scripts\alchproc.sql;',sysdate,'sysdat
e+1/96);
end;
**************************************
In above code,"alchproc.sql" is my simple Pl/SQL code.
Thanks

      and we said...
You are trying to run a SQL Script that you might submit in
SQL*Plus.  It is not something that dbms_job can run.  DBMS_JOB
can only run SQL (insert/update/delete/etc) and PLSQL (things
that begin with BEGIN or DECLARE).  Dbms_job works by taking the
"what" you submit and wrapping a BEGIN/END; around it and then
executing it as a SQL statment.  somefile.sql is not a sql
statment.

You can take the contents of alchproc.sql (minus ANY
sqlplus'isms like "column foo format a10" and so on) and submit
that.  For example:

...
begin
   dbms_job.submit( :jobno, 'begin
                                ..... plsql code here
                             end;', sysdate, 'sysdate+1/96');
end;
....

Review &
      Followup

      Rating: 4
sqlplus'isms
        June 23, 2002
Reviewer:  Irfan  from
      Kuwait
Hi Tom,

How can I use sqlplus'isms if I want to use in your given example. I need the
format hence I do some job daily which transfers data to a flat file. -- Thanks.

          Followup:

you won't be using dbms_job then.  Or if you do, you'll be using dbms_job to run
an OS command since sqlplus is just a program (like notepad, awk, ls, etc are
just programs).

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:952229840241
for a method of running host commands in 8i and up.  Then, you'll schedule:

'rc( sqlplus user/pass @c:\scripts\alchproc.sql );'

June
      23, 2002 Deep -- Thanks for the
      question regarding "Creating a table from few tables", version 8.1.7

      You Asked
Tom,

I have a requirement by which I have to build a table selecting data from quite
a few big tables(4/5 millions rows). What will be best method of building such a
table using functions or creating some intermediate tables (using join) and then
building target table from those intermediate tables.

Table A
A1 A2 A3 A4 A5
Table B
A1 B1 B2 B3 ..B10
Table C
B1 C1 C2 C3 C4 ..
Table D
C1 D1 D2 D3 D4
Table E
D1 E1 E2 E3 E4 E5

Now lets say I have to build a table consisting of
A1 B1 C1 D1 E1 based on some join conditions

Please can you help me with an example.

Regards,

      and we said...
I would do this with a single statement.  Either one CREATE TABLE AS SELECT or
one INSERT /*+ APPEND */ into an existing table.  Using parallel query and the
unrecoverable options.

If you introduce procedural code or multiple-steps into this, it will almost
certainly run slower then a single statement will.

Review &
      Followup

      Rating:
      5
  June 23, 2002
Reviewer:  Deep
       from UK
Tom,

Thanks for your help.

June 23, 2002 -- Thanks for the question regarding "SQL
      OBJECTS", version 8.1.7

      You Asked
Tom,

I created the following TYPES and procedure to retrieve data from a tables
and pass back to a calling program as an array. Although I have tested this
and I can see what is returned by using the enclosed dbms_out.put_line
statement.

Could you please advise that the line where I it's stated that "resultset :=
rst".
The resultset variable is what is finally passed back to calling program. (java
thin clients).

Also, if you could be kind enough to write some java to get and parse this
returned variable.
/*
CREATE OR REPLACE TYPE mytest_typ AS OBJECT
(usr_id      VARCHAR2(8),
 lst_nm     VARCHAR2(25)
)
/

CREATE OR REPLACE TYPE mytest_tab is TABLE OF mytest_typ;
/
*/

create or replace PROCEDURE prc_get_values(resultset OUT mytest_tab)
IS
CURSOR c_corp IS
     SELECT ename, job
           FROM emp;

rst mytest_tab := mytest_tab();

BEGIN
    FOR corp_rec IN c_corp LOOP
             rst.extend;
         rst(rst.count) := mytest_typ(corp_rec.ename,corp_rec.job);

     END LOOP;

     for i in 1 .. rst.count loop
      dbms_output.put_line ( rst(i).ename ||' - ' || rst(i).job);
     end loop;

>>>>>>>>>     resultset := rst;  <<<<<<<<<<<<<<<<< IS this statement correct? I
am not sure.
END prc_get_values;
/
show errors

      and we said...
Well, yes, the assignment is correct - but not necessary (the variable rst
itself is not necessary.

You could code this procedure as:

scott@ORA817DEV.US.ORACLE.COM> create or replace PROCEDURE
prc_get_values(resultset OUT mytest_tab)
  2  IS
  3  BEGIN
  4      select cast( multiset( select ename, job from emp ) as mytest_tab )
  5        into resultset
  6        from dual;
  7  END prc_get_values;
  8  /

Procedure created.

that does everything your procedural code does -- HOWEVER -- this is not the
right way to return a result set from a stored procedure.  See

http://asktom.oracle.com/~tkyte/ResultSets/index.html
This procedure will boil down to:

scott@ORA817DEV.US.ORACLE.COM> create or replace PROCEDURE
prc_get_values(resultset IN OUT types.rc )
  2  IS
  3  BEGIN
  4      open resultset for select ename, job from emp;
  7  END prc_get_values;
  8  /

Procedure created.

and that is it.  If you really want to do the object way (not suggested, it is
harder then a simple jdbc resultset), you'll want to read the JPublisher
documentation.  It'll take your type defs and create java classes that map to
them.

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/java.817/a81357/toc.htm

Review &
      Followup

      Rating: 4
SQL
      OBJECTS  June 23, 2002
Reviewer:  femi

Thanks Tom.

The reason I did it that way was

1) I know very little about JAVA now. So someone else will code the JAVA bit. He
confirmed that they could MAP the OBJECT type, so I had to convince my self that
some is actually being returned.

2) The actual code itself is a little more complicated than that, I am passing
in 2 parameters to the procedure. Based on these parameters I'll then used
CURSORS to satisfy the following logic

for each reccord in CURSOR 1 LOOP
 open  cursor 2 ( get some values into a variable)
 open  cursor 3 ( get more variables )

>>>> it's at this point i need to populate the result set
which is to be passed out of the  procedure <<<<<<

so I did someting like

   l_data.extend
   l_data(l_data.count).sometning1 := cursor1_rec.something1
   l_data(l_data.count).sometning2 := cursor2_rec.something2

end loop

Hence, my resultset's from a number of tables.

I'll try your method and see if I can get it to work.

regards
Femi

          Followup:

I bet if you look at your logic, you can do it in a query!  faster, better,
cheaper, easier then writing, debugging, maintaining a ton of procedural code.

June 23, 2002 Ganesan -- Thanks for the question regarding
      "Installing our Own Developer Application", version 8.1.6

      You Asked
Dear Tom,

We are having an application which is developed in Oracle
Developer 6i and the database Scripts are generated through
Designer. Note that our backend is Oracle 8.1.x.

In order to install our application at the customer site, now
manually we are running the scripts that are generated by
Designer, but as you are aware this is not a good practice
because manual intervention is required for this operation. So,
we are looking for a tool, which will help us to install our
application using GUI tool.

Note that, later we are interested to add the storage clauses
for the tables based on the customer requirements rather than
having a hard coded values. So, the tool should have some
options to accept the same.

Hope you might have understood our requirements and in case of
any further clarification is required please contact us in
cganesan@cssoman.com address.

Thank you very much in Advance and looking forward for your
earliest reply.

Best Regards,

C. Ganesan

      and we said...
See the Oracle Software Packager,

http://technet.oracle.com/software/products/osp/software_index.htm
Quote from there:
...
Oracle Software Packager is Oracle's new solution for building
the installation of Oracle8i and products following. Software
Packager is a new Java tool that allows you to package software
into a format which can be interpreted by the Oracle Universal
Installer. The Software Packager replaces the ORCA scripting
language with a Java-based, object-oriented product that is easy
to learn and support but yet has the complexity required for
defining installations of large sets of software for any
environment.
.....

Review &
      Followup

      Rating: 1
i follow
      instruction but it does not work  June 23, 2002

Reviewer:  R Diab
hi Tom,
i  need to know how to add my database structure    ( schama... tables....)
to my package . using software packger?

          Followup:

"it does not work", how descriptive.

June 23, 2002 Suresh -- Thanks for the question regarding "9i
      features", version 8.1.7

      You Asked
Tom,
  Could you please point me to the URL that gives a list and details of only
Oracle 9i features. Thanks.

      and we said...
Well, that would be the New Features guide

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/nav/docindex.htm#index-NEW
also available by going to otn.oracle.com, clicking on documentation -> 9i
database -> list of books -> NEW
June 23, 2002 Suresh -- Thanks for the question regarding
      "External tables", version 9.0.1

      You Asked
Tom,
  I was working with External tables and Oracle came up with an error when I
tried to insert, update or delete from external tables, as shown below.

SQL> delete from student_ext;
delete from student_ext
            *
ERROR at line 1:
ORA-30657: operation not supported on external organized table

SQL> update student_ext set total = 50;
update student_ext set total = 50
       *
ERROR at line 1:
ORA-30657: operation not supported on external organized table

SQL> insert into student_ext values (6, 'NEWDATA', 'NEWDATA', 90);
insert into student_ext values (6, 'NEWDATA', 'NEWDATA', 90)
            *
ERROR at line 1:
ORA-30657: operation not supported on external organized table

  I understand why it is throwing those errors. My question is do you have any
"tricks" by which you can insert, delete, update on external tables.

Thanks,
Suresh

      and we said...
Sure,

SQL> host vi filename

that'll do it.  Seriously though, no -- there is no way to modify these files
using SQL.  There is no locking, no concurrency controls, no nothing.  They are
read only.
Review & Followup

      Rating: 5
Thank you so
      much  June 23, 2002
Reviewer:  Suresh

Tom,
  I just wanted to confirm with you whether it could be done before deciding so.
Thanks a lot for your help.

June 24, 2002 Marc -- Thanks for the question regarding
      "Uncomplete execution plan?", version 8.1.7

      You Asked
Hi,

when explaining a query like

SELECT ...,
       (SELECT attribute FROM another_table WHERE yadda = blub) AS something,
       ...
  FROM one_table,
       other_table
 WHERE bla = blub
   AND ...

the subquery being part of the SELECT-clause doesn't show off in the execution
plan. Why that?

Thank you in advance
Marc

      and we said...
It just doesn't, they don't have that in the explain plan.

You would need to exaplain it separately like this:

variable blub number
set autotrace traceonly explain
select attribute from another_table where yadda = :blub;
set autotrace off

Review &
      Followup

      Rating:
      4
Excellent  June 20, 2002
Reviewer:
      Chris  from Dublin, Ireland.
This explains one or two things.

Tom, is it possible to make a general statement about this
- any inline view will not be explained ?.

However, it does not show up on 10053 trace either (this is prob. why it doesn't
show up in explain either) , so is it a fair guess that it is parsed completely
differently and then the results combined ?

          Followup:

No, you cannot make a statement like that:

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select *
  2    from ( select object_id from big_table where object_name like 'ABC' ),
  3         ( select object_name from big_table where object_id = 55 )
  4  /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=114 Card=10991 Bytes=659460)
   1    0   NESTED LOOPS (Cost=114 Card=10991 Bytes=659460)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'BIG_TABLE' (Cost=3 Card=1
Bytes=30)
   3    2       INDEX (UNIQUE SCAN) OF 'BIG_TABLE_PK' (UNIQUE) (Cost=2
Card=10991)
   4    1     TABLE ACCESS (BY INDEX ROWID) OF 'BIG_TABLE' (Cost=111 Card=10991
Bytes=329730)
   5    4       INDEX (RANGE SCAN) OF 'OBJECT_NAME_IDX' (NON-UNIQUE) (Cost=2
Card=10991)

Clearly here, the inline views are explained.

It is when you use a Subquery in place of an EXPRESSION in the select list that
it will not:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from dual
  2  where 2 = ( select count(*) from big_table );

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=82 Bytes=164)
   1    0   FILTER
   2    1     TABLE ACCESS (FULL) OF 'DUAL' (Cost=1 Card=82 Bytes=164)
   3    1     SORT (AGGREGATE)
   4    3       INDEX (FAST FULL SCAN) OF 'BIG_TABLE_PK' (UNIQUE) (Cost=67
Card=1099008)

  1  select ( select count(*) from big_table )
  2* from dual
ops$tkyte@ORA817DEV.US.ORACLE.COM> /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=82)
   1    0   TABLE ACCESS (FULL) OF 'DUAL' (Cost=1 Card=82)

it is just the placement of the query in this particular case -- they just don't
explain the selected SELECTs.

      Rating: 2
Enhancement
      Request  June 24, 2002
Reviewer:  Marc Blum
       from Aachen, Germany
Now I know, that there's no bug, but sorrily I won't call it a feature. Can we
turn it into an enhancement request? I see no obvious reason, not to explain the
SELECT inside the SELECT-clause. Just stating "We don't do that." is a bit thin.

          Followup:

There is an official, formal and documented enhancement request system.  It is
part of support.  Enhancement requests are placed into the system with a
business case justification.  User groups and customer advisory boards vote on
how to prioritize these enhancements.  That is how new features get into the
product.

All you need do is file the enhancement request.  I could do it -- but (and this
is fairly important)

o i'm not a paying customer, what I think is "nice" but not as relevant as what
  customers ask for

o I don't have a business case to go along with this

What I do here is try to explain the how or why and provide solutions (bridging
the gap).  That is what I've done here -- I demonstrated how to get the plan.

If you would like an enhancement request to be logged -- that is easy, just file
a TAR.

      Rating: 4
It's a known
      bug# 1721097  June 24, 2002
Reviewer:
      A reader

June 24, 2002 Shakir -- Thanks for the question regarding
      "Adding a hidden column in a table.", version 8.1.7.2

      You Asked
Can you please tell me, how to add a column which doesn't appear when a table is
described.

      and we said...
We call that a VIEW.

You don't expose the table, you expose a view.  Otherwise, the column is there
and will be seen.

Review &
      Followup

      Rating: 1
Hiding a
      table's column  June 24, 2002
Reviewer:  Shakir
      Shaik  from Houston, TX USA
Thanks for the response..
But, I have a peculiar case in which I have to hide the column of a table.

For instance, I have a table T with column C1 and C2. Now, I want to add a
column H1 to T such that if I do desc T, I should get only C1 and C2. If I do
select * from T, then, I should get values for C1 and C2, But, I if I query for
the column H1 explicitly in a select statement, I should be able to see it.

I know, this has been implemented in OLS ( Oralce Label Security ) where in we
have an option of specifying if the label column is to be made hidden with the
above behaviour. I was wondering if the same can be extended to any other table
column.

          Followup:

No, as I said, No.  This facility -- adding of psuedo columns (like rownum,
rowid, level, etc) does not exist.

Your solution is a VIEW.

rename table T to T_table;
alter table T_table add h1 number;
create view T as select c1, c2 from T_Table;

Now, T is what you want and the people that need H1 will query T_table instead
(hey, they KNOW about H1, they are already "in the know".  It is not any harder
for them to select * from t_table then it is to select h1 from t -- they are
"special" already)

      Rating: 2
Another example
      of a hidden column  June 24, 2002
Reviewer:
      Wolfgang Matt  from Germany
Creating a materialized view without primary key generates a table with a hidden
column:

create snapshot log on SCOTT.BONUS with ROWID;
create snapshot SCOTT.BONUS_MV refresh with ROWID as select * from SCOTT.BONUS;
insert into SCOTT.BONUS values ('MATT', 'MGR', 3000, 30);
commit;

exec dbms_snapshot.refresh('SCOTT.BONUS_MV');

select * from SCOTT.BONUS_MV;

ENAME      JOB              SAL       COMM
---------- --------- ---------- ----------
MATT       MGR             3000         30

select M_ROW$$ from SCOTT.BONUS_MV;

M_ROW$$
------------------
AAAHW9AABAAAMUyAAA

M_ROW$$ is hidden. It is not shon by select * but can be reteived directly.
There is even an index on that column.

          Followup:

So, I can give you dozens of examples where (since we wrote the code) we do
things you cannot.

You asked a question -- I gave you the answer.  I also told you how you would go
about doing this yourself.  Sorry you don't like it -- but -- there is nothing
more that I can do.

June 24, 2002 --
      Thanks for the question regarding "Batch and OLTP in 24*7", version 8.1.5

      You Asked
Dear Tom,
Thanks for this site.
A long running batch would lock the table for the duration it would run. In the
meanwhile an OLTP would wait till the batch would complete and release the
resource. How then to plan/design for an 24*7 environment. I mean, the batch
should release the resource/lock once it has processed it and not when the
complete batch is committed in the end.
Regards,

      and we said...
You have conflicting goals that cannot coexist.

Think about it.  Batch is defined by:  long running process that gains resources
and uses them to take the database from one consistent state to the next.  OLTP
is defined by: short, very very short, transactions that take the database from
one consistent state to the next.

I suppose you'll have to rethink your "batch" process and instead turn it into a
OLTP simulator.  eg: if it has a bunch of orders to load into the database,
it'll do them one at a time - real quick - like it was the oltp client.

If you have a batch update to do (mass update), you'll most likely have to
schedule that knowing that oltp will be momentarily affected.
Review &
      Followup

      Rating:
      4
Thanks  June 24, 2002
Reviewer:
      A reader
Dear Tom,
I think I understood.
1. For a real 27*7 it should be an OLTP simulater. We can even think of applying
updates like that, i.e. say for ATMs to function, bank updates should be run in
OLTP simulation.
2. If we can afford OLTP affection for the period of batch run, may be at a time
when OLTP is low, then schedule it at that time.
I would fancy first method. But, OLTP simulation for 24*7 is the simplest design
or the only and good design. What if it floods the system with reqests, does not
fit in the batch window or slows down the whole system?
Thanks very much for answering my question.
Best regards,

          Followup:

Trickle feed it -- don't "save it" -- process it continously.  Batch has a
specific connotation, it was a solution to a specific problem (hey, run the
transactions at night, after everyone has gone home).  Your people don't go
home.


Subject | Article | Last Updated (East coast time)
Materialized View, is it a good choice for my situation? | article only | Jun 25, 2002 08:28 am
When constraints are checked | article only | Jun 25, 2002 08:52 am
Multi-Process Test Case | article only | Jun 25, 2002 11:06 am
using SQl types to build a selectable bidimensional structure. | article only | Jun 25, 2002 11:25 am
PL-SQL Coding Conventional | article only | Jun 25, 2002 11:33 am
Loader on steroids | article only | Jun 25, 2002 12:55 pm
Input truncated | article only | Jun 25, 2002 06:50 pm
Describing Stored procedures using OCI(8.0) | article only | Jun 25, 2002 06:52 pm
How to send rows to a trashcan or null sink for CPU test | article only | Jun 26, 2002 08:08 am
Loading spread data from text files with sql*loader | article only | Jun 26, 2002 08:11 am
Bind Variables.... | article only | Jun 26, 2002 08:24 am
Query Limitation | article only | Jun 26, 2002 08:53 am
Import/Export Views | article only | Jun 26, 2002 03:19 pm
calling ref cursor from stored procedure | article only | Jun 26, 2002 03:21 pm
dynamic sql, use of bind vars in predicate functions for unknown execution time. | article only | Jun 26, 2002 03:30 pm
Deadlocks | article only | Jun 26, 2002 03:35 pm
Limit access to read-only while data is being accessed by someone | article only | Jun 26, 2002 09:31 pm
Ageing analysis report | article only | Jun 27, 2002 08:13 am
Indexing views | article only | Jun 27, 2002 08:14 am
Describing a MEMBER of a package by non-owner | article only | Jun 27, 2002 08:33 am
Tracing sessions from Java App | article only | Jun 27, 2002 09:31 am
RE: Ask Tom Submission, changing block size | article only | Jun 27, 2002 10:41 am
Materialized view | article only | Jun 27, 2002 12:08 pm
indexing strategy | article only | Jun 27, 2002 01:41 pm
Lock Record When multiple users are using a JSP application | article only | Jun 27, 2002 02:05 pm
compile a views | article only | Jun 27, 2002 02:07 pm
Capturing SQL statments | article only | Jun 27, 2002 03:25 pm
Code size | article only | Jun 27, 2002 04:36 pm
Arrays and text replacement | article only | Jun 27, 2002 08:20 pm
Creating users in remote database | article only | Jun 27, 2002 08:20 pm
Archive log files deletion | article only | Jun 28, 2002 06:40 am
Java StoredProcedure Vs PL/SQL StoredProcedure | article only | Jun 28, 2002 06:53 am
Why does ORACLE Takes long time for processing data | article only | Jun 28, 2002 07:36 am
how to use copy command in oracle | article only | Jun 28, 2002 07:50 am
Oracle Tablespace | article only | Jun 28, 2002 08:30 am
Oracle Directory Services | article only | Jun 28, 2002 08:37 am
dynamic sql? | article only | Jun 28, 2002 09:58 am
Global Temporary Table | article only | Jun 28, 2002 12:47 pm
Does Character set changes require column size change. | article only | Jun 28, 2002 12:48 pm
Hiding data | article only | Jun 28, 2002 12:51 pm
How to create package in Java | article only | Jun 28, 2002 01:47 pm
Getting the next working day | article only | Jun 28, 2002 05:06 pm
Exclusive locks | article only | Jun 29, 2002 10:35 am
How to use pl/sql table with jdbc | article only | Jun 29, 2002 10:47 am
Passing OCI array to the procedure and retruning as a TABLE | article only | Jun 30, 2002 09:49 am
Is there a way to convert the 0.12 to 0.15 , 0.16 to 0.20 etc decimals | article only | Jun 30, 2002 09:56 am
How to handle purge large table | article only | Jun 30, 2002 07:49 pm
Indexes usage | article only | Jul 01, 2002 07:22 am
Most Admirable Oracle 9i Database Feature | article only | Jul 01, 2002 07:33 am
v$session_longops | article only | Jul 01, 2002 09:21 am
Deployment files for D2K | article only | Jul 01, 2002 10:02 am
FGAC problem | article only | Jul 01, 2002 10:58 am
LMT question | article only | Jul 01, 2002 12:28 pm
Using Text_IO package | article only | Jul 01, 2002 12:29 pm
Few questions !!! | article only | Jul 01, 2002 12:41 pm
Regarding rowid | article only | Jul 01, 2002 01:13 pm
Combining data from two tablespaces. | article only | Jul 01, 2002 03:03 pm
Why the following query behaving like this? | article only | Jul 01, 2002 07:16 pm
row(s) 1 - 58 of 58
June 25, 2002 Shrek -- Thanks for the question regarding "Materialized View, is it a good choice for my situation?", version 8.1.7
You Asked
Tom:

We have a situation where we have several batch scheduled (daily) processes that
copy large amounts of data from a remote database. In most cases the data in the
remote database hasn't changed. We only have Read Only access to the remote
database tables. But we have the flexibility to add indexes, analyze, etc. on
the local tables once they have been copied.

A typical query looks like this:

INSERT --+ APPEND
   into t
SELECT --+ driving_site(t1)
   t1.a, t2.b
FROM
   t1@remote_db t1, t2@remote_db t2
/

My problem is:
   We truncate the local table, and insert into it all the rows everyday even if
a single record has been added. this is too slow.

Question:

Can I use a Materialized View with a refresh on demand for this case? However, I
only want to copy the increments (deltas). I notice the available syntax for
DBMS_MVIEW.REFRESH_ALL_MVIEWS allows a fast incremental refresh (Method = 'F').
However, my question is how does Oracle know how to copy only the changed
records?

Please note that I have Read Only access to the remote tables t1, and t2.

Thanks,

Shrek
and we said...
In order to get the increments, you need to change the table on the remote site.
 This change will be to create a snapshot log on the remote site to capture the
changes.  It'll introduce a trigger on the remote table and a log table.

If you can do that, then yes, that will most likely speed up the refresh as only
new/changed rows will be transmitted.  It sounds like though that you will not
be able to do this.

Another option that might be faster, if you can facilitate it, would be to use
transportable tablespaces.  If you can work that out, you can move this data as
fast as you can move a file from one system to the other (indexes and
everything).

If you cannot do that -- just make sure your indexes are disabled during the
insert append and rebuild them in parallel/unrecoveral after you are done.

In 9iR2, we introduced a feature, Oracle Streams, that will do this as well
without having to modify the remote table at all (log based)
Review & Followup
Rating: 5
Informative plus more June 23, 2002
Reviewer: Shrek from MD, VA
Thanks for the precise response.
   This 9i feature seems to be very promising. Our client will be moving to 9i
soon.
   Does the remote database need to be a 9i database also? Right now it is
8.1.7.

Followup:
For the necessary amount of information to be in the redo stream, it'll have to
be 9i on the remote side.

Rating: 5
MERGE command June 24, 2002
Reviewer: Suresh from New York
Tom,
   If they were using 9i, I think MERGE command would be useful in this
scenario..What is your opinion?

Followup:
If I was redoing the entire table - inserting and/or updating every record -- I
would prefer the truncate/insert append if I could.  No rollback, no redo (if
nologging).  Faster then merging.

If continous availability were key, merge would be appropriate (during the
truncate/insert, there is no data to query)

Rating: 5
Replication? June 24, 2002
Reviewer: Alan from Frankfurt, Germany
Hi,

Is Oracle Streams then a complement to the standard replication routines, or a
replacement of it within 9i.
I'm not entirely up to date with replication and the changes that have occurred
since 8i.
...or am I completely wrong?

Followup:
It is an alternative, a different method.  It is not the same as, but similar
outcomes can be achieved with it.

June 25, 2002 Venkatesa -- Thanks for the question regarding "When constraints are checked", version 8.0.6
You Asked
Hi Tom,
   In your book you have written that Oracle checks for constraints not during
transaction but after the completion of transaction. You have given an example
of updating Unique Key. The reason is valid for Unique key updation. But what
about check constraints/ NOT NULL constraints and even Foreign Key constraints.
Assume I want to update , say, 1000000 rows and the very 1st record fails due to
some check constraint. What will be Oracle's behaviour. Will it report the error
only after updating all rows or does it throws the error immediately.

  Moreover I feel, Updating Unique Key / Primary Key is not a very common or
even good scenario in RDBMS.

Thanks in advance.

Venkatesa Prasad. N
and we said...
For the constraints, it'll depend on the TYPE of constraint.

For example, a NOT NULL -- it knows right after the row was modified whether the
statement has a chance of being successful or not.  Hence, the first row
processed by the update that fails the NOT NULL constraint -- will fail the
statement (assuming non-deferred constraint checking of course!).  Consider:

big_table@ORA817DEV.US.ORACLE.COM> update big_table set owner = null;
update big_table set owner = null
       *
ERROR at line 1:
ORA-01407: cannot update ("BIG_TABLE"."BIG_TABLE"."OWNER") to NULL

Elapsed: 00:00:00.17
big_table@ORA817DEV.US.ORACLE.COM> select count(*) from big_table;

  COUNT(*)
----------
   1459904

Elapsed: 00:00:10.92

That update failed on the first row -- it could not have touched all 1.4 million
records in that time!.

I agree with updating a primary key (although it happens).  I thoroughly
disagree with you on the unique key however.  That is the fundemental difference
between a primary key and a unique constraint in my mind.  Unique just means
unique, primary key means unique AND immutable (not changing)
Review & Followup
Rating: 3
Reader June 24, 2002
Reviewer: A reader
By "immutable (not changing)", you meant can not be
updated to NULL. However it can be updated to
not null unique values. Is this correct

Thanks

Followup:
No, I meant it should not be updated.  A primary key has a specific semantic
connotation -- "I am the primary key, I am a value, I am never changing.  If you
know my value and come back 5 years from now, I will have the same value".

Strictly speaking, the "technical" diffs between unique and primary key are:

o unique can be null
o primary key cannot be null

Semantically speaking, they are night and day different.

June 25, 2002 Vinnie -- Thanks for the question regarding "Multi-Process Test Case", version 8.1.7
You Asked
Tom,

After ordering your book(Excellant) I started building test scripts to get a
look see for myself for some of your examples in the book.
In the Tuning section, you give an example of how to check waits for inserts
using the v$session_event & how it may be effected by scaling the number of
simultaneuos executions of the same script.

My question is:
How can I simulate this without having multiple UNIX terminal windows & trying
to press enter at the same time?

I tried building a .sh script that essentially launches SQLPLUS N number of
times & executes the same script, but when I run this I do not see Latch waits
change.
When I try using 2 terminal windows and pressing enter at the same time I can
see the Latch waits go up just as expeected.
I would really like to use using some sort of script that can launch N number of
sessions to really get a true benchmark.

What would your approach be?

Thanks again as always!
Vinnie
and we said...
In order to get them started at the same time -- I've used DBMS_ALERT in the
past.

You can create a script like this:

--------------------------------------------------------
variable msg varchar2(4000)
variable status number

exec dbms_alert.register( 'GO' );
exec dbms_alert.waitOne( 'GO', :msg, :status );
exec dbms_alert.remove( 'GO' );

STUFF TO DO HERE
-----------------------------------------------------

and then run a script like this:

#!/bin/csh -f

sqlplus perfstat/perfstat <<EOF
exec statspack.snap(10);
exit
EOF

sqlplus / @test &
sqlplus / @test &
sqlplus / @test &

sqlplus / << EOF
exec dbms_alert.signal( 'GO', '' );
commit;
EOF

wait

sqlplus perfstat/perfstat <<EOF
exec statspack.snap(10);
exit
EOF

That'll log all of the scripts in, and have them wait.  I set this one up for
statspack monitoring -- but you get the gist...
June 25, 2002 Luigi -- Thanks for the question regarding "using SQl types to build a selectable bidimensional structure.", version 9.0.1
You Asked
Hi, Tom. Here is my question for you.
I have to store some values in a bidimensional structure of varchar2(100).
I will need to issue a select statement on this structure (in order to build an
XML from it, for example), so it can't be a PL/SQL table.
Furthermore, nor the no. of rows nor the no. of columns are available at
compile-time.
In a monodimensional fashion, I would do the following (consider it just an
example):

CREATE OR REPLACE
Type rigaQuery
AS table of varchar2(100)
/

CREATE OR REPLACE
Function setRiga(indice number)
  RETURN  rigaQuery is
   miaRiga  rigaQuery := rigaQuery();
BEGIN
    miaRiga.extend();
    miaRiga(1) := 'A'||indice;
    miaRiga.extend();
    miaRiga(2) := 'B'||indice;
    miaRiga.extend();
    miaRiga(3) := 'C'||indice;
    RETURN miaRiga;
/*
** Obviously, I would extend miaRiga to the no. of columns desidered.
*/
END;
/

Now, if I run the following select statement:

select * from table(select setriga(1) from dual)

I can see this output:

A1
B1
C1

Fine. I'm quite near my needs (or I was thinking so..).
So, I then created the following type:

CREATE OR REPLACE
Type GENUSER.tabQuery
AS table of rigaQuery

and the following function, similar to the one above:

CREATE OR REPLACE
Function setQuery
  RETURN  tabQuery is
   miaRiga  rigaQuery := rigaQuery();
   miaTab   tabQuery := tabQuery();
BEGIN
    for indice in 1..3 loop
    miaTab.extend;
    miaTab(indice) := setRiga(indice);
    end loop;
    dbms_output.put_line(miaTab.last);
    RETURN miaTab;
END;
/

The call to dbms_output was for testing purpose, and it correctly give 3.

But if I issue a select statement similar to the previous:

select * from table(select setquery() from dual)

I see the following output

RIGAQUERY('A1', 'B1', 'C1')
RIGAQUERY('A2', 'B2', 'C2')
RIGAQUERY('A3', 'B3', 'C3')

while my need is to obtain something like:

A1  B1  C1
A2  B2  C2
A3  B3  C3

Any suggestions?
Thanks in advance.

Luigi Di Maggio.
and we said...
SQL is SQL -- SQL needs to know the number and types of columns.  You started
with "i don't know the number of columns" -- hence, sql will have to deal with
this as an array.

Sorry, but i do not see a way to achieve your goals using collection types.  I
would just use an "oversized" object with lots of columns (i used 3, you would
use more)

ops$tkyte@ORA9I.WORLD> create or replace type myscalarType as object
  2  ( c1 varchar2(10), c2 varchar2(10), c3 varchar2(10), /* .... others */
  3    static function new( c1 in varchar2 default null,
  4                                c2 in varchar2 default null,
  5                                c3 in varchar2 default null ) return
myscalarType
  6  )
  7  /

Type created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> create or replace type body myScalarType
  2  as
  3
  4  static function new( c1 in varchar2 default null,
  5                       c2 in varchar2 default null,
  6                       c3 in varchar2 default null ) return myscalarType
  7  is
  8  begin
  9      return myScalarType( c1, c2, c3 );
 10  end;
 11
 12  end;
 13  /

Type body created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> create or replace type myArrayType as table of
myScalarType
  2  /

Type created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> create or replace function foo return myArrayType
  2  as
  3      l_data myArrayType :=
  4                myArrayType( myScalarType.new( 1, 2 ),
  5                             myScalarType.new( 3, 4 ),
  6                             myScalarType.new( 5, 6 ) );
  7  begin
  8      return l_data;
  9  end;
 10  /

Function created.

ops$tkyte@ORA9I.WORLD> create or replace function bar return myArrayType
  2  as
  3      l_data myArrayType :=
  4                myArrayType( myScalarType.new( 1, 2, 'a' ),
  5                             myScalarType.new( 3, 4, 'b' ),
  6                             myScalarType.new( 5, 6, 'c' ) );
  7  begin
  8      return l_data;
  9  end;
 10  /

Function created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> select c1, c2 from table( foo() );

C1         C2
---------- ----------
1          2
3          4
5          6

ops$tkyte@ORA9I.WORLD> select c1, c2, c3 from table( bar() );

C1         C2         C3
---------- ---------- ----------
1          2          a
3          4          b
5          6          c

ops$tkyte@ORA9I.WORLD>
Review & Followup
Rating: 5
Useful as usual ! June 25, 2002
Reviewer: Luigi Di Maggio from Rome, Italy
Thank you very much, Tom.
Once again, you're absolutely right, SQL is just SQL, and maybe I was asking too
much from it. My problem was likely an unsolveable one.
So, I don't have the solution I was looking for (in the fashion I wished), but
you kept me from wasting a lot of time, trying something that will never work as
I need. And, which is even more, reading your answer I found some statements
that suggest me another (and simpler!!) way to do what I want.
You probably know that when you go on and on trying to find the solution to a
problem, after some time you start considering just the hardest alternatives,
and you no more  see that you have simpler ways to achieve the task. It happened
to me yesterday, when I posted my question.
The only thing that can make me achieve my goal is that the data type of the
inner portion of information is always the same (varchar2). So, I'm considering
the possibility to build an oversized object, as you suggested, and it could be
simply a temporary table. I could then find, at runtime, the no. of columns that
hold some data (don't know how yet, but I will find!!), and issue a dynamic SQL
select statement (involving only the desidered no. of columns from the oversized
table) from a PL/SQL stored procedure to get data in the fashion I need,
generating (by the way) an XML file, too.
I think this could be a reasonable way to accomplish my task. It's not so "pure"
as I wished, but it should work.
Thanks again for paying attention to my question.

June 25, 2002 Parag -- Thanks for the question regarding "PL-SQL Coding Conventional", version 8.1.7
You Asked
Hi Tom :

   Can u please give me some tips for good Coding Conventional for PL-Sql * SQL.

Thanks
-Parag
and we said...
I don't have too many coding conventions -- the only real ones I have are:

o use p_, l_, and g_ as leading prefixes on plsql variable name.  p_ for
parameters.  l_ for local variables.  g_ for package global variables.  It will
avoid scoping issues with column names and helps you know who owns the variable.

o use implicit cursors -- they are easier to code, have less code, and are in
general faster.

o use PACKAGES, never use standalong procedures or functions -- never.

o don't consider plsql to be a scripting language, treat it with the same
respect you would Java, C or any "real" language.  It is as capable language as
any other -- and there are none more suited to programming the database.
Review & Followup
Rating: 4
never use standalong procedures?? June 25, 2002
Reviewer: Patricio from Argentina
why should I not use standalone procedures or functions??

does it cover a specific version/release?? or is for all the version??

I've been using packages and standalone procedures and functions and I've never
got performance problems.

Is there any specific explanation about it?

Thanks.

Followup:
see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:286816015990
for the primary reason.  Packages also provide encapsulation, function/procedure
hiding, modular code, and so on.  What I see is people write REALLY huge
procedures -- that should be big packages with LOTS of very very small
procedures in it (modular, readable code).  I have a rule -- a subroutine must
fit on your screen from top to bottom.  Anything longer that then -- is too
long.

All versions. (just good coding rules -- not even really database related).

Let me ask you -- what is the number of lines in your longest procedure?  If it
is more then 100 or so, I would reject it probably as being too long to
understand easily.  Packages let you do this easily.

Rating: 5
June 25, 2002
Reviewer: Dave from Ohio
Where some of us are forced, by third-party applications, to use stand-alone
procedures (in my case it's Informatica Powermart, which will only execute
stand-alone procedures, not packaged procedures), is it your opinion that we
should still be coding inside packages and just using the stand-alone to call
the package?

I had though before that we lost the benefits of packages by calling them from
stand-alone's, but I'm keen to hear your views on this.

Followup:
Yup -- 100%.  Call those packages from a one line procedure.

Your standalone procedure will be compiled ONCE and never again.

It is dependent on the package spec -- the spec will be compiled ONCE and not
again generally.

Procedures will not be dependent on ANYTHING else (so they won't go invalid).

The package body will allow you to do all of the things I've described (better
code, encapsulation, break the dependency chain, and so on)

June 25, 2002 Lee -- Thanks for the question regarding "Loader on steroids", version 8.1.7
You Asked
Hi Tom,
I’m looking for a tool that will do the same functionality of SQL/LODER but with
a little twist, for example:
I have a text file that has records that look like this

1    Keyword=show&hour=4&stat=ny&product=dvd
2    State=ct&keyword=money&hour=9&product=tv

In other words, I have a keyword followed by “=” followed by a value followed by
a delimiter, the most important thing to notice is that the order is not always
the same. What I need is for the tool to load the values inot the appropriate
columns based on the keyword.

Is there any tool out there that will be able to load this flat file into an
Oracle table? I know that I can built something using UTL_FILE with LOBs but I'm
hoping you know of any tool that is already in the market and probably optimized
for this kind of work.

Thanks in advanced.
Lee.
and we said...
load data
infile *
INTO TABLE t truncate
(
keyword position(1:4096)
"decode(sign(instr(upper(:keyword),'KEYWORD=')),
1,substr(:keyword,instr(upper(:keyword),'KEYWORD=')+8,
instr(:keyword||'&','&',instr(upper(:keyword),'KEYWORD='))-
(instr(upper(:keyword),'KEYWORD=')+8)),null)",
hour    position(1:1)
"decode(sign(instr(upper(:keyword),'HOUR=')),
1,substr(:keyword,instr(upper(:keyword),'HOUR=')+5,
instr(:keyword||'&','&',instr(upper(:keyword),'HOUR='))-
(instr(upper(:keyword),'HOUR=')+5)),null)",
stat position(1:1)
"decode(sign(instr(upper(:keyword),'STATE=')),
1,substr(:keyword,instr(upper(:keyword),'STATE=')+6,
instr(:keyword||'&','&',instr(upper(:keyword),'STATE='))-
(instr(upper(:keyword),'STATE=')+6)),null)",
product position(1:1)
"decode(sign(instr(upper(:keyword),'PRODUCT=')),
1,substr(:keyword,instr(upper(:keyword),'PRODUCT=')+8,
instr(:keyword||'&','&',instr(upper(:keyword),'PRODUCT='))-
(instr(upper(:keyword),'PRODUCT=')+8)),null)"
)
begindata
Keyword=show&hour=4&state=ny&product=dvd
State=ct&keyword=money&hour=9&product=tv

that'll load it right up.  You can simplify this as well using PLSQL if you
like:

create or replace function parse( p_str in varchar2, p_word in varchar2 ) return
varchar2
as
    n number := instr( upper(p_str), p_word )+length(p_word);
begin
    if ( sign(n) = 1 )
    then
        return substr( p_str, n, instr(p_str||'&','&',n)-n );
    else
        return null;
    end if;
end;
/

and then the ctl file is:

load data
infile *
INTO TABLE t truncate
(
keyword position(1:4096) "parse(:keyword,'KEYWORD=')",
hour    position(1:1)    "parse(:keyword,'HOUR=')",
stat    position(1:1)    "parse(:keyword,'STATE=')",
product position(1:1)    "parse(:keyword,'PRODUCT=')"
)
begindata
Keyword=show&hour=4&state=ny&product=dvd
State=ct&keyword=money&hour=9&product=tv
Review & Followup
Rating: 5
Well there you go! June 25, 2002
Reviewer: Jim
I have usually written a preload processor
like your solution Tom!

June 25, 2002 Samir K. -- Thanks for the question regarding "Input truncated", version 8.1.6
You Asked
Hi Tom,

This is rare an opportunity that you encounter a simplest question. I have added
an entry into glogin.sql file. But when I log in to SQLPlus after that I am
getting a message " Input truncated to 1 character". What is this error (if at
all it is) and how to avoid this massege to surface again?

Allow me to ask you another question.

In order to enable Parallel Query option in SQL (using hints or Parallelism), is
it mandatory that the Oracle should be resident on a machine with multiple CPUs?

Samir
and we said...
You waited so long to ask ... but everything was answerable with a simple
search.

input truncated

would have pointed you to:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:410219280872

and a search for

parallel query multiple cpus

would have found this:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1027606249408
Everyone -- try the search -- it really works!
Review & Followup
Rating: 5
re: Everyone -- try the search -- it really works! June 25, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
In regard to "Everyone -- try the search -- it really works!", I suppose next
you'll say something totally outrageous and out there, like, say, read the
documentation? Boy, where do you come up with these wacky ideas? :)

Seriously though, I think the search is horribly under-utilized.  One thing that
would be beneficial is the ability to search the "feedbacks" as well as the
original question... is that possible?

Thanks!
- Mark

Followup:
Ok, ok, ok.

Finally, I was forced into it ;)

I added to the index the reviews.  You can see this if you search for:

say something totally outrageous and out there, like, say, read the
documentation

you'll find this document.  It won't hilight the hits in the reviews (thats
another day ;)  but it'll find it

Rating: 5
Thanks! June 25, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
Well, I didn't want you to be forced into it, but I do think it is a helpful
addition...  now, what about that monthly challenge thing we talked about a few
months ago? :)

Thanks again,

- Mark

Followup:
that requires UI code and I despise doing UI stuff ;)

server stuff -- thats cool, UI, thats a drag...

Rating: 5
Monthly challenge June 25, 2002
Reviewer: jim from australia
Monthly challenge sounds like a good idea..

Whilst I agree with Tom that server stuff is cool
I'm willing to have a go at the UI code.

I have a simple site I can use to host it.
If we can establish what the minimum requirements are
I'll give it a bash, so all Tom would have to do
is come up with the challenges

Regards

Jim

June 25, 2002 sridhar -- Thanks for the question regarding "Describing Stored procedures using OCI(8.0)", version 8i
You Asked
Hi Tom,
  I have a PL/SQL stored procedure which has one argument of REF CURSOR type.
  How can i get a description of this argument through OCI calls.
I want to to get the information about columns pointed to by this REF CURSOR.
Iam using OCI library version 8.0
Iam using 'C'.
Iam connected to Oracle database version 8i.

waiting for your reply
Thanks
and we said...
I assume you mean a STRONGLY typed ref cursor?  Eg:

ops$tkyte@ORA8I.WORLD> create or replace package my_pkg
  2  as
  3          type rc1 is ref cursor return emp%rowtype;
  4
  5          type rc2 is ref cursor;
  6
  7          procedure p1( x in out rc1 );
  8          procedure p2( x in out rc2 );
  9  end;
 10  /

Package created.

ops$tkyte@ORA8I.WORLD> desc my_pkg
PROCEDURE P1
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 X                              REF CURSOR              IN/OUT
                                RECORD                  IN/OUT
     EMPNO                      NUMBER(4)               IN/OUT
     ENAME                      VARCHAR2(10)            IN/OUT
     JOB                        VARCHAR2(9)             IN/OUT
     MGR                        NUMBER(4)               IN/OUT
     HIREDATE                   DATE                    IN/OUT
     SAL                        NUMBER(7,2)             IN/OUT
     COMM                       NUMBER(7,2)             IN/OUT
     DEPTNO                     NUMBER(2)               IN/OUT
PROCEDURE P2
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 X                              REF CURSOR              IN/OUT

type rc1 is a strongly TYPED ref cursor, rc2 is weakly typed.

There are a variety of ways to do this.

You can all dbms_describe.describe_procedure (thats what DESC above does and
ultimately what the OCI functions call).  It'll return you all of this
information:

ops$tkyte@ORA8I.WORLD> desc dbms_describe
PROCEDURE DESCRIBE_PROCEDURE
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 OBJECT_NAME                    VARCHAR2                IN
 RESERVED1                      VARCHAR2                IN
 RESERVED2                      VARCHAR2                IN
 OVERLOAD                       TABLE OF NUMBER         OUT
 POSITION                       TABLE OF NUMBER         OUT
 LEVEL                          TABLE OF NUMBER         OUT
 ARGUMENT_NAME                  TABLE OF VARCHAR2(30)   OUT
 DATATYPE                       TABLE OF NUMBER         OUT
 DEFAULT_VALUE                  TABLE OF NUMBER         OUT
 IN_OUT                         TABLE OF NUMBER         OUT
 LENGTH                         TABLE OF NUMBER         OUT
 PRECISION                      TABLE OF NUMBER         OUT
 SCALE                          TABLE OF NUMBER         OUT
 RADIX                          TABLE OF NUMBER         OUT
 SPARE                          TABLE OF NUMBER         OUT

see
http://technet.oracle.com/doc/oracle8i_816/server.816/a76936/dbms_des.htm#998100

You can run a query against all_arguments:

ops$tkyte@ORA8I.WORLD> select argument_name, position, sequence, data_level,
data_type
  2  from all_arguments where owner = user and package_name = 'MY_PKG' and
object_name = 'P1';

ARGUMENT_NAME                    POSITION   SEQUENCE DATA_LEVEL DATA_TYPE
------------------------------ ---------- ---------- ----------
------------------------------
X                                       1          1          0 REF CURSOR
                                        1          2          1 PL/SQL RECORD
EMPNO                                   1          3          2 NUMBER
ENAME                                   2          4          2 VARCHAR2
JOB                                     3          5          2 VARCHAR2
MGR                                     4          6          2 NUMBER
HIREDATE                                5          7          2 DATE
SAL                                     6          8          2 NUMBER
COMM                                    7          9          2 NUMBER
DEPTNO                                  8         10          2 NUMBER

to get this information (if calling a stored procedure isn't what you want to
do)

Lastly, you could do this using the builtin OCIDescribeAny OCI function call as
well. see
http://technet.oracle.com/doc/oracle8i_816/appdev.816/a76975/oci06des.htm#441313
for an example, it shows a RECORD type being described but its identical to the
REF CURSOR type.
Review & Followup
Rating: 3
I got a fair amount of idea July 08, 2001
Reviewer: sridhar kondoji from New Hampshire
It was usefull.I thought of getting the solution thru OCI calls.But this should
work.

Rating: 5
Describing a package January 14, 2002
Reviewer: Partha Mondal from INDIA
I was looking for the data dictionary table which gives the description of
stored procedure/packages.  This one article provided all comprehensive help in
one shot.  But again, this is only expected from AskTom.

Thanks a lot Tom.

Rating: 5
desc packge.procedure doesn't work June 25, 2002
Reviewer: Sameer Utrankar from NJ
In sqlplus, help desc says:

To describe the procedure APROC in the package APACK, enter

SQL> DESCRIBE apack.aproc

PROCEDURE apack.aproc
Argument Name Type     In/Out Default?
------------- -------- ------ -------------
P1            CHAR     IN
P2            NUMBER   IN

But when I try this, I get

SQL*Plus: Release 8.1.7.0.0 - Production on Tue Jun 25 15:30:32 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected.
edata> desc edb_main_nh.create_accnt_pr
ERROR:
ORA-04043: object edb_main_nh.create_accnt_pr does not exist

But I know that this is a valid proc in this pkg (owned by me so accessible and
isn't grant issue).

desc on the entire package works and describes all procs in this pkg but not
good because the pkg has too many procs so that's not what I need.

What am I missing ?

Thanks,

Followup:
That *used* to work -- they changed the way it worked in 8.0 and up.  They must
not have updated the help file in sqlplus.

You can

select <info you want>
from user_arguments
where package_name = 'EDB_MAIN_NH' and object_name = 'CREATE_ACCNT_PR';

instead (or write a small desc.sql script so you can just:

@desc pkg_name proc_name

and have it run that query for you.

June 26, 2002 Doug -- Thanks for the question regarding "How to send rows to a trashcan or null sink for CPU test", version 8.1.7
You Asked
Tom, currently I have a situation where we are interested in finding out the
overhead of a transparent gateway to DB2 in pulling rows into Oracle in terms of
CPU seconds.  This is not entirely easy to figure out but we are in the middle
of the proposed solution.  In the OS/390 environment, we have a query that will
retrieve about 1 million rows from a table and we can measure the DB2 CPU used
to do that.  However, the million rows on that side of the fence can be
"tossed".
Now I would like to run the same query through Oracle and retrieve the same
1,000,000 rows from DB2.  We do not care at all about the CPU on the Oracle
side.  The gateway is a process that runs on the mainframe and keeps DB2 cpu
stats inclusive in it's number.  So I am trying to figure out how to take the
brakes off.  If I merely select the rows over the gateway, more time will be
spent putting them onto the screen and may cause extra fetch calls as the rows
will not be able to come across all at once.  Creating a table with them? I'm
not sure.  I thought I'd ask you.  What's the best way to execute a select
statement in Oracle without slowing it down one bit.  I don't need the data at
all.  Is there a way to >/dev/null them so to speak?
Thanks.
and we said...
use traceonly -- it'll fetch and discard them.  You'll get the stats and the
plan as a side-effect.

Make sure to set the sqlplus array size to something reasonable for such a test
(say 100)

ops$tkyte@ORA8I.WORLD> set autotrace traceonly
ops$tkyte@ORA8I.WORLD> select * from scott.emp
  2  /

14 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=14 Bytes=560)
   1    0   TABLE ACCESS (FULL) OF 'EMP' (Cost=1 Card=14 Bytes=560)

Statistics
----------------------------------------------------------
        203  recursive calls
          4  db block gets
         45  consistent gets
          5  physical reads
          0  redo size
       1979  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          6  sorts (memory)
          0  sorts (disk)
         14  rows processed

ops$tkyte@ORA8I.WORLD>
Review & Followup
Rating: 5
Great!! February 09, 2002
Reviewer: Doug from CT, USA
Simple.. to the point.. I will try it on Monday.
Thanks again Tom.

Rating: 5
Why not set array size higher? February 11, 2002
Reviewer: Doug from CT, USA
Quick follow up - why did you suggest an array size of 100?
Why not max it out at 500?

Followup:
i've found 100 to be a good overall size.  You want to keep the pipe "flowing".
Larger sizes don't necessarily translate into faster performance.

By all means, vary it and see the differences.

Rating: 4
How about from pl/sql? February 12, 2002
Reviewer: Andrew from ca, usa
Tom
Are these similar ways to do it in pl/sql?
1.) for rec in (select * from scott.emp) loop
      EXIT;
    end loop;

2.) select null into v_dummy
    from (select * from scott.emp)
    where rownum <= 1;

Followup:
plsql will add its own overhead (so if the goal is to test the remote system --
no).  Also, plsql won't "array fetch" and cannot bulk collect over a db link so
those are all single row fetches -- you'll introduce lots of network overhead
(and again, if the goal is to test the cpu on the remote system....)

The second one won't fetch but the first row -- we optimize that one....

Rating: 4
Big query returns millions of rows 8.0.x June 25, 2002
Reviewer: andrew from ca, usa
Tom
I have a big query which I want to benchmark from sqlplus on my PC. The server
is a big Unix box. The statement in question comes from an app. module on the
same server as the  database - it was pulled from the shared pool. The aim of
the benchmark is to establish the server impact when the statement runs from the
app.

I guess it's possible that the query is from an explicit cursor and that the app
uses a stop-key after n rows.

1.) If the app. server module would normally receive all or part of the result
set - it it reasonable for me to "set autotrace traceonly" in this case? Should
I rather write a PL/SQL test stub with a stop key?

2.) Does "set autotrace traceonly" act at the server or PC level if I issue it
from sqlplus on my PC?

Followup:
traceonly will pull all of the records back to the client (your pc).  you will
be benchmarking your pc, your network between your pc and your server -- as much
as anything else.

why would you not just enable sql_trace and tkprof the REAL thing?

Rating: 5
Yes it was June 25, 2002
Reviewer: Shrek from MD
About that Arraysize:

Can arraysize influence the speed if I have SQL as below. In this case I am not
fetching, but inserting from a table accessed through a db_link.

If not, is there a way to reduce the number of trips back and forth from the
databases? especially over a busy network?

Insert into x
    SELECT * FROM y@my_db_link
    WHERE a = b
/

Thanks.

Followup:
No, the arraysize setting will have no effect on that as they never fetches back
to the client.

If you use the sqlplus copy command, then arraysize will have an effect.

June 26, 2002 J. Laurindo -- Thanks for the question regarding "Loading spread data from text files with sql*loader", version 8.1.7
You Asked
Everyone says "impossible" , but lets ask, anyway... I want to load data from a
ascii text-file, using loader only, but the format/position/separator of the
records varies (but inside a range). One example :

Tables
======
ALEAL Bytes: Total=20480,Free=16384,Used=4096,Qty. Lines=0
ANALISER Bytes: Total=5242880,Free=16384,Used=5226496,Qty. Lines=62929
AN1 Bytes: Total=5242880,Free=3313664,Used=192916,Qty.Lines=58330
BBCTC12ASB Bytes: Total=1064960,Free=1060864,Used=4096,Qty. Lines=0
.....
......
Indexes
=======
ALEADUAL1 Bytes: Total=1024000,Free=1015808,Used=8192
ANALISECERT12_I Bytes: Total=5242880,Free=2191360,Used=3051520
ANALISECERT1_I Bytes: Total=5242880,Free=3387392,Used=1855488
ANALISECERT_I Bytes: Total=2129920,Free=122880,Used=2007040
BBVDXPTXT1 Bytes: Total=102400,Free=94208,Used=8192
......
......

See, the first and the second lines are titles (must be skipped), the data are
the table and indexes names and the values in each line (total, used,free, etc).
The problem is : I can´t use fixed-format because the length varies, and I can´t
use delimited because some fields are separated by commas (like Total and Free),
others by ":" and others by spaces.
Any sugestions ??

Regards,

J. Laurindo
and we said...
Well, I love it when "everyone" says impossible ;)  Makes it more of a
challenge...

Using this table:

create table t (
name varchar2(25),
bytes_total Number,
bytes_free Number,
bytes_used Number,
qty_lines Number )
/

And this control file:

LOAD DATA
INFILE *
INTO TABLE t
REPLACE
when name != 'Tables' and name != '======' and name != 'Indexes' and name !=
'======='
( name         position (1:4096) "substr(:name,1,instr(:name,' Bytes:')-1)",
  bytes_total  position (1:1)
"substr(:name,instr(:name,'Total=')+6,instr(:name,',')-(instr(:name,'Total=')+6))
",
  bytes_free   position (1:1)
"substr(:name,instr(:name,'Free=')+5,instr(:name,',',1,2)-(instr(:name,'Free=')+5
))",
  bytes_used   position (1:1)
"substr(:name,instr(:name,'Used=')+5,instr(:name,',',1,3)-(instr(:name,'Used=')+5
))",
  qty_lines    position (1:1)
"decode(instr(:name,'Lines='),0,NULL,substr(:name,instr(:name,'Lines=')+6))"
)
BEGINDATA
Tables
======
ALEAL Bytes: Total=20480,Free=16384,Used=4096,Qty. Lines=0
ANALISER Bytes: Total=5242880,Free=16384,Used=5226496,Qty. Lines=62929
AN1 Bytes: Total=5242880,Free=3313664,Used=192916,Qty.Lines=58330
BBCTC12ASB Bytes: Total=1064960,Free=1060864,Used=4096,Qty. Lines=0
Indexes
=======
ALEADUAL1 Bytes: Total=1024000,Free=1015808,Used=8192
ANALISECERT12_I Bytes: Total=5242880,Free=2191360,Used=3051520
ANALISECERT1_I Bytes: Total=5242880,Free=3387392,Used=1855488
ANALISECERT_I Bytes: Total=2129920,Free=122880,Used=2007040
BBVDXPTXT1 Bytes: Total=102400,Free=94208,Used=8192

I got this loaded:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

NAME                           BYTES_TOTAL BYTES_FREE BYTES_USED  QTY_LINES
------------------------------ ----------- ---------- ---------- ----------
ALEAL                                20480      16384       4096          0
ANALISER                           5242880      16384    5226496      62929
AN1                                5242880    3313664     192916      58330
BBCTC12ASB                         1064960    1060864       4096          0
ALEADUAL1                          1024000    1015808
ANALISECERT12_I                    5242880    2191360
ANALISECERT1_I                     5242880    3387392
ANALISECERT_I                      2129920     122880
BBVDXPTXT1                          102400      94208

9 rows selected.

So -- it may be possible ;)
Review & Followup
Rating: 5
I never thought this way ... June 09, 2002
Reviewer: J. Laurindo Chiappa from SP, Brazil
Simple and efective . Many thanks !

Rating: 5
June 09, 2002
Reviewer: A reader

Rating: 4
June 10, 2002
Reviewer: Sagirk from India
Hi Tom !

Once again you are really great and your answers are simple and to the point.

Thank You.

Regards,

Rating: 5
Loading spread data from text files with sql*loader June 26, 2002
Reviewer: Jegannathan S from Chennai, INDIA
HI TOm,
it was grt :)
excellent piece of work
But Tom how to post to this forum?

Followup:
you goto
http://asktom.oracle.com/
and if I'm accepting questions, there is a link.

June 26, 2002 atul -- Thanks for the question regarding "Bind Variables....", version 8.0.4
You Asked
Sir,

According to your script in book "One on One"..
I checked if i am using bind variables or not..
I got following output..

SELECT
COUNTER_NO,VENDOR_CD,ICC_TAG,CHALLAN_NO,CHALLAN_DT,COUNTER_DT,INVOICE_NO,INVOICE_
DT,BILL_AMT,FBR_NO,OCT_DEB_MEMO_NO,OCT_DB_MEMO_DT,TRANSP_CD,TRUCK_NO,LR_NO,LR_DT,
BILL_REG_NO,ROWID FROM CHALLAN_VENDOR WHERE (VENDOR_CD = '#') AND (CHALLAN_NO
LIKE '#') ORDER BY CHALLAN_DT DESC
        45

SELECT
PRODUCT_CD,PV_MAJ_DEFECT_CD,CHA_SRL,CHA_YM,CHA_CD,CHA_TYPE,PV_MIN_DEFECT_CD,ROWID
 FROM PV_CARD_DEFECT WHERE (PV_MAJ_DEFECT_CD = '#') AND (CHA_SRL = '#')
AND (CHA_YM = '#') AND (CHA_CD = '#') AND (CHA_TYPE = '#') ORDER BY
PV_MIN_DEFECT_CD

46

SELECT
DIV_CD,RECPT_LOC_CD,USER_ID,ITEM_CD,PO_NO,RCIA_TYPE,HOLD_LOC,DIE_NO,INSP_RESP_CD,
CHALLAN_AU,CHALLAN_QTY,PO_ITEM_VENDOR_TAG,SCHEDULE_TAG,EXCESS_TAG,DOC_@F@_TAG,ACC
EPT_REJ_TAG,STORAGE_LOC,MOD_NO_CURRENT,ITEM_SRNO,MOD_NO_LATEST,LAB_NO,MERF_NO,COU
NTER_NO,FP_CODE,PKG_DET_CD,NO_OF_PKGS,MOD_FLAG_YN,PKG_CONTENTS,ITEM_DESC,BUYER_CO
DE,IMA_AU,ROWID FROM CHALLAN_ITEM WHERE NVL(CNCL_TAG,'#') != '#' AND
NVL(CNCL_TAG,'#') != '#' AND (COUNTER_NO = @) ORDER BY ITEM_SRNO
        49

I got many counts between 28 to 50 but i am giving you 3 only..

Could you tell how this can be changed to use bind variables,,
so i can change for remaining..

Thanks.
atul
and we said...
It depends on the language being used.  The api's for each are different, the
methods are different -- but -- they ALL support doing it.  You'll need to refer
to the documentation for your language.

In java/jdbc -- you'll use callable or preparedstatements

In plsql, if you use static sql, it is already "bound", if you use dynamic sql
-- read the chapter I have on dynamic sql in the book.  Many examples.

In pro*c, you'll use host variables.

In OCI, you'll use the bind subroutines...

and so on.
Review & Followup
Rating: 5
Thanks. June 23, 2002
Reviewer: atul from india
Sir,

But as count is 45,46,49 means it's not using bind varibles is that correct?

Could you just give me one example if say for plain static sql and for pl-sql??

Thanks.
atul

Followup:
Yes, count = 45, count = 46, count = 49 almost certainly means it is not using
bind variables.  If you want to verify that, just

select sql_text from v$sql where upper(sql_text) like 'SELECT COUNTER_NO%ORDER
BY CHALLAN_DT DESC%';

and look at the 45 rows that come back.  IF the only difference between them is
the constants (which I replace with @ and #), then they are NOT using binds.

I do not understand the second request at all.

Rating: 2
BLOB June 24, 2002
Reviewer: Yhab Abiad from France
What is the best practice for performance.
store the file as BLOB into the database
or store it in a file system ?

Here is a sample of what I want to do :
Suppose we have an intranet application written in java : Name_of_author,
Title_of_the_book, The_book (as BLOB)
And a client (may be every where in the intranet) can read a book online when he
knows the title or the author...
My question : What is the better way to proceed ?
I think that the filesystem is the best idea.
reasons :
1) Performance based OS (Unix) is better than access the file throw SQL...,
2) managing filesystem is easier than managing a database
3) There is many functions and libraries to process files, transform it and make
pagination.

some help ?
(if possible improve the two solutions with sample to implement it)
Thanks
yhab.abiad-sica@renault.com

Followup:
well, you probably gave this two stars cause it didn't answer your question I
suppose.  But given as how this question has NOTHING to do with the original
question, that makes sense.

If you search for:

blob database file system

You'll find an article entitled:

Storing Images in database as BLOB Vs storing images on file system on
Application Server

(i disagree 100% with your point #2.  #3 doesn't even seem relevant as you will
be retrieving the document in an application server anyway)

Rating: 5
Thanks. June 24, 2002
Reviewer: atul from India
Sir,

As per your query i found 30 counts like..

SELECT
COUNTER_NO,VENDOR_CD,ICC_TAG,CHALLAN_NO,CHALLAN_DT,COUNTER_DT,INVOICE_NO,INVOICE_
DT,BILL_AMT,FBR_NO,OCT_DEB_MEMO_NO,OCT_DB_MEMO_DT,TRANSP_CD,TRUCK_NO,LR_NO,LR_DT,
BILL_REG_NO,ROWID FROM CHALLAN_VENDOR WHERE (VENDOR_CD = '82158') and (
CHALLAN_NO LIKE '1043177%%%%%%%%') order by challan_dt desc

So i asked developers about that they are saying this they have written in
sql*forms so how to change these to make them use bind variables...

In above query users are giving input of VENDOR_CD only..

Thanks.
atul

Followup:
begin
    for x in ( SELECT
COUNTER_NO,VENDOR_CD,ICC_TAG,CHALLAN_NO,CHALLAN_DT,COUNTER_DT,INVOICE_NO,INVOICE_

DT,BILL_AMT,FBR_NO,OCT_DEB_MEMO_NO,OCT_DB_MEMO_DT,TRANSP_CD,TRUCK_NO,LR_NO,LR_DT,

BILL_REG_NO,ROWID FROM CHALLAN_VENDOR WHERE (VENDOR_CD = :block.item1 ) and (
CHALLAN_NO LIKE :block.item2 || '%%%%%%%%') order by challan_dt desc )

     .....

Just use :block.item names in there.

Rating: 4
Collections and Host Arrays June 26, 2002
Reviewer: Andrew Schonberger from Sydney, Australia
Sir,

you wrote

>> It depends on the language being used.
>> The api's for each are different, the methods are
>> different -- but -- they ALL support doing it.
>> You'll need to refer to the documentation for
>> your language.

I am trying to pick up a HOST ARAY into SqlPlus.
Is this possible at all ?   I have the feeling I'm
out of luck, at least with 8.1.7.

From the PL/SQL manual:

>> Client-side programs can use anonymous
>> PL/SQL blocks to bulk-bind input and
>> output host arrays. In fact, that is the most
>> efficient way to pass collections to and
>> from the database server.

Pushing Collections into SqlPlus could be a bit
far-fetched. But the standard JDBC would not allow
that either (i.e. I'm not sure there is any way
in Java to use a CallableStatement which has an
Array as an output parameter. )

My reason for looking at Collections is not
performance-related..( ..I might slip into a
different topic here.  I have a read-only database,
where I can select from tables and I can execute
anonymous blocks. Would be nice if I could print
collections in SqlPLus, overcoming the limitation
of 1,000,000 on dbms_output  )

In short, the question is: can I bind a Collection
as an output to a host array, in SqlPlus or Jdbc ?

My best regards,

Andrew Schonberger

BTW: great book. I bought it, but my son, who is
at university, asked to read it first.

Followup:
I am not understanding what SQLPlus has to do with this -- sqlplus implicitly
does array fetching (see the SET ARRAYSIZE option -- defaults to array fetches
of 15 rows).  You need do nothing else.  It has pretty much always had this
capability.

Yes, you can "bind" a collection as output -- using PLSQL in sqlplus or natively
in JDBC.

June 26, 2002 Andrew -- Thanks for the question regarding "Query Limitation", version 9.0.0
You Asked
What is the max length of a query can be excuted?

Thanks,

Andrew
and we said...
depends on the environment.  Different environments may have different limits.

 32k is a reasonable limit but it can be larger.
June 26, 2002 Sameer -- Thanks for the question regarding "Import/Export Views", version 8.1.7
You Asked
Hi Tom,

On one of my instance i'm facing problem in export utility.
Well it exports most of the things that i want but exits before exporting stored
procedures. here is the log...

Export done in US7ASCII character set and WE8ISO8859P1 NCHAR character set
server uses WE8ISO8859P1 character set (possible charset conversion)
. exporting pre-schema procedural objects and actions
. exporting foreign function library names for user DEV
. exporting object type definitions for user DEV
About to export DEV's objects ...
. exporting database links
. exporting sequence numbers
. exporting cluster definitions
. about to export TRANSCPSDEV's tables via Conventional Path ...
. . exporting table            LEVEL_MASTER          3 rows exported
. . exporting table           FILE_DETAILS          2 rows exported. . exporting
table      MESSAGES_MASTER          5 rows exported
. exporting synonyms
EXP-00008: ORACLE error 904 encountered
ORA-00904: invalid column name
EXP-00000: Export terminated unsuccessfully

The database was upgraded from 8.1.6 to 8.1.7 and the problem could be there
itself(untidy upgradation). But how should i fix it?
I feel the views which are needed for exporting are not created properly. If you
could provide me with with the list of views and the scripts for creating it
then i think so it could serve my purpose.

Thanks and regards

Sameer Ternikar
and we said...
I think the DBA should read the migration guide -- I think all of the requisite
steps for PROPERLY upgrading a database are contained therein.  This is just the
TIP of the iceberg.  Sure, I can fix this individual problem, but you'll be back
tomorrow with something else (quick fix for this = catexp.sql, run it)

Get your DBA to do it *right*, or get a new DBA.

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

Most likely, this database was upgraded from a previous release and the DBA that
did that did not run the proper upgrade scripts.

Can you tell me -- is this an upgraded database and did the DBA follow the
procedures exactly as outlined in the migration guide?
June 26, 2002 Sunil -- Thanks for the question regarding "calling ref cursor from stored procedure", version Oracle 8.1.5
You Asked
You've a example calling a function using ref cursor.  I'm
trying to test procedure that uses ref cursor.

I did soemthing like this:

variable c refcursor;
sp_search(1,'gl',c);
print c;

It doesn't work.  I didn't get any materials calling stored
procedures anywhere except your site.  I would appreicate your
input.

Thanks,
sunil
and we said...
"it does'nt work" isn't very informative.  If you've entered
exactly what you have above -- you are missing the colon in
front of C in the call to sp_search.  In SQLPlus, the following
will work:

ops$tkyte@ORA8I.WORLD> create or replace package types
  2  as
  3      type rc is ref cursor;
  4  end;
  5  /

Package created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> create or replace
  2  procedure sp_search( p_num in number,
  3                       p_char in varchar2,
  4                       p_cursor in out types.rc )
  5  is
  6  begin
  7      open p_cursor for
  8      'select *
  9         from dept
 10        where deptno > :x
 11          and dname like :y'
 12      USING p_num, p_char;
 13  end;
 14  /

Procedure created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> variable c refcursor
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> exec sp_search( 1, '%', :c )

PL/SQL procedure successfully completed.

ops$tkyte@ORA8I.WORLD> print c

    DEPTNO DNAME          LOC
---------- -------------- -------------
        10 accounting     NEW YORK
        20 research       DALLAS
        30 sales          CHICAGO
        40 operations     BOSTON

ops$tkyte@ORA8I.WORLD>
Review & Followup
Rating: 4
running a pl/sql block to return output for Ref Cursor June 26, 2002
Reviewer: TS from USA
ops$tkyte@ORA8I.WORLD> variable c refcursor
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> exec sp_search( 1, '%', :c )

Referring to the above example, Instead of running it from sqlplus I tried to
create a pl/sql block to call the procedure to get the output, this is what I
did :
DECLARE
 type c is ref cursor;
BEGIN
   sp_emp(201,'%',:c)
END;

and I was getting the following error :-
SP2-0552: Bind variable "C" not declared.

How could I call the procedure using a pl/sql block to display the output of the
Ref Cursor , please explain ?

Thanks,
TS

Followup:
well, exec sp_search( 1, '%', :c ) is REALLY:

begin
   sp_search(1,'%',:c);
end;

anyway.  What you want -- since you don't what to use SQLPlus bind variables is
to code

DECLARE
 type c is ref cursor;
BEGIN
   sp_emp(201,'%',c);

   loop
      fetch c into <some other variables>
      exit when c%notfound;
      dbms_output.put_line( <output> );
   end loop;
   close c;
END;

I think "print x" is much much easier.

June 26, 2002 JJ -- Thanks for the question regarding "dynamic sql, use of bind vars in predicate functions for unknown execution time.", version 8.1.7
You Asked
Hi Tom,

In my application I generate queries based on user selected criteria from web
based reports. I store the criteria in db for later retrieval and use for
another instance of similar report.
There are too many predicates that I need to manipulate/generate as a part of a
query and it contains a in list of valaues and/or app specific default values of
number and varchar type on top of "from" clause. Actually we use a package to
build this query and
store it in database.

I am not able use sys_context in this case to make use of bind variables since
the time when we build the query is not related to when we actually execute the
query (other then the fact
that we execute the query sometime after its built)

So for this situation how would one use bind variables?
Can you suggest me a way to store this context information per query in other
set of tables that we can send across to report server (Actuate report server)
with the query?
Is there any other option available?

My app runs on Actuate report server, web pages, OAS, Oracle 8.1.7 EE.

Thanks,
JJ
and we said...
Use CURSOR_SHARING (if you have my book, read chapter 10)

scott@ORA817DEV.US.ORACLE.COM> declare
  2          type rc is ref cursor;
  3
  4          l_cursor rc;
  5          l_empno  number;
  6          l_ename  varchar2(25);
  7  begin
  8
  9          EXECUTE IMMEDIATE 'alter session set cursor_sharing=force';
 10          open l_cursor for 'select empno, ename from emp DEMO_QUERY where
ename = ''KING'' ';
 11          EXECUTE IMMEDIATE 'alter session set cursor_sharing=exact';
 12
 13          fetch l_cursor into l_empno, l_ename;
 14          if ( l_cursor%found )
 15          then
 16                  dbms_output.put_line( l_empno || ', ' || l_ename );
 17          end if;
 18
 19          close l_cursor;
 20  end;
 21  /
7839, KING

PL/SQL procedure successfully completed.

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> select sql_text from v$sql where sql_text like
'%DEMO_QUERY%';

SQL_TEXT
---------------------------------------------------------------------------------
--------------------------------------------------
select sql_text from v$sql where sql_text like '%DEMO_QUERY%'
declare  type rc is ref cursor;   l_cursor rc;  l_empno  number;  l_ename
varchar2(25); begin   EXECUTE IMMEDIATE 'alter session s
et cursor_sharing=force';  open l_cursor for 'select empno, ename from emp
DEMO_QUERY where ename = ''KING'' ';  EXECUTE IMMEDIATE
'alter session set cursor_sharing=exact';   fetch l_cursor into l_empno,
l_ename;  if ( l_cursor%found )  then   dbms_output.put_li
ne( l_empno || ', ' || l_ename );  end if;   close l_cursor; end;

select empno, ename from emp DEMO_QUERY where ename = :"SYS_B_0"

scott@ORA817DEV.US.ORACLE.COM>

it is an "auto-binder" of sorts.   It is a crutch for when you BELIEVE you
cannot use bind variables (you could, just store the query with binds and a
related "variable name" "value" table with the bind variables and their values.
If you can BUILD the string, you most certainly can BIND the string!)  It gets
much of the benefits of binding for you.
Review & Followup
Rating: 5
February 11, 2002
Reviewer: A reader
Tom

When i use cursor_sharing=force.

Does Sever has an additional overhead(Consume more resources)?

If YES then what impact on performance(Ie. performance gain
on auto binding V/S overhead ).

Followup:
If you do it yourself, you use the least resources.

It'll tokenize the query, find the literals, rewrite the query and then bind the
literals in.  This is extra work (cpu) that it would not do otherwise.  Its
small.

Compared to NOT using bind variables, its an extremely tiny price to pay.  If
you do not use bind variables, you will fail, you will not scale, you will trash
the shared pool.  Period.

Rating: 5
February 11, 2002
Reviewer: A reader
Tom
Our application uses bind variables but sometimes
developer may hardcode some variable so based on your response i am planning to
use  cursor_sharing =force in
init.ora. is it ok?

Will it perform below process  for each query even if program use bind variable?
------
i.e.
It'll tokenize the query, find the literals, rewrite the query and then bind the

literals in small.
-------

Followup:
NO, train the programmers -- fix the problem, don't use the crutch.  You'll only
be getting a small portion of the total benefit AND you could break other parts
of your system (if you have my book -- chapter 10 -- I describe the ins and outs
of using cursor-sharing=force, it is NOT an end all, be all fix).

Rating: 5
force bind for forms default_where February 11, 2002
Reviewer: rimblas
This is great and I'm wondering if there is a way to apply it when you set the
DEFAULT_WHERE in an Oracle Form.  (And I’m waiting for your book to arrive, just
bought it yesterday)

We're using a interMedia Text index and use the contains clause.  We dynamically
build it like this:

   set_block_property('ITEM_SEARCH', DEFAULT_WHERE,
'ctxsys.contains(description, '||''''||:lines.item||''''||',1) > 0');
   go_block('ITEM_SEARCH');
   execute_query;

I know this is bad for the shared pool, would I just add the "execute immediate"
with cursor_sharing command before the execute_query and turn it off afterwards?

-Jorge

Followup:
   forms_ddl('alter session set cursor_sharing=force');
   execute_query;
   forms_ddl('alter session set cursor_sharing=exact');

will do it.

Rating: 5
succinct February 11, 2002
Reviewer: JJ from plant of beetlegues
easy to implement!

Rating: 5
How to use a bind variable for a column name rather than a literal June 26, 2002
Reviewer: tom from england
Tom,

How do I bind a bind variable to have a column value.  For example

create table test (x number, y number);

If i wish to search for all rows where y is equal to a user defined value then I
execute

intUserVal := USER ENTERED VALUE
execute immediate 'select * from test where y=:y' using intUserVal;

However, how do I perform the query select all rows where the value of x is
equal to the value of y using bind variables?

Followup:
You cannot.  That is something that would drastically change the plan.  Anything
that can change the plan -- cannot be BOUND.

Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int, y int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx on t(x);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_stats.set_table_stats( user, 'T',
numrows => 100000 );

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> select x from t where x = y;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1000 Bytes=26000)
   1    0   TABLE ACCESS (FULL) OF 'T' (Cost=3 Card=1000 Bytes=26000)

ops$tkyte@ORA817DEV.US.ORACLE.COM> select x from t where x = x;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=5000 Bytes=65000)
   1    0   INDEX (FAST FULL SCAN) OF 'T_IDX' (NON-UNIQUE) (Cost=1 Card=5000
Bytes=65000)

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

you can ONLY bind where a character string literal could be placed.

Rating: 5
Thanks June 29, 2002
Reviewer: tom
Thanks Tom,

I assumed that was the case since modifying the parameter would cause the
optimizer to use the wrong plan, but I was hoping this wasn't the case!!

Oh well....new plan needeed.

Thanks for the help.

June 26, 2002 Harold -- Thanks for the question regarding "Deadlocks", version 8.1.7
You Asked
Hello

1.What is a deadlock and how can i detected, how can i fix a deadlock??

2. I've a temp tablespace with 3 GB of size, acording to the high water mark
anly have been use about 300 MB of the file, i try to resize to 1.5 Gb but it
doesn't let me, why is that?? the same thing happen on a RBS tablespace.

Regards

Harold
and we said...
1) if you have my book -- i spend more then a couple of pages on this topic.
deadlocks are when you have a resource I want locked, I have a resource you want
locked.  You are blocked on me, I am blocked on you.  Obviously, unless someone
comes along and breaks one of us out of this deadlock, we would hang there
forever.

That is a deadlock.

see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1068032649872
deadlocks fix themselves.  You want to AVOID them, you don't need to "fix" them.

2) search this site for

maxshrink

it computes the HWM for files in the only way possible.  You'll see the HWM with
this (and get the alter commands to make the files as small as possible)
Review & Followup
Rating: 5
Dead locks will only occer June 26, 2002
Reviewer: Thanks
when multiple users are viewing for resource locked by each other .. but this is
only in case of updates or deleletes.. and does not apply to selects . Isnt it
?( because you never lock rows for selects).

Followup:
In Oracle -- that is the case, yes.  A select (without a FOR UPDATE) won't
deadlock with a modification.

In most others (db2, informix, sybase, sqlserver, etc) that is NOT the case and
selects deadlocking modifications is very common.

June 26, 2002 Timothy -- Thanks for the question regarding "Limit access to read-only while data is being accessed by someone", version 9.0.1
You Asked
Hi Tom,

I use Form Developer 6i to develope an application for multi-user environment.
In a module, I need to achieve the following objectives:-

When the 1st user logon to the form and create/modify a batch of records
(controlled by a primary key called bacth#), subsequent users who wants to
access to that batch of records can only limit to read-only.

I was thinking to create a table which logs the user's access to the form and do
some control from there. Or is there any better solution to this?

Thank you.
and we said...
in the on-lock trigger for that block, you can call dbms_lock to allocate a
named lock -- named after the BATCH#.  You can use dbms_lock in a non-blocking
fashion so that you get an exclusive lock or return immediately with an error.
If you are successful in getting an exclusive lock named after the batch number
-- you will call the default on-lock processing.  Else, you will FAIL the
trigger -- meaning they cannot lock the row and will have it "read only"
June 27, 2002 Pratap -- Thanks for the question regarding "Ageing analysis report", version 9.0.0
You Asked
Hi Tom

I have a table called CREDIT_OVERDUE_BUCKET_CONFIG ( BUCKET_SEQUENCE, MIN_VALUE,
MAX_VALUE )

A table called credit_details (OVERDUE_DAYS, AMOUNT )
I have to generate a report with total amounts in a bucket.

0-30   31-60

1000   1000

The buckets are configurable in CREDIT_OVERDUE_BUCKET_CONFIG with min_val as say
0 and max_val as say 31. Sequence will be the order in which the buckets are to
be displayed.

etc. How do I write a query here?

CREATE TABLE credit_overdue_bucket_config (
       bucket_sequence      NUMBER(2) NOT NULL,
       min_value            NUMBER(3) NOT NULL,
       max_value            NUMBER(3) NULL,
       insert_user_id       VARCHAR2(30) DEFAULT user NOT NULL,
       insert_date          DATE DEFAULT sysdate NOT NULL,
       CONSTRAINT pk_credit_overdue_bucket
              PRIMARY KEY (bucket_sequence)
);

CREATE TABLE CREDIT_DETAILS (
  OVERDUE_DAYS    NUMBER (3)    NOT NULL,
  AMOUNT          NUMBER (14,2) NOT NULL
 )

Thanks and regards

Pratap
and we said...
Well, since a sql query *must* know the number of columns and you want to
dynamically configure the number of columns, we'll have to use dynamic sql.

This works:

ops$tkyte@ORA9I.WORLD> insert into credit_details
  2  select mod(rownum,100), rownum from all_objects;

22465 rows created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> insert into credit_overdue_bucket_config values( 1, 0,
10, 'x', sysdate );

1 row created.

ops$tkyte@ORA9I.WORLD> insert into credit_overdue_bucket_config values( 2, 11,
25, 'x', sysdate );

1 row created.

ops$tkyte@ORA9I.WORLD> insert into credit_overdue_bucket_config values( 3, 26,
40, 'x', sysdate );

1 row created.

ops$tkyte@ORA9I.WORLD> insert into credit_overdue_bucket_config values( 4, 41,
75, 'x', sysdate );

1 row created.

ops$tkyte@ORA9I.WORLD> insert into credit_overdue_bucket_config values( 5, 76,
NULL, 'x', sysdate );

1 row created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> create or replace procedure query( p_cursor in out
SYS_REFCURSOR )
  2  as
  3      l_query long := 'select ';
  4  begin
  5      for x in ( select * from credit_overdue_bucket_config )
  6      loop
  7          if ( x.max_value is not null )
  8          then
  9              l_query := l_query ||
 10                          'sum( case when overdue_days between ' ||
 11                                        x.min_value || ' and ' || x.max_value
||
 12                               ' then amount ' ||
 13                               ' else 0 ' ||
 14                               ' end ) "' || x.min_value || '-' ||
x.max_value || '",';
 15          else
 16              l_query := l_query ||
 17                          'sum( case when overdue_days >= ' || x.min_value ||
 18                               ' then amount ' ||
 19                               ' else 0 ' ||
 20                               ' end ) "' || x.min_value || '-infinity",';
 21          end if;
 22      end loop;
 23      l_query := rtrim(l_query,',') || ' from credit_details';
 24
 25      p( l_query );
 26      open p_cursor for l_query;
 27  end;
 28  /

Procedure created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> variable x refcursor
ops$tkyte@ORA9I.WORLD> exec query(:x)
select sum( case when overdue_days between 0 and 10 then amount  else 0  end )
"0-10",sum( case when overdue_days between 11 and 25
then amount  else 0  end ) "11-25",sum( case when overdue_days between 26 and 40
then amount  else 0  end ) "26-40",su
m( case when overdue_days between 41 and 75 then amount  else 0  end )
"41-75",sum( case when overdue_days >= 76 then amount  else
0  end ) "76-infinity" from credit_details

PL/SQL procedure successfully completed.

ops$tkyte@ORA9I.WORLD> print x

      0-10      11-25      26-40      41-75 76-infinity
---------- ---------- ---------- ---------- -----------
  27732375   37860750   37911375   88432045    60412800

ops$tkyte@ORA9I.WORLD>
Review & Followup
Rating: 5
But how to use a materialized view for this June 26, 2002
Reviewer: Pratap from India
Hi Tom

Thanks a lot for your answer. But I need to create a materialized view to avoid
computation at run time. It would be OK even if I can get it in this following
format

OVERDUE_BUCKET    AMOUNT_SUM
--------------    ----------
0-30              1000
31-60             2000

I can create a table and refresh it every night, but I think a MV will reduce a
lot of coding efforts in doing that.

Thanks a lot again for your help.

Pratap

Followup:
ops$tkyte@ORA817DEV.US.ORACLE.COM> select min_value || '-' || max_value label,
  2         sum(amount) amount
  3    from credit_overdue_bucket_config, CREDIT_DETAILS
  4   where overdue_days between min_value
  5                      and nvl(max_value,99999999999999999)
  6   group by bucket_sequence,
  7            min_value || '-' || max_value
  8   order by bucket_sequence
  9  /

LABEL                    AMOUNT
-------------------- ----------
0-10                   28729195
11-25                  39220830
26-40                  39272355
41-75                  91447131
76-                    62586000

might be your query then.  that can be the defining query for the MV.

Rating: 5
Thanks again TOM June 27, 2002
Reviewer: A reader
That's exactly what I wanted Tom!

June 27, 2002 Vittorio -- Thanks for the question regarding "Indexing views", version 8.1.6
You Asked
With Oracle 8.05 if i want to perform a contrain search on a
view, i have to create a index on the view (a context index).

I 'm not be able to do the same with 8i (rel 2)

Is it possible to create context index on views with Oracle 8i ?
and we said...
Its that not that HAVE TO index the view in 8.0, it is that it
was possible (because of context's architecture) to index a
view.

In Oracle8i, the architecture is very very different.  Context
is very tightly integrated and as such has the same profile as a
"regular" index.  In practice, regular indexes cannot be used to
index a view.  We put indexes on base tables and any view of
that table has access to the index.  With context this is now
true as well.

The reasons for indexing a view in 8.0 was to

o index a concatention of many fields
o index only selected records

(if you did it for some other reason, let me know and I'll try
to address that).

In Oracle8i, the approach we would take for 1 or 2 would be to
index a "user function".  This plsql stored procedure will be
invoked by context at runtime with a rowid and a CLOB you would
fill in.  You can, at that time, decide to return a concatention
of fields from various tables (that covers #1 above) -- or you
can decide whether to return some data or no data (that covers
#2 above).

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:440419921146
for an example of this technique.
Review & Followup
Rating: 3
Indexes on Views June 27, 2002
Reviewer: Rohit Gulati from Ambala,India
Dear Tom
             I have Oracle 7.1.4.1.0 on UNIX Platform. I have two tables having
the same structure. One is for current year data and another containing last
year data. i have indexes on both the tables. i created a view containing both
the tables. Now when i execute some query to retrieve data from  some third
table and form this view based on some condition then the query takes about five
mintues to execute and all the processing get slower when this process execute.
Please suggest.

Expecting the Best as usual.

Regards

Followup:
Suggest you first upgrade to software created less then 8 years ago!  714!  wow.
 That isn't even the terminal version of Oracle on most unix platforms (716
was).

Write the query without the view.  Tune that query as you would any other query
in the world.  Create a view as that freshly tuned query.

June 27, 2002 Tom -- Thanks for the question regarding "Describing a MEMBER of a package by non-owner", version 8.1.7
You Asked
I create a package "p", owned by user "packager_owner".  Package p has member m.
 I can do this, while logged in as package_owner:

describe p.m

and it gives me the calling sequence description.

But, if I log in as another user "other_user" who has been granted execute
permission on the package "p", if I do:

describe package_owner.p.m

it says it never heard of it.  I also tried creating a public synonym for
package_owner.p and then do:

describe p.m

but it still does not find it.

I noticed if I do:

  grant all on p to other_user

then I can successfully describe p.m as other_user, but only in SQL Worksheet.
It does NOT work in sqlplus.

Any ideas on how the non-owner can describe a member of a package it does not
own?  (I understand I can describe the package as a whole, but, programatically,
I need to be able to describe the member).

Thanks,
Tom B.
and we said...
Well -- I don't use sql "worksheet" personally -- prefering greatly sqlplus to
pretty much all other tools...

In plus, you can only describe the package -- desc p.m doesn't work there (not
since 7.3 anyway).

You can always query ALL_ARGUMENTS:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select argument_name, data_type from
  2  all_arguments where owner = 'OPS$TKYTE' and package_name = 'PKG' and
object_name = 'M';

ARGUMENT_NAME                  DATA_TYPE
------------------------------ ------------------------------
X                              NUMBER

Perhaps that will satisfy your programatic needs?
Review & Followup
Rating: 4
June 23, 2002
Reviewer: aya from egypt
describe us the steps for making the packger?

Followup:
I assume you mean the Oracle Software packager (which has nothing whatsoever to
do with this question)

You goto:
http://otn.oracle.com/docs/products/osp/content.html
You download the pdf, you follow the instructions....  chapter 7, a tutorial,
might be of interest to you.  It has step 1 .. step 8 laid out from start to
finish.

Rating: 4
June 23, 2002
Reviewer: aya
how can i use  the packager?

Rating: 5
June 27, 2002
Reviewer: R Diab
hi Tom,
please i want u to describe how can i make my data base portable using software
packager?
i.e.... i follow the instructions  in chapter7  but no result?

Followup:
I've never used it.  It does not make your database "portable".  It is a tool to
take software you've developed and create an installable version of it.

I'll say again "but no result" is as vague as your initial "it does not work" in
the other question you tagged this on.

It has the step by steps right in the docs -- step 1, step 2, step 3 and so on.
If you have followed those and they do not work -- then you have an excellent
reason to contact support.  With something like "but no result" there is quite
simply NOTHING I can personally do.

Rating: 5
manual database June 27, 2002
Reviewer: RDiab from egy
Dear Tom,
please say to me how can i make my database manualy ?
i want to install my database in adifferent machine , but manualy .
i wiat your response.

Followup:
You read the ADMIN guide for that platform -- every platform has such a guide.
For example on windows, you might read:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/win.817/a73008/ch5.htm#1004964
for example.

Rating: 5
Image on the web June 27, 2002
Reviewer: Diab from egy
Dear Tom,
i design my database it contain a field called image .
i want every one connected to my database on the web to add his photo from his
client from browser, i use the function read_image_file, but it get the
imagelocation from my server not from his client ,? what is the answer for this
problem?
thank u

Followup:
Diab,

as the form you used to submit this says "please don't use this area to ask a
new question"  This has nothing to do with anything on this page.  Nothing.

June 27, 2002 Steve -- Thanks for the question regarding "Tracing sessions from Java App", version 901
You Asked
Hi Tom,
I am having to do some query tuning where the front end is a home grown java app
passing requests to the database through a 9i Oracle JDBC driver. I'd like to
generate explain plans but I am not sure how to do so through the developers
Java code. I know how to this through sql*plus.

Any ideas? Should I just be going to v$sqlarea to grab what they are submitting
and then generating explain plans on that. I'd like to use tkprof as well but I
do not know how without setting sql_trace=true for the instance instead of per
session.

tks

steve
and we said...
Ask the developers to make it so they can enable / disable sql_trace with a
setting (configuration) somewhere.

That is the easiest (and THEY should be involved in this tuning as well else
you'll have an army of java programmers killing you constantly and continously
for the rest of your life throughing bad stuff at the database without any
concern for how it really works!)

This is why I firmly believe that a good java program as ZERO
select/insert/update/delete statements in it and only has BEGIN/END; blocks!

You can also use sys.dbms_system.set_sql_trace_in_session to enable sql_trace.
You can also use a LOGON database trigger to issue an alter session to enable
sql_trace.

But -- get the java developers to instruement the code so you can easily turn
sql_trace on and off, teach them all about tkprof and make them part of the
process (not the problem)....

If you have my book -- chapter 10 goes into detail on the various ways to turn
on sql_trace.
Review & Followup
Rating: 5
tks tom June 27, 2002
Reviewer: steve montgomerie from dallas
very good advice as always, i'll check that stuff out

June 27, 2002 Roy -- Thanks for the question regarding "RE: Ask Tom Submission, changing block size", version
You Asked
what is safest way to change db_block_size
of a 10GB Oracle 8 database?
and we said...
There is only one way to change the block size of an existing
database -- create a new database.  You cannot alter the block
size of an existing system.

The process will involve totally exporting all data and
importing it into a new instance you create.  Typically you
would perform a full export of the existing instance (after
kicking everyone off).  You would create a new instance, shut
down the old (keep it around) and import into the new instance.

If you have time, you can just exp the whole database and then
import it.  This will be a serial process, totally unoptimized
and may take a significant amount of time.

On the other hand, you can export individual users into separate
export files in parallel.  You can then import them in parallel
as well -- making better use of time and available resources.

This second method takes some practice.  You'll have to copy the
user accounts from the old instance to the new manually (fairly
easy with some scripts) and test to see how thing go.
Review & Followup
Rating: 3
imp June 27, 2002
Reviewer: Alex Daher from Brazil
Tom, I want to change the db_block_size for a test database,
so downtime is not a request. Therefore I will not create another database, I'd
like to use the same.
In 'imp', I must set destroy=y? Is that all? What about the parameter ignore?
Regards,

Followup:
You cannot change the blocksize for a database, you must recreate another new
database with a new blocksize.

June 27, 2002 Pawan -- Thanks for the question regarding "Materialized view", version 8.1.7
You Asked
A simple question - I was recently asked at an informal interview - There are
2000 ( 2 thousand) small tables ( maximum 10,000 records each)It is required
that data be pulled from these tables into 10 different tables based on certain
criteria. Can we create a materialized view for these 2 thousand tables? What is
the maximumm number of tables that can be joined to create a materialized view.
Thanks
and we said...
What a stupid question. (not directed at you, but the people asking such a
thing)

I would have walked.  If they didn't have a really good reason -- I'd be out of
there.  They are apparently impressed with trivia.

I'd have asked them "why, why in the world is this relevant to anything".

There is no documented limit.  I just did 2000 in a union all, and 2000 in a
join (it takes a really really long time).

Is it a good idea -- now, maybe that is a relevant question.   No, it is a
horrible idea.  I cannot FATHOM the design that would cause me to have to join 2
thousand tables (or even a hundred tables).
Review & Followup
Rating: 5
Thanks June 28, 2002
Reviewer: pawan from USA
I would have walked off too. But I need a job I just off my project 15 days back
and am taking all that the crap that they ( the so called experts) are throwing
at me.
Thanks

June 27, 2002 Susan -- Thanks for the question regarding "indexing strategy", version 8.1.7
You Asked
Hi Tom,

We have a table that has over 3 million rows with no indexes (not my fault I'm
new).  There is a candidate for a primary key, but, this key is just a generic
id key and won't be queried.  There is another column which is not unique but is
queried against often so this is the column I'm choosing for indexing.  Two
questions how should I size the initial extent for a big index like this and do
I need a primary key index if the primary key is not queried against?  Thanks.
and we said...
A primary key would be there to enforce business rules -- eg, that the field
must be unique.

There is no "law" saying you must have a primary key -- but it is convention.
If you truly do not have a primary key -- don't create one.

The only way I know to truly size something is to create a small example of it
(somewhere between .01 and 10% of the data depending on size) and analyze it.
See what it takes.  An index can be tricky due to the fact that
inserts/updates/deletes will affect it differently depending on how it is used.

An index on 3million rows where the column is a number will probably take about
75-100meg.  An extent size between 1 and 10meg would be appropriate.

I use only locally managed tablespaces and prefer UNIFORM extents over system
assigned ones.  In my world the initial extent is the same size as the next
extent always.
June 27, 2002 Isabella -- Thanks for the question regarding "Lock Record When multiple users are using a JSP application", version JDeveloper 9i RC
You Asked
I've a UIX JSP Application with BC4J developed with JDeveloper RC.
Multiple users (200 people) are using the application and they often got this
error: JBO-26030: "Failed to lock the record, another user holds the lock."

It seems that when a user generates a lock and he closes his browser without
commit or rollback the transaction, the lock on database is not removed.

How can I solve this problem ?
Is it possible to get the application server removes the session when the user
exit from the application ?

Thanks in advance
Isabella
and we said...
The lock is "timed out", your JSP sessions have a time to live.

The application server has NO IDEA that the user closed their browser.  The
connection from the browser to the application server is 100% stateless.  JSP's
give the illusion of state by using cookies (so your browser is hooked back up
with your "session state").

So no, the app server cannot remove the session when the user closes the browser
since the app server has no way of knowing that.

You can configure your app server to time out sessions faster if you like.  With
9iAS 9.0.2 for example:
http://otn.oracle.com/docs/products/ias/doc_library/90200doc_otn/web.902/a95878/developi.htm#999977
would do it.
June 27, 2002 Reddy -- Thanks for the question regarding "compile a views", version 8.1.5
You Asked
Hi tom

Today I exported my database from one user to another user. After exporting
data, I compiled using DBMS_UTILITY.COMPILE_SCHEMA('HRMSDEMO').  While compile
error or came like
"connect by loop in user data". After I personaly investigated there is some
view are invalid, but base tables or existing in my database.
How can I manage views while exporting from one user to another user. this view
are created by QUERY BUILDER Extension is *.QRY

Thanks

Reddy
and we said...
See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1418403973266
....
June 27, 2002 venkatesh -- Thanks for the question regarding "Capturing SQL statments", version 8.1.7
You Asked
Hi Tom,

I have to capture all the sql statments issued by the users irrespective of the
length of the sql statments....
also i have to capture all the statments used by the user who are currently
logged in or logged out at a given time....

Pl. tell me where i can find all these values.

Thanks in advance
venkatesh
and we said...
You need to upgrade (usually I don't say this but in your case, it is "need
to") to Oracle9i for two features:

1) fine grained auditing.  You can capture the SQL SELECT's issued as well as
their bind variable values and the SCN at which the statement was issued.

2) the ora_sql_text built in function exists.  So that a trigger on a table can
capture the actual triggering SQL statement.  The actual insert/update/delete.

Without those two features, your only other option is to set sql_trace=true and
capture it all to trace files on the server which will be

a) really really really slllllooooowwww.  Like horribly slow
b) take a ton of disk space
Review & Followup
Rating: 4
Can we not poll v$sqlarea to get it? June 27, 2002
Reviewer: A reader
Is it because v$sqlarea captures only the first 1000 chars you dont suggest it?

Followup:
you can get it (the sql text) all (in other v$ views) however

v$sql (better then v$sqlarea, faster) doesn't hae all of the sql.  It has as
much sql as the shared pool can currently hold.  It does not tell you who
executed it or anything.  It is not an inclusive set of sql statements.

June 27, 2002 Bob -- Thanks for the question regarding "Code size", version 7.3.4
You Asked
Tom,
  I received the following information from a third party vendor.  I've never
heard of this situation, and I haven't been able to verify it on metalink.  Can
you validate this information, and point me to a metalink document that
discusses this?
  In Oracle7 there is an issue regarding the code size for a procedure within a
package getting too large.  Code size dosen't just refer to the number of lines
of code, things like passing large records can dramatically increase the size of
the code even though its just one line of code.  If your procedure gets too
large you are likely to experience problems related to function calls and
raising exceptions.
  Is this true, and if so, does this situation continue to current versions?

Thanks for the help.
Bob Rich
and we said...
The limit comes into play when COMPILING but not during runtime (if you can
compile it, we can run it).

There is a limit on the "complexity" of the code -- one routine might fail to
compile at 1,000 lines of code whilst another compiles successfully at 5,000
lines of code.

If you use good modular programming techniques -- whereby your functions and
procedures are small, concise, readable -- you will never hit this (i personally
have never run into this issue, ever.  Not because I avoid it but because it is
only hit with truly monolithic pieces of run on code.  Code generators are most
prone to hitting it as they tend to generate monolithic pieces of code).

In 8.0 and up, the limit was dramatically raised (on the complexity of the code)
-- hence you should not hit this even with monolithic packages in that release
and up.
Review & Followup
Rating: 5
Follow up question June 26, 2002
Reviewer: Bob Rich from Indianapolis IN
Tom,
  We encountered this problem again, and I found the note the vendor was
referring to, note:62603.1.  This time I opened a ticket with support, and here
is their response:

Yes, your vendor is correct. Please note that size of the program is dependant
upon the number and type of parameters passed and the number of calls etc.,
i.e., passing large records will increase the size of a code at run time.
Moreover, if your code has many spaces, long identifiers, etc., you may end up
with larger source code.

  I'm more confused, does code size change at or during runtime?

Followup:
let me see the tar #

Rating: 5
Tar # June 27, 2002
Reviewer: Bob Rich from Indianapolis IN
The tar# is 2317224.995

Followup:
I followed up with them...

Rating: 5
Thanks June 27, 2002
Reviewer: Bob Rich from Indianapolis IN
Tom,
   Thank you for making the extra effort.

June 27, 2002 Munzer -- Thanks for the question regarding "Arrays and text replacement", version 8.17.
You Asked
Tom:

1.  Is there a SQL function and/or PL/SQL package function that strips out text
from a string.

E.g.  SELECT function(CNN-1000) from dual ----->1000

I am using this in web development, where I am displaying for user in a text
field in front of a seqence but I want to only insert or update the number since
column is of number type.

2. I have the following code using arrays:

procedure  vendor_form as

vend_cursor   integer;
i  integer;
TYPE t_vendor_temp  is table of vendor_temp.vendor_code%type
  index by binary_integer;

TYPE t_poc_temp is table of vendor_temp.poc_name%type
  index by binary_integer;

  v_vendor_code  t_vendor_temp;
  v_poc_name     t_poc_name;

BEGIN

  v_vendor_code(1) :=null;
  v_vendor_code(2) :=null;
  v_vendor_code(3) :=null;
  v_vendor_code(4) :=null;

 i:=1
 FOR x in (SELECT * from vendor_temp where vendor_code is not null)
 LOOP
 v_vendor_code(i) := x.vendor_code;
 v_poc_name(i) := x.poc_name;
 END LOOP;

FOR i in 1..4
LOOP
  vend_cursor:=owa_util.bind_variables('select null,''Pick one --> '',
decode(:vendor_code,null,''Y'',null) from dual
union all
select orgcd,name,decode(orgcd,:vendor_code,''y'', null) from org@orgd where
orgtype=''VEN'' order by 2',
'vendor_code',v_vendor_code(i));

owa_util.listprint(vend_cursor,'i_vendor_code',null,null);
dbms_sql.close_cursor(vend_cursor);

END;

My QUESTION:

1. Can you initialize the array variables when you declare them.  let us say you
have 100 of those, it would be tedious to write 100 initialization statements or
would you set up a loop?

2.I could have declared a type of row tye based on vendor_temp table. But then I
did not know how to assign the variable value.  Would it be like:

TYPE t_vendor_temp IS TABLE OF vendor_temp%rowtype
  index by binary_integer;

v_vendor_code  t_vendor_temp.vendor_code;

is that how you retrieve it?

Thank you,
and we said...
1) no, you would have to write your own.  If the format is always xxxx-nnnnn, I
would use substr( field, instr(field,'-')+1 )

2.1)

declare
  type collectionType is table of varchar2(25);

  l_data collectionType := collectionType( 'a','b','c' );

use a collection type instead of a plsql index by table....

2.2)
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          type empArray is table of emp%rowtype index by binary_integer;
  3          l_data empArray;
  4  begin
  5          for x in ( select * from emp )
  6          loop
  7                  l_data(l_data.count+1) := x;
  8          end loop;
  9
 10          for i in 1 .. l_data.count
 11          loop
 12                  dbms_output.put_line( l_data(i).ename );
 13          end loop;
 14  end;
 15  /
x
y
WARD
JONES
MARTIN
BLAKE
Review & Followup
Rating: 5
Tom: April 27, 2002
Reviewer: Munz from Reston, USA
AS a followup:

1.  Is COLLECTIONTYPE a name (labe) or it is oracle defined type.  How is this
different than PL/SQL table.

2. You said:
l_data(l_data.count+1) := x;

Now l_data.count is the number of records in emp table.   Id table has 10
records, yo uare starting with
l_data(11):=x (1st record)
and it goes to l_data(20)

AM I right or you are incrementing the variable here somehow.

Followup:
declare
  type collectionType is table of varchar2(25);
       ^^^^^^^^^^^^^^ could be foobar, it is the name of a type...

It is a plsql table -- it is NOT a plsql INDEX BY table.  It differs in that:

a) you can initialize it as shown
b) you have to "allocate" it as needed.

eg, a plsql index by table can just go:

   array(1) := value;

a collection type would need:

   array.extend;
   array(1) := value;

you have to allocate it.  Otherwise, they are very similar.

2) in MY example, l_data starts out empty, hence l_data.count = 0, hence this is
a way to fill up the array from 1 ... N.

Rating: 5
Arrays April 27, 2002
Reviewer: Munz from Reston, USA
Tom:

OK great.  I undesrtand your code very well now.

1. Is there a need to initialize l_data to null and can you do that?

2.  You are saying if you know how many elements in the array, use  a collection
not "index by Binary_integer".  These can be initialized like:

l_data  CollectionType:=CollectionType(null,null,null,null);

Correct?

3.  AS a followup to pattern matching function, I found oracle has a package:

OWA_PATTERN.CHANGE(string,from_value,to_value,flag)

are you aware of it?

Thank you,

Followup:
1) which l_data.

l_data when defined as an "index by binary_integer" -- no need to initialize.

l_data when defined as a collection type -- must be initialized before using:

...
  l_data  collectionType := collectionType();
......

2) No, I do not recall saying that at all.  I said, if you want to initialize it
-- use a collection type.  Just because you set l_data to 4 nulls does not in
any way limit the number of elements l_data can have.  I can use l_data.extend
to add many thousands more.

3) yes, I am aware of all of the funtions in the OWA toolkit.

Rating: 4
I am a different converting problem ... June 27, 2002
Reviewer: Trinh from atlanta
Tom,
I have a task, and I have trouble to accomplish:

Given the query returns a few columns

select attribute1, attribute2,..., attribute15
from tab1,...
where
...

the atttributes is in VARCAHR2(150), but the lengths of the real data very.

The requirement of the new outputs the query is based on:

for the result of an attribute,
* returns ONE NULL if all zeros;
* if an attribute likes 32000dds00F
, returns 32_dds_F (the underscore represents NULL; SINGLE NULL one OR more
consecutive
zeros)

Could the task be accomplished by SQL without PL/SQL? If not, then how is it
done in PL/SQL?
Thanks in advance

Followup:
I don't understand the question.  Sorry, you'll have to rephrase it.

Rating: 4
try it again, hope you enable to read it June 27, 2002
Reviewer: Trinh from atlanta
I have a task, which I have trouble to finsh:

    The Given the query likes

    select attribute1||' ' ||attribute2||' '||,...,||' ' ||attribute15
LINE_OF_ACC
    from tab1,...
    where
    ...

The result may look like
 LINE_OF_ACC
------------------------------------------------
000010 F90YUI000 32000dds00F 0000000000000 RTE01  .....

    the attributes is in VARCAHR2(150), but the lengths of the real datas may
very.

    The requirement of the new output of the query is based on:

    * The query shall return ONE NULL for the attribute, if an attribute has all
the zeros;
    * AND  if an attribute has any non-zeros in such as 32000dds00F
    , returns 32_dds_F (the underscore represents NULL; ONE NULL for one OR more

    consecutive zeros)

So the results shall look like after the convertion:

LINE_OF_ACC
------------------------------------------------
1  F9 YUI 32 dds F  RTE 1 ....

    Could the task be accomplished by SQL without PL/SQL? If not, then how is it

    done in PL/SQL?

Hope you can read it this time.

    Thanks in advance

Rating: 4
June 27, 2002
Reviewer: Trinh
 * AND  if an attribute has any non-zeros in such as 32000dds00F
_______
the "AND" shall be removed in the above.

Rating: 3
Easy! June 28, 2002
Reviewer: Andrew Clarke from London, England
SQL> select replace('000010 F90YUI000 32000dds00F 0000000000000 RTE01', '0',
null)
  2  from dual;

REPLACE('000010F90YU
--------------------
1 F9YUI 32ddsF  RTE1

SQL>

Cheers, APC

Rating: 3
Only of course your spec doesn't match your layout June 28, 2002
Reviewer: Andrew Clarke from London, England
You don't want to replace zeroes with NULLs but SPACEs.  Try this instead.

SQL> CREATE OR REPLACE FUNCTION f_replace (in_string IN VARCHAR2)
  2     RETURN varchar2
  3  IS
  4  out_string VARCHAR2(32000) := null;
  5  ln number;
  6  lv_zero varchar2(1);
  7  BEGIN
  8  ln := length(in_string);
  9  lv_zero := 'N';
 10  FOR i IN 1..ln LOOP
 11    IF substr(in_string, i, 1) = '0' AND lv_zero = 'N' THEN
 12       out_string := out_string||' ';
 13       lv_zero := 'Y';
 14    ELSIF substr(in_string, i, 1) != '0' THEN
 15       out_string := out_string||substr(in_string, i, 1);
 16       lv_zero := 'N';
 17    END IF;
 18  END LOOP;
 19  RETURN out_string;
 20  END;
 21  /

Function created.

SQL> select f_replace('000010 F90YUI000 32000dds00F 0000000000000 RTE01')
  2  from dual
  3  /

F_REPLACE('000010F90YUI00032000DDS00F0000000000000RTE01')
--------------------------------------------------------------------------------
 1  F9 YUI  32 dds F   RTE 1

SQL>

Cheers, APC

Rating: 5
June 28, 2002
Reviewer: A reader
Thanks Andrew!!

June 27, 2002 -- Thanks for the question regarding "Creating users in remote database", version 6i
You Asked
Hi Tom,

I have a scenario where I am connected to database HRDEV through a form. With
the necessary privileges and all that, I know that I can create users in
instance HRDEV via the form. But, is there a way to create users in instance,
say PEOPLEDEV from the form while still connected to instance HRDEV?

Thanks for taking the time,

Regards,
madhavi
and we said...
No, you cannot do DDL over a dblink, you must do the create in the remote
database.

what I've done in the past is to use dbms_job remotely and schedule something
like:

dbms_job.submit@remote_site( :job_number,
                             'execute immediate ''create user ....'';' );
commit;

assuming job queues are running on the other site, the user will be created
shortly after you commit
Review & Followup
Rating: 3
I can't do it... June 27, 2002
Reviewer: andrew from ca, usa
Tom
I'm trying someting very similar and am hitting a restriction in 8.1.7.3 (local
and remote DB) - am I doing something wrong?

create database link dev@scott connect to scott identified by tiger using 'dev';

declare
   l_job number;
begin
    dbms_job.submit@dev@scott( job => l_job,
                     what => 'begin null; end;');
    commit;
    dbms_output.put_line( ' your job is # '|| l_job);
end;
/

ORA-06550: line 45, column 41:
PLS-00512: Implementation Restriction: 'ANY_INSTANCE': Cannot directly access
remote package variable or cursor
ORA-06550: line 4, column 5:
PL/SQL: Statement ignored

Followup:
there is a default value set for the second to last parameter of submit and it
defaults to a REMOTE package variable and it is have "heartburn" with that.

dbms_job.submit@dev@scott( job=>l_job, what => 'begin null; end;',
                           INSTANCE => dbms_job.ANY_INSTANCE );

just add that and it'll be OK.

Rating: 3
Some progress... June 27, 2002
Reviewer: andrew from ca, usa
Tom
I got the following to execute - but how do I determine the instance number to
specify?

declare
   l_job number;
begin
    dbms_job.submit@dev@scott(job => l_job,
    what => 'begin null; end;',
    --next_date => null,
    --interval => null,
    --no_parse => null,
    instance => 1
    --force => null
    );
    commit;
    dbms_output.put_line( ' your job is # '|| l_job);
end;

Followup:
if you are non-ops (non-rac) it is not relevant.  use dbms_job.any_instance.

June 28, 2002 atul -- Thanks for the question regarding "Archive log files deletion", version 8.0.4
You Asked
Sir,

Due to space constraints i have to deleted archived log files from the
filesystem...

I know archived log files are very crutial but anyhow i have to do it safely..

I have a script for that,Could you kindly check it and give your suggestions
about safty........

Script to delete archive log files for space

sqlplus sys/sys02@db2 @run.sql
remove_log.ksh

This ksh file first calls the run.sql file, whose contents are these:

set head off
set pagesize 0
set feedback off
set linesize 100
spool /tmp/remove_log.ksh
select 'rm -fe '||name from v$archived_log
where trunc(completion_time) < trunc(sysdate-7)
/
spool off
exit

Thanks.
atul
and we said...
This is horrible.

So, you spent all of that energy to archive the logs, just to trash them.
Sooooo, what happens when you have a failure?   You goto your backups and goto
apply the archives -- opps -- you deleted them, rendering your backups totally
and wholly USELESS.

You need to read:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76993/toc.htm
from start to finish and learn about what is really going on here.  You need
your archives in order to make your backups useful.  Otherwise, your backups are
sort of like random bits and bytes on a tape somewhere.  Not very useful.
Review & Followup
Rating: 5
June 27, 2002
Reviewer: atul from india
Sir,

we are taking everyday hot backup on seperate tapes + daily full export..

and we have terrible space constraint..
so could you tell me way to get out of the situation

Thanks.
atul

Followup:
As I said, your backups are usless once you trash the archives.  You need to
keep

o the backup
o plus ALL ARCHIVES since the backup
o at least two copies of ARCHIVES actually (in the event that one of the tapes
containing the backups and archives fails)

You need to discover that a 36gig disk costs about $200/$250 USD.  Go get 4 or 5
of them, problem solved (for less money then we just spent discussing this issue
in all likelyhood).

Don't erase the archives, put them to tape if you are truly not able to get
sufficient disk to run your system.  (archives NEED to be put out to tape or
some other storage anyway -- that is something you need to do)

June 28, 2002 Raja -- Thanks for the question regarding "Java StoredProcedure Vs PL/SQL StoredProcedure", version 8.1.6
You Asked
Iam using java application to call a stored procedure written in
PL/SQL.It is taking lot of time to execute. Should i write the
stored procedure in java. Pls let me know which will better in
performance issue. We have lot of packages created in PL/SQL. If
performance is the main issue then do we need to convert all the
packages to java. Let me know the situation where i should go
fro java and where i should go for PL/SQL.

Thanks for any help.

regards,
Rajasekhar.
and we said...
Java can be faster then plsql, it can be slower.  Existing PLSQL
can always be tuned to go faster as well.  Do not recode all of
your PLSQL in java, that would be a waste of time.  The
interaction between Java and PLSQL is potentially faster then
the interaction between Java and a Java stored procedure (for
Java to call a java stored procedure we must take java types ->
turn them into sql types -> turn them into java types and do the
reverse on the way out.  for Java to call a plsql stored
procedure we must take a java type -> turn it into a sql type
and do the reverse on the way out)

My experience shows that

- if you give me a non-trivial Java stored procedure, I'll be
able to write it to run in PLSQL faster.

- if you give me a non-trivial PLSQL stored procedure, I'll be
able to write it to run in Java faster.

point being -- they are about the same, there are times when
java will beat plsql and vice versa.

The problem is not in the execution speed of PLSQL in as much as
it will be an untuned series of queries/statements in your PLSQL
routine.

I suggest you look at:

o timed_statistics
o SQL_TRACE
o and especially TKPROF

and failing that, see the dbms_profiler package for source code
profiling of PLSQL.

To get started with TKPROF, which will probably identify your
performance bottleneck, it'll find that query(s) that is causing
your slowness, see
http://technet.oracle.com/doc/oracle8i_816/server.816/a76992/ch14_str.htm#1018
If you have problems interpreting the results of TKPROF, feel
free to follow up with that.

The profiler, which should only be used AFTER your queries are
running at the speed of light, can be read about here:
http://technet.oracle.com/doc/oracle8i_816/server.816/a76936/dbms_pro.htm#1003424
....
Review & Followup
Rating: 5
How to update or insert into a SQLServer without using Transparent Gateway? June 27, 2002
Reviewer: A reader from USA
Hi Tom,
In your book (one on one, p844)you wrote:
"But if we only need to run a simple query or update on DB2, without any of the
other fancy features, then we could simply load the DB2 Java JDBC drivers into
the database, and do it that way (this doesn't apply DB2, of course)."

We have Oracle 8.1.7.0.0. (standard edition for that one)database and want
simply insert or update (maybe delete) into some tables in SQLServer 7.0, can we
do it using the method mentioned without using TG(the above) in your book?  If
we can, can you please show us an example about the steps and details or
recommend some documents that we can follow.
Thank you very much in advance.

Followup:
You can do that - just load their type 4 (thin) jdbc drivers into the database
with loadjava.

see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4406709207206
for another way as well if you have "odbc"

Rating: 4
I have to ask June 27, 2002
Reviewer: Doug from Danbury, CT USA
Sorry - what do you mean by "non-trivial" ?

Followup:
non-trivial means "more complex then System.out.println( "Hello World" ) or
dbms_output.put_line( 'Hello World' )".

Something of enough complexity.   If it contains sufficient complex logic --
that means the problem it was solving was complex -- meaning there are many ways
to solve the problem -- meaning, I'll find a faster algorithm in the other
language hence besting the first language.

Rating: 5
February 12, 2003
Reviewer: Helena Markova from Bratislava, Slovakia

June 28, 2002 Falguni -- Thanks for the question regarding " Why does ORACLE Takes long time for processing data", version 8i
You Asked
Hi Tom,
  First of all thanks for answering my earlier Question. i have new Question for
u i think u will find it silly to answer it but i am really curious to know
about it .

I have installed oracle 8i on my WIN NT server now whenever i fire update
statement on certain table (these tables have triggers on them and there is
30,000 rows in it ) oracle takes long time for processing . plus some of the log
files size increases terribly why does such thing happen ?How can i stop those
log files to increase .Actually i want to know wht is exactly going on
internally with oracle when such update statement are fired .

Thanks in Advance.

From
Falguni
and we said...
Well, you need a lot of background information -- a lot.

I'll refer you to two documents, one free -- one not.  The free one is the most
under read manual on the planet, the Oracle Server Concepts:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/toc.htm
In fact, the entire doc set for 8i is here:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/index.htm

The other, not free one, is my book -- If you like the way things are answered
here, I have 1,200 pages laid out in a logical order using the same techniques
as I use here (lots of examples and such).  I cover the internals as well -- how
your statement above would process -- what happens with undo, redo, and so on.
June 28, 2002 Falguni -- Thanks for the question regarding "how to use copy command in oracle", version 8i
You Asked
hi tom

Another Question how to use copy command in oracle . iam having two oracle
server which are connected thru wan now i want to copy data from one oracle
server to another oracle using copy command i know the syntax but it does not
work on server why so ...
i am using following copy command

copy from tos/tos@baroda to tos/tos create table card_mst as select  * from
card_mst1;

This gives me error Can u please tell me where am i going wrong .

Thanks in advance

regards
falguni
and we said...
<rant = on> You know this gives me an error is so vague as to be useless in
diagnosing issues.  Why don't people include what I believe to be blatantly
obvious information???  I'll bet over 50% of the questions I get don't have any
information beyond "i got an error".  <rant=off>

Ok, so I run the example in order to see what the eror message just might be...:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int, constraint t_pk
primary key(x), constraint t_check check (x>0) )
  2  /
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select rownum from all_users;
41 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;
Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> copy from ops$tkyte/xxx@ora817dev to
ops$tkyte/xxx create t2 ( x ) using select * from t;
SP2-0520: TO string missing Net8 @database specification

The error message in this case seems self explanatory.... The docs say:

<quote>

database

Specifies username[/password] @net_service_name of the Oracle source or
destination database you wish to COPY FROM or COPY TO. If you do
not specify password in either the FROM clause or the TO clause,
SQL*Plus will prompt you for it. SQL*Plus suppresses the display of
your password response.

You must include the net_service_name clause which consists of a Net8
connection string, to specify the source or destination database. The
exact syntax depends upon the Net8 communications protocol your....
</quote>

So, it must be:

ops$tkyte@ORA817DEV.US.ORACLE.COM> copy from ops$tkyte/xxx@ora817dev to
ops$tkyte/xxx@ora817dev create t2 ( x ) using select * from t;

Array fetch/bind size is 15. (arraysize is 15)
Will commit when done. (copycommit is 0)
Maximum long size is 5000. (long is 5000)
Table T2 created.

   41 rows selected from ops$tkyte@ora817dev.
   41 rows inserted into T2.
   41 rows committed into T2 at ops$tkyte@ora817dev.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review & Followup
Rating: 5
October 22, 2003
Reviewer: A reader

June 28, 2002 Sajid -- Thanks for the question regarding "Oracle Tablespace", version 8.1.7
You Asked
Hi Tom,

I dont know whether this is the right place or not. I have just submitted one
question related to Oracle WebServer/iSQL etc. There i by mistake mention the
wrong version. Actually i am using Oracle 9i Release 1.

Now my question. I have seen/read many articles here on TableSpace but still
couldnt follow it. Where does it come in Oracle Database in over all picture.
Like Instance --> TableSpace --> Segments --> Block...like this...

Could you please explain it brief starting from Instance to the end. It is so
hard to visualise Database without it. One more request please dont refer me to
the Oracle Documentation, although i have never seen any other software having
fully explained documentation like Oracle has. Still i would prefer to have in
your words. May be because i am used to it. I have your book Oracle Expert: One
on One.
I have read it but not page by page.

Hope i have put my question clearly.

Best Regards,
Sajid Anwar, London.
and we said...
Well, you need a lot of background information -- a lot.  And I'm not in the
business of repeating lots of stuff.

You have my book -- read chapter 2.  It, in my own words, does EXACTLY that.  I
describe the memory structures, the files (including database->tablespace, etc),
the processes -- everything.

It is 50 pages, it is about as short as you can be yet cover it all.

The CONCEPTS guide is a must read. The free one is the most under read manual on
the planet, the Oracle Server Concepts:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/toc.htm
In fact, the entire doc set for 8i is here:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/index.htm
I see red when people say "don't refer me to the oracle documentation".  That is
a request that

a) i will never honor
b) makes me really upset
Review & Followup
Rating: 3
thanks June 28, 2002
Reviewer: Sajid Anwar from London
thanks for your help.

Rating: 5
TOM ur right June 28, 2002
Reviewer: Sikandar Hayat Awan from Pindi Bhattian - Pakistan
I will also prefer to read the manuals/books and try what is there. I have
joined different forums and read annoying questions from Oracle users (not
experts). That’s why those forums are closed now.
Secondly we should ask technical questions from Mr. TOM and should appreciate
him if he give time to our question as u know he has to answer questions from
all over the world.
I know reading books is not so easy but to give answers of theoretical questions
for Mr. TOM is also not so easy. As for this purpose he wrote a book so read it
and then....
As Mr. TOM has requested many time to search an answer b4 posting so we should
search it first and I am sure that u will get answers of your 95% questions.

I hope that Oracle community will appreciate my point of view.

Rating: 5
Excellent June 28, 2002
Reviewer: Sanjay Raj from USA
Tom,
   I have really learnt a lot of stuff from this site. Oracle Concepts manual is
one thing I read like a story book over and over again inspite my 7 yr daughter
and wife complaining that I should move on. I really appreciate the great
service you are provideing to the Oracle Community.

Thanks

June 28, 2002 Sajid -- Thanks for the question regarding "Oracle Directory Services", version 8.1.7
You Asked
Hi Tom,

One of my colleague who is working on Direct Services and LDAP related stuff,
told me that i can use the Directory Services in Oracle as well. So i have
following questions for your.

a) What is Directoy Services (in term of database)?
b) What is the use ot it and Why one should use it?
c) How do i use it with my Oracle 9i database?
d) How do i configure my Oracle to start working with Directory Service?
e) Whats PROS and CONS of using Directory Services in relation to Oracle
Database?

Please forgive me to ask silly question. Hope you dont mind. Again i would
prefer few words from you rather than Oracle Documentaion Link.

Best Regards,
Sajid Anwar
London
and we said...
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/network.817/index.htm

a) amazingly, the documentation has a section called "what is a directory"

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/network.817/a86101/intro.htm#996904
b) and even a section on "why"

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/network.817/a86101/intro.htm#998236
c & d) and even "how"

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/network.817/a86101/start_se.htm#1002638
e) and even that!

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/network.817/a86101/intro.htm#999729

Sorry, but as you know by now -- a request to not be sent to the documentation
is a sure fire way to get sent to the documentation.

The documentation actually is pretty good.  Once you take the time to read it
and you know what -- this stuff (technology) takes time.  Read....

Review &
      Followup

      Rating:
      5
thanks  June 28, 2002
Reviewer:  Sajid
      Anwar  from London
Thanks a lot for the prompt reply.
I like the way you reply certain question.

June 28, 2002 xiaojun -- Thanks for the question regarding
      "dynamic sql?", version oracle 8.1.6

      You Asked
Here is the scenario:
Table A is studet which has the columns of
Student_id,
Student_name

Table B is audit_trail which has the columns of
Audit_trail_id (generated from a sequence)
Table_name
Before_image
After_image)

There is trigger which is as:
create or replace trigger test
before update or insert on student
for each row
declare
v_before_image audit_trail.before_image%type;
v_after_image audit_trail.after_image%type;
Begin
v_before_image :='<student_id>'||:old.student_id
||'</student_id>'||
'<student_name>'||:old.student_name||'</student_name>;
v_after_image:='<student_id>'||:new.student_id||'</student_id>'||

'<student_name>'||:new.student_name||'</student_name>;
insert into audit_trail (audit_trail_id, table_name,
before_image, after_image)
values (audit_trail_seq.nextval, student, v_before_image,
v_after_image);
end;
/
So basically each time the trigger fires, it will insert a XML
format
before image and after image for the audit_trail table.

I could do the hard-coding for every table I would like to
capture the
before image and after image. But one problem is that if the
tables/columns changes which happens quite
often during our development phase
then I need to rewrite the codes again.  I wonder if I could
write a dynamic sql which can take
my parameter table_name, and then based on my table_name, it
will give me all the columns.  Otherwise,
everytime the underlying table changes, I need to change my
trigger.  I have thought about dynamic sql
which is still new to me.  Could you please shed me some light?
I am thinking in the right direction?

Thanks,
-Catherine

      and we said...
You will have to use static sql since there is simply no way to
dynamically reference the :old and :new bind variables inside of
a trigger.  So, how to minimize the effort?  I use SQL to write
SQL.  For example:

---------------------------------------------------------
set verify off
set feedback off
set heading off

spool tmp.sql
prompt create or replace trigger &1._audit
prompt after update or insert on &1
prompt for each row
prompt declare
prompt     l_bi audit_trail.before_image%type;;
prompt     l_ai audit_trail.after_image%type;;
prompt begin
prompt     l_ai :=
select '     ' || decode( column_id, 1, '', '||' ) ||
       replace( '"<' || column_name || '>" || :new.' ||
                column_name ||
                '|| "</'|| column_name || '>"', '"', '''' )
 from user_tab_columns
 where table_name = upper('&1')
order by column_id
/
prompt ;;
prompt     if ( updating ) then
prompt     l_bi :=
select '     ' || decode( column_id, 1, '', '||' ) ||
       replace( '"<' || column_name || '>" || :old.' ||
                column_name ||
                '|| "</'|| column_name || '>"', '"', '''' )
 from user_tab_columns
 where table_name = upper('&1')
order by column_id
/
prompt ;;
prompt   end if;;
prompt   insert into audit_trail(audit_trail_id,table_name,
prompt   before_image, after_image )
prompt   values ( audit_trail_seq.nextval, '&1', l_bi, l_ai );;
prompt end;;
prompt /
spool off
set verify on
set feedback on
set heading on
@tmp
-----------------------------------------------------

So, if I call that sqlplus script "genaudit.sql" I can now:

tkyte@ORA806.WORLD> @genaudit emp

create or replace trigger emp_audit
after update or insert on emp
for each row
declare
l_bi audit_trail.before_image%type;
l_ai audit_trail.after_image%type;
begin
l_ai :=

     '<EMPNO>' || :new.EMPNO|| '</EMPNO>'
     ||'<ENAME>' || :new.ENAME|| '</ENAME>'
     ||'<JOB>' || :new.JOB|| '</JOB>'
     ||'<MGR>' || :new.MGR|| '</MGR>'
     ||'<HIREDATE>' || :new.HIREDATE|| '</HIREDATE>'
     ||'<SAL>' || :new.SAL|| '</SAL>'
     ||'<COMM>' || :new.COMM|| '</COMM>'
     ||'<DEPTNO>' || :new.DEPTNO|| '</DEPTNO>'
;
if ( updating ) then
l_bi :=

     '<EMPNO>' || :old.EMPNO|| '</EMPNO>'
     ||'<ENAME>' || :old.ENAME|| '</ENAME>'
     ||'<JOB>' || :old.JOB|| '</JOB>'
     ||'<MGR>' || :old.MGR|| '</MGR>'
     ||'<HIREDATE>' || :old.HIREDATE|| '</HIREDATE>'
     ||'<SAL>' || :old.SAL|| '</SAL>'
     ||'<COMM>' || :old.COMM|| '</COMM>'
     ||'<DEPTNO>' || :old.DEPTNO|| '</DEPTNO>'
;
end if;
insert into audit_trail(audit_trail_id,table_name,
before_image, after_image )
values ( audit_trail_seq.nextval, 'emp', l_bi, l_ai );
end;
/

Trigger created.

tkyte@ORA806.WORLD>

Now, you do need to remember to run this after any structural
changes -- but it does make it much easier to manage.

In fact -- another idea just popped into my head.  In 8.1 as you
have, you can have TRIGGERS on CREATES and ALTERS in a schema.
You could use the same concept I setup for doing a GRANT on any
new table in a schema to a specific user (see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:646423863863
for the details) to setup a procedure that can create a
trigger on these tables -- that procedure would be started by a
CREATE /ALTER trigger.  You would use dbms_job to submit this
procedure to run AFTER the create or alter finished -- but it
might help to automate this process....

Review &
      Followup

      Rating: 4
Get rows from
      table  January 11, 2002
Reviewer:  alex
       from NJ,USA
How do you search on the audit table ?  If i need to look for student name in
the XML concatenated string i need to parse through every line.

Is there a more efficient way?

thx

          Followup:

don't use XML, use regular rows and columns.  The person asking this question
wanted XML, that doesn't mean everyone in the world must use XML.

      Rating: 4
Dynamic SQL in
      a Trigger  June 28, 2002
Reviewer:
      A reader
Hi Tom,
I have a trigger that uses dynamic SQL, which is similar to the example on your
Expert One-on-One Oracle (p710):

execute immediate
'SELECT COUNT(*) FROM '||v_table_name ||' where
'||v_column_name=:new.JudgeMent_ID
INTO v_variable1;

I simply cannot get the right order when there is a 'WHERE' in there, which I
need.  I get the following error:
PL/SQL: Statement ignored
PLS-00382: expression is of wrong type

I have tried other different combinations.  Can we put the "WHERE" in this type
of application?  If yes, what is the right order?

Thank you very much help.

          Followup:

execute immediate

'SELECT COUNT(*) FROM '||v_table_name ||
' where '|| v_column_name || '= :x '
INTO v_variable
using :new.JudgeMent_ID;

June
      28, 2002 Jesse -- Thanks for the
      question regarding "Global Temporary Table", version 8.1.7

      You Asked
Hi Tom,
  Thanks a lot for answering previous questions.
  Appreciate if you could help in the following questions:

  1.  Is there a big difference between  a global temporary table with on commit
preserve rows and a global temporary table without on commit preserve rows in
terms of performance wise?

  2.  Is it possible yo assign a specific rollback segment to an update
statement?

  3.  If I use COPY command to copy a table from a db to another, it is copying
the constraints of tables but with different name, why?

  Thanks.

      and we said...
1) nope, it is a matter of when the segment is truncated.

2) a transaction, not a statement -- yes.  It is a horrible practice (and I love
undo segments in 9i that just totally and 100% obsolete that statement forever)

3) the sqlplus copy command does not copy many constraints.  Those that it does,
it uses 'un-named' constraints (system generated).  Why?  I suppose because it
would not otherwise be assured of not having a namespace conflct (consider what
happens when you copy T to T2 in the same schema/database for example...)
Review &
      Followup

      Rating:
      4
  June 28, 2002
Reviewer:
      A reader
Thanks for the answer. But I have more doubts.

For Q1) from the manual,
a)default temporary table=transaction specific
b)on commit preserve rows=session specific

Does that mean the rows will be there if I don't log out for b) ?
What about a)? Do I still can see the rows after I insert into the table??

So will b) taken up more resources because the rows retain until the users log
out?
Because we are facing problems in executing a procedure which is used by a lot
of users in a 15 minutes interval.
It is usually very fast in executing this procedure but recently the execution
just take very long time to complete....
What the procedure does is to insert into a few temporary tables and the front
end program will read from this temporary tables to display the result on to the
screen.

For Q3), does that mean export/import is still the best way to copy a schema
into the other schema in different db?

Thanks.

          Followup:

q1) it means the rows stay there for your session, when you log out, we truncate
your segement.

If you NEED the rows to stay there, then you are not taking up more resources
then necessary since you NEED THEM.

If you don't need the row to stay there for the session, but only for the
transaction, then of course you would use on commit delete.

It is not a matter of resource utilization here, it is a simple matter of "what
do you NEED"

You need to apply typical and traditional methods to diagnosing your issue.  You
are shooting in the dark trying to find cause and effect.  That will never work.

You need to use tools such as tkprof, sql_trace, timed_statistics and statspack.
 If you have my book -- chapter 10 is for you.  All of these tools are
documented in the Oracle documentation as well.

for q3, it again depends on your needs.  Need the constraints?  exp/imp is one
way.  Having the CREATE SCRIPTS is yet another.  And so on.  You use the tool
that best fits your needs.  Me, I use both, sometimes I copy -- sometimes not.

      Rating: 3
global
      temporary tables  June 28, 2002
Reviewer:
      A reader
Tom,

I am not sure how the oracle sessions are handled when there
is jdbc connection pools which are an open connections to the database which are
shared among processes. what will happen to global temporary tables with on
commit preserve rows in these case?

I am not even sure whether this is a valid question.

Thanks,

          Followup:

You could get hosed depending on how the connection pool actually handles
connections....  You might not be able to use session based ones -- unless you
grab the connection, keep it across calls and then "log off" -- physically end
the session.

June
      28, 2002 Reddy -- Thanks for the
      question regarding "Does Character set changes require column size
      change.", version 9.0.1

      You Asked
Hi Tom,

I have a DB with character set WE8DEC.
I have tables in the db with English data.
Now I am planning to convert the DB to UTF-8 character set.
Most importantly insert Asia Pacific data into existing database tables.
Does this require changes to the columns.
i.e increase the column size in the db to accomodate same # of Asia Pacific
characters. I heard some of the Asia Pacific languages require about 3 bytes.

Example : A 30 character name in English and 30 character name of Asia Pacific
language.

If I have name    varchar2(30) when I use WE8DEC,
I guess I may have to make it name  varchar2(90) if I want to insert Asia
Pacific characters and use UTF-8.

What is the impact on the database when the character set changes.
Does it require more table space.....etc

Where can I find which Operating Systems (we use) support UTF-8.
I heard Windows NT 4 does not support UTF8.

Thanks
Reddy

      and we said...
In 9i, you can use

varchar2(30 CHAR)

That will allow the column to hold 30 characters in your characterset.  A
varchar2(30) holds 30 bytes -- which may be much less then 30 characters is.

If you use varchar2(30), you'll need to increase it.  if you use varchar2(30
CHAR) you won't.
Review & Followup

      Rating: 5
Less than
      expected changes required   June 28, 2002

Reviewer:  Reddy KS  from Atlanta, GA USA
Thanks for the quick response. Your response saved me a lot of time.

What is the impact on the db table sizing due to the increase in the characters.

          Followup:

No affect on sizing if you size the way I size.

I size by loading somewhere between 0.01 to 10% of the table using
REPRESENTATIVE data.  Then I analyze.  Then I multiply.  (the bigger the end
table, the smaller percentage you need).  No muss, no fuss.

June 28, 2002 Sajid -- Thanks for the question regarding
      "Hiding data ", version 8.1.7

      You Asked
Hi Tom,

I have a situation where as i dont want anybody to see the data i put in the
table. also i shouldnot be able to see his/her data. How to do that in Oracle
(simplest way)

Regards,

      and we said...
use fine grained access control

http://asktom.oracle.com/~tkyte/article2/index.html
....
June 28, 2002 Sajid -- Thanks for the question regarding "How
      to create package in Java", version 8.1.7

      You Asked
Hi Tom,

I know that now we can write store procedure in java. I would like to two
things.

a)Is it possible to create PACKAGE in Java? If yes, then please tell us how?
b)Which is the best way :

   i) Package written in PL/SQL
      Function/Procedures in Java
   ii) Package written in Java
      Function/Procedure in PL/SQL

I am using Perl to do server side coding and using same to talk to oracle. Can i
still call Java store procedure within a package? If yes, please how?

Regards,
Sajid Anwar

      and we said...
a) yes, just execute a "create or replace " statment in the same fashion you
would execute "update"

or iii) all in plsql?

My choice is iii.  Inside the database, you will find PLSQL is the more
efficient "data processing language".  Inside the database, I use java to do
things PLSQL cannot do -- such as networking and file IO stuff.

In order to call a java stored procedure, you need to write a PLSQL wrapper.
Once this wrapper is written -- it is called just like ANY procedure would be.

Search for

create replace java source named compile

on this site for lots of examples.  If you have my book -- i go into great
detail on this and lots of other stuff as well.
June 28, 2002 Ericson -- Thanks for the question regarding
      "Getting the next working day", version 8.1.5

      You Asked
do you have any idea where i can track a holiday date.
bec right now im working on to get the next working day. elimintating
weekends and holidays.
should i create a new table for holiday. how should i do it.

      and we said...
I would probably approach this problem like this:

ops$tkyte@ORA8I.WORLD> create table holidays ( d date primary key ) organization
index;

Table created.

ops$tkyte@ORA8I.WORLD> REM Yeah, tomorrow is a holiday!
ops$tkyte@ORA8I.WORLD> insert into holidays values ( trunc(sysdate)+1 );

1 row created.

ops$tkyte@ORA8I.WORLD> create or replace function next_bday( p_date in date )
return date
  2  as
  3      l_date date default trunc(p_date)+1;
  4      l_cnt  number;
  5  begin
  6      for i in 1 .. 100000
  7      loop
  8          select count(*) into l_cnt
  9            from dual
 10           where to_char(l_date, 'dy') NOT in ( 'sat','sun' )
 11             and not exists ( select NULL
 12                                from holidays
 13                               where d = l_date );
 14
 15          exit when (l_cnt <> 0);
 16          l_date := l_date + 1;
 17      end loop;
 18
 19      return l_date;
 20  end;
 21  /

Function created.

ops$tkyte@ORA8I.WORLD> show err
No errors.
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> begin
  2      for i in 0 .. 20
  3      loop
  4          dbms_output.put_line
  5          ( 'Input ' || to_char(sysdate+i,'Dy Mon YYYY') ||
  6            ' Output ' || to_char( next_bday(sysdate+i), 'Dy Mon YYYY') );
  7      end loop;
  8  end;
  9  /
Input Wed Apr 2001 Output Fri Apr 2001
Input Thu Apr 2001 Output Fri Apr 2001
Input Fri Apr 2001 Output Mon Apr 2001
Input Sat Apr 2001 Output Mon Apr 2001
Input Sun Apr 2001 Output Mon Apr 2001
Input Mon Apr 2001 Output Tue May 2001
Input Tue May 2001 Output Wed May 2001
Input Wed May 2001 Output Thu May 2001
Input Thu May 2001 Output Fri May 2001
Input Fri May 2001 Output Mon May 2001
Input Sat May 2001 Output Mon May 2001
Input Sun May 2001 Output Mon May 2001
Input Mon May 2001 Output Tue May 2001
Input Tue May 2001 Output Wed May 2001
Input Wed May 2001 Output Thu May 2001
Input Thu May 2001 Output Fri May 2001
Input Fri May 2001 Output Mon May 2001
Input Sat May 2001 Output Mon May 2001
Input Sun May 2001 Output Mon May 2001
Input Mon May 2001 Output Tue May 2001
Input Tue May 2001 Output Wed May 2001

PL/SQL procedure successfully completed.

Review &
      Followup

      Rating: 3
3rd working
      day  June 28, 2002
Reviewer:  Ravi Kumar
       from Chicago, IL, USA
Hi Tom,

I have a calendar table with all 365 days in it and also its type ('WRK', 'END',
'HOL'). I am trying to query this table to get 3rd working day of every month.
And also 3rd working day after 15th of every month. how to write this query?

sample date for Feb'2002

2/1/2002    WRK
2/2/2002    END
2/3/2002    END
2/4/2002    WRK
2/5/2002    WRK
2/6/2002    WRK
2/7/2002    WRK
2/8/2002    WRK
2/9/2002    END
2/10/2002    END
2/11/2002    WRK
2/12/2002    WRK
2/13/2002    WRK
2/14/2002    WRK
2/15/2002    WRK
2/16/2002    END
2/17/2002    END
2/18/2002    WRK
2/19/2002    WRK
2/20/2002    WRK
2/21/2002    WRK
2/22/2002    WRK
2/23/2002    END
2/24/2002    END
2/25/2002    WRK
2/26/2002    WRK
2/27/2002    WRK
2/28/2002    WRK

and the result :

2/5/2002
2/20/2002

Thanks,
Ravi.

          Followup:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select dt
  2    from (
  3  select dt,
  4         row_number() over ( partition by month_part order by dt ) rn
  5    from (
  6  select dt, trunc(dt,'month')+(case when to_char(dt,'dd')<= 15 then 0 else
15 end) month_part
  7    from t
  8   where what = 'WRK'
  9         )
 10         )
 11   where rn = 3
 12  /

DT
----------
02/05/2002
02/20/2002

in 816 EE and up will do that (analytics RULE)

      Rating: 3
What about
      localization?  June 28, 2002
Reviewer:  Satish
      Kamatkar  from Redwood City, CA USA
Hi Tom,

The solution is focused solely for USA. For many other countries weekends are
not Sat or Sun but some other day(s).

I know that the original poster's question did not mention about any specific
localization requirements and that's why you too didn't worry about
localization.

But it would be nice if your routine next_bday could be parameterized or is able
to read some configuration information of what holidays/weekends are. This will
require removing/changing the hard-coded "NOT in ( 'sat','sun' )" clause.

Just a thought.

Regards,

- Satish

          Followup:

sorry, you know -- I try to show ideas, concepts -- solutions to specific
problems.  It is a bummer people cannot extend them for their own use.  Making
this table driven would be pretty easy don't you think?

June
      29, 2002 Savitha -- Thanks for the
      question regarding "Exclusive locks", version 8.1.6

      You Asked
Hi Tom,
I am running a select statement with a where cause & it shows me in ToAD that
there is an exclusive lock on that table. Can you explain what the locks do?
Oracle uses only row level locking am I right?
Thanks
Savitha

      and we said...
The lock is a DDL lock.  It prevents someone from dropping or otherwise changing
the physical structure of the table whilst you are using it.

Readers never block writers -- we take no row locks for the select at all.

Writers only block other writers of the same exact row, selects are not blocked
by writes.

If you are interested in all of the details either:

o read the server concepts guide - excellent manual.
o if you like the way I present material, get my book.  I cover locking and
concurrency in Oracle in detail.
June 29, 2002 Rajendra -- Thanks for the question regarding
      "How to use pl/sql table with jdbc", version 8.1.7

      You Asked
Hi Tom,

This is first time we are planning to use objects to send and receive
information between Java and Oracle.

My question is can we pass pl/sql table from Java to Oracle and vice / versa.

If yes Can you please give some example how it works because we don't know how
to use pl/sql table with Jpublisher.

Second question

Is it possible to use nested table in Advance Queue as a part of Payload. If yes
then how to create queue table having nested table as part of it (Example
please).

Thanks in advance.

      and we said...
You can, in 817 with the OCI thick drivers -- but I would suggest using
collection (sql) types instead of plsql table types.  It is easier and more
flexible.

Search for

java array

on this site for many examples (if you have my book -- I have a large section on
this as well)

Yes, the payload of AQ is either an object type or a raw.  object types include
support for nested tables.  Any example you already have that uses objects can
be using nested tables (the book on AQ has an example with an object -- a nested
table is just part of an object)

June 30, 2002 Prince -- Thanks for the question regarding
      "Passing OCI array to the procedure and retruning as a TABLE", version
      8.1.7.4

      You Asked
Dear Tom,

Suppose I have an array (say $arr, in my host program which is php).
Is there anyway, I can get this array as a table. Say by doing something like
"select TABLE ( :$arr) from dual"

How do I do this in anyother OCI program (say, C) ? (assume I have defined an
object type similar to the host array in my DB)

In summary I want to treat an host array like a table.

This is similar to what you have discussed in
http://asktom.oracle.com/pls/ask/f?p=4950:8:985294::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:110612348061,%7Bin%7D%20and%20%7Blist%7D
I want my str2tabl to accept the host array and return the values as table, so
that I can use them in SQL.

Thanks.

      and we said...
As for php, i don't know if it can bind arrays or not - that is something you
have to query the php documentation for.

OCI can definitely bind host arrays for input and output -- definitely.

See

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76975/oci03typ.htm#422594

You can "select * from TABLE( cast( :host_array as yourTableType ) )".  You
don't even need a function like str2table in this case.

Review &
      Followup

      Rating: 4
This is what, I
      want.  June 29, 2002
Reviewer:  Prince

Thanks Tom,

Sorry to bug you again. Can you point me to any simple example (URL) which does
exactly the same (C or Pro*C)?

I went thru the doc but couldn't find the right example.

Thanks,

          Followup:

look in $ORACLE_HOME/precomp/demo/proc and you'll see three demos for objects --
coldemo1 is all about collections with pro*c.  You need to simply

a) create a collection
b) bind it.

no more or less then in plsql.

      Rating: 4
Thanks Tom. I
      figured it out.  June 30, 2002
Reviewer:
      Prince.
You may ignore my follow up quetion.

Thanks.

June 30, 2002 prashanth -- Thanks for the question regarding
      "Is there a way to convert the 0.12 to 0.15 , 0.16 to 0.20 etc decimals",
      version 7.3

      You Asked
Hi

I have the following data

1.11
0.11
0.99
0.14

I need to round this value to the following values

1.15
0.15
1.00
0.15

can you help me out in getting a solution for the same.

Thanks in advance.

      and we said...
something like:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x number );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1.11 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 0.11 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 0.99 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 0.14 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2          for i in 0..99
  3          loop
  4                  insert into t values ( 1 + i/100 );
  5          end loop;
  6  end;
  7  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> column converted format 0.99
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select x, x-trunc(x),
trunc(x)+(ceil( (x-trunc(x))*100/5 )*5)/100 converted from t;

         X X-TRUNC(X) CONVERTED
---------- ---------- ---------
      1.11        .11      1.15
       .11        .11      0.15
       .99        .99      1.00
       .14        .14      0.15
         1          0      1.00
      1.01        .01      1.05
      1.02        .02      1.05
      1.03        .03      1.05
      1.04        .04      1.05
      1.05        .05      1.05
      1.06        .06      1.10
      1.07        .07      1.10
      1.08        .08      1.10
      1.09        .09      1.10
       1.1         .1      1.10
      1.11        .11      1.15

will do it.
Review & Followup

      Rating: 2
Related To this
      Rounding i have one similar problem  June 30, 2002

Reviewer:  IB  from China
If i give
Select Round((204.27)*35/100,2) From Dual;

Which Returns

71.49

But if you calculate manually it comes as 71.4945

So the Result should be 71.50

Simply to say  i wanted to Round Up but oracle function Round(n,m) where m=2
returns like 5/4 electronic calculator rounding  .Is there any way to do like
above(tht is to get the result 71.50 by using oracle function).

Anybody has any suggessions .Pls let me know.

Regards,
IB From India.

          Followup:

Umm, look at your data again.

You asked "please round to the second decimal place".  You give us input:

  71.4945

will, .494 rounds to .49 doesn't it? (yes, yes it does).

Why would you believe it should be .50???? That doesn't make sense to me (it'll
go to .5 which is .50 if you round( x, 1 ) -- to a SINGLE decimal point.

This is behaviing correctly, 100%

June 30, 2002 Richard -- Thanks for the question regarding "How
      to handle purge large table", version 8.1.7

      You Asked
Hi Tom,

I have a transaction table which gets a lot of inserts per day (around
a million).  The data is then archived to a warehouse.

What's the best way to handle purging this table to keep it at
a small size without using partitioning?

I know if I use partitioning I could then easily drop the partition
for the previous day as days roll by.  But for a reason we can't use
it (it's a long story).

Now I was thinking I could have two tables, table_1 and table_2, then
I can just use a synonym to point to table_1 on Monday, then on Tuesday
I can switch the synonym to point to talbe_2 and so I can truncate
table_1.  Except for a very brief time (probably less than a second)
I need to drop the synonym and point it to a different table.  Since I
will get around 20 transactions per second.  Some transaction will
fail right after the synonym is dropped and before it's created.  And
lose a single transaction is not acceptable in this application.

So I was wondering how does people handle this situation before
partitioning option was available?  Sure I am not the first one
run into situation like this right. :)

Thank you for your time.

Richard

      and we said...
The problem with your approach will be that you want to do DDL on an object that
has 20 tps continously.  What will happen is:

o you'll try to get your DDL lock
o people will be prevented from doing any new inserts on this object
o you'll be waiting till all outstanding transactions complete
o people will be waiting on you now
o if you get to the point where you can do the synonym (DDL) replacement, you'll
now invalidate all dependent objects (triggers, stored procedures, views,
whatever)
o you might not be able to invalidate them as people who are waiting on your to
finish the DDL might be running that code (probably, not might)

- you get stuck.  You cannot finish your DDL, people cannot do their inserts,
you screech to a halt until either

 -- your system overloads
 -- your DDL times out and lets people go forward (but your DDL fails)

One thing you can try -- but you'll REALLY REALLY want to benchmark this --
might be a procedure to do the insert.  The procedure will use dynamic sql to
insert the row (that way, it is not dependent on the table/synonym directly, it
won't get invalidated).  You'll want to have session_cached_cursors set
sufficiently high or use dbms_sql (so you are able to parse once, execute many).
  That way, you won't have dependent objects (objects dependent on the
transaction table) and when you do the DDL all that will happen is "new inserts
will wait", "ddl will wait for outstanding transactions to complete".  when the
outstanding transactions are complete, the new inserts will continue on.

Something like this:

create table trans_1 ( data varchar2(25) );

create table trans_2 ( data varchar2(25) );

create view trans
as
select * from trans_1;

create or replace package demo_pkg
as
    procedure log( p_data in varchar2 );
end;
/

create or replace package body demo_pkg
as
    g_cursor int := dbms_sql.open_cursor;

procedure log( p_data in varchar2 )
is
begin
    dbms_sql.bind_variable( g_cursor, ':data', p_data, 2000 );
    if ( dbms_sql.execute(g_cursor) <> 1 )
    then
        raise_application_error( -20001, 'didnt work' );
    end if;
end;

BEGIN
    dbms_sql.parse(g_cursor, 'insert into trans values ( :data )',
                   dbms_sql.native);
END;
/

This works as far as being able to change the view on the fly, I fired off many
sessions like this:

begin
        for i in 1 .. 100000
        loop
                demo_pkg.log( userenv('sessionid') || ' row ' || i );
                commit;
        end loop;
end;
/
exit

and then waited a bit.  In another session I did this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from trans_1;

  COUNT(*)
----------
      8545

1 row selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from trans_2;

  COUNT(*)
----------
         0

1 row selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view trans as select * from
trans_2;

View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from trans_1;

  COUNT(*)
----------
      8593

1 row selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from trans_2;

  COUNT(*)
----------
       321

1 row selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> /

  COUNT(*)
----------
     37532

1 row selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view trans as select * from
trans_1;

View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from trans_1;

  COUNT(*)
----------
     15428

1 row selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from trans_2;

  COUNT(*)
----------
    112418

1 row selected.

Shows that using the view and not having objects dependent on the view -- we can
do the swap.

(interesting point -- even with partitioning, the truncate partition would
invalidate the dependent objects!  you'd have the same sort of situation and
concerns)

This is how asktom handles this logging issue.  We log every request but want to
roll the logs.  Believing the database is the only place to keep such stuff --
we use a table.  We roll the tables using this approach.
Review &
      Followup

      Rating: 4
Quite complex
      but useful...  June 30, 2002
Reviewer:
      andrew  from ca, usa
Interesting stuff. Does 9i's online redefinition come close to matching this
approach?

          Followup:

The online redef will invalidate objects (you can structurally change things)
but there is a quiesce database capability to prevent the "jamming" up issue.

July 01, 2002 Subhakar -- Thanks for the question regarding
      "Indexes usage", version 8.1.6

      You Asked
Hi Tom,

1) I have a table with sixteen indexes and I wish to drop some
of these..Is there any way I can find out which Indexes r least
used,  so that I can drop them?

2) system@prod15> select substr('ABCDEF', 2, 4) STR  from dual;

STR
--------------------------------
BCDE

How can I make this output look like my second query?
This behaviour changed recently on the above database,
and it's causing some problems. Please advise.

system@dev15.rii.com> select substr('ABCDEF', 2, 4) STR  from
dual;

STR
----
BCDE

Thankx in advance

Hi Tom,

Yes, I meant the column size. Login.sql is same for both the
databases. In fact, this behaviour is only on this database.

system@prod15> select substr('ABCDEF', 2, 4) STR  from dual;

STR
--------------------------------
BCDE

system@prod15> show release
release 801060000

SunOS gibson 5.7 Generic_106541-12 sun4u sparc
SUNW,Ultra-Enterprise

system@prod15> conn system@agra (It shouldn't use login.sql)
Enter password: *******
Connected.
system@prod15> select substr('ABCDEF', 2, 4) STR  from dual;

STR
----
BCDE

system@prod15> show release
release 801060000

SunOS demeter 5.8 Generic_108528-05 sun4u sparc SUNW,Ultra-80

Hi Tom,

They look exactly tha same way, except for NLS_DATE_FORMAT. Like
I mentioned, this behaviour started on this database only a week
ago, and I don't remember changing any NLS parameters...

system@prod15> select * from nls_database_parameters;

PARAMETER                      VALUE
------------------------------
----------------------------------------
NLS_LANGUAGE                   AMERICAN
NLS_TERRITORY                  AMERICA
NLS_CURRENCY                   $
NLS_ISO_CURRENCY               AMERICA
NLS_NUMERIC_CHARACTERS         .,
NLS_CHARACTERSET               US7ASCII
NLS_CALENDAR                   GREGORIAN
NLS_DATE_FORMAT                DD-MON-RR
NLS_DATE_LANGUAGE              AMERICAN
NLS_SORT                       BINARY
NLS_TIME_FORMAT                HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT           DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT             HH.MI.SSXFF AM TZH:TZM
NLS_TIMESTAMP_TZ_FORMAT        DD-MON-RR HH.MI.SSXFF AM TZH:TZM
NLS_DUAL_CURRENCY              $
NLS_COMP                       BINARY
NLS_NCHAR_CHARACTERSET         US7ASCII
NLS_RDBMS_VERSION              8.1.6.0.0

18 rows selected.

system@prod15> SELECT USERENV ('LANGUAGE') FROM DUAL;

USERENV('LANGUAGE')
----------------------------------------------------
AMERICAN_AMERICA.US7ASCII

system@agra.rii.com> select * from nls_database_parameters;

PARAMETER                      VALUE
------------------------------
-----------------------------------
NLS_LANGUAGE                   AMERICAN
NLS_TERRITORY                  AMERICA
NLS_CURRENCY                   $
NLS_ISO_CURRENCY               AMERICA
NLS_NUMERIC_CHARACTERS         .,
NLS_CHARACTERSET               US7ASCII
NLS_CALENDAR                   GREGORIAN
NLS_DATE_FORMAT                DD-MON-YYYY
NLS_DATE_LANGUAGE              AMERICAN
NLS_SORT                       BINARY
NLS_TIME_FORMAT                HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT           DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT             HH.MI.SSXFF AM TZH:TZM
NLS_TIMESTAMP_TZ_FORMAT        DD-MON-RR HH.MI.SSXFF AM TZH:TZM
NLS_DUAL_CURRENCY              $
NLS_COMP                       BINARY
NLS_NCHAR_CHARACTERSET         US7ASCII
NLS_RDBMS_VERSION              8.1.6.0.0

18 rows selected.

system@agra.rii.com> SELECT USERENV ('LANGUAGE') FROM DUAL;

USERENV('LANGUAGE')
----------------------------------------------------
AMERICAN_AMERICA.US7ASCII

Thankx
Subhakar

Hi tom,

These were the differences between the instances:
buffer_pool_keep        buffers:23552, lru_latches:4
cpu_count            6        2
cursor_sharing            force        EXACT
cursor_space_for_time        TRUE        FALSE
db_block_lru_latches        16        8
db_name                PROD15        AGRA
db_writer_processes        2        1
dbwr_io_slaves            0        4
distributed_transactions    64        10
fast_start_io_target        189660        0
hash_area_size            33554432    134217728
instance_name            PROD15        AGRA
large_pool_size            96M        64M
log_buffer            16777216    8388608
log_checkpoint_timeout        0        1800
max_dump_file_size        2024000        500000
mts_service            PROD15        AGRA
nls_date_format            DD-MON-YYYY
optimizer_percent_parallel    0        70
oracle_trace_enable        FALSE        TRUE
os_authent_prefix        ops$
parallel_adaptive_multi_user    FALSE        TRUE
parallel_max_servers        40        4
parallel_min_servers        2        0
remote_login_passwordfile    EXCLUSIVE    NONE
service_names            prod15.rii.com, prod15    AGRA.rii.com,
AGRA
shared_pool_reserved_size    4800000        6400000
shared_pool_size        96M        128M
sort_area_size            16777216    67108864
timed_statistics        FALSE        TRUE
transactions            256        240

It behaves the same way in svrmgr too. So, I believe it has
nothing to do with "SHOOW ALL" command in SQL*PLUS, right?

Thankx,
Subhakar

      and we said...
That's what is it -- cursor sharing.  There is a side effect I
never anticipated but it makes sense.

ops$tkyte@DEV816> alter session set cursor_sharing=force;

Session altered.

ops$tkyte@DEV816> select substr( 'abcdedf', 1, 4 ) from dual;

SUBSTR('ABCDEDF',1,4)
--------------------------------
abcd

that is because the query above:

select substr( 'abcdedf', 1, 4 ) from dual;

is being executed as:

select substr( :SYS_B_0, :SYS_B_1, :SYS_B_2 ) from dual

the database no longer has a CLUE as to how long that field is.
I'm surprised it is only as short as it is -- I might have
thought they would have had a varchar2(4000) default for that.

Thanks for making me track this down.  I'm writing a book -- I
have a section on cursor_sharing, a parameter I'm not too fond
of.  I believe it to be a crutch for poorly written programs.
This is yet ANOTHER bad side effect of using it -- among
another.  You cannot use column histograms with it at all -- the
optimizer is given less information.

Review &
      Followup

      Rating: 5
No Effect of
      "cursor_sharing=force"  June 18, 2002
Reviewer:
      Sikandar Hayat Awan  from Pindi Bhattian - Pakistan

Hi Tom,
I have tried in Oracle 8.1.7 and the results are as under,

SCOTT > select substr( 'abcdedf', 1, 4 ) from dual;

SUBS
----
abcd

SCOTT > alter session set cursor_sharing=force;

Session altered.

SCOTT > select substr( 'abcdedf', 1, 4 ) from dual;

SUBS
----
abcd

          Followup:

scott@ORA817DEV.US.ORACLE.COM> select substr( 'abcdedf', 1, 4 ) from dual;

SUBS
----
abcd

scott@ORA817DEV.US.ORACLE.COM> alter session set cursor_sharing=force;

Session altered.

scott@ORA817DEV.US.ORACLE.COM> select substr( 'abcdedf', 1, 4 ) from dual;

SUBS
----
abcd

scott@ORA817DEV.US.ORACLE.COM> select substr( 'abcdedf', 1, 4 ) from dual
MAKE_ME_LOOK_DIFFERENT;

SUBSTR('ABCDEDF',1,4)
--------------------------------
abcd

scott@ORA817DEV.US.ORACLE.COM>

you got a false positive on that one, you had already run the query, it just
picked it up...

      Rating: 5
Very Useful
      Information  June 18, 2002
Reviewer:  A
      reader  from USA
Thanks Tom, for the information on the cursor_sharing.

What is the new book you talked about? Is it a later edition of your
already-at-bible-status book Expert One to One or a brand new one? If the
latter, what is the theme of the book? and when it's coming out.

          Followup:

the book I was referring to is "Expert One on One"

      Rating:
      5
Excellent  July 01, 2002
Reviewer:
      Sikandar Hayat Awan  from Pindi Bhattian - Pakistan

Now the results are as following,
SCOTT >  alter session set cursor_sharing=force;

Session altered.

SCOTT > select substr( 'abcdedf', 1, 4 ) from dual;

SUBSTR('ABCDEDF',1,4)
--------------------------------
abcd

I want to autoset width like,

SCOTT > select sex from my_emps;

S > It should display SEX.
-
M

          Followup:

SQLPlus, a simple little tool, sets the width of the output display column based
on the DATA -- not the name of the data.

You would have to issue:

column sex format a3

to see the column heading in full.

July 01, 2002 Raja -- Thanks for the question regarding "Most
      Admirable Oracle 9i Database Feature", version 8.1.6

      You Asked
In Oracle 8i , you seem to admire the Feature "Locally Managed Tablespace" the
Most as you  metioned about it countless times in these Columns.

Like Wise in Oracle 9i , can you single out one most admirable feature from your
point of View ?

Please give your insight into those features as well.

Thank You and Regards
Raja

      and we said...
There are a couple

o undo tablespace
o flashback query
o pipelined functions
o external tables

Guess if I had to pick one, it might be external tables ( coupled with
multi-table inserts and merge of course ;)

With that, sqlldr is obsolete.  I have direct path loads (via insert /*+ append
*/ or create table as select) and can use SQL to do everything.  So much easier.

I did a benchmark where this was our preferred method of loading.  It was so
cool to do a parallel direct path load with a simple insert.

Review &
      Followup

      Rating: 3
Bug in 9.2
      external tables  June 28, 2002
Reviewer:
      Richard  from somewhere cold and wet - UK
There seems to be a bug in 9.2 where a CREATE TABLE statement to mount an
external table which works in SQL*Plus will not work when using execute
immediate in a packaged procedure.

The problem is currently the subject of a TAR and the analyst dealing thinks it
is a bug.

Oh, and it doesn't work in a standalone procedure but will from an anonymous
block....

If you need to mount external tables for a series of data files, over time for
example, the only alternative is either to spawn an external SQL*Plus routine
(which I assume will work but I haven't tried because it defeats the object) or
create the ext table as part of the database build and rename the input files to
a common name to mount them.

External tables react to changes in the underlying file - you can actually edit
the file and see the changes immediately in the table.

          Followup:

tar#?  error msg?  (my crystal ball is in the shop this week, my sixth sense is
a little off due to that)

Based on:

"Oh, and it doesn't work in a standalone procedure but will from an anonymous
block...."

I'll betcha the error is an ORA-1031.  If so, read:

http://asktom.oracle.com/~tkyte/Misc/RolesAndProcedures.html
and close your tar.... (and grant yourself CREATE TABLE directly...)

      Rating:
      5
benchmark  July 01, 2002
Reviewer:
      Garry Robinson  from Edinburgh, Scotland
Could you please make available details of the benchmark you carried out and the
results which proved the usefulness of the external tables feature?

          Followup:

I'll not be sharing numbers as raw numbers without background information are
useless.  Sort of like someone saying "hey, I had 100,000 waits for IO -- I
waited a total of 1000 seconds for this.  Is that bad?".  You cannot tell given
that information  -- you need a ton of supporting facts (like the duration --
was that over 5 years, 5 months, 5 days, 5 hours, 5 minutes.  Like the number of
attempted IO's -- was that 100,000 waits out of 1 trillion, billion, million....
 Like the number of concurrent users  and so on)

I'll tell you what I did and my observations.

We had dozens of fixed width files to load up.  About 150 gig of raw input data.

We had a very short period of time to do it in (normally, I would schedule 4-6
weeks for a benchmark to scope it out, set it up, tune the processes, and so
on).  We had 4 days from end to end (it was a mistake to enter into this
particular benchmark -- it was more a race to see whose team was smarter, not
which product was better.  I *hate* benchmarks that cannot be used to provide
truly useful information.  The customer was not willing to take the time
necessary to learn anything from this exercise.  In the end -- it was an utter
waste of  time and resources, but that is another discussion)

Normally, you might look at that much data and try to figure out things like,
Hmm, how many parallel direct path loaders would be optimal in order to load
this data as fast as possible?  Should I run 20 for this input set, 10 for this
other one and how many should I run concurrently?

Then, you have to sit down and write the scripts to fire off 20 loaders for that
input set, 10 for that one and so on.

What we did instead?  We enabled parallel automatic tuning (let the database
figure out the optimum degree of parallelism based on current workload and the
request itself.  We used dbms_stats to jamm in information about the flat files
(so the DB would have a clue as to the volume of data).  We then just used
CREATE TABLE AS SELECT <from external table> with the parallel and unrecoverable
options.  The DB then did all of the work for us -- doing the parallel direct
path load.  Not only that, but we were able to apply our sql functions easily to
transform the data.

We did not benchmark a finely tuned sqlldr parallel direct path load VS
"database -- do the work, load this stuff for us".  So, I don't know if it was
faster (hey, we as humans can probably out tune the software given sufficient
time) but I can say this -- the time we saved by not having to figure all of
that stuff out  on our own allowed us to finish and it was more then fast
enough.

July 01, 2002 Steve -- Thanks for the question regarding
      "v$session_longops", version 8.1.5

      You Asked
Tom,

I have a large query that, when run against our development database, fetches
45000 records from a total of about 200000, and can be monitored in
v$session_longops. I run the same query on the production database (1000000
records from 20,000000) and no sign of it in v$session_longops. Both databases
produce the same plan albeit with different cost.

The only differences between the init.ora parameters are...

                           Production  Development

db_block_max_dirty_target    0     1000
db_files                  500     240
db_name                         STAP     STAD
enqueue_resources          721     461
job_queue_processes            4     2
log_checkpoints_to_alert    FALSE     TRUE
parallel_max_servers            2     5
rollback_segments            RBS01, RBS02, RBS03, RBS04
                                     RS01, RS02, RS03, RS04
shared_pool_reserved_size    1200000     800000
shared_pool_size            24000000 16000000
utl_file_dir                         /oracle/utlfileb/stad

Timed statistics are off.

Could you suggest any reasons for this.

Many thanks in advance

Steve

      and we said...
=====>>>>> Timed statistics are off.  <<<<<============

turn them on.

See

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76961/ch3140.htm#12904
it has the prereq's for using this v$ view.

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

timed statistics turned on or off?

what are the differences in the parameters (eg: in prod, do this:

create table t as select * from v$parameter

exp t

on test, imp t, join and print out the differences.
Review &
      Followup

      Rating: 3
If this is the
      case...  July 01, 2002
Reviewer:  Steve
      Kiteley  from Worcester, UK
...it doesn't explain why I can see the progress of other queries on the
production database!

TIMED_STATISTICS and SQL_TRACE are not set in the v$parameter table of
production and I am not setting it manually. So I'm still a little confused!

Steve

          Followup:

timed_stats should be set for this to work properly.

sql_trace should not be set.

enable timed stats and see what happens.

      Rating: 4
It works now
      but....  July 01, 2002
Reviewer:  Steve
      Kiteley  from Worcester, UK
...I can now track the query (thanks for that) but it seems to get to very near
the end and hang (ie it 'does' 65390 units out of 65391). When I kill the query
v$session_longops shows the totalwork as 65391 but shows two identical records
for the same query, the only difference being the context column, one of which
shows 1000 and one 0!

I'm getting there slowly!

Any further advice/suggestions you could add?

Thanks

Steve

          Followup:

Well, now you might be wanting to involve support as they can help do some
system state dumps that'll tell us in more detail exactly what is going on.

July 01, 2002 A
      -- Thanks for the question regarding "Deployment files for D2K", version
9i

      You Asked
Hello TOM,
        I got a chance to enter the Questions after a long time. I  started to
work on developer 2000 ( Forms 6i ) myself and i need to know what are the
deployment files we have to install in the client (Customer side ) side while
installing my developed exe.

      I know .fmb , .fmx , . fmt .

Thanks in advance and expecting your reply.

with regards,
siva

      and we said...
You need to install the forms runtime if you are doing client server.

Then, you install just the FMX files.

If you do web deployment (best option), you install nothing on the client.
July 01, 2002 William -- Thanks for the question regarding
      "FGAC problem", version 8.1.6

      You Asked
Hi Tom,
   I am trying to run the sample in chapter 21 of your book. After I loged on to
my data baseI tried to run set_app_role procedure but fail to set role for a
user. Here is what I got.

SQL> exec wli8350.expert_one_on_one.set_app_role;
BEGIN wli8350.expert_one_on_one.set_app_role; END;

*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at "SYS.DBMS_SESSION", line 58
ORA-06512: at "WLI8350.EXPERT_ONE_ON_ONE", line 35
ORA-06512: at line 1

I created data and uers exactly like those in your book but don't understand why
it did not work. Please advise. Thanks a lot

William

      and we said...
How did you create the context?  When I created the context it was

create conect hr_app_ctx using set_app_role;

meaning only the PROCEDURE set_app_role could touch it.  You are using the
procedure expert_one_on_one.set_app_role (you put it in the package).  Unless
you provide me the entire example I cannot diagnose it but I would believe you
created  the context using a standalone procedure name but you are trying to set
it with a packaged procedure.

(did you know the code is downloadable from the wrox website?)
Review &
      Followup

      Rating: 4
FGAC
      problem  October 29, 2001
Reviewer:  William
       from Ontario Canada
Yes, you are right. After I recreate set_app_role as a stand alone procedure and
create context Hr_App_ctx again it works. Here was what I did before;

Create or replace context Hr_App_ctx using expert_one_on_one.set_app_role;

(after logon)
begin
exec wli8350.expert_one_on_one.set_app_role;
end;

then I got the error mesage.

Do you mean that set_app_role should not be in a package ?
Please advise. Thanks a lot

      Rating: 5
used individual
      procedure  October 29, 2001
Reviewer:  Naveed
      Amin Khan  from West Des Moines, Iowa
I was also using an individual procedure before to set context variable, but
when I made it part of a package, I got similar error message.

For example,
create or replace context test_ctx using
test_package.set_role;

and procedure call like
exec test_package.set_role;

Where in package body I had:
dbms_session.set_context( 'test_ctx', 'UserName', 'test_user' );

      Rating: 4
Correction to
      earlier review  October 30, 2001
Reviewer:
      Naveed Amin Khan  from West Des Moines, Iowa
Actually I had made a mistake in the code set I just wrote. The create context
statement should be

create or replace context test_ctx using test_package

This works if you wanted to create a package instead of the using an individual
procedure. You are able to set the context using the test_package, and you do
not get any errors.

      Rating: 4
FGAC
      problem  June 30, 2002
Reviewer:
      A reader
Tom,
Suppose we declare a context for a package:
create or replace context MY_CTX using APP_CONTEXT_PKG;
In that package say we have 2 procedures..
I know that both the procedures can use the same context then.
while doing dbms_session, should the name of the variables be different in both
the procedures.
For example procedure A can have:
dbms_session.set_context( 'MY_CTX', 'ENAME',

'%'||upper(p_ename)||'%')

and procedure B can also have:
dbms_session.set_context( 'MY_CTX', 'ENAME',

'%'||upper(p_ename)||'%')

Here "ENAME" variable name is the same..
I guess I am wondering about "SCOPE" of a context in a packaged procedure.

Thanks.

          Followup:

Well, a context is not "for a package".  A context is global to the session --
it is accesible in a read fashion everywhere in that session.

A context is SETTABLE (writeable) by only one PACKAGE or one PROCEDURE/FUNCTION
(standalone proc/func).

The scope of a context is always global -- always.  everyone can "see it".  The
pacakge you provide is just the package that is allowed to write it (and it is
the only package that is allowed to write it)....

      Rating: 4
How about the
      same variable name  July 01, 2002
Reviewer:
      A reader
Thanks for a quick reply Tom...
How about the other question: Two procedures having the same
variable name..
For example procedure A can have:
dbms_session.set_context( 'MY_CTX', 'ENAME',

'%'||upper(p_ename)||'%')

and procedure B can also have:
dbms_session.set_context( 'MY_CTX', 'ENAME',

'%'||upper(p_ename)||'%')

Here "ENAME" variable name is the same..

is it O.K. to do that...

Thanks Again and Again and Again for being there...

          Followup:

Sure it is ok.  As I said -- the context is GLOBAL -- you do not have two
"variables" -- you have one session state that happens to have a context
variable that is called ENAME with some value.

Just like having a global package variable

      Rating: 5
Wow!!! That was
      quick....  July 01, 2002
Reviewer:
      A reader
Thanks Again...
Thanks for taking time to write "Expert One on One"....
It has changed my life completely...
I am following Bind variables as a religion...
When you get some time :)
please please consider writing a book on Bind Variables..
There are so many confused and misguided souls out there :)

Thanks...

July 01, 2002 Eric -- Thanks for the question regarding "LMT
      question", version 9.2.0

      You Asked
Hi Tom, My question is about the Locally Managed Tablespaces.
I work as DBA of two datawarehouses which in size are 3.5 Tb an 1.5 Tb, and
here, every Meg we can save it's really gold for us.
Every month after the ETL is done, i "compress" each tablespace and change they
status to read only.
The compress procedure is as follows: move the partition to a temp TS, coalesce
the original TS, move the partition back to the original TS, do a deallocate
unused on the partition, resize the TS and finally turn the TS into RO state.
Now, as i understand, if i try to do the same with LMT I'll never deallocate all
the unused space because Oracle round the size of the object up to the size of
the next extent.
Am I correct?
NOTE: I'm considring only uniform extent allocation because in the tests I done
with automatic extent allocation i could'n define an extent size ¿is there a way
to do it?
Thanks.

Eric.

      and we said...
Answering in reverse:  when you use automatic -- it is, yes, automatic and you
have no control over it.

With the LMT and uniform extent sizes -- every extent will be exactly the same
size.  You will not have any extents that are not the uniform size.  So the
deallocate unused won't really have any effect on the last extent.
Review &
      Followup

      Rating: 5
LMT question
      (cond)  July 01, 2002
Reviewer:  Eric Racero
       from Argentina
Tom, thanks for your quick answer.
Regarding the first question, if i want to deallocate the free space beyond high
water mark (in the case that my size estimation was not accurate), the only way
to do it is using the old dictionary exteent management. Am i right?

          Followup:

yes.

July 01, 2002 G.
      -- Thanks for the question regarding "Using Text_IO package ", version
      Developer 2000

      You Asked
On my Oracle server 7.3.4 I have not standard package Text_IO
(or similar). How to create and include it, and for example open
and close a file (Text_IO.fopen() and Text_IO.fclose()) ?

      and we said...
TEXT_IO is a client side package used in forms and such to
create files on the client.

In the server, UTL_FILE is the name of the package you want.  It
creates files on the server.  UTL_FILE is very similar as
far as the API goes to TEXT_IO only its hosted in the server,
not on the client.

In 7.3, see the application developers guide, PLSQL io chapter
for more info.
Review & Followup

      Rating: 3
Want more on
      it!  July 01, 2002
Reviewer:  Barathi A C
       from India
How to make use of IO operations in PL/SQL block?

          Followup:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76936/utl_file.htm#998101
or search for

utl_file

on this site.  Literally tons of examples.

July 01, 2002 Rahul -- Thanks for the question regarding "Few
      questions !!!", version 8.1.7

      You Asked
Hi Tom,

Here are few questions which i am not sure about the answers...

1.Which TWO types of data files can be considered
non-essential?
A) Datafiles belong to a lost tablespace.
B) Datafiles belong to an index tablespace.
C) Datafiles belong to a system tablespace.
D) Datafiles belong to a temporary tablespace.
E) Datafiles belong to an application data tablespaces.
F) Datafiles belong to a rollback segment tablespace

2.Which statement about RMAN w/ a database in
NOARCHIVELOG mode is true?
A) RMAN requires a recovery catalog.
B) RMAN supports multi-level incremental backups.
C) RMAN is compatible w/ Enterprise Backup utility.
D) RMAN only works in conjunction w/ a tape subsystem

3.Level 1 backup is taken on the first of each
month.  Level 2 backup is taken every Sunday.  Level 3
backup is taken daily.  If database gets corrupted on the
2nd Wednesday of the month and is lost, which scenario will
restore the database completely?
A) Level 2 & Level 3 backups should be used.
B) Level 1 & Level 3 backups should be used.
C) A baseline backup (Level 0) is missing so nothing can
   be recovered.
D) Level 1, Level 2, & Level 3 backups should be used.

4.Which technique does Oracle use to perform full backup of data
files?
A. The server process reads and backs up the entire file.
B. The server process reads the entire file and skips blocks that  have never
been used.
C. The server process reads the entire file and skips blocks assigned
to temporary segments.
D. The server process reads the entire file and skips blocks currently not being
used.
E. The server process reads the entire file and skips blocks unchanged from last
backup.

Thanks for all your wonderful help to oracle community..

Rahul.

      and we said...
What is this?  A test???

for #1, I would say b) (since indexes are easily recreated) and d) (since temp
is temp).  Now, I'm assuming that by index tablespace they mean a tablespace you
JUST HAPPEN TO BE USING exclusively for indexes since there is really no such
thing.....

for #2, it is somewhat of a trick question.

a) is not true
b) MIGHT BE true
c) is not true
d) is not true

Hence, I deduce it is b.  It might be true since you also must have the
enterprise edition for incrementals.  So, it could be that you are in
noarchivelog mode and still not be able to do incrementals....  Hmmm....

For #3, you should have started with a level 0 technically, however rman will do
a level 0 if no level 0 exists:

6052, 1, "no parent backup or copy of datafile %d found"
// *Cause:  An incremental backup at level 1 or higher could not find
//          any parent backup or copy of the indicated datafile.
//          A level 0 backup of the datafile will be taken automatically.
// *Action: This is an informational message only

Sooo -- it could be d) but they might have said c)

You might have to go WAY back in time to recover though, since you are not doing
a level 0 on a recurring basis ;)

4 -- b) is the correct answer.

#4 is actually the only question that is not at least partially ambigous in my
opinion (i hate multiple guess)
Review &
      Followup

      Rating: 4
Full or
      Complete?  July 01, 2002
Reviewer:  Randy
       from Texas
It seems the terms I see in the docs are full backups and complete backups.  I
wondered about this, but can only deduce that 'full' means cold and 'complete'
means level 0 hot, because it seems the only kind of full that wouldn't look at
individual blocks would have to be a cold.  Still not sure though.

          Followup:

full and complete seem synonymous to me -- would need an explicit reference in
order to say anything else.

I do not see them relating to hot and cold.  You can do a full hot backup.  You
can do a complete hot backup. Same with cold.

      Rating:
      4
  July 01, 2002
Reviewer:  Rahul

Yeah Tom you guessed it right, these were few of the questions that were asked
during a recent interview which i wasn't sure.

Thanks.

July 01, 2002 Learner -- Thanks for the question regarding
      "Regarding rowid", version 8.1.7

      You Asked
Hi Tom, a small academic question:

In a normal(heap) table, can rowid be safely used to obtain the last inserted
record? Something like:

SELECT rowid r, t.* FROM t
WHERE r = (SELECT MAX(rowid) FROM t);

      and we said...
NO NO NO....

A gazillion times NO.

search for:

last inserted row

on this site for many counter examples.  The ONLY way to find the "last inserted
record" is to have some data in the record that you can use to order the data to
find the "newest stuff".  Regardless of the table type!
Review &
      Followup

      Rating:
      5
Thanks  July 02, 2002
Reviewer:
      Learner
A discussion on this with a colleague brought this up. The contention was that
if a table was insert-only, the rowids were non-reusable; hence we could use the
rowid to obtain the last inserted row. I doubted this, but wasn't able to prove
it. But reading from

http://asktom.oracle.com/pls/ask/f?p=4950:8:992164::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:912210644860,%7Blast%7D%20and%20%7Binserted%7D%20and%20%7Brow%7D
<quote>
Rowid - won't work.  Its based on flie/block/slot.  We reuse rowids, we can even

change them (partitioned tables, IOTS).  You might have extent #1 in file 55 and

extent #2 in file 2.   Extent 4 might start on block 555 of file 3, Extent 5
might start on block 2 of file 3.  Rowids are NOT sortable.
</quote>

gave me the information I needed.

Thanks Tom, and apologies for the trouble. And yeah I need to improve on my
searching skills.

July 01, 2002 Krishna -- Thanks for the question regarding
      "Combining data from two tablespaces.", version 8.1.6

      You Asked
Tom,
Thanks for all the previous answers. I have a questions. If I change the default
tablespace for a user, what will happen to data of a particular table. Let us
say we have 1000 rows in a table belonging to table space "A" and we change the
default table space and load 1000 more rows. Now the table belongs to tablespace
"B". How can we get all the 2000 rows in one table space easily.
Thanks

      and we said...
The default tablespace is used only during object CREATION

Once a table is created, it is in a particular tablespace and will stay there,
regardless of the default tablespace setting.
Review &
      Followup

      Rating:
      3
Followup  July 01, 2002
Reviewer:
      Krishna Roy  from Michigan
Let us say we have 1000 rows in that table and I try to insert 1000 rows and it
fails because of insufficient space. Now I create one more table space and make
this tablespace the default table space for the User/schema containing this
table and try to insert again. What happens?

          Followup:

Same thing as I described.

A segment is in a tablespace.  When the segment extends, it'll extend in that
tablespace ONLY.  The default tablespace is consider ONLY during the creation of
new segments -- never during the extension of an existing one.

      Rating: 3
after creation
      of object  July 01, 2002
Reviewer:  Reddy
       from Bangalore, India
Hi tom

Suppose I already have some tables and around 10000 rows of data. If I change
the default tablespace, where those objects will reside, If it is reside in old
tablespace, how to get back to new one.

Reddy

          Followup:

you would need to MOVE the object

alter table T move tablespace ....;
alter index I rebuild tablespace .....;

July
      01, 2002 Nasir -- Thanks for the
      question regarding "Why the following query behaving like this?", version
      9.2.0

      You Asked
create table test_nodes (node_id varchar2(180) not null);
insert into test_nodes values ('AA');
insert into test_nodes values ('BB');
insert into test_nodes values ('CC');
insert into test_nodes values ('DD');

create table test_centra_reserved_ids (node_id varchar2(180));
insert into test_centra_reserved_ids values ('AA');
insert into test_centra_reserved_ids values ('BB');
insert into test_centra_reserved_ids values ('CC');
insert into test_centra_reserved_ids values (null); << without this insert the
query works. With this the query works only if we add where node_id is not
'null'

select node_id from test_nodes where node_id not in
(select node_id from test_centra_reserved_ids);

-- The above query returns 'DD' as it should without the entry 'null' line
above.
It works if we do the following:
insert into test_centra_reserved_ids values (null);
with the following query:
select node_id from nodes where node_id not in
(select node_id from centra_reserved_ids where node_id is not null);
Why is that? We will be glad if you can answer.

      and we said...
It is the defined behavior in SQL.  See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:442029737684
for example.

Using tri valued logic, it is not known (neither true nor false) that NODE_ID is
not in a set that contains a NULL -- we "don't know" and that is the correct
answer when using NOT IN.
Review & Followup

      Rating:
      4
  July 02, 2002
Reviewer:  Nasir
      Sarwar  from Houston, TX USA
Thanks Tom It is very helpful.


Subject | Article | Last Updated (East coast time)
Oracle 8.x clients to connect Oracle Rel 2 Database | article only | Jul 02, 2002 08:13 am
SQL query | article only | Jul 02, 2002 08:15 am
Optimizing query | article only | Jul 02, 2002 07:49 pm
recieving email into the batabase | article only | Jul 03, 2002 06:54 am
Books | article only | Jul 03, 2002 07:15 am
business rule; no inserts after a certain value has been inserted | article only | Jul 03, 2002 08:35 am
character set, constraints, bind variables | article only | Jul 03, 2002 08:43 am
Dynamic Array Binding | article only | Jul 03, 2002 12:50 pm
Where can I find information on how to create a standby database ?? | article only | Jul 03, 2002 12:54 pm
Using DBMS_JOB timestamp or extract date in procedure as a constraint | article only | Jul 03, 2002 01:05 pm
HIT RATIO | article only | Jul 03, 2002 02:25 pm
General Question | article only | Jul 04, 2002 10:19 am
blocks, cost of the query | article only | Jul 04, 2002 11:00 am
Join Tables | article only | Jul 04, 2002 11:07 am
IOTs | article only | Jul 04, 2002 11:17 am
How should i join my tables in my queries? | article only | Jul 04, 2002 11:28 am
Dropping a tablespace with a lot of free extents. | article only | Jul 04, 2002 12:00 pm
Why isn't my Index Getting Used? [Expert One-on-one Oracle: pg 303] | article only | Jul 04, 2002 12:05 pm
Question on BEFORE Trigger | article only | Jul 05, 2002 10:38 am
fun fun fun -- (you are wrong wrong wrong) | article only | Jul 05, 2002 09:06 pm
Which one is more efficient PL/SQL procedure or Java stored procedure | article only | Jul 06, 2002 08:54 am
Using the record of an implicit cursor as a parameter in a funcion | article only | Jul 06, 2002 08:57 am
PGA !!!! | article only | Jul 07, 2002 09:03 am
Changing a database field datatype | article only | Jul 07, 2002 09:36 am
CBO vs. analytic functions | article only | Jul 07, 2002 04:21 pm
Characterset Change | article only | Jul 08, 2002 06:54 am
LMT | article only | Jul 08, 2002 07:33 am
When do blocks go on the free list | article only | Jul 08, 2002 07:44 am
Database queuing | article only | Jul 08, 2002 08:07 am
Entity Attribute Value(EAV) | article only | Jul 08, 2002 07:50 pm
row(s) 1 - 30 of 30
July 02, 2002 Seetharaman -- Thanks for the question regarding "Oracle 8.x clients to connect Oracle Rel 2 Database", version 8.1.6
You Asked
Hi Tom,

  For my previous question, you have answered to create entries in client Oracle
8.0.  My Oracle Server on NT being Oracle 8i Rel 2. ONLY i'm getting errors like

  ORA - 12545 if i give the sid name as <dbname> in the Oracle Client 8.0.5  ( I
have tested with 10 Oracle 8.0 clients but it's not connecting )

  ORA - 12505 if i give sid name and system name <dbname.hostname>  I want to
get rid of this problem soon.

Thanks in advance

Seetharaman Srinivasan
and we said...
Have you setup the TNSNAMES.ORA entry on the client machines?  they would look
something like this:

ora8i.us.oracle.com =
(DESCRIPTION =
  (ADDRESS=(PROTOCOL=TCP)(HOST=aria.us.oracle.com)(PORT=1521))
  (CONNECT_DATA=(SID=ora8i))
)

where you will of course change the host=, port=,sid= to be whatever is correct
for you.

If you are trying to just use the simple "hostname" method, please refer to
http://technet.oracle.com/doc/oracle8i_816/network.816/a76933/namingcf.htm#498308
for the setup.  It has the 4 steps you must take to get that going (just tested
myself with 8.0.3 clients against 8.1.6 -- works right away once the listener is
running on the correct port, the global_dbname is setup, and the clients have
HOSTNAME as a valid method to try)
Review & Followup
Rating: 5
Error (12545) DB Config Assistant June 26, 2002
Reviewer: Sikandar Hayat Awan from Pindi Bhattian - Pakistan
Hello,
In oracle 8.1.7 when I am trying to modify the DB with configuration assistant
in WIN-NT and entering the password for Internal and getting the following
error,
"Unable to determine database version or detect installed option
ora-12545: connect failed because target host or object does not exist"

I have two instances on this machine one I have stopped and the other is
running. I have tried with tnsnames.ora modification but same result.

Followup:
> oerr ora 12545
12545, 00000, "Connect failed because target host or object does not exist"
// *Cause: The address specified is not valid, or the program being
// connected to does not exist.
// *Action: Ensure the ADDRESS parameters have been entered correctly; the
// most likely incorrect parameter is the node name.  Ensure that the
// executable for the server exists (perhaps "oracle" is missing.)
// If the protocol is TCP/IP, edit the TNSNAMES.ORA file to change the
// host name to a numeric IP address and try again.

Rating: 5
No Success July 02, 2002
Reviewer: Sikandar Hayat Awan from Pindi Bhattian - Pakistan
After changing hostname to IP I received "ora-12560:TNS Protocol Adapter Error"
after reading your following thread
http://asktom.oracle.com/pls/ask/f?p=4950:8:992387::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:486671571494,%7B12560%7D
I have changed ORACLE_SID but still the same. What is going wrong with me?

Followup:
Well, I don't know why you would change your ORACLE_SID (put it back -- that
thread was in reference to a svrmgrl message -- the oracle sid in the users
environment was set to a sid that didn't exist and svrmgrl was always trying to
"touch" that sid)

You most likely put the wrong IP address in.  consider, I changed to an ip
address (the wrong one) and then the right one:

$ nslookup aria-dev
Name:    aria-dev.us.oracle.com
Address:  138.2.5.52

$ grep 138 listener.ora
        (ADDRESS = (PROTOCOL = TCP)(HOST = 138.2.5.111)(PORT = 1521))
      (ADDRESS = (PROTOCOL = TCP)(HOST = 138.2.5.111)(PORT = 2482))

$ lsnrctl start

LSNRCTL for Solaris: Version 8.1.7.3.0 - Production on 02-JUL-2002 08:10:45

(c) Copyright 1998 Oracle Corporation.  All rights reserved.

Starting /export/home/ora817/bin/tnslsnr: please wait...

TNSLSNR for Solaris: Version 8.1.7.3.0 - Production
System parameter file is /export/home/ora817/network/admin/listener.ora
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC817)))
Attempted to listen on:
(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=138.2.5.111)(PORT=1521)))
TNS-12545: Connect failed because target host or object does not exist
 TNS-12560: TNS:protocol adapter error
  TNS-00515: Connect failed because target host or object does not exist
   Solaris Error: 126: Cannot assign requested address

$ !grep
grep 138 listener.ora
        (ADDRESS = (PROTOCOL = TCP)(HOST = 138.2.5.52)(PORT = 1521))
      (ADDRESS = (PROTOCOL = TCP)(HOST = 138.2.5.52)(PORT = 2482))

$ lsnrctl start

LSNRCTL for Solaris: Version 8.1.7.3.0 - Production on 02-JUL-2002 08:11:18

(c) Copyright 1998 Oracle Corporation.  All rights reserved.

Starting /export/home/ora817/bin/tnslsnr: please wait...

TNSLSNR for Solaris: Version 8.1.7.3.0 - Production
System parameter file is /export/home/ora817/network/admin/listener.ora
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC817)))
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=138.2.5.52)(PORT=1521)))
Listening on:
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=138.2.5.52)(PORT=2482))(PROTOCOL_STACK=
(PRESENTATION=GIOP)(SESSION=RAW)))

question is -- why would you want a hard coded ip address.

Rating: 5
Update...8.1.7 WINNT4 July 02, 2002
Reviewer: Sikandar Hayat Awan from Pindi Bhattian - Pakistan
I have changed the ORACLE_SID as it was reflecting the 2nd DB but I want to
connect to 1st. Current scenerio is,
I am able to connect with svrmgr, sqlplus, from clients but not from DB
configuration.
You are right that for wrong IP it will give error but I am changing in tnsnames
not in listener as listener is working fine. Other than DB config assistant I
can connect both of DB's but with DB config can't connect to anyone.
Is DB config is using some other configuration files like (tnsnames, listener
etc).
Both the databases are running smoothly.

July 02, 2002 -- Thanks for the question regarding "SQL query", version 8.0
You Asked
Hi Tom,

       I am not able to solve this query for long long time,
i d'not know wheather this is possible or what?, with an single select statement
with out using function in select statement.

I have a table

TEMP
-----
10
20
30
40
.
.
.
N ROWS

I need to find the rows multiplied by prior rows
ie. like 10*20*30*40... and so on... with out using function in select
statement.

Hope you have some way to work with this query.

Thanks
and we said...
well, i have to use BUILTIN functions!  if using builtin's isn't OK -- there is
no answer.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select rownum*10 from all_users
where rownum < 5;
4 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select exp(sum(ln(x))) from t;

EXP(SUM(LN(X)))
---------------
         240000

And in 8iR2 and up -- if you need them "incrementally" you can easily do that:

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select x, exp( sum(ln(x)) over (order by x))
from t;

         X EXP(SUM(LN(X))OVER(ORDERBYX))
---------- -----------------------------
        10                            10
        20                           200
        30                          6000
        40                        240000

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review & Followup
Rating: 4
Tom has answer for everything July 02, 2002
Reviewer: senthil from OH USA

Rating: 3
July 02, 2002
Reviewer: Sagi from India
Hi Tom !

I have seen in couple of your FAQs the "OVER" clause/statement.

Can you explain when and why we use "OVER" with an example.

Thank you in advance.

Regards,

Followup:
search this site for

analytic

These are known as analytic functions, if you have my book -- i have a whole
chapter on them.  They are also documented in the data warehousing and SQL
reference manuals.

They allow you to do things never before possible (or very slow) in SQL.

July 02, 2002 D -- Thanks for the question regarding "Optimizing query", version 8i
You Asked
how to optimize query with a where clause like this

select * from document where (documentid=1234 and version number
=1.1)
or (documnetid=1233 and versionnumber=1.2) or...

it goes on like that for about 1500 documents in another sense
1500 or's.

it is taking about 3.5 minutes to execute the query.
and we said...
Well, hopefully you aren't really coding the literals into the
query -- that would be a terrible performance hit right there.
You should be using bind variables in all queries like that.

My suggestion would be to not use an OR of a bunch of ANDs but
rather to do something like this:

create global temporary table t ( docid number, version number )
on commit delete rows;

and then before running the query, populate that temporary table
(using array processing if at all possible and definitely --
DEFINTELY with bind variables).  Then simply query:

select *
  from document
 where (documentid,versionnumber) in (select docid,version from
t );

If you do not want to use the temporary table -- I would still
use an IN but might do it like this:

ops$tkyte@ORA8I.WORLD> create or replace type myScalarType as
object
  2  ( docid number, version number )
  3  /

Type created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> create or replace type myTableType
  2  as table of myScalarType
  3  /

Type created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> create or replace
  2  function in_list( p_string in varchar2 )
  3  return myTableType
  4  as
  5      l_string        long default p_string || '|';
  6      l_data          myTableType := myTableType();
  7      n                number;
  8      l_piece            varchar2(255);
  9  begin
 10      loop
 11          exit when l_string is null;
 12          n := instr( l_string, '|' );
 13          l_data.extend;
 14          l_piece := substr( l_string, 1, n-1 );
 15          l_string := substr( l_string, n+1 );
 16
 17          n := instr( l_piece,',');
 18          l_data(l_data.count) :=
 19              myScalarType( substr(l_piece,1,n-1),
 20                            substr(l_piece,n+1) );
 21      end loop;
 22
 23      return l_data;
 24  end;
 25  /

Function created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> drop table document;

Table dropped.

ops$tkyte@ORA8I.WORLD> create table document ( docid number,
version number );

Table created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> insert into document values ( 1, 1 );

1 row created.

ops$tkyte@ORA8I.WORLD> insert into document values ( 2, 2 );

1 row created.

ops$tkyte@ORA8I.WORLD> insert into document values ( 3, 3 );

1 row created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> variable str varchar2(4000);
ops$tkyte@ORA8I.WORLD> exec :str := '1,1|3,3'

PL/SQL procedure successfully completed.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> select *
  2    from document
  3   where (docid,version) in
  4             ( select *
  5                 from TABLE(cast (in_list(:str) as
myTableType)
  6                           )
  7             )
  8  /

     DOCID    VERSION
---------- ----------
         1          1
         3          3

ops$tkyte@ORA8I.WORLD>
Review & Followup
Rating: 4
Compare with respect to overheads July 02, 2002
Reviewer: SK from US
Hi Tom,

1. How do these two process (using temporary tables or using table array)
compares with respect to overheads? I am using global temporary table in my
function.

2. I have one partioned table having more than 500 million rows. I have a non
unique index on it. Some time this could happen that it may have 10K rows for
the same keys in the table. I have analyzed this table as well.

SQL>create table t (
st varchar2(2) not null,
c1 varchar2(3) not null,
c2 number)
partition by list (st) (
    partition r1 values ('NY') tablespace tblsp1,
    partition r1 values ('NJ') tablespace tblsp2,
    partition r1 values ('MA') tablespace tblsp3,
    partition r1 values ('TX') tablespace tblsp4
) parallel nologging;

Loaded the table with data (500 million rows).

SQL>create index t_idx on t(st,c1)
local (
partition r1 tablespace tblspidx1,
partition r2 tablespace tblspidx2,
partition r3 tablespace tblspidx3,
partition r4 tablespace tblspidx4
) parallel nologging;

SQL>Analyze table t compute statistics
    for all indexes for all indexed columns for table;

SQL>set autotrace traceonly;
SQL>select c2 from t where st='NY' and c1='ABC';
1917 rows selected.

Elapsed: 00:00:35.01

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=4337 Card=29192 Bytes=525456)
   1    0   TABLE ACCESS* (FULL) OF 'T' (Cost=4337 Card=29192 Bytes=525456)
:Q338000

   1 PARALLEL_TO_SERIAL            SELECT /*+ NO_EXPAND ROWID(A1) */ A1."C2"
                                   FROM "T" PX_GRANULE(0, BLOCK_RANGE, DYNAMIC)
A1 WHERE A1."C1"='ABC'

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
     180272  consistent gets
      90170  physical reads
          0  redo size
      30827  bytes sent via SQL*Net to client
       1896  bytes received via SQL*Net from client
        129  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
       1917  rows processed

SQL> select /*+ index(t t_idx)*/ c2 from t where st='NY' and c1 = 'ABC';

1917 rows selected.

Elapsed: 00:00:13.03

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=24997 Card=29192 Bytes=525456)
   1    0   TABLE ACCESS (BY LOCAL INDEX ROWID) OF 'T' (Cost=24997 Card=29192
           Bytes=525456)

   2    1     INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE) (Cost=76 Card=29192)

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       3912  consistent gets
       1827  physical reads
     109260  redo size
      30827  bytes sent via SQL*Net to client
       1896  bytes received via SQL*Net from client
        129  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       1917  rows processed

First query does full table scan, has less cost, takes longer to execute. Second
query uses hint, has higher cost but executes faster. I certainly agree with you
that lower cost does not makes the query faster.

Why first query did not use index? It had only 1917 rows matched out of 32
million rows (0.006%). Explain plan also does not show any partition
elimination. Which other method (index or hint) will make these query even
faster?

Thanks for your valuable service.

Regards

Followup:
1) benchmark it! it is what I would do (i would use the array for smallish
things, the table for bigger things)

2) question not relevant to existing question -- interesting but not relevant.
I'll wait till I see it in the regular queue...

July 03, 2002 steve -- Thanks for the question regarding "recieving email into the batabase", version 8.1.7
You Asked
I would really like to see a coded example of reading email into the database (
from the java package), I am using your send email package( java from sun), and
find it both reliable and consistent.
 I understand the basics of what is needed, but just cannot get my head round
it, i would also suspect there are a large number of other people looking for
the same.

thanks
and we said...
Do you code java?

Have you ever used the Java MAIL api's

If the answer is yes to both, this should be trivial for you.  You open a
connection to the server (email server), you interrogate it for new mail, you
ask it for new mail, you recieve the mail.

If the answer is no to either -- then you have some work to do.  You either need
to learn or find a java programmer or you need to study and learn the java mail
API's.

I cannot just write every piece of interesting code in the world.  This is
really upto you at this point to develop your application.  You need to figure
out

o what database tables would I like to use to store this email (design your
structures)

o how will I handle things like storing the username/password securely in the
database so I can access this email server "unattended"

and so on.  You have an application build - I'll be glad to answer specific
questions -- but write it?  no.
Review & Followup
Rating: 2
July 02, 2002
Reviewer: A reader
sorry to waste your time,
no i don't code java, but i assumed that as you had a send mail example  between
 sql & java, you might actually have the  code to recieve mail.

Followup:
Nope, we use the Oracle email server -- all of my email has always been in a
database.  I've never had a need to read it into yet another one.

July 03, 2002 Alex -- Thanks for the question regarding "Books", version 8.1.7
You Asked
Hi Tom,

Can you recommend some DBA pocket book? A book the DBA can use as a good and
quick reference. I am looking for a PL/SQL pocket book too. Maybe one that has
the most important and useful packages that every Developer/DBA need/have to
use.

Thank you very much!

Regards,
Alex
and we said...
Sorry I cannot -- I'll publish this question so that others may comment.

Personally -- I find I need the SQL reference manual, Admin Guide, supplied
packages guide and other documents frequently.  Hence, I tend to access the HTML
or PDF's on the web (or locally, even PC i own has them on them) whenever I
need.  I don't have any paper documents beyond

o my books
o Jonathan Lewis's book

;)  otherwise, my office is quite bereft of paper.
Review & Followup
Rating: 3
DBA books July 03, 2002
Reviewer: Peter Hooiveld from The Netherlands
Steve Adams, Oracle 8i Internals, has been quite useful to me.

Rating: 2
PL/SQL pocket books July 03, 2002
Reviewer: Scott from Chicago, IL, USA
When I was in consulting I always had the pocket versions of Steven Feuerstein's
books with me: "Oracle PL/SQL Programming" and "Oracle Built-in Packages".

Rating: 4
Oracle Documentation CD July 03, 2002
Reviewer: Randy from Chicago
I always carry Oracle Documentation CD wherever I go for consultancy. I also
made a few copies of it and kept them in sites where I frequently visit.

July 03, 2002 Freek -- Thanks for the question regarding "business rule; no inserts after a certain value has been inserted", version 8.1.7
You Asked
I have a table order_status

Create table orderstatus
(
    orderid number(5,0),
    status number(5,0),
    date date
)
/

On this table I need to implement a business rule that says that after a certain
status has been inserted for an orderid, no other statusses can be inserted
anymore for that order.

e.g.

insert into orderstatus(orderid, status, date)
values (1,2,sysdate)
/

-- here I insert a "final" status, no other statusses can be inserted for order
1
insert into orderstatus(orderid, status,date)
values(1,3, sysdate)
/

--so, this should give an error
insert into orderstatus(orderid, status, date)
values(1,1,sysdate)
/

I did this by using your example at
http://asktom.oracle.com/pls/ask/f?p=4950:8:997834
I put the call to dbms_lock in the after insert for each row trigger, to be able
to use the orderid as the lock id as this will put the lock on the level off the
order (don't block an insert for another order)

Is this the best way to implement the required business logic?

How do I avoid that using the same logic on a second table, the lock requests
won't interfere with each other (since the lock id might be the same)?
and we said...
I don't see the tie in between the referenced link and this problem but...

I might try this approach instead:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table orderstatus ( orderid number,
status number, dt date );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table closed_orders (orderid number
primary key) organization index;
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger orderstatus_trigger
  2  after insert or update of status on orderstatus for each row
  3  begin
  4          insert into closed_orders (orderid) values ( :new.orderid );
  5          if ( :new.status <> 2 )
  6          then
  7                  delete from closed_orders where orderid = :new.orderid;
  8          end if;
  9  end;
 10  /
Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into orderstatus values ( 1, 1,
sysdate );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;
Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into orderstatus values ( 1, 3,
sysdate );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;
Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into orderstatus values ( 1, 2,
sysdate );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;
Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into orderstatus values ( 1, 1,
sysdate );
insert into orderstatus values ( 1, 1, sysdate )
*
ERROR at line 1:
ORA-00001: unique constraint (OPS$TKYTE.SYS_IOT_TOP_41482) violated
ORA-06512: at "OPS$TKYTE.ORDERSTATUS_TRIGGER", line 2
ORA-04088: error during execution of trigger 'OPS$TKYTE.ORDERSTATUS_TRIGGER'

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into orderstatus values ( 1, 2,
sysdate );
insert into orderstatus values ( 1, 2, sysdate )
*
ERROR at line 1:
ORA-00001: unique constraint (OPS$TKYTE.SYS_IOT_TOP_41482) violated
ORA-06512: at "OPS$TKYTE.ORDERSTATUS_TRIGGER", line 2
ORA-04088: error during execution of trigger 'OPS$TKYTE.ORDERSTATUS_TRIGGER'

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into orderstatus values ( 1, 3,
sysdate );
insert into orderstatus values ( 1, 3, sysdate )
*
ERROR at line 1:
ORA-00001: unique constraint (OPS$TKYTE.SYS_IOT_TOP_41482) violated
ORA-06512: at "OPS$TKYTE.ORDERSTATUS_TRIGGER", line 2
ORA-04088: error during execution of trigger 'OPS$TKYTE.ORDERSTATUS_TRIGGER'

ops$tkyte@ORA817DEV.US.ORACLE.COM>

All sessions will still serialize at the ORDERID level (due to the insert) but
as soon as someone commits a '2' for an orderid -- the inserts will all start to
fail with dup val on index -- rolling back the modifications to orderstatus.
Review & Followup
Rating: 5
simple, but very useful July 03, 2002
Reviewer: Freek D'Hooge from Belgium
Looks likes there was a problem with the link, should be
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:474221407101
I put it there as information about the implementation.

anyway, your solution is much more simple then what I did.

Thanks!!!

July 03, 2002 Venkatasubramanian -- Thanks for the question regarding "character set, constraints, bind variables", version 8.1.7
You Asked
Dear Tom,

As I could not get a chance to post the questions in the last one week, I am
putting all my six questions now. please bear with me.

1. When we want to change the database character set from US7ASCII to UTF8, is
it better to change the national character set also  to UTF8 using 'alter
database' command? Kindly explain the pros and cons of changing the national
charater set also alongwith database character set.

2. UTF8 being the superset, is it always better to create the databases with
UTF8 character set to avoid any language support problem at a later date when
needed? What is your suggestion?

3. When US7ASCII is adequate, going for UTF8 will create any potential problem?

4. When direct path export is made, character set of user session and the
database should be the same. Can you give an example as when this may or needs
to be different?

5. will reparsing take place if there are white space difference between two
identical SQL statements as shown below:

select object_name,status from user_objects where object_type='&type';
select   object_name,   status from   user_objects where object_type  = '&type';

6. We have the following situation.

Table A                Table B

col1- primary key        col2- foreign key
                referring col1 in Table A.

col1-foreign key         col2- primary key
referring col2 in Table B

In the above scenario, how to enter data in the above tables through
application. Can you pl. help me out?

Thanks and Regards,
Venkat
and we said...
1) do you even use the national character set?  If you do and you need to change
it, by all means, change it at the same time.  If you do not, ignore it.   If
you have UTF8, it would seem that the need for the national character set
support sort of "disappears"

2) There are inefficiencies in UTF8.  It is a variable length encoding scheme.
You will find that stored procedures and other string intensive operations may
be adversely impacted by the use of UTF8.  So no, I do not "blanket recommend"
using UTF8 -- only when you have a need.

3) no, been there, done that

4) No, I cannot.  I do not know of a time when it would need to be different.
Import can do the translation if translation is needed just as well as export
could

5) yes.

6) Here are two ways:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table a ( x int primary key, y int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table b ( m int primary key, n int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table a
  2  add constraint a_fk_b
  3  foreign key(y)
  4  references b(m) deferrable;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table b
  2  add constraint b_fk_a
  3  foreign key(n)
  4  references a(x) deferrable;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into a values ( 1, null );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into b values ( -1, 1 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> update a set y = -1 where x = 1;

1 row updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set constraints all deferred;

Constraint set.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into a values ( 2, -2 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into b values ( -2, 2 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review & Followup
Rating: 4
chracter set July 03, 2002
Reviewer: Venkatasubramanian from Chennai, India
Tom,

Thank you very much for your immediate response. I find the information very
useful.

Thanks
Venkat

July 03, 2002 Pranav -- Thanks for the question regarding "Dynamic Array Binding", version 8.1.7.3
You Asked
Hi,
  I am trying to uplaod a file throug UTL_FILE.  I have a procedure that will
upload the file into a table.  I am trying to modify that procedure and use
FORALL and array binding to accomplish this.  Where I fail is in making the
FORALL dynamic.  I know how many columns will be used only at run-time when the
procedure is called.  Here is the code for the FORALL statement:

v_Table := Table1
v_Columns := 'Col1, Col2, Col3 ';
execute immediate
'begin
   forall i in 1.. :Max
     insert into v_Table ( :v_Columns )
     values (pkg.l_data(i));
 end;'
USING pkg.l_data.count, v_Table, v_Column;

Here l_data is defined as dbms_sql.varchar2_table in the package.
The data inside could be different everytime but in this case it would be 'Col1,
ol2, Col3' and so on.
Is there a way in which I can specify the table, columns and values to be
dynamic.

Thanks,
Pranav
and we said...
You'll be wanting to use DBMS_SQL with array binding for this.

The problems with your example are that you need to bind in each table -- but --
you don't know how many columns you have.  Native Dynamic SQL demands that you
know at COMPILE time how many binds you have -- you don't know until runtime how
many you'll have.

Thus you'll need to use dbms_sql and its procedural approach to this.

If you have my book --I've tons of examples in the chapter on dynamic sql and
even a mini-loader you can use in the sqlldr chapter.
July 03, 2002 Rich -- Thanks for the question regarding "Where can I find information on how to create a standby database ?? ", version 8.1.7
You Asked
Is there a white paper or some other documentation explaining the steps
necessary to implement a standby database ??  This is new territory for me and I
am not finding anything except cursory discussions about it.
and we said...
I'm curious because this came up more then once this week -- so I'll ask
everyone.

Is the documentation really that truly hard to navigate OR
Is the documentation really that hard to find OR
Don't you know where to even look to find the documentation

(I'm not beating up on you on this request here, it just must be that one of the
above is true?)
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/index.htm
has the
Oracle8i Standby Database Concepts and Administration

with covers this in detail (there is one for 9iR1 and 9iR2 in their respective
sections as well)
Review & Followup
Rating: 5
Maybe this is why? July 03, 2002
Reviewer: Tom Best from Exton, PA
Tom:

If people don't expect or know that (or where) docs are online, they'd probably
go to www.oracle.com and look around.  They would not know to click on "Oracle
Technology Network".  If they DID guess that link, then that asks them to
register, and they may not be willing to expend that amound of energy to
register because they're not really sure the docs will be there even if they do.
 I bail on stuff at that point sometimes unless I'm certain it has what I want.

People have also become very "ask the internet"-centric... meaning they
"scrounge around" for ad hoc information (newsgroups, search engines, etc) from
piers sometimes first, and forget to go to the source.  Even if they know the
docs are there, they are after a quick and specific answer to *their* question.
Manuals do not give that - you have to digest more and understand concepts and
then apply them to your problem.  But finding someone out there who's already
answered your question is more straightforward.

Just some thoughts.

Tom B

Rating: 5
RE: Documentation July 03, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
To perform any of the tasks that Tom answers, one must have as a minimum the
Oracle Server, right?  The documentation CD ships right there in the media pack
with the Server CD.  Now, if someone just downloads the media from OTN and
doesn't have a media pack, that means that they do have an OTN account, and the
documentation link is right there on the left-hand side of the download page.

So that leaves us with people who are in a "corporate environment" where they
neither acquired the media pack or downloaded the media themseleves.  This is
what I do:

I place all the documentation on our intranet server so that is it available to
anyone inside the company.  I don't know of any DBA that keeps the documentation
secret...  and if they do, shame on them.

So, there's my $0.02...

- Mark

Rating: 3
Simple July 04, 2002
Reviewer: Akthar from Sun Micro-Singpore
If folks try to search in any search engines available for 'oracle standby
database' string ,I bet they will return with 100+ results.

July 03, 2002 Abel -- Thanks for the question regarding "Using DBMS_JOB timestamp or extract date in procedure as a constraint", version 8.1/7
You Asked
Thanks in advance.
I've searched your knowledge base but could not find a solution. How do you use
the DBMS_JOB timestamp or an extract date in a procedure. I am trying to pull
all data that was entered or modified after an extract. I am using a create and
modify field that gives me the data I need but need to incorporate the timestamp
of an extract. eg. select emp_id,
decode(modify_date,null,create_date,modify_date) as Trans_Date from <table>
where modify_date >= [the previous dbms_job timestamp or OS scheduler
timestamp]. Is there a log that is created that contains the timestamp or in a
system table?
I hope I have clearly explained my problem. Thank you Tom.
and we said...
DBMS_JOB doesn't keep a timestamp -- but there is nothing preventing YOU from
doing so.  It is YOU that should be keeping a log of successful refreshes so
that you know what rows to pull.

Something like:

procedure p
is
  l_date DATE := sysdate;
  l_last_date DATE;
begin
  select nvl( max(last_date), sysdate-1 ) into l_last_date from my_log_table;

  for x in ( select * from T where modify_date between l_last_date and l_date )
  loop
      ......
  end loop;

  update my_log_table set last_date = l_date;
  if ( sql%rowcount = 0 ) then insert into my_log_table values ( l_date );
  end if;
  commit;
end;

and that'll do it.  BEWARE -- timestamps are only good down to the second -- you
might pull a row(s) twice due to that !!!
July 03, 2002 Sajid -- Thanks for the question regarding "HIT RATIO", version 8.1.7
You Asked
Hi Tom,

I have 2 questions this time for you.

1) What is HIT RATIO and how it effects the performance of a query?
2) Is it possible to write simple SELECT statement using only built-in functions
to generate the factorial of a given number? If yes, then please share with us,
and if not, then please say so. Because i had argument with my colleague. So i
need confirmation from you.

Thanks,
and we said...
1) a hit ratio is generally a "cache hit ratio".  it is a ratio of how many
blocks required PHYSICAL READS vs how many blocks were needed to process the
query.

In "general", a higher cache hit may imply better performance then a lower cache
hit since a cache hit is RAM vs disk.  "IN GENERAL", it is not always true.

2) It is possible to write a factorial using only builtins.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select exp(sum(ln(rownum)))
  2  from all_objects
  3  where rownum <= 10;

EXP(SUM(LN(ROWNUM)))
--------------------
             3628800

ops$tkyte@ORA817DEV.US.ORACLE.COM> select 10*9*8*7*6*5*4*3*2*1 from dual;

10*9*8*7*6*5*4*3*2*1
--------------------
             3628800

ops$tkyte@ORA817DEV.US.ORACLE.COM>

shows two trivial ways to do it.
Review & Followup
Rating: 5
thanks a million July 03, 2002
Reviewer: Sajid Anwar from London

Rating: 5
Thanks a million July 03, 2002
Reviewer: Sajid Anwar from London

Rating: 5
Nice select! July 03, 2002
Reviewer: chuck jolley from OKC, OK USA
1) What could cause it not to be true?

Followup:
For example, sometimes a parallel query will do some checkpointing to get the
blocks flushed to disk as of at least a point in time so that the rest of the
process knows "just full scan these segments, don't bother with that there
buffer cache -- just read and process ASAP".  When dealing with tons of stuff,
searching a big buffer cache can be overhead as opposed to large sequential
IO's.

A high cache hit is not a sign of "we've done good" in itself.  It is one of
many possible metrics that when looked at in a holostic way (with lots of other
stuff) can tell you how you are doing.

July 04, 2002 Tony -- Thanks for the question regarding "General Question", version 8.1.7
You Asked
Hi Tom,
This is general question, and you may or may not need to answer.
The quickness in response with level of accuracy, with different database
versions, complex and tricky questions, variety in various areas forces me to
think if you give can give some tips to community, how they can atleast try to
reach at this level of proficiency???

Regards,
and we said...
Experience.  Exploit the product, learn it, use it.  Do it every day for many
years.

One of the ways in which I became really good at this was hanging out in the
newsgroups.  Find the interesting question that makes you go "hmmm".  Research
it, answer it, post it.  Defend the answer (or acknowledge a mistake).  Learn
from it and go onto the next question.

Always be willing to rethink the same question with each dot release also.  What
was true in 7.x won't apply in 8.x necessarily.  Don't assume that "things
should work in a certain way" (this is a common trap people fall into) -- things
work the way they work, not the way we think or feel they should work.
Review & Followup
Rating: 5
July 02, 2002
Reviewer: Robert from Memphis, USA
Thanks!

Rating: 3
What about for DBA? July 02, 2002
Reviewer: Robert from Memphis
Tom,

Can you elaborate on the above question with specific suggestions geared at
becoming an expert DBA?

For example, should one keep a private test database on which to experiment with
different things? (Of course the private test database might be much smaller
than a 'live' database).

Do you test a theory from all angles, trying to 'break' it, and if it survives
your testing, then accept it as 'fact' (for a particular version)?

If you had only a single Oracle manual (as a DBA) would it be the Concepts
manual?

What are the 3 most important areas for a DBA to be an *expert* ?

Thanks for any detailed elaboration.

Robert.

Followup:
I think everyone should have their own test database -- dba, developer,
whatever.  something you can tear and and put back up whenever.

I think Jonathan Lewis said it best in his book (that would be a very good book
to have by the way -- Practical Oracle8i).  Test to destruction.

How to "prove" a theory is hard as there are many different ways to prove
somethings and fewer ways for others.  Sometimes a simple example is all that is
needed, other times more.  It dependes.

The concepts manual would be a good choice, yes.  I might go for the SQL
reference tho.

The last part, the 3 things, I'll pass on that.

Rating: 5
July 02, 2002
Reviewer: A reader
Hi Tom,

Do you think that all DBAs should have the certification although they have 2 or
3 years of experience as an Oracle DBA?

Thanks for your invaluable help

Followup:
Certification is in the eye of the beholder.  I've met "certified" people who

a) knew exactly what they were doing
b) didn't have a clue

Rating: 4
Thanks a lot! July 03, 2002
Reviewer: Tony
Tom,
It was really appreciated. But not many people are lucky to work in many areas.
One is not allowed to be given a chance in specific area as one is not
experienced in that, so how to get experience, really a cash 22 situation.
Moreover, reading, understanding and then applying more than 20,000 pages of
Oracle manual + understanding OS, understanding application, meeting business
deliverable deadlines etc., involvement in users groups,  and all this with the
short life of a Oracle version, looks to be very difficult...

Rating: 3
July 03, 2002
Reviewer: A reader
tom
are you ocp

Followup:
no, I have never taken the tests.

July 04, 2002 Venkatasubramanian -- Thanks for the question regarding "blocks, cost of the query", version 8.1.7
You Asked
Dear Tom,

1. How to find the no. of blocks visited during an index scan and a full table
scan? Can you give me a query for this?

2. How to define the cost of the query? Pl. explain how the same is computed at
when using CBO?

Thanks and Regards
Venkat
and we said...
1)
http://asktom.oracle.com/~tkyte/tkprof.html
tkprof is my favorite tool for this, however, autotrace
http://asktom.oracle.com/~tkyte/article1/autotrace.html
works as well:

big_table@ORA817DEV.US.ORACLE.COM> set autotrace traceonly
big_table@ORA817DEV.US.ORACLE.COM> select * from big_table;

1459904 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=577 Card=1459904
Bytes=150370112)
   1    0   TABLE ACCESS (FULL) OF 'BIG_TABLE' (Cost=577 Card=1459904
Bytes=150370112)

Statistics
----------------------------------------------------------
          0  recursive calls
        105  db block gets
     116051  consistent gets
      20032  physical reads
          0  redo size
  185831456  bytes sent via SQL*Net to client
   10803611  bytes received via SQL*Net from client
      97328  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
    1459904  rows processed

big_table@ORA817DEV.US.ORACLE.COM> select * from big_table where id = 55;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1 Bytes=103)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'BIG_TABLE' (Cost=3 Card=1
Bytes=103)
   2    1     INDEX (UNIQUE SCAN) OF 'BIG_TABLE_PK' (UNIQUE) (Cost=2 Card=1)

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

2) see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:313416745628
for a lengthly discussion of this.
Review & Followup
Rating: 3
cost of the query July 04, 2002
Reviewer: Venkatasubramanian from Chennai, India
Tom,

Information is useful. After going through other related information provided by
you on this topic, the inference drawn is that cost cannot be the determining
factor during query tuning. Only the response time has to be given the
weightage. Am I right?

Added to the above, like to know the following:

is there any dependency between cost and response time?

can you give the elements involved by the optimizer in determining the cost?

If cost cannot be the primary consideration factor while query tuning, which
important values we should look for?

Regards,
Venkat

Followup:
a) "dependency between cost and response time"

One only needs look at the example in the link:

cost 577, response time 3:34
cost 1760, response time 2:41

big_table@ORA817DEV.US.ORACLE.COM> select * from big_table where exists ( select

null from big_table bt2 where bt2.id = big_table.id );

1459904 rows selected.

Elapsed: 00:03:34.90

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=577 Card=72996 Bytes=7518588)
   1    0   FILTER
   2    1     TABLE ACCESS (FULL) OF 'BIG_TABLE' (Cost=577 Card=72996
Bytes=7518588)
   3    1     INDEX (UNIQUE SCAN) OF 'BIG_TABLE_PK' (UNIQUE) (Cost=2 Card=1
Bytes=5)

big_table@ORA817DEV.US.ORACLE.COM> select * from big_table where id in ( select
id from big_table );

1459904 rows selected.

Elapsed: 00:02:41.62

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1760 Card=1459904
Bytes=157669632)
   1    0   HASH JOIN (Cost=1760 Card=1459904 Bytes=157669632)
   2    1     INDEX (FAST FULL SCAN) OF 'BIG_TABLE_PK' (UNIQUE) (Cost=90
Card=1459904 Bytes=7299520)
   3    1     TABLE ACCESS (FULL) OF 'BIG_TABLE' (Cost=577 Card=1459904
Bytes=150370112)

and draw their own conclusions ( i would (and have) say adamantly NO, you
cannot)

b) the elements -- vary by release, by version -- but there is a good write up
on the workings of the optimizer in the performance guide.
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76992/toc.htm

Actual empirical observations are what I look for, what else matters?  If a
piece of software says "this will take 5 seconds to run" -- do you believe it?
(i don't).

July 04, 2002 Nick -- Thanks for the question regarding "Join Tables", version 8.1.7
You Asked
We have an existing application that uses an 8.1.7 database. Our company has
hired a consulting firm to design and develop major enhancements for our
application. We will also migrate to 9.2 when we roll out the enhancements, but
I'm not sure the version is important to my question.

The consultants want to use a join table when there are many to many
relationships between 2 tables. The join table would have the primary keys of
both the other tables as the data to represent the relationship.

They also want to use exactly the same concept to represent 1 to many
(parent/children) relationships. They say that having a third table has many
benefits when using todays programming tools.

To me it seems that having to maintain three tables and their indexes, rather
that just 2, is unnecessary, and adds overhead.

Is the use of join tables in this fashion considered appropriate these days? We
have gotten along just fine without them in the past, but maybe things have
changed. I will admit that I learned most of my application development skills
in the days of Oracle 5 and Fortran 77.

What do you see as the pros and cons to the use of join tables in this fashion?
and we said...
For many to many, you have to use an association table as they are known.  This
is standard.

For a one to many -- they are "smoking something funny".

Not only does it suffer from the problem you describe, but, it makes it
infinitely harder to maintain that 1 to many relationship -- you need yet
another additional unique index on the association table in order to enforce the
one'ness.

You incurr additional IO overhead in each and every join as well -- as much as:

a) n IO's to index access association table
b) 1 IO to read association table

You could be adding 2-4 IO's nominally to each row joined.  For no reason.

I've never heard of anyone using this as a standard operating procedure before
in my life.  It sounds like a really bad idea.  I know of no tool that would be
easier to use with this construct.  Tell them to THINK ABOUT IT -- there is a
1:1 relationship and a M:1 relationship going on here.  They still have a 1:M to
deal with -- if 1:M relations were difficult in their tool - well, they still
have the 1:M relationship to deal with!
Review & Followup
Rating: 4
July 03, 2002
Reviewer: A reader
Tom, Could you please explain more about the concept of "association tables". Is
this something new in 9i? I read about bitmap join indexes in 9i. Is it anyway
related to that?

Thanks a lot for your enlightening answers!

Followup:
They are a data modeling thing -- as old as relational databases (at least).

Suppose you have a model:

CLASS ( class_id primary key, description varchar2(25), room number, time date
);
STUDENT( student_id primary key, name varchar2, grade number );

A CLASS has many STUDENTS, hence you will need also:

CLASS_ASSIGNMENTS ( class_id references class, student_id references student,
primary key(class_id,student_id) );

CLASS_ASSIGNMENTS is an association object, it relates students to classes and
vice versa.

Rating: 5
Direct and to the point as usual July 03, 2002
Reviewer: Nick from Richmond, VA USA
Thanks for the comments.

Rating: 5
Can u explain the IO concerned ? July 03, 2002
Reviewer: KENNY from HK
Tom, I am interested in the IO overhead you concerned.
Can u explain more details why there are :
a) n IO's to index access association table
b) 1 IO to read association table

How IO is calculated ?

Thank You !

Followup:
An index is a data structure.  There is a root block, interior branch blocks and
leaf nodes.

Lead nodes are where the data of interest to you is -- that has the KEYS and the
ROWIDS of the rows in the tables.

In order to get to a leaf node, you need to walk the tree from the root, through
the branches to a leaf node. Typically this takes (nominally) 2 or 3 IO's --
root+branch+leaf.

IO is calculated as the number of blocks you needed to read to get the data.

Rating: 5
July 03, 2002
Reviewer: A reader

Rating: 3
Overcomes adding an FK column to existing table July 03, 2002
Reviewer: andrew from ca, usa
The technique can used where the aim is to add a parent relationship to an
existing table without changing that table's structure (to limit impact to
existing the app). Rather than adding an FK column to the existing table - a M:M
relationship is created but you only use it as a 1:M. You enforce that by
maintaining a 1:1 between the existing table and the new resolution table (by
using a unique constraint on the FK end of that relationship.

existing        new         parent
+------+     +-------+     +-----+
|      | 1:1 |       | M:1 |     |
|      |-----|       |>----|     |
|      |     |       |     |     |
+------+     +-------+     +-----+

It's a pain to work with and makes the ERD difficult to understand.

Rating: 5
Bite the bullet!!! July 03, 2002
Reviewer: chuck jolley from OKC, OK USA
Adding columns can break some kinds of PowerBuilder code.
But it is MUCH less effort, long term, to fix the code than to create the join
table and complicate things for the rest of time.

Rating: 3
Why not a view? July 03, 2002
Reviewer: Cory D. Wittenborn from Chicago, IL USA
If adding a column somehow breaks PowerBuilder or some other tool, why not just
have PB reference a view that excludes the column?  Then you can expose the
column to those pieces of SQL that need to see it (either they look at the table
itself or another view), and "fragile" code can simply look at the view.  If the
idea of changing the name that the code is looking at is troublesome, create
synonyms for all of your objects in an application schema (a la what Oracle Apps
does in the APPS schema)--you can create a synonym for the new view that has the
same name as the original table, and a synonym to the modified table (or to a
view that includes that additional column) with a different name.  Then just
have the app login to the new schema--no code change needed for existing code
(less, maybe, login stuff that should be in params anyway) and the new code is,
of course, new code.

Just a thought...

Followup:
And a correct thought at that.  That is what views are all about...

Rating: 2
Attributes to the relationship July 04, 2002
Reviewer: sdwarka
How would you model a relationship that has attributes? If for any reason we
have to add / remove attributes from the relationship, the parent or child
tables would not be affected because by using a join table we can cleanly model
relationships that have attributes. (Ref. Database System Concepts, Korth and
Silberschatz, II ed., chapter 2).

Followup:
So, add/remove columns to the parent or child table as needed.

VIEWS are the physical constructs that allow you to do this without impacting
existing applications. Although, truth be told -- existing applications that
are impacted by the addition of a non-mandatory/defaulted column are BROKEN way
before you add the column!!! That means they are doing a "select *" -- not
"select c1, c2, c3" or insert into t values ( ... and not "insert into t ( c1,
c2, c3 ) values (...."  They are relying on the physical order and number of
columns to be never changing.  Thats a bug waiting to happen.  You should be
able to add a column (that allows NULLS or has a default) without IMPACTING any
application.

Now, removing a column -- that will affect some applications that use that
column REGARDLESS of what table you put it into -- won't it (yes, rhetorical
question))

Also, remember, you can LOGICALLY model this however you like.  If you like ERDs
with association objects for every relation, go for it. Physically, they should
be in the table when you have a 1:m relationship.  The physical doesn't have to
be the same as the logical model.

There is a huge difference between a LOGICAL MODEL and a PHYSICAL MODEL.

warning: opinion coming up...

I also find for pragmatic reasons that people who over analyze what is a fairly
simple problem (like going for the 100th normal form or something like that)
spend far far too much time "modeling" and not enough time "doing".

July 04, 2002 Ivan -- Thanks for the question regarding "IOTs", version 8.1.6
You Asked
I tried to find any serious information/discussion about storage clause for
IOTs, but on Metalink and documentation it is very poor. Even on your site only
few issues but without any discussion about storage parameters.
How you finding this definition:
CREATE TABLE WAR_PARTS_TEXT
(
    COUNTRY_CODE            VARCHAR2( 5),
    PART_NO                 VARCHAR2(24),
    LANGUAGE_CD             VARCHAR2( 2),
    TEXT                    VARCHAR2(25),
    MPCI_CREATE_DATE        DATE,
    MPCI_UPDATE_DATE        DATE,
    CONSTRAINT WAR_PARTS_TEXT_PK PRIMARY KEY (PART_NO,
                                              LANGUAGE_CD)
)
ORGANIZATION INDEX
TABLESPACE  &INDEX_TABLESPACE_BIG.
PCTFREE     10
INITRANS     2
MAXTRANS   255
STORAGE
(
    INITIAL     &EXTENT_SIZE_BIG.
    NEXT        &EXTENT_SIZE_BIG.
    MINEXTENTS  1
    MAXEXTENTS  UNLIMITED
    PCTINCREASE 0
);
What is with bug for INITRANS =1 ?

Ok it is fine for static lookup tables.  But how much static ?

What when 4 million records consult 250.000 big table with descriptions, or
more. IOTs or denormalization ?
What to put in storage parameters.

Comment please

Nice regards
and we said...
Well, I don't see why there would be any serious discussion for initial, next,
minextents, maxextents, pctincrease for anything anymore.

Use Locally managed tablespaces with uniform extent sizes.  Segegrate objects by
"small", "medium", "large" and "extra large".  Don't worry about objects with
100 or more extents -- that is OK, normal, to be expected, just fine (even
1000!)

If you have my book, I do discuss the IMPORTANT things for IOTS such as the
overflow segment.  That is relevant, the ones you ask about -- not.  If you use
dictionary managed extents (yuck), just set initial=next, maxextents=unlimted
and pctincrease=0 to simulate a LMT.

As for "bug for initrans=1", you'll have to be more specific.

The rest of the question from there on in just doesn't make sense to me.  I
don't know what you are asking there.
Review & Followup
Rating: 5
More concrete June 30, 2002
Reviewer: Ivan Korac from Ulm, Germany
I am sorry I was not precise enough. You are top oracle but you can’t predict
what is not said. I’ll try to be more clear:
our conventional lookup tables have from quarter of million till few millions
records and I thought to use IOTs for better performances. As I understand IOT
closes to index I am worried about behaviour of table (balancing of big threes
and other issues). That is a reason I am trying to find appropriate definition
for.

when I said: how much static ? I meant following:
we are every week supplied with the entire amount of lookup records.
-    will I upsert all records ?
-    or to disabled fks , truncate IOTs, insert whole amount and enabled fks
once again ?
-    or somehow to recognize incremental part and to upsert only it

tricky is that in our fact tables are added (redundant) text fields from lookups
(on default language). That is mean refreshing of facts is necessary. What I am
writing now and I am very unhappy with, is:

cursor c_default_language_il is
   select construction_group, text
   from   il_w_const_group_text
   where  language_cd = g_language_code
   minus
   select construction_group, text
   from   w_const_group_text
   where  language_cd = g_language_code;

cursor c_il is
select *
from   il_w_const_group_text
where (construction_group,language_cd, text)
   in (
       select construction_group,language_cd, text
       from   il_w_const_group_text
       minus
       select construction_group,language_cd, text
       from   w_const_group_text
      );

I loop first cursor in intention to refresh facts (with incremental part of
changes):
        open c_default_language_il
        . . .
         update w_fact
            set construction_group_text = l_rec_il.text,
                 update_date        = sysdate
         where construction_group = l_rec_il.construction_group
         and   language_cd              = g_language_code;

and than i loop second cursor to implement changes in lookup table.
        open c_il
        . . .
   insert into w_const_group_text (
      country_code      ,
      construction_group,
      language_cd       ,
      text              ,
      create_date  ,
      update_date)
   values(
      p_rec_sl.country_code      ,
      p_rec_sl.construction_group,
      p_rec_sl.language_cd       ,
      p_rec_sl.text              ,
      p_rec_sl.create_date  ,
      p_rec_sl.update_date);
exception
   when dup_val_on_index
   then
      l_replog.statement := 'update w_const_group_text from
il_w_const_group_text';
      update w_const_group_text
      set
         text              = p_rec_sl.text            ,
         mpci_update_date  = p_rec_sl.mpci_update_date
      where  construction_group = p_rec_sl.construction_group
      and    language_cd        = p_rec_sl.language_cd;
   when others
    . . .

As your ideas were always of great help I hope you could suggest improvements on
this algorithm, I am very very unsatisfied.

------------
As usual on this job, a lot of things are inherited, so I am working in
environment with the dictionary managed extents.
-------------
Trying to find some examples or recommendations I met with some amount of bugs
and one of was:
Bug:788100
----------
When trying to create a index organized table with an INITRANS = 1 returns
ORA-00600.
Workaround: INITRANS of 1 is invalid. Set INITRANS to a value of TWO (2) or
greater.

With great respect

Ivan

Followup:
Well, I don't understand why you do a minus and an update in a loop when you
should just be able to update the data.  But -- not having a good understanding
of all of your tables, what your staging tables are and such (eg: i have no idea
why you minus w_const_group_text from il_w_const_group_text in order to update
w_fact).  Not fully understanding your logic and needs -- I cannot comment on
that.

Same with the insert.  Unless there are dups in il_w_const_group_text, I don't
see how you get can a dup val on index in your insert.  And if you do - you
update but that means the contents of w_const_group_text is "in determinate"
(you can run the process two times and get different end results).  You have no
control over which "dup" will come second.  This logic looks all around "funny"
(the end result would be ambigous).  If there are no dups, just insert into
w_const_group_text select * from il_w_const_group_text where NOT EXISTS ( select
from w_const_group_text where ...).  That'll do it in a single statement.

Well, the recommandation for 788100 would be -- leave initrans as its default of
2 (which in 9i will be true for HEAP tables as well as indexes, even if you ask
for 1, it'll be 2) or set it higher.  Don't go lower.

Rating: 5
June 30, 2002
Reviewer: Ivan from Ulm, Germany
Tom,
I can not believe how your answer is prompt.

I’ll try simplify  as much as I can:
every week whole amount of  records  ( e.g. 3 million ids with descriptions)
are forward to us as ascii file.
Very small amount of records are new or with change description (1%)
Records are load in ILs (input layers) via SQL*Loader.

Tasks are
1 . to update SLs (storage layers = lookup IOTs)
2. to update (redundant) text description from lookup in facts tables.

My not good describe idea was:
-    via set operation to reach difference between IL and SL (mentioned 1%) for
default language
-    to open cursor for this 1 % and to update all fects text fields with equal
id (first cursor)
-    finally to update lookup (second cursor)

to me this sounds as good idea especially because amounts of records will
increase, and for facts even drastically.
That is what I am trying in my funny code:
 cursor c_il is
select *
from   il_w_const_group_text                                          - input
layer
where (construction_group,language_cd, text)               - key fields + text
   in (
       select construction_group,language_cd, text            - expected 1 % of
differ
       from   il_w_const_group_text
       minus
       select construction_group,language_cd, text
       from   w_const_group_text
      );

but from idea to proper solution is long and painful way,
any improvements is more than welcome

thank you very much for your time (here is Sunday ?! )

Ivan

Followup:
If you are going to load up the data anyway, why not just replace and be done
with it?  Seems much simplier doesn't it??

Rating: 3
present logic July 01, 2002
Reviewer: Ivan from Ulm, Germany
Hi Tom,

About present logic:

Table1 lookup :  id1 , description_id1

Table2  fact:   . . .   id1,  description_id1 . . .  (index on id1 exists)

Table 2 is incrementally loaded. Facts are sometimes delivered without master
records (not jet supplied in table1); when it happens: in table1 is inserted
omitted master record as:  id1, NULL (description_id1). When proper master
arrives, because of difference in (null / text) records in table2 with id1 have
to be refreshed.
I am supplied every week with whole amount of table1 (even more than 99% of
records are already in table1).

My question is: If I truncate IOT table1 and insert bulk of few millions
records, how IOT would behave? I expect big time consumption because all this
millions mess has to be packed in index in PK order.

Big weakness is that after replace, I cannot recognize subset of changed masters
(1%) and I have to refresh field description_id1 for complete table2 (dozens of
millions record).
My question is: do you see some clever algorithm how to improve this ? Could my
ideas with subsets be used and how?

Regards

Followup:
Truncating and filling the IOT will take less time then just inserting them.

I do not see a clever algorithm here, you cannot tell what records need to be
updated without inspecting each and every one -- hence, you'll be inspecting
each and every one.

Rating: 4
Thanks July 01, 2002
Reviewer: Ivan from Ulm, Germany
Dear Tom,
Thanks for your efforts, ideas and time.
It is always stimulating for us.

All best

Ivan

Rating: 5
One more clarification July 02, 2002
Reviewer: Ivan from Ulm, Germany
I plan to add field STATUS (0/1) to IOT lookup table via I will know that record
is upsert.
As I expect very very small amount of this records, has sense to build
additional bitmap index for STATUS in lookup ?
Because of logical rowids are bitmap indexes generally ok for implementing on
IOTs ?

Followup:
why do you want a bitmap index? what would you be doing this this field in order
to require one.  bitmaps have a HIGH penalty during DML.

Rating: 5
what organization is better ? July 02, 2002
Reviewer: Ivan from Ulm, Germany
I recognize small domain of upsert lookup’s records and differ it via status =1.
And for it I have to refresh facts.

for lookup IOT table (l), I thought: As fact tables are huge, can bitmap on
status field speedup:

update fact f
   set f.descreption_id1 = l.descreption_id1
where l.status = 1
and      l.id1 = f.id1

Do I need at all secondary (bitmap) index on IOT ?
Is for me better to use heap table organization for lookup ?

Regards

Rating: 5
strategy for ?denormalized fields? and lookups July 04, 2002
Reviewer: Ivan from Ulm, Germany
I add here complete strategy , please advice if some better approach is
possible:
Task:
- in bunch sent for lookup, recognizing of “incremental” part of ids,
implementing insert/update in lookup
- refreshing of corresponding texts in facts tables

proposal:
------------------------------------------------------------
1.  first update for existing ids in lookup (sl):

update ( select sl.text, il.text, sl.status, sl.mpci_update_date, il.load_date
         from sl, il
         where il.id          = sl.id
         and   il.language_cd = sl.language_cd
         and   il.text       != sl.text )
set   sl.text             = il.text     ,
      sl.status           = 1           ,
      sl.mpci_update_date = il.load_date;

------------------------------------------------------------
2. than recognizing, and insert of set of new ids in lookup (sl)

insert into sl (
   select *
   from il
   where NOT EXISTS (select null
                     from   sl
                     where  il.id          = sl.id
                     and    il.language_cd = sl.language_cd)
               );

------------------------------------------------------------
3. update of the facts

update ( select f.text, sl.text
         from fact f, lookup sl
         where sl.id          = f.id
         and   sl.language_cd = f.language_cd
         and   sl.status      = 1 )
set f.text = sl.text
/

----------------------------------------------------
4. finally update of the sl

update  lookup sl
   set  sl.status = 1
where sl.status = 0;

Followup:
Looks good -- just consider NOT IN in place of NOT EXISTS if you are using the
CBO (you have IOTS, must be) and the ID, LANGUAGE_CD is "not null".  If that is
true, it may use a hash anti join which in general is better then not exists
(iff IL is a large table of course, if IL is smallish, the not exists won't fire
too much)

Rating: 5
you make us better July 05, 2002
Reviewer: Ivan from Ulm, Germany
Thanks Tom,

As most of us, I have to deal with daily pressure, which not helps me to find
enough time and concentration to explain my points completely clearly and
precisely.

Big apology for my errors, spread and confusing questions and missing parts.

Please continue with your mission !

Regards

July 04, 2002 Yogeeraj -- Thanks for the question regarding "How should i join my tables in my queries?", version 8.17.4
You Asked
Hello,

I was analysing one of our TOP queries on our database and wanted to use that
for a training session with my collegue software developers.

Please help me understand the differences and clear up a few confusions.

A. Can the cost of the queries change with time?

B. I have two options for a join. Based on my execution plan and statistics how
to chose the best one? Why did the execution plan change significantly?
CASE 1:
=========
swprod@CMTDB.CMT.MU> r
  1  Select *
  2  From    cut100,ort140,ort120,ort130,ort100
  3  where   cut100.lcncod like nvl(:p1,'%')
  4  and     cut100.maklcn like nvl(:p2,'%')
  5  and     cut100.cutdate>=to_date(:p3,'dd/mm/yyyyhh24:mi')
  6  and     cut100.cutdate<=to_date(:p4,'dd/mm/yyyyhh24:mi')
  7  and     cut100.fabcat=:p5
  8  and     cut100.ordnum=ort100.ordnum
  9  and     ort130.ordnum=ort100.ordnum  <<<<<Modified in query below
 10  and     ort130.gmtseq=cut100.gmtseq
 11  and     ort140.ordnum=cut100.ordnum
 12  and     ort140.clrseq=cut100.clrseqg
 13  and     ort120.ordnum=ort100.ordnum
 14* and     ort120.fabseq=cut100.fabseq

8 rows selected.

Elapsed: 00:00:05.46

Execution Plan
__________________________________________________________
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=344 Card=7 Bytes=5026)
   1    0   NESTED LOOPS (Cost=344 Card=7 Bytes=5026)
   2    1     NESTED LOOPS (Cost=343 Card=1 Bytes=671)
   3    2       NESTED LOOPS (Cost=341 Card=2 Bytes=1240)
   4    3         HASH JOIN (Cost=190 Card=151 Bytes=72027)
   5    4           TABLE ACCESS (FULL) OF 'CUT100' (Cost=175 Card=1 Bytes=391)
   6    4           TABLE ACCESS (FULL) OF 'ORT130' (Cost=12 Card=15149
Bytes=1302814)
   7    3         TABLE ACCESS (BY INDEX ROWID) OF 'ORT100' (Cost=1 Card=22718
Bytes=3248674)
   8    7           INDEX (UNIQUE SCAN) OF 'PK_ORT100' (UNIQUE)
   9    2       TABLE ACCESS (BY INDEX ROWID) OF 'ORT120' (Cost=1 Card=74772
Bytes=3813372)
  10    9         INDEX (UNIQUE SCAN) OF 'PK_ORT120' (UNIQUE)
  11    1     TABLE ACCESS (BY INDEX ROWID) OF 'ORT140' (Cost=1 Card=68917
Bytes=3239099)
  12   11       INDEX (UNIQUE SCAN) OF 'PK_ORT140' (UNIQUE)

Statistics
__________________________________________________________
        890  recursive calls
         10  db block gets
     101042  consistent gets
       3022  physical reads
         64  redo size
      21250  bytes sent via SQL*Net to client
       1007  bytes received via SQL*Net from client
         10  SQL*Net roundtrips to/from client
          5  sorts (memory)
          0  sorts (disk)
          8  rows processed

CASE 2:
=========
swprod@CMTDB.CMT.MU> r
  1  Select  *
  2  From    cut100,ort100,ort140,ort120,ort130
  3  where   cut100.lcncod like nvl(:p1,'%')
  4  and     cut100.maklcn like nvl(:p2,'%')
  5  and     cut100.cutdate>=to_date(:p3,'dd/mm/yyyyhh24:mi')
  6  and     cut100.cutdate<=to_date(:p4,'dd/mm/yyyyhh24:mi')
  7  and     cut100.fabcat = :p5
  8  and     ort140.ordnum=cut100.ordnum
  9  and     ort140.clrseq=cut100.clrseqg
 10  and     ort130.ordnum=ort100.ordnum  <<<<<Different from above
 11  and     cut100.ordnum=ort120.ordnum
 12  and     cut100.fabseq=ort120.fabseq
 13  and     cut100.ordnum=ort130.ordnum  <<<<<Different from above
 14* and     cut100.gmtseq=ort130.gmtseq  <<<<<Different from above

8 rows selected.

Elapsed: 00:00:07.27

Execution Plan
__________________________________________________________
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=185 Card=52 Bytes=37336)
   1    0   NESTED LOOPS (Cost=185 Card=52 Bytes=37336)
   2    1     NESTED LOOPS (Cost=178 Card=7 Bytes=4669)
   3    2       NESTED LOOPS (Cost=177 Card=1 Bytes=620)
   4    3         NESTED LOOPS (Cost=176 Card=1 Bytes=477)
   5    4           TABLE ACCESS (FULL) OF 'CUT100' (Cost=175 Card=1 Bytes=391)
   6    4           TABLE ACCESS (BY INDEX ROWID) OF 'ORT130' (Cost=1 Card=15149
Bytes=1302814)
   7    6             INDEX (UNIQUE SCAN) OF 'PK_ORT130' (UNIQUE)
   8    3         TABLE ACCESS (BY INDEX ROWID) OF 'ORT100' (Cost=1 Card=22718
Bytes=3248674)
   9    8           INDEX (UNIQUE SCAN) OF 'PK_ORT100' (UNIQUE)
  10    2       TABLE ACCESS (BY INDEX ROWID) OF 'ORT140' (Cost=1 Card=68917
Bytes=3239099)
  11   10         INDEX (UNIQUE SCAN) OF 'PK_ORT140' (UNIQUE)
  12    1     TABLE ACCESS (BY INDEX ROWID) OF 'ORT120' (Cost=1 Card=74772
Bytes=3813372)
  13   12       INDEX (UNIQUE SCAN) OF 'PK_ORT120' (UNIQUE)

Statistics
__________________________________________________________
          0  recursive calls
          6  db block gets
       2984  consistent gets
       2864  physical reads
         64  redo size
      21250  bytes sent via SQL*Net to client
       1007  bytes received via SQL*Net from client
         10  SQL*Net roundtrips to/from client
          5  sorts (memory)
          0  sorts (disk)
          8  rows processed

swprod@CMTDB.CMT.MU>

C. Could you please explain the difference between the 2 queries below and how i
can dispel the confusion?
case 1:
=========
swprod@CMTDB.CMT.MU> r
  1  select lcncod,sum(pcscut)
  2  from cut100
  3  where cutdate between to_date('01/07/2002','dd/mm/yyyy')
  4  and        to_date('03/07/2002','dd/mm/yyyy')
  5  and        fabcat = 'F'
  6* group by lcncod

LCN SUM(PCSCUT)
___ ___________
LTK       68549
PHX       51691
VER       36374

Elapsed: 00:00:00.99
swprod@CMTDB.CMT.MU>

case 2:
=========
swprod@CMTDB.CMT.MU> r
  1  select lcncod,sum(pcscut)
  2  from cut100
  3  where trunc(cutdate) between to_date('01/07/2002','dd/mm/yyyy')
  4  and   to_date('03/07/2002','dd/mm/yyyy')
  5  and   fabcat = 'F'
  6* group by lcncod

LCN SUM(PCSCUT)
___ ___________
LTK      102448
PHX       84389
VER       50656

Elapsed: 00:00:03.97
swprod@CMTDB.CMT.MU>

===================================================================

Thank you very much for your time and patience.
(Sorry for being long ;))

Best Regards
Yogeeraj
and we said...
a) Yes, using the CBO, the cost of a query can change over time and it'll be
accompanied with a corresponding change in plan typically.  Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_stats.set_table_stats( user, 'T',
numrows => 100, numblks => 10 );

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=100 Bytes=1300)
   1    0   TABLE ACCESS (FULL) OF 'T' (Cost=1 Card=100 Bytes=1300)

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_stats.set_table_stats( user, 'T',
numrows => 10000, numblks => 1000 );

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=29 Card=10000 Bytes=130000)
   1    0   TABLE ACCESS (FULL) OF 'T' (Cost=29 Card=10000 Bytes=130000)

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_stats.set_table_stats( user, 'T',
numrows => 1000000, numblks => 100000 );

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2879 Card=1000000
Bytes=13000000)
   1    0   TABLE ACCESS (FULL) OF 'T' (Cost=2879 Card=1000000 Bytes=13000000)

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace of

As table T fills up, the cost of the query changes.  In many cases, we might see
a corresponding change in plan (eg: the full scan becomes too expensive for some
query and a index starts being used, or the skew of data changes and an index
stops being used and so on)

2) those queries are SO different as to not be comparable.  I would suspect they
are not even equivalent in general (eg: do not return the same ANSWERS).  You
are joining the same set of tables together -- using totally different criteria.
 I would expect the plans to be hugely different as they are night and day
different queries!
Review & Followup
Rating: 5
Clear explanation as usual. Thank you. July 04, 2002
Reviewer: Yogeeraj from Mauritius
Hello,
thank you a lot for the reply.

Very good reply to the first part of questions. Clear and simple explanation.

About my previous question (B):
I wanted to know whether joining of tables could be guided by a rule.

Suppose, i had the chance of joining a table, A to any of 2 other tables B and
C. Which one should i choose? What should be my decision based on?

Also, could you also help me understand the difference between two queries
involving date fields:
a. with a trunc in the predicate field
where trunc(cutdate) between to_date('01/07/2002','dd/mm/yyyy')  and
to_date('03/07/2002','dd/mm/yyyy')

b. no trunc in the predicate field
where cutdate between to_date('01/07/2002','dd/mm/yyyy')
and to_date('03/07/2002','dd/mm/yyyy')

again thanks a lot.

Best Regards
Yogeeraj

Followup:
Your decisions should be based on the data you want to get -- nothing more,
nothing less.

I don't understand the model that would let me pick what tables should be joined
to what other tables willy nilly like that.

In general, if I have A, B, C -- the method of joining is DICTATED by the
relationships I want to traverse.  I don't get to pick whether A should be
joined to B which is then joined to C "vs" A joined to B, A joined to C.  Those
are two entirely different queries.

As for the date field questions -- the ranges are totally different.

the dates you have are:

  01-Jul-2002 00:00:00  and 03-Jul-2002 00:00:00

Now, if cutdate = 03-Jul-2002 01:00:00  (1am), then cutdate is NOT between those
two dates.  But, the trunc(cutdate) = 03-Jul-2002 00:00:00 and hence it is
between the two dates.

Your query with trunc(cutdate) includes all values on 03-jul-2022, whereas the
one without trunc(cutdate) precludes most of those observations (only the ones
that happened at midnight exactly are included)

July 04, 2002 Chris -- Thanks for the question regarding "Dropping a tablespace with a lot of free extents.", version 8.1.6
You Asked
I have a tablespace that has 1186783 free extents associated with it and 0 used
extents.  There are no objects in this tablespace.  I would like to drop it.
Would it be faster to coalesce first and then drop it?  Will coalesce combine
all entries in the fet$ into one entry? Do you know of the fastest way to drop a
tablespace of this size?
and we said...
I'm assuming you mean a DICTIONARY managed tablespace...

I have no "hard facts" on this but I would be tempted to go with coalesce and
then drop.

A quick and dirty example seems to bear this out:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create tablespace testing
  2  datafile
  3  '/d04/oradata/ora817dev/testing.dbf' size 1m  reuse
  4  autoextend on next 1m
  5  /
Tablespace created.

Elapsed: 00:00:00.56

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int )
  2  tablespace testing
  3  storage (initial 1k next 1k pctincrease 0 minextents 25000 maxextents
unlimited )
  4  /
Table created.
Elapsed: 00:17:15.06

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;
Table dropped.

Elapsed: 00:19:49.30

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from dba_free_space where
TABLESPACE_NAME = 'TESTING'
  2  /

  COUNT(*)
----------
      4009

Elapsed: 00:00:00.14
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter tablespace testing coalesce;

Tablespace altered.

Elapsed: 00:00:41.20
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop tablespace testing;

Tablespace dropped.

Elapsed: 00:00:00.23
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create tablespace testing
  2  datafile
  3  '/d04/oradata/ora817dev/testing.dbf' size 1m  reuse
  4  autoextend on next 1m
  5  /

Tablespace created.

Elapsed: 00:00:00.51
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int )
  2  tablespace testing
  3  storage (initial 1k next 1k pctincrease 0 minextents 25000 maxextents
unlimited )
  4  /

Table created.

Elapsed: 00:18:06.59
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;

Table dropped.

Elapsed: 00:19:21.32
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from dba_free_space where
TABLESPACE_NAME = 'TESTING'
  2  /

  COUNT(*)
----------
      5593

Elapsed: 00:00:00.20
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop tablespace testing;

Tablespace dropped.

Elapsed: 00:03:19.78

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review & Followup
Rating: 4
Thanks for the confirmation. July 04, 2002
Reviewer: Chris Sirois from Edmonton, Alberta, Canada
I suspected that the coalesce would be the better choice, but I want to be sure
before I attempt something of this magnitude.  Thank-you for your immediate
reponse.

Rating: 5
FYI July 04, 2002
Reviewer: Connor from UK
A similar problem with a massive number of fet$ entries was solved (with the
blessing and guidance of Oracle support) using a straight delete from fet$ (in
restrict mode, plus some subsequent shutdown/startup and a number of other
pieces of advice from support)...but if you need to go down this path, I stress
the blessing/guidance bit!  ie do NOT  read the 'delete from fet$' bit of this
post and say "yep - thats the way to go"

I'd also stress taking remedial action to ensure avoidance of a repeat
occurrence

July 04, 2002 Yogeeraj -- Thanks for the question regarding "Why isn't my Index Getting Used? [Expert One-on-one Oracle: pg 303]", version 8.1.7.4
You Asked
Hello,

Sorry that i steal others chances to ask a question. ;) I could not convince my
software developers about what you describe in your book on page 303.

<quote> (with modifications for ease of presentation)
Case 1:
B*Tree index + predicate does not use the leading edge of index

Table:  T     Index: T(x,y)

Query 1:  SELECT * FROM T WHERE Y=5;
The optimizer will tend not to use the index since your predicate did not
involve the column X - it must inspect each and every index entry in this case.
It will typically opt for a full table scan of T instead.

Query 2:  SELECT X,Y FROM T WHERE Y=5;
The optimizer would notice that it did not have to go to the table in order to
get either X or Y (they are in the index) and may very well opt for a Fast Full
Scan of the index itself, as the index is typically much smaller than the
underlying table.
<quote>

Does this mean that the optimizer also looks at the select part? What would have
happened if i had "select a,b,c,d,x,y ..." would it be the same?

thank you for a simple explanation.

Best Regards
Yogeeraj
and we said...
Yes, the optimizer looks at the select list.

There is a huge difference in the above if you just select X, or Y or X,Y and
leave out the other columns.

The optimizer KNOWS that an index on X,Y can be used like a "skinny" version of
the table.

also, in 9i there is an index skip scan that makes it so that the index will be
consider for queries like "select * from t where y=5" even when the index is on
t(x,y)!

search for

index skip scan

on this site for examples.
Review & Followup
Rating: 5
thank you. July 05, 2002
Reviewer: Yogeeraj from Mauritius
hi
Thank you for the clarifications. Will discuss with my colleague software
developers about during our next training session. I will have to work on a few
examples to be able to demonstrate this behaviour of the optimizer (CBO).

Best Regards
Yogeeraj

July 05, 2002 Senthil Kumar -- Thanks for the question regarding "Question on BEFORE Trigger", version 8.0.6
You Asked
Hello Tom,
    Thanks for clarifying my doubts.
    I am looking forward to give your say on the following.

SQl@Ora805>CREATE TABLE CONTRACT_HEAD ( CONT_H_ID NUMBER(12),
2               CONT_H_DATE DATE,
3               CONT_H_PARTY VARCHAR2(50),
4               CONT_H_STATUS   CHAR(1),
5               CONT_H_FLAG  CHAR(1) );

Table created.

SQl@Ora805>ALTER TABLE CONTRACT_HEAD ADD CONSTRAINT PK_CONT_HEAD PRIMARY KEY
(CONT_H_ID);

Table altered.
SQl@Ora805>CREATE TABLE CONTRACT_DETAIL (  CONT_D_ID NUMBER(12),
2       CONT_D_POS NUMBER(12),
3      CONT_D_DESC VARCHAR2(50),
4      CONT_D_QTY NUMBER(12,5),
5      CONT_D_FLAG CHAR(1),
6     CONSTRAINT PK_CONT_DETAIL PRIMARY KEY( CONT_D_ID, CONT_D_POS),
7     CONSTRAINT FK_HEAD_DETAIL FOREIGN KEY(CONT_D_ID) REFERENCES
CONTRACT_HEAD(CONT_H_ID));
Table created.

SQl@Ora805>CREATE OR REPLACE TRIGGER AUDIT_DETAIL BEFORE UPDATE OF cont_d_desc,
cont_d_qty ON contract_detail
2  REFERENCING OLD AS OLD NEW AS NEW FOR EACH ROW
3
4  BEGIN
5   IF NVL(:OLD.cont_d_qty,0) <> NVL(:NEW.cont_d_qty,0) THEN
6    UPDATE contract_head SET cont_h_flag = 'Y' WHERE cont_h_id =
:OLD.cont_d_id;
7   END IF;
8
9   IF NVL(:OLD.cont_d_desc,' ') <> NVL(:NEW.cont_d_desc,' ') THEN
0    UPDATE contract_head SET cont_h_flag = 'Y' WHERE cont_h_id =
:OLD.cont_d_id;
11   END IF;
12  END;
13  /
Trigger created.

SQl@Ora805>INSERT INTO  CONTRACT_HEAD VALUES ( 1, SYSDATE, 'MIKE', 'Y','N');
1 row created.
SQl@Ora805>INSERT INTO  CONTRACT_HEAD VALUES ( 2, SYSDATE, 'JOHN', 'Y','N');
1 row created.

SQl@Ora805>COMMIT;
Commit complete.

SQl@Ora805>INSERT INTO CONTRACT_DETAIL VALUES(1,1,'PIZZA', 5,'Y');
1 row created.
SQl@Ora805>INSERT INTO CONTRACT_DETAIL VALUES(1,2,'PIE', 5,'N');
1 row created.
SQl@Ora805>COMMIT;
Commit complete.

SQl@Ora805>UPDATE CONTRACT_DETAIL SET cont_d_qty=10 where cont_d_id =1 and
cont_d_pos=2;
1 row updated.

SQl@Ora805>commit;
Commit complete.

SQl@Ora805>SELECT * FROM contract_head;
CONT_H_ID CONT_H_DA CONT_H_PARTY
CONT_H_STATUS CONT_H_FLAG
--------- --------- -------------------------------------------------- - -
        1 04-JUL-02 MIKE                                               Y Y
        2 04-JUL-02 JOHN                                               Y N

    I see that my trigger works fine.
    1. But one of my clients told me that there is a flaw in this logic and is
UN-USABLE when there is a transaction involving multiple updates and then
commit.
    2. More worse is that Oracle many times will not give an error message and
let the user (or programmer) thinks that the update is successful whereas
nothing is updated.

   Please let me know your opinion on this. If you find this logic to be a
faulty, please let me know your say.

   Thanks in advance,
and we said...
Looks just dandy to me.

If your client knows so much, why are they the client and not the developer?

There are no concurrency issues I see here (other then you will of course be
locking a row in the head table, decreasing concurrency)

Oracle will not silently ignore errors, on that point, they are gravely (and
seriously) mistaken.

If they have a point to make - it can be made with an example.  If they say
something "doesn't work", they should have a test case that shows it.  Ask them
for that.

This logic functions as is.

I would rather you code:

  if ( :new.x <> :old.x or
       (:new.x is null and :old.x is not null) or
       (:new.x is not null and :old.x is null) )
  then
     ....

instead of NVL'ing but other then that, the logic is "sound"...

There are no silent errors that will be ignored.  This is not an opinion.
Review & Followup
Rating: 3
What's Wrong with NVL? July 05, 2002
Reviewer: Richard from Oxfordshire, UK
Hi,

In your reply to Senthil's question, you say that you'd rather

if ( :new.x <> :old.x or
       (:new.x is null and :old.x is not null) or
       (:new.x is not null and :old.x is null) )
  then
     ....

was used, than NVL. Why? Is it a performance issue?

Followup:
IF NVL(:OLD.cont_d_qty,0) <> NVL(:NEW.cont_d_qty,0)

what is :old.cont_d_qty was NULL and :new.cont_d_qty was ZERO.

it wouldn't work -- that is why.  With the NVL trick you have to pick a value
that can never exist, thats not "nice".  At the expense of a few keystrokes -- i
would rather key in something that always works rather then something that might
usually work.

Maybe today ZERO is invalid for qty - 3 years from now, after you have moved
onto the next project, ZERO might become valid due to changing requirements.
Then you have a real problem...

July 05, 2002 Antonio -- Thanks for the question regarding "fun fun fun -- (you are wrong wrong wrong)", version 7.3, 7.4,8.04, 8.5
You Asked
i'm a simple programmer with
an account in a DB with this grants on an NT machine
- connect
- create procedure

--So I can program  this :
procedure fun
begin
  hosts('format c: --siwtches_for_format_come_here');
end;

-- I compile and exec the procedure

I think the DB administrator is going to have a lot of fun....
and we said...
hosts (or host) is not a PLSQL command.

There are no built in facilities in PLSQL stored in the server
to execute a host command.  Special care is taken and
facilities provided to the DBA to ensure nothing like the above
could ever happen on the server.

For example, given the appropriate priveleges, you could write
an External Procedure in C.  This external procedure could in
fact do a format since C can pretty do as it likes.  However,
the DBA should set up external procedures to run as some "safe"
user (not Oracle, not Root, not Administrator) thus preventing
you from doing this at the OS level.

The HOST() builtin command is a feature of PLSQL in Oracle FORMS
only.  It executes a HOST() command on the client machine.  Here
you could do that (run the format) if the client is priveleged
enough (not that it would actually work however since you cannot
format a drive that is being used and C: is mostly likely USED).
 This is no different then you writing a C program, of a VB
program, or a program in any language that does that.  It is
called a trojan horse -- its supposed to do something useful but
does something damaging.

The server is safe.
Review & Followup
Rating: 3
It is funny August 08, 2001
Reviewer: Anthony Lai Cheuk Tung from Ma On Shan, Hong Kong SAR
Tom, your answer is good and helpful.
Simple programmer, your question is good but you are too simple ^_^//.

Rating: 5
A related question July 04, 2002
Reviewer: A reader
Dear Tom,
With reference to your comment:
"DBA should set up external procedures to run as some "safe" user (not Oracle,
not Root, not Administrator) "
would it be possible to read the environment variables set for this other user
in an external procedure. I think it is possible using OCI ( using funtions like
GetOCIEnv or createOCIEnv etc. to get the environment handle ), but without
using OCI?
Thanks.

Followup:
environment variables are an OS thing, not an OCI thing.  or I'm not
understanding what you are asking for.  Do you mean environment variables like
ORACLE_HOME or what?

Rating: 2
Explaing July 05, 2002
Reviewer: A reader
Dear Tom,
These are some user defined environment varaibles. Just like ORACLE_HOME, on
UNIX say $ export PARAMETER1="abc"
Thanks

Followup:
the environment in the external procedure will be the environment inherited from
the listener that spawns the extproc process.  No more, no less.

You can only see the environment that was in place when the listener itself was
started....

July 06, 2002 Sudip -- Thanks for the question regarding "Which one is more efficient PL/SQL procedure or Java stored procedure", version 8.1.7
You Asked
Hi Tom,
If I have a complex business logic to code, should I do it as a Pl/SQL package
or a java stored procedure. Which one is more efficient.

Regards
Sudip
and we said...
My answer will always be:

benchmark it.

I believe in 99% of the cases, you will find PLSQL to be

o more efficient to code in (you can write the code faster, the level of
integration between PLSQL and SQL is unmatched)

o more efficient speedwise (no impedance mismatch between SQL types and PLSQL
types as there is with Java types and SQL types)

But, in the end, I can also say that if you give me a piece of non-trivial PLSQL
code, I could probably write a java stored procedure that runs faster (and vice
versa).  Meaning most code is written "untuned" and if we just tuned what we had
-- it would all run faster ;)  They are both compentent languages, you would be
successful with either or.
Review & Followup
Rating: 3
Thanks a lot July 09, 2002
Reviewer: Sudip Sarkar from Calcutta, India.
Hi Tom,
Thanks a lot for the feedback. I am a avid user of PL/SQL so far. Your feedback
is inducing me in learning java :-) . It will anyway help me in long term.

Thanx a lot.

July 06, 2002 Sandy -- Thanks for the question regarding "Using the record of an implicit cursor as a parameter in a funcion", version 8.1.7
You Asked
Hi Tom!

Thanks for all the excellent solutions and tips. Maybe you'd like to help me
with this problem:

I'd like to do something like this:

DECLARE
 CURSOR c1
  IS SELECT a,b,c
     FROM t;
BEGIN
 FOR rec_c1 IN c1 LOOP
    pack_somepackage.func_somefunction(rec_c1);
 END LOOP;
END;

I'd like to keep the foor loop with the implicit record as this is faster but I
want to send the record to a function in another package.

What is the best way to achieve this if I want to keep the implicit FOOR LOOP ?

Is there some way to assign the implicit record to an explicit one?

Thanks a lot for your help!
and we said...
that is an implicit cursor for loop.

You have no OPEN, CLOSE or checks for cursor%notfound.  You are not explicitly
controlling the cursor -- hence it is implicit.

That aside, as long as the record structure matches -- it'll work OK.  consider:

ops$tkyte@ORA9I.WORLD> create or replace package demo_pkg
  2  as
  3          cursor c1 is select 'a', 1, sysdate from dual;
  4
  5          function foo( x in c1%rowtype ) return number;
  6  end;
  7  /

Package created.

ops$tkyte@ORA9I.WORLD> create or replace package body demo_pkg
  2  as
  3          function foo( x in c1%rowtype ) return number
  4          is
  5          begin
  6                  return 1;
  7          end;
  8  end;
  9  /

Package body created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> declare
  2          n number;
  3  begin
  4          for rec in ( select 'aa', 22, sysdate-55 from all_users )
  5          loop
  6                  n := demo_pkg.foo( rec );
  7          end loop;
  8  end;
  9  /

PL/SQL procedure successfully completed.
Review & Followup
Rating: 5
Works perfectly July 06, 2002
Reviewer: Sandy Mamoli from Vienna, Austria
Thanks a lot for your quick reply.

This works and is definitely the easiest and most elegant solution.

July 07, 2002 Justin -- Thanks for the question regarding "PGA !!!!", version 8.1.7
You Asked
Hi Tom,

I have dedicated connections setup for my clients...These are some silly
questions which i wanted to know...

1.PGA is on the server side or client side.

2.If i have a table that has lets say 10 million records
  Will my pga give me error if i give a simple select statement..
  select * from ten_mill_rec_table;
  Or will it freeze.

Thanks for all your great help....

Justin.
and we said...
If you have my book -- i go over this in detail in the architecture chapter.

1) PGA = process global area = memory allocated by a dedicate or shared server
on the server system.

2) no, and no.  we do not cache a query result set in memory like that.  That
would not be a very good approach.  When you issue "select * from
10_mill_row_table", we do nothing IO wise.  When you fetch the first row, we get
it and give it to you.  When you fetch the second, we get it and give it to you.
 We do not even store the rows we give to you -- you get them and we don't care
about them anymore -- they are gone.  They are not gotten until we need them.
Review & Followup
Rating: 5
Dababase Block Buffer July 01, 2002
Reviewer: Asif Momen from Riyadh
Hi Tom,

  I have some question on SGA and PGA, ASSUME my instance crashed and i had 10
Uncommitted Inserts in Session1 and 3 Commited Updates in Session3, 5 Rollback
Deletes in Session3. Some Inserts are written to Data files and none of the
Updates are in Data Files. All the 10 Inserts and 3 Updates are in Redo logs.

  During the instance recovery, when it is rolling forward and rolling back
could you tell me what will be there in PGA, Rollback Segment, Database Block
Buffer, Redo Log Buffer and other related area.

  In the above scenario will the rollback segment hold any data when the
instance starts up after a crash.

Thank You

Followup:
I'll have to refer you to my book -- it takes about 4/5 pages of text with
pictures to describe what is where and when and all of the hypothetical cases.
I do it in the book.

Rating: 4
July 06, 2002
Reviewer: A reader
Hi Tom,

What will happen if the query with ten million records is issued with a group by
clause...Will it give some error related to PGA...

Followup:
It'll not blow out the PGA unless you have your sort_area_size or
pga_aggregate_target set too high.

It'll swap to disk -- IF it needs to -- we can do group bys on the fly in some
cases as well, without using TEMP.

We don't blow out RAM, we use TEMP disk space.

July 07, 2002 -- Thanks for the question regarding "Changing a database field datatype", version 8.0.5.0.0
You Asked
Hello,

How can we change the datatype of a varchar2 field to Number without empty the
table ?

Bye
and we said...
You cannot.  You have to "empty" out the field before changing its datatype like
that.

I would be tempted to:

alter table t add new_column number;
update t set new_column = old_column, old_column=null;
alter table t add constraint check_cons check ( old_column is NULL );

and have people use the new_column.  The old_column will be there until you get
to 8i when you can drop a column. If you wanted, you could:

rename table t to t_table;
create view t as select .... new_column OLD_COLUMN from t_table;

now, the original old column would be hidden from people, and new_column would
appear to have the name of the old column.  Do the grants on T and you are set
to go.
July 07, 2002 Richard -- Thanks for the question regarding "CBO vs. analytic functions", version 9i
You Asked
/*

Hello Tom,

I have problems with CBO in a query referencing
a view using analytic functions.

Could you please explain, why CBO choses sub-optimal plan
for the query at the end of the example as compared with the
one before the last one?

Thanks,

Richard

-- Oracle 9i
*/

drop user a cascade;
create user a identified by a;
grant connect, resource, plustrace to a;

connect a/a@onc

-- telephone line
create table line (
  line_id number(7) not null primary key,
  loc_id number(7) not null -- references loc(loc_id)
)
/

-- actions about a line
create table linepos (
  linepos_id number(10) not null primary key,
  line_id number(7) not null references line(line_id),
  carr_id number(3) not null, -- references carrier(carr_id)
  request varchar2(6) not null check (request in ('ADD', 'REMOVE')),
  status number(1)
)
/

-- generate (random?) data
insert /*+append*/ into line (line_id, loc_id)
select rownum, ceil(dbms_random.value(1, 300))
from all_objects
where rownum <= 1000;
commit;

begin
  for x in (select rownum r from all_objects where rownum < 20) loop
    insert /*+append*/ into linepos
    select
      rownum + x.r * 1000,
      rownum,
      mod(rownum, 6),
     'ADD',
     floor(dbms_random.value(1, 4))
   from all_objects
   where rownum <= 1000;
   commit;
   end loop;
end;
/

create index line_loc_id on line(loc_id)
/
create index linepos_line_idx on linepos(line_id)
/

/*
activating "for all indexed columns" would
prevent using index on a query like:

select * from linepos where line = 100

Why?

*/

analyze table linepos compute statistics
   for table
   for all indexes
--   for all indexed columns
;

analyze table line compute statistics
   for table
   for all indexes
--   for all indexed columns
;

/*
This view should return then last (current)
status for a carr_id, line_id combination
*/

create or replace view line_status as
select carr_id, line_id, request, status
from (
  select
    carr_id,
    line_id,
    last_value(request) over (partition by carr_id, line_id) request,
    last_value(status) over (partition by carr_id, line_id) status,
    dense_rank() over (partition by carr_id, line_id order by linepos_id desc)
dr
  from
    linepos
  order by
    carr_id, line_id, linepos_id
)
where dr <= 1;

set autotrace on explain
select * from linepos where line_id = 100;

/*
OK: Uses index

LINEPOS_ID    LINE_ID     CARR_ID REQUES     STATUS
---------- ---------- ---------- ------ ----------
      1100      100           4 ADD         1
      2100      100           4 ADD         1
      3100      100           4 ADD         2
      4100      100           4 ADD         1
      5100      100           4 ADD         2
      6100      100           4 ADD         1
      7100      100           4 ADD         2
      8100      100           4 ADD         1
      9100      100           4 ADD         2
     10100      100           4 ADD         2
     11100      100           4 ADD         2
     12100      100           4 ADD         2
     13100      100           4 ADD         1
     14100      100           4 ADD         2
     15100      100           4 ADD         1
     16100      100           4 ADD         3
     17100      100           4 ADD         1
     18100      100           4 ADD         2
     19100      100           4 ADD         2

19 rows selected.

Elapsed: 00:00:00.05

Execution Plan
----------------------------------------------------------
      0
SELECT STATEMENT Optimizer=CHOOSE (Cost=10 Card=190 Bytes=39
90)

      1             0
  TABLE ACCESS (BY INDEX ROWID) OF 'LINEPOS' (Cost=10 Card=1
90 Bytes=3990)

      2             1
    INDEX (RANGE SCAN) OF 'LINEPOS_LINE_IDX' (NON-UNIQUE) (C
ost=1 Card=190)

*/

select * from line_status where line_id = 100;

/*
OK: uses index

   CARR_ID    LINE_ID REQUES     STATUS
---------- ---------- ------ ----------
     4      100 ADD          2

Elapsed: 00:00:00.02

Execution Plan
----------------------------------------------------------
      0
SELECT STATEMENT Optimizer=CHOOSE (Cost=63 Card=190 Bytes=10
830)

      1             0
  VIEW (Cost=63 Card=190 Bytes=10830)

      2             1
    WINDOW (SORT) (Cost=63 Card=190 Bytes=3990)

      3             2
      WINDOW (SORT) (Cost=63 Card=190 Bytes=3990)

      4             3
    TABLE ACCESS (BY INDEX ROWID) OF 'LINEPOS' (Cost=10
Card=190 Bytes=3990)

      5             4
      INDEX (RANGE SCAN) OF 'LINEPOS_LINE_IDX' (NON-UNIQ
UE) (Cost=1 Card=190)

*/

select
  *
from
  line l,
  line_status ls
where
  l.line_id = 100
  and l.line_id = ls.line_id
/

/*
OK: uses index

   LINE_ID     LOC_ID     CARR_ID    LINE_ID REQUES     STATUS
---------- ---------- ---------- ---------- ------ ----------
       100      288           4    100 ADD         2

Elapsed: 00:00:00.01

Execution Plan
----------------------------------------------------------
      0
SELECT STATEMENT Optimizer=CHOOSE (Cost=631 Card=1900 Bytes=129200)

      1             0
  NESTED LOOPS (Cost=631 Card=1900 Bytes=129200)

      2             1
    TABLE ACCESS (BY INDEX ROWID) OF 'LINE' (Cost=1 Card=10 Bytes=110)

      3             2
      INDEX (RANGE SCAN) OF 'SYS_C006531' (UNIQUE) (Cost=1 Card=10)

      4             1
    VIEW

      5             4
      WINDOW (SORT) (Cost=63 Card=190 Bytes=3990)

      6             5
    WINDOW (SORT) (Cost=63 Card=190 Bytes=3990)

      7             6
      TABLE ACCESS (BY INDEX ROWID) OF 'LINEPOS' (Cost=10 Card=190 Bytes=3990)

      8             7
        INDEX (RANGE SCAN) OF 'LINEPOS_LINE_IDX' (NON-UNIQUE) (Cost=1 Card=190)

*/

-- but now: FULL SCAN
select
  *
from
  line_status ls,
  line l
where
  l.loc_id = 100
  and l.line_id = ls.line_id
/

/*
   CARR_ID    LINE_ID REQUES     STATUS    LINE_ID     LOC_ID
---------- ---------- ------ ---------- ---------- ----------
     0      636 ADD          1        636      100
     1      697 ADD          1        697      100
     1      757 ADD          1        757      100
     2      674 ADD          3        674      100
     4      856 ADD          3        856      100
     5      677 ADD          2        677      100

6 rows selected.

Elapsed: 00:00:00.59

Execution Plan
----------------------------------------------------------
      0
SELECT STATEMENT Optimizer=CHOOSE (Cost=144 Card=190 Bytes=12920)

      1             0
  HASH JOIN (Cost=144 Card=190 Bytes=12920)

      2             1
    TABLE ACCESS (BY INDEX ROWID) OF 'LINE' (Cost=1 Card=10 Bytes=110)

      3             2
      INDEX (RANGE SCAN) OF 'LINE_LOC_ID' (NON-UNIQUE) (Cost=1 Card=10)

      4             1
    VIEW (Cost=142 Card=19000 Bytes=1083000)

      5             4
      WINDOW (SORT) (Cost=142 Card=19000 Bytes=399000)

      6             5
    WINDOW (SORT) (Cost=142 Card=19000 Bytes=399000)

      7             6
      TABLE ACCESS (FULL) OF 'LINEPOS' (Cost=10 Card=19000 Bytes=399000)

===========> WHY FULL TABLE SCAN here?
*/
and we said...
why not (a full scan)?

I did this with sql_trace (not looking at "set timing on" which is wall clock
time and almost meaningless unless you are timing a process that takes many many
minutes and you want a "ballpark figure".

I did the queries like this:

alter session set sql_trace=true;
select * from linepos no_histograms where line_id = 100;
select * from line_status no_histograms where line_id = 100;
select /* no_histograms */ * from line l, line_status ls where l.line_id = 100
and l.line_id = ls.line_id;

analyze table linepos compute statistics
   for table
   for all indexes
   for all indexed columns
;

analyze table line compute statistics
   for table
   for all indexes
   for all indexed columns
;

select * from linepos histograms where line_id = 100;
select * from line_status histograms where line_id = 100;
select /* histograms */ * from line l, line_status ls where l.line_id = 100 and
l.line_id = ls.line_id;

And on this tiny bit of data, the CBO choose to full scan over index read when
histograms were computed.

Please remember the following (many people assume the opposite it true)

Indexes do not imply speed
Indexes do not mean "it will be faster"
Indexes are not always the best path
Indexes are not inheritly good (or evil)

Full scans are not evil
Full scans can be very good

In this case -- it was a total wash.  On my system with 8k blocks using my
showspace script (search this site if you would like that)

a@ORA920> exec show_space( 'LINE' )
Free Blocks.............................0
Total Blocks............................8
Total Bytes.............................65536
Unused Blocks...........................5
Unused Bytes............................40960
Last Used Ext FileId....................1
Last Used Ext BlockId...................50705
Last Used Block.........................3

PL/SQL procedure successfully completed.

a@ORA920> exec show_space( 'LINEPOS' )
Free Blocks.............................3
Total Blocks............................64
Total Bytes.............................524288
Unused Blocks...........................0
Unused Bytes............................0
Last Used Ext FileId....................1
Last Used Ext BlockId...................50833
Last Used Block.........................8

PL/SQL procedure successfully completed.

LINE is a 3block table (3 blocks!!!)
LINEPOS is a 64 block table.

I can read EITHER of those in a single IO (given that my multi-block-read-count
is set to 64).

why bother reading and interpreting a complex index structure using singleton
IO's with such trivial amounts of data??

To me, I guess, the real question is -- why didn't the CBO pick a full scan
without the histograms -- why did it choose the index plan *ever*.

In this case, there just isn't enough raw data to really care.  Just rememer --
the smaller the table, the less an index has to do with anything.  If you use
sql_trace and tkprof, you'll see these queries differ by 1/100's of seconds --
autotrace itself can skew timings by 1/10's or even full seconds.  Don't use set
timing on to measure something really tiny.
July 08, 2002 NIRMALKUMAR -- Thanks for the question regarding "Characterset Change ", version 817
You Asked
Dear Tom,

After Database creation, how can i change the database's characterset?. Somebody
told me to directly update data dictionary table PROPS$.

Is this a correct method?, is there any impact on that(hope so something should
be there)?.

Help me on this?.

Regards,
Nirmal.
and we said...
don't listen to that person anymore.

read
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76966/toc.htm
and especially:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76966/toc.htm
which is "Changing the Character Set After Database Creation"
Review & Followup
Rating: 5
Same Link July 07, 2002
Reviewer: Sikandar Hayat Awan from Pindi Bhattian - Pakistan
Hi TOM,
These both are same links,

read
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76966/toc.htm
and especially:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76966/toc.htm

          Followup:

sorry.

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76966/ch3.htm#47136
is the second link

July 08, 2002 Venkatasubramanian -- Thanks for the question
      regarding "LMT", version 8.1.7

      You Asked
Dear Tom,

Can we have  the system tablespace as LMT in oracle 8.1.7?
If it is DMT, can we change it to LMT at any point of time?
If so, how it can be done? Can you explain?

Thanks and Regards,
Venkat

      and we said...
In 9.2 (Oracle9i release 2) having SYSTEM as an LMT is supported.

Prior to that, no, system cannot be a LMT
Review &
      Followup

      Rating:
      4
  July 06, 2002
Reviewer:
      A reader
Having System tablespace is a good idea tom...

      Rating:
      3
LMT  July 08, 2002
Reviewer:
      Venkatasubramanian  from Chennai, India
Tom,

The following clipping is from oracle 8 documentaion. (Server
concetpts/Tablespaces and Datafiles). It says that for system tablespace, we can
specify the extent management as LOCAL in the create database command. But it is
said by you that prior to oracle 9.2, we cannot have system tablespace as LMT.
Can you please clarify on this?

Thanks and Regards
Venkat

------------------------------------------------------------
Locally-Managed Tablespaces

     For the SYSTEM tablespace, you can specify EXTENT MANGEMENT LOCAL in the
CREATE DATABASE command. If the SYSTEM tablespace is locally managed, other
tablespaces in the database can be dictionary-managed but you must create all
rollback segments in locally-managed tablespaces.

     For a permanent tablespace other than SYSTEM, you can specify EXTENT
MANGEMENT LOCAL in the CREATTABLESPACE command.

     For a temporary tablespace, you can specify EXTENT MANGEMENT LOCAL in the
CREATE TEMPORARY TABLESPACE command. (See "Temporary Tablespaces".)

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

          Followup:

it is a bug in the documentation.

not until 9.2.

July 08, 2002 Rob
      -- Thanks for the question regarding "When do blocks go on the free list",
      version 8.1.7

      You Asked
Tom:

I have a data warehouse table that is loaded with parallel dml,
i.e. insert select stmt with append hint. Before the load, I
truncate the table. Is there any advantage to using the reuse
storage clause in thee truncate. This table is only loaded once, no other dml
takes
place. I am assuming from info that I have gleaned from searching
your archive that there should be no free list for this table,
therefore the truncate table reuse storage will cause all blocks
to be below the high water mark. I am wondering if blocks go on
the free list only from delete and update stmts? Would doing a
standard insert and then truncate table reuse storage cause blocks
to go on the free list? Do doing a truncate table drop storage
get rid of the free list?

Thanks as always,

Rob

      and we said...
the blocks will only go on free lists dues to updates and deletes and NORMAL
non-append inserts (an insert of a new row that finds the freelists empty will
get some blocks from beyond the HWM and put them on the freelist if the new row
does not exceed the pctfree) -- truncate resets the HWM and obliterates the
freelists (regardless of how you use truncate).

You should be using locally managed tablespaces and I would not save the extents
(i'd truncate back to nothing) and then insert.  The newly allocated extents
(only as many as you need) will be allocated near the "front" of the existing
datafiles effectively reusing the space as needed.
Review &
      Followup

      Rating: 3
Further
      question  March 22, 2002
Reviewer:  Rob
      Balter  from Pittsburgh, PA
If I have a table with 100 allocated extents and I do
a truncate table reuse storage, will the direct path insert
reuse the 100 extents for data or create an additonal
100 extents.

Does a partitioned table have freelists per partition or
per table.

          Followup:

direct path INSERTS insert above the HWM, into existing allocated but not used
space.  So, yes, it'll reuse them if they are "empty" and above the HWM.

each partition will have its own list since a block can only belong to one
partition (so they must be separated out)

      Rating:
      4
  March 25, 2002
Reviewer:  Irfan
       from Kuwait
Hi Tom,
a) I would like to know how the one block is created in free list as normally
after deletion it goes into free list.

SQL> create table t ( x int, y char(2000) default '*' )
  2  storage ( initial 40k next 40k minextents 5 )
  3  tablespace system;

Table created.

SQL> insert into t (x) values ( 1 );

1 row created.

SQL> analyze table t compute statistics;

Table analyzed.

SQL> compute sum of blocks on report
SQL> break on report
SQL> select extent_id, bytes, blocks
  2  from user_extents
  3  where segment_name = 'T'
  4  and segment_type = 'TABLE';

 EXTENT_ID      BYTES     BLOCKS
---------- ---------- ----------
         2      61440         15
         3     102400         25
         4     143360         35
         0      40960         10
         1      40960         10
                      ----------
sum                           95

SQL>  clear breaks
breaks cleared
SQL> select blocks, empty_blocks, avg_space, num_freelist_blocks
  2  from user_tables
  3  where table_name = 'T';

    BLOCKS EMPTY_BLOCKS  AVG_SPACE NUM_FREELIST_BLOCKS
---------- ------------ ---------- -------------------
         1           93       1971                   1

b) In one of your followup you have mentioned the first block of each table is
used by the system. So it means
totally 96 blocks are allocated to this table?

c) Your solution
(an insert of a new row that finds the freelists empty will
get some blocks from beyond the HWM and put them on the freelist if the new row
does not exceed the pctfree)
Can you please explain - put them on the freelist if the new row does not exceed
the pctfree -

Thanks.

          Followup:

a) blocks are on the freelist UNTIL they exceed their pctfree.  Assuming an 8k
blocksize and 10 pctfree, you would have to insert about 3 rows before that
block would be taken OFF of the freelist in the first place (all blocks
originate on the freelist for normal inserts like that).

It stays on the freelist till pctfree is exceeded, then comes back on after it
hits pctused.  If you have my book -- I go over this in some detail in the
chapter on tables.

b) search this site for show_space.  It will show you exactly what is allocated,
used, on the freelist, etc.  You have 95 blocks.  One of them is in use by the
system.  One of them is in use by you.

      Rating:
      4
  March 25, 2002
Reviewer:  Irfan
       from Kuwait
Hi Tom,

I checked the followin link; you have explained clearly;

http://asktom.oracle.com/pls/ask/f?p=4950:8:627321::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:231414051079,%7Bshow_space%7D
but still I could not able to make out, considering the  followup (example), if
one block is used by system, then the EMPTY_BLOCKS should be 92. Becuase one is
used by me, one in freelist and the one by the system. Can you please clear me.

Thanks...

          Followup:

One is used by you AND it is the same one that is on the freelist.  The block
with your single row is the same block as is on the freelist.

You are double counting your block.

      Rating: 4
Empty
      Freelist  July 08, 2002
Reviewer:  Vikas
      Sharma  from Delhi India
I read in your book and in this thread also, that the block which are above the
high water marks are included in to freelist when freelist are empty. Can you
please tell when freelist get empty.

          Followup:

when you use them and they exceed their pctfree and get taken off of the
freelist.  So, when a table is "full" and the very next insert would cause it to
allocate another extent.

      Rating:
      3
Freelist  July 09, 2002
Reviewer:  Vikas
      Sharma  from Delhi India
Thanks Tom it clears my doubt.
Thanks

July 08, 2002 mathew -- Thanks for the question regarding
      "Database queuing", version 9i

      You Asked
hi,
   How does one use queuing in the database and how different is it performance
wise as compared to other queuing options like JMS/MSMQ?
Where would you advice a person to use queuing in the database and outside it?
  Could you also provide an example of queuing? Does you site use it?

thanks,
mathew

      and we said...
http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/appdev.920/a96587/toc.htm
covers AQ in the database.

JMS is just a specification, not an implementation.  JMS in Oracle is physically
implemented on top of AQ in the database.  JMS is just an API, AQ is the guts of
the engine.

MSMQ is very similar to AQ -- both are queueing systems built on top of a
database, something that is very much needed for a queueing system (you need a
"stable" store underneath the queueing system else messages are not assured of
delivery)

I do not use queueing on this site as I am not a high volume OLTP system that
needs the facilities provided by messaging.

The above referenced document, chapter 8, has a full sample application.
July 08, 2002 Brian -- Thanks for the question regarding
      "Entity Attribute Value(EAV)", version 8.1.6

      You Asked
Hi, Tom:
     Thanks for you answer my questions.
     This is a question about clinical trial database design and performance.
Clinical patient records needs to store many possible facts, for examples,
(patient XYZ 1/5/2001, 12:00AM 12.5 gm/dl, 4.9 Meq/L, 80 IU....)

     EAV design represents a column to row transformation, like this:
       Patient       date     time     lab_test_name  result
      (patient XYZ 1/5/2001, 12:00AM 'Hemoglobin", 12.5gm/dl)
      (patient XYZ 1/5/2001, 12:00AM 'Potassium" 4.9 Meq/L)
      ..................

      But it raises a problem: Query performance of a big clinical report.
Because each attribute-value pair for a patient is stored as separate row in a
table, when performing a similar complex Boolean query on a single EAV table,
the conceptual AND, OR and NOT operations must be translated into the row-based
operations of set union, set intersection and set differences...., and select
statements involved in distinct, max, full outer join (missing data problem),
self join, nested subqueries etc, which are more complexes, inefficient.

     From your experience, what is the best model to store this kind of clinical
data from database design and performance point of view?  How does oracle handle
this kind of issues?

Thanks

Brian

      and we said...
While the above is flexible, it is not scalable if you need to perform queries
like "find the patients that have had test X and test Y or something_else"

If at all possible, I would use a model like:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3361148209692
The problem there may well be that you have more then say 900 observations -- we
run out of columns.

What you might also consider would be XML (especially in 9i with some of the new
features therein).

There you could create an XML document for the patient with their history and
using Oracle Text (in 9i) or Intermedia (8i) you can index the XML document.
So, instead of inserting a new observation, you would update an XML tagged
document.  Then you can do very efficient CONTAINS searching on that using the
tagged information.
Review & Followup

      Rating:
      5
  April 26, 2002
Reviewer:
      A reader

      Rating: 3
what is "Oracle
      clinical " ?  July 08, 2002
Reviewer:
      A reader
Hi tom,

  I don't know if this is relevent question in the above contex, but I don't
know what is Oracle clinical ?
is it possible to down load that software ? if yes can you plase provide the
link ?

Thanks,

          Followup:

It is an application (packaged off the shelf application) like HR or Fin Apps.
It is used primarily in health care related industries for performing clinical
trials of medications.

I do not believe it is available for download.

I did find some docs in Metalink. Under Top Tech Docs, click on E-Business
Suite: ERP.  If you hit ctrl-f and search on "Clinical" you can hit the
documentation link.  There are some install guides, user guides


Subject | Article | Last Updated (East coast time)
PL/SQL Problem | article only | Jul 09, 2002 06:56 am
Drop columns from high column table | article only | Jul 09, 2002 07:08 am
Migrating data from Informix to Oracle | article only | Jul 09, 2002 07:20 am
8i > 9i OCP DBA Upgrade | article only | Jul 09, 2002 07:39 am
paging through results on the web with PLSQL | article only | Jul 09, 2002 08:53 am
Oracle & Web, regexp, 1-1 relationship | article only | Jul 09, 2002 12:43 pm
Compatibility | article only | Jul 09, 2002 12:44 pm
ORA-00604 - what does it mean | article only | Jul 09, 2002 12:45 pm
problem in connecting to 9i from oc4j appln server as a sysdba | article only | Jul 09, 2002 02:59 pm
Cursor Expressions | article only | Jul 09, 2002 03:35 pm
Application Design Principles | article only | Jul 09, 2002 03:45 pm
public synonym | article only | Jul 09, 2002 04:42 pm
attributes of an object definition | article only | Jul 09, 2002 04:49 pm
Graph validation | article only | Jul 09, 2002 07:50 pm
Replication of Designer 6i Repository | article only | Jul 09, 2002 07:53 pm
Does PL/SQL support composite types in 8.1.7 | article only | Jul 09, 2002 08:10 pm
mapping an oracle.exe process to an instance on NT | article only | Jul 10, 2002 06:48 am
Writing Trigger or Procedure on a table to update/insert record(s) in another table. | article only | Jul 10, 2002 06:53 am
Average of time | article only | Jul 10, 2002 07:17 am
Generating XML doc seems slow & Cursor Results Sets | article only | Jul 10, 2002 07:19 am
Connect Internal | article only | Jul 10, 2002 07:36 am
Authentication methods for batch jobs running against a database in a Win2000 environment. | article only | Jul 10, 2002 12:21 pm
Disabling Statement Caching of PL/SQL engine when executing PL/SQL procedure. | article only | Jul 10, 2002 12:22 pm
Few Questions | article only | Jul 10, 2002 12:32 pm
Extracting Table/Column Comments as XML | article only | Jul 10, 2002 03:08 pm
SQLPLUS does not execute java function from a remote client | article only | Jul 10, 2002 03:19 pm
Difference between count(1) and count(*) | article only | Jul 10, 2002 04:12 pm
Question on Windows Compression | article only | Jul 10, 2002 07:33 pm
intermedia Text | article only | Jul 11, 2002 07:41 am
Multiple statements in a file | article only | Jul 12, 2002 07:43 am
Ref cursor | article only | Jul 12, 2002 07:53 am
IN versus OR in SUBQUERY | article only | Jul 12, 2002 08:37 am
Last Index REBUILD | article only | Jul 12, 2002 05:28 pm
Regarding Export and Import | article only | Jul 13, 2002 10:14 am
Multiplex a control file in Primary and Standby server. | article only | Jul 13, 2002 10:18 am
Checking the data? | article only | Jul 13, 2002 12:53 pm
How can set audit for select statement | article only | Jul 13, 2002 12:54 pm
StatsPack | article only | Jul 13, 2002 05:47 pm
How to store Double.POSITIVE_INFINITY in database? | article only | Jul 13, 2002 05:56 pm
log miner | article only | Jul 14, 2002 10:09 am
want to read Users Password as a Text | article only | Jul 14, 2002 11:24 am
Normalization..... | article only | Jul 14, 2002 05:28 pm
delete using multi sub-queries / lpad | article only | Jul 14, 2002 06:25 pm
delete procedures | article only | Jul 14, 2002 09:21 pm
Tunning Oracle connectivity With non Oracle systems | article only | Jul 15, 2002 09:02 am
Time Eastimation for Update/Uploading Data/Backup/Recovery/Export-Import | article only | Jul 15, 2002 09:42 am
Select statement on 2 tables using group by... | article only | Jul 15, 2002 10:23 am
Locally Managed Tablespaces. | article only | Jul 15, 2002 12:36 pm
SQL | article only | Jul 15, 2002 08:30 pm
owa_util.listprint Query | article only | Jul 15, 2002 08:40 pm
Preference of Cost based optimizer over rule based optimizer | article only | Jul 15, 2002 08:42 pm
DBMS_SQL and Oracle Object REF | article only | Jul 15, 2002 08:45 pm
Locally Managed Tablespace | article only | Jul 15, 2002 11:27 pm
row(s) 1 - 53 of 53
July 09, 2002 DW -- Thanks for the question regarding "PL/SQL Problem", version 7.3.4
You Asked
hi tom....

 i have this problem on my pl/sql procedure. is it possible to pass
a variable from a fetch statement and used it as the name of the table
on a select statement? to illustrate further, here's my procedure:

declare
tbl varchar2(30);
ownr varchar2(10);
cnt number;
cursor xcur is select owner, table_name from table_mon;

begin

open xcur;

loop

fetch xcur into ownr, tbl;
exit when xcur%NOTFOUND;

select count(*) into cnt from ownr.tbl;

is it possible??
and we said...
Using dynamic sql -- sure

create or replace
function get_row_cnts( p_tname in varchar2 ) return number
as
    l_theCursor     integer default dbms_sql.open_cursor;
    l_columnValue   number  default NULL;
    l_status        integer;
begin
    dbms_sql.parse(  l_theCursor,
                    'select count(*) from ' || p_tname,
                     dbms_sql.native );

    dbms_sql.define_column( l_theCursor, 1, l_columnValue );
    l_status := dbms_sql.execute(l_theCursor);
    if ( dbms_sql.fetch_rows(l_theCursor) > 0 )
    then
        dbms_sql.column_value( l_theCursor, 1, l_columnValue );
    end if;
    dbms_sql.close_cursor( l_theCursor );
    return l_columnValue;
end;
/

Note that in 8i -- this becomes very trivial:

create or replace
function get_rows( p_tname in varchar2 ) return number
as
    l_columnValue    number default NULL;
begin
    execute immediate
       'select count(*)
          from ' || p_tname INTO l_columnValue;

    return l_columnValue;
end;
/
Review & Followup
Rating: 5
July 08, 2002
Reviewer: A reader
hhmm... its kinda complicated but anyway, thats why i need to get the number of
rows on each tables thru CNT variable listed under table_mon so that i can use
it on my other query as the total number of rows. Here's my complete procedure:
(with errors =( )

declare
tbl varchar2(30);
ownr varchar2(10);
cnt number;
cursor xcur is select owner, table_name from table_mon;

begin

open xcur;

loop

fetch xcur into ownr, tbl;
exit when xcur%NOTFOUND;

insert into table_mon
select
sysdate,substr(owner,1,15),substr(segment_name,1,30),bytes,extents,cnt,initial_ex
tent,
next_extent,pct_increase from sys.dba_segments a where exists (select 1 from
table_mon_list where owner = a.owner and
table_name = a.segment_name);

end loop;
close xcur;
commit;
end;
/

the result of my procedure to get the important statistics on every table for my
monitoring. If you have idea on how to get the data other than this, I would
really appreciate it.... thanks tom!

Followup:
Why not just analyze the table, NUM_ROWS is then filled in.  Then your entire
procedure becomes

insert into table_mon
select sysdate, owner, segment_name, bytes, extents, ....
 from dba_segments, dba_tables
 where (owner,segment_name) in ( ... );

July 09, 2002 Ashish -- Thanks for the question regarding "Drop columns from high column table", version 8.1.5.0.0
You Asked
I have a partitioned table with 175 million records. The table
has 7 partitions with 25 million records in each. I want to drop
5 columns from this table. These columns does not have any
indexes on them. What would be the best way to do it?
Thanks for your help!
and we said...
In Oracle8i you can drop and set unused various columns in a
table.  The big difference between the two is DROP will
physically reclaim space (rewrites the data) whereas SET UNUSED
is a flag delete -- the columns are gone (and you can never get
them back) however it happens very fast since the data is not
rewritten. At some time in the future, you can DROP UNUSED to
reclaim the space if you want.

For that table, I would probably use the

alter table T set unused column X;

to remove them.
Review & Followup
Rating: 4
How do you tell which columns are unused? July 08, 2002
Reviewer: John from Indianapolis, Indiana
Is there anyway to tell which columns have been flagged as unused? The
documentation shows how to find the number of columns flagged as unused.

SELECT * FROM DBA_UNUSED_COL_TABS;

But it doesn't show how you find the names or attributes of the unused columns.

Followup:
Well, I don't see why it is relevant (i mean, you cannot recover them - only
that they are there taking up space is all that matters).  But, since you asked
anyway

In looking at the view:

create or replace view dba_unused_col_tabs (
OWNER
,TABLE_NAME
,COUNT
) as
select u.name, o.name, count(*)
from sys.user$ u, sys.obj$ o, sys.col$ c
where c.obj# = o.obj#
      and bitand(c.property,32768) = 32768          -- is unused column
      and bitand(c.property, 1) != 1                -- not ADT attribute col
      and bitand(c.property, 1024) != 1024          -- not NTAB's setid col
      and u.user# = o.owner#
      group by u.name, o.name

/

we see we could suck some of the data out from sys.col$.  You will find however
the column name is "wiped out" (replaced with SYS_C...... stuff).  That is so
the unique index on col$(obj#,name) is obeyed if you add a column back with the
same name....

So, the name is gone, but the datatype and size is still there.  If you take the
above view and hack out a bit from the DBA_TAB_COLUMNS view, you can get:

select u.name, o.name,
       decode(c.type#, 1, decode(c.charsetform, 2, 'NVARCHAR2', 'VARCHAR2'),
                       2, decode(c.scale, null,
                                 decode(c.precision#, null, 'NUMBER', 'FLOAT'),
                                 'NUMBER'),
                       8, 'LONG',
                       9, decode(c.charsetform, 2, 'NCHAR VARYING', 'VARCHAR'),
                       12, 'DATE', 23, 'RAW', 24, 'LONG RAW',
                       69, 'ROWID',
                       96, decode(c.charsetform, 2, 'NCHAR', 'CHAR'),
                       105, 'MLSLABEL',
                       106, 'MLSLABEL',
                       111, 'unknown',
                       112, decode(c.charsetform, 2, 'NCLOB', 'CLOB'),
                       113, 'BLOB', 114, 'BFILE', 115, 'CFILE',
                       121, 'unknown',
                       122, 'unknown',
                       123, 'unknown',
                       178, 'TIME(' ||c.scale|| ')',
                       179, 'TIME(' ||c.scale|| ')' || ' WITH TIME ZONE',
                       180, 'TIMESTAMP(' ||c.scale|| ')',
                       181, 'TIMESTAMP(' ||c.scale|| ')' || ' WITH TIME ZONE',
                       231, 'TIMESTAMP(' ||c.scale|| ')' || ' WITH LOCAL TIME
ZONE',
                       182, 'INTERVAL YEAR(' ||c.precision#||') TO MONTH',
                       183, 'INTERVAL DAY(' ||c.precision#||') TO SECOND(' ||
                             c.scale || ')',
                       208, 'UROWID',
                       'UNDEFINED'),
       c.length, c.precision#, c.scale
from sys.user$ u, sys.obj$ o, sys.col$ c
where c.obj# = o.obj#
      and bitand(c.property,32768) = 32768          -- is unused column
      and bitand(c.property, 1) != 1                -- not ADT attribute col
      and bitand(c.property, 1024) != 1024          -- not NTAB's setid col
      and u.user# = o.owner#
/

which shows you the stuff that we can see.

Rating: 5
What about rollback? July 08, 2002
Reviewer: Doug from Danbury, CT USA
Tom - I have noticed that droping a column can take a lot of rollback and time
even on a million row table. (These terms being relative of course).   Will
using the set unused option prior to actually dropping them assist with this or
not?

Followup:
No, setting unused is simply a flag delete -- the space is not reclaimed.

Later, when you drop, it does EXACTLY the work that would have been done by DROP
COLUMN in the first place.

You know -- you can incrementally checkpoint the drop column command as it is
going along.

Yes, it can take a relatively long time to REWRITE a 1,000,000 row table
depending on your system.

Big Table = "set unused"
Small Table = "drop column"

big and small being relative terms of course.

July 09, 2002 Venkateshwaran -- Thanks for the question regarding "Migrating data from Informix to Oracle", version 8.0
You Asked
Hi Tom,

Hope you are doing good.
Could you please guide me how to migrtate data from Informix to Oracle.

Thank you,
Venkat
and we said...
Go here:
http://otn.oracle.com/software/tech/migration/workbench/content.html
there are migration toolkits available.
Review & Followup
Rating: 3
Useful information July 09, 2002
Reviewer: Ramesh from CA USA
Hi,

The answer which you provided was useful for me. I would like to know the
information on how to proceed with a disconnected model method for converting
Informix to Oracle.

Thanks,

Followup:
describe what you mean by "disconnected" model.

Rating: 5
migration February 18, 2003
Reviewer: mo
Tom:

I am migrating an informix database into oracle. Before I do that I like to
define my database sizing requirements for dba correctly. Here is the stats on
the informix database.

All numbers are in pages and each page is 4KB.

Physical Logs  64000
Logical Logs   64000
Data           512000 total 225667 used
Indexees       81920 total  18294 used
Temporary      5 dbspaces of 15360

Any suggestions,hints or links on how I do this?

Thank you,

Followup:
I guess you would multiply the page counts by 4?  Maybe divide that by 1024 so
you can tell them in megabytes.

forget the physical/logical logs -- let the DBA size out the redo as they figure
out the transaction rates.

Rating: 3
informix migration February 18, 2003
Reviewer: mo
Tom:

1.  Are the logical and physical logs for  sizing the rollback segment? How
would the dba determine this based on transaction rates (is it after he monitors
the database or when he initially creates database)?

2. This translates to the following megabytes:

Data = 2000 M bytes
Index = 320 M bytes
Temporary = 75 M bytes

Would you agree with creating the following 4 tablespaces:

DATA - 2000 M
TEMP - 75 M
RBS - check transaction rate
INDEX - 320 M

Would you create any others like USERS or TOOLS?
would you create 5 TEMP spaces like informix or just one?

Thank you,

Followup:
1) in informix -- undo and redo are stored in the logs.  they would be for
rollback and redo but since we are about as differently architecturally there as
you can be -- their sizes are not relevant.

For example, In Oracle you do not need to size the logs big enough to hold the
largest transaction.  You could have 100meg of log and do a 2000meg update.
Informix cannot, they would fail the transaction at 100meg.

We are very different. Please let your Oracle DBA size out the Oracle stuff --
don't confuse them with irrelevant Informix errata.

2) I aggree with anything you want to do.  Go for it.  that is a tiny database,
you cannot go very wrong.

And again -- let your DBA take care of temp, undo, redo, the stuff they are
hired for.  It would not make sense to have 5 temp tablespaces, no.  But -- they
will know that already.

Rating: 5
infromix migration February 19, 2003
Reviewer: mo
Tom:

Thanks. You just worried me with the word "Tiny" . do you mean not much data? Do
you recommend adding 20% on the sizes I suggested or it is OK to have a tiny
production database and expand later as needed?

Followup:
No, I mean this database is so small that pretty much whatever you do can be
fixed in seconds if it was wrong so tons of "thought" about optimum this that
and the other isn't relevant.  I can fit this on my pendrive and stick it in my
pocket.

July 09, 2002 P -- Thanks for the question regarding "8i > 9i OCP DBA Upgrade", version 9i
You Asked
tom,
I was wondering if you could post it out there for everyone...

I am reading for 8I > 9I OCP DBA upgrade exam.
I just want to know if any good book or reading material is available for 8I to
9I DBA upgrade exam
Right now I am using online documentation but it is so huge that I think I will
never be able to finish it :)
Anyway I appreciate if any of you guys could give me some sort of feedback like
What books or reading material to use?
What type of questions they ask?
Let me know

Thank you for all your help
P
and we said...
I asked Pete Sharman, who is actually an OCP and not only that -- wrote one of
the books on it -- for some feedback here. He says:

...
About the only book that is available for this particular exam that I’m aware of
is the one by Daniel Benjamin:

OCP Oracle9i Database: New Features for Administrators Exam Guide
by Daniel Benjamin (URL on Amazon is
http://www.amazon.com/exec/obidos/ASIN/0072195304/qid=1026179523/sr=1-15/ref=sr_1_15/103-2708932-5577410
There are practice questions available at
http://www.selftestsoftware.com/
in downloadable form or for
limited online access (30 days or 90 days).  There are also details on Oracle’s
web site (
http://www.oracle.com/education/certification/objectives/index.html?content.html
, including the test candidate guide, test content checklist, and
exam details.  If you already know quite a bit of the new material and just need
to brush up on some features, I’d suggest getting the test content checklist to
see what you’re missing and then scan through the online documentation at
http://tahiti.oracle.com/
to fill in the gaps.
Review & Followup
Rating: 3
OCP Upgrade July 12, 2002
Reviewer: Ron Chennells from Newcastle on Tyne (UK)
The Oracle Learning Network (OLN) has an excellent set of
online courses for the upgrade. The book is a little buggy
as are some of the practice questions on the CD that comes
with it. Invest in OLN ...

July 09, 2002 nitin -- Thanks for the question regarding "paging through results on the web with PLSQL", version
You Asked
i am developing a pl/sql cartridge based
package-procedure which shows the LOV for the
addresses(vendors/customers). The window pops up when the lov
button is pressed or a key-event occurs. Now the address table
is
having around 100 thousand records. So we require a facility of
showing the list in chucks with next set/prev set buttons on the

page. So i used a cursor to populate the page and having the
constraint on "rownum" with two parameters - p_from_rows and
p_to_rows. But as we know "rownum >= p_from_rows" wont work. And

we dont want to do the same work with a "rownum" physical column

in the database or using some other kind of workaround. Could
you
help for the page showing chunks of data depending on the user
setting the chunksize?
and we said...
Look at owa_util.cellsprint:

PROCEDURE CELLSPRINT
 Argument Name                  Type                    In/Out
Default?
 ------------------------------ ----------------------- ------
--------
 P_THEQUERY                     VARCHAR2                IN
 P_MAX_ROWS                     NUMBER                  IN
DEFAULT
 P_FORMAT_NUMBERS               VARCHAR2                IN
DEFAULT
 P_SKIP_REC                     NUMBER                  IN
DEFAULT
 P_MORE_DATA                    BOOLEAN                 OUT
 P_RECCNT                       NUMBER                  OUT

In order to print out rows 20-25 of a result set, you can:

  htp.tableOpen;
  owa_util.cellsprint( 'select * from addresses where .....
order by .....',
                        5
                       'N',
                        19,
                        p_more_data,
                        p_reccnt );
  htp.tableClose;

What I like to do, if some of the data in the result set must be
a LINK, is to create a view.  Lets say I want to be able to
click on the ZIP_CODE and have the zip_code be a link to "Yahoo"
to show a map or something.  I would create a view like:

create or replace view addresses_view
select name, addr1, addr2, city, state,
      '<a href=
http://maps.yahoo.com/foo/bar/maps.cgi?zip='
|| zip_code || '>' || zip_code || '</a>'
from addresses
/

So, if i do a cellsprint on that -- it'll have the zip code be a
link.

Cellsprint works by "throwing away" the P_skip_rec records (it
has to, the cursors are closed from call to call as we are
basically "stateless" and then printing the first p_max_rows
rows after that.

You can do this yourself in a way by coding the query as:

select *
  from ( select a.*, rownum rnum
           from ( YOUR_QUERY_GOES_HERE ) a
          where rownum <= MAX_ROWNUMBER )
 where rnum >= MIN_ROWNUMBER
/

In Oracle8i, release 8.1 and up, YOUR_QUERY_GOES_HERE may
include an ORDER BY.  In Oracle8.0 and before IT MAY NOT include
an order by (was not supported).
Review & Followup
Rating: 2
Close but not exactly October 29, 2001
Reviewer: Dennis Magill from Dallas, TX
I need to do this but in a different way, I need to be able to pull a specific
number of records...but I need to do it per product returned.  Any way to modify
this for that.

select *
from ( select a.*, rownum rnum
     from (SELECT e.shipdate,a.acctnum,a.custnum as
parentnum,b.Listable,b.ListDisplayCount,
           a.jobnum,a.acctnum,b.ShowOne,b.custnum,c.proddesc as description
           From venturecore.co_acctdtl a,v_custprodinfo b,v_products c,v_job e
           Where a.custnum = '67500Z' and a.prodnum = b.custnum
           and a.acctnum = '1138361' and
           a.custnum = b.parentnum and c.custnum = a.custnum and c.prodnum =
a.prodnum and
           b.docviewenabled = 'Y' AND a.custnum = e.custnum AND
           a.jobnum = e.jobnum AND b.Listable = 'Y' AND rtrim(a.redojob) is
null) a
     where rownum <= 3 )
 where rnum >= 1

Followup:
In Oracle8i release 2 (816) and up -- yes.  Lets say you wanted the top two paid
employees by department (similar to your requirement, you would order by
something and partition by product).  It could be:

scott@ORA717DEV.US.ORACLE.COM> select deptno, ename, sal
  2    from ( select ename, deptno, sal,
  3                  row_number() over ( partition by deptno
  4                                      order by SAL desc NULLS LAST ) rn
  5             from emp
  6         )
  7   where rn <= 2
  8   order by deptno, sal desc NULLS LAST
  9  /

    DEPTNO ENAME             SAL
---------- ---------- ----------
        10 KING             5000
        10 CLARK            2450
        20 SCOTT            3000
        20 FORD             3000
        30 BLAKE            2850
        30 ALLEN            1600

6 rows selected.

Analytic functions rule -- if you are interested, I have a large chapter on them
in my book that goes over their use in detail.

Rating: 4
paging through results on the web with PLSQL April 11, 2002
Reviewer: Ian Fickling from NC USA
Exactly what I was after. Example is straight to the point.
Also the tip on using hyper links in a view is something I've been doing for
over a year. This confirmed that its OK do to that.

Thanks Tom

Rating: 5
analytic functions coverage in expert one-on-one is great July 05, 2002
Reviewer: A reader
great info.  very helpful, suprised the use of these isn't more prominent and at
least introduced/included in the new 9i SQL exam curriculum.  You can do
something about that Tom can't you?

sometimes the logic for a problem being solved is complicated enough that
procedural code just works out better and provides more future flexibility

Rating: 5
Maximum Page size allowed July 09, 2002
Reviewer: Johan from JHB SA
Tom,

Is there a limit to the amount of data that owa_util.cellsprint can output onto
a single page (besides obviously practical limitations of the browser, which is
typically designed for relatively small page sizes). Is there a limit as with
dbms_output ?

Followup:
nope, you are limited by RAM only -- the htp packages (used by
owa_util.cellsprint) buffer the output in a plsql table (an array) in memory.

July 09, 2002 Piotr -- Thanks for the question regarding "Oracle & Web, regexp, 1-1 relationship", version 9.2.1
You Asked
Hi Tom,

I have 4 questions and I think your valuable answers to them might also be of
use to the Oracle developers out there. Sorry that they are pretty long but I
could not find a free slot in your schedule for weeks so I have bundled them
together...

1. What is the best way to implement a navigable hitlist with Oracle and HTML?
Suppose we have a result set with 200 rows (produced by some search engine) but
we want to display only 10 results per page and let the user navigate the list.
If this were a standalone client we could implement row navigation keeping the
cursor open and fetching rows as necessary and possibly caching them on the
client side (e.g. scrollable ResultSet in Java). But in web app we can't keep
the SQL cursor open across HTTP requests so our navigation must be one SQL query
(parse,bind,execute,fetch,close) per page. My idea is to use a DB hitlist table
where those 200 results would be stored after the search, for instance primary
key of the sought entity, current user session id, search id (user in one
session can do more searches) and an item ordinal number (for sake of page
formatting first 10, next 10, and so on). Then the first page would fetch first
10 results from the hitlist and display them. Navigation bar would be populated
with links having appropriate page number as parameter.

The hitlist could look like this:

create table hitlist (
item_pk number,
sid varchar2(128),
search_id number,
ordinal number,
constraint pk_hitlist_idx primary key (sid,search_id,ordinal))
organization index

Indexed on (sid, search_id, ordinal) so table could/should be an IOT.
A query to fetch the next 10 results from n-th page:

select item_pk from hitlist where sid=:sid and search_id=:search_id and
ordinal>=10*:n and ordinal<10*(:n+1)

The hitlist could be occasionally (every nigth at low activity time) flushed to
keep things small.

The hitlist can also be implemented as a flat file in the form: hitlist.sid. In
this solution we don't need explicitly store the sid and search_id (next search
results would simply overwrite the previous content) and the ordinal would
simply be a position of the row in the file. This however would require to
implement extra logic for file locking but might be even faster.

Another idea would be to use the same search query as to produce original 200
results each time (per each navigation request) and skip initial 10*n rows. But
this might be wrong if the result set could change in between.

I'd like to know which of the 3 above approaches is the best. And maybe there is
another more efficient/simpler way to do it. Could you, please, comment on this?

2. Regexp in Oracle. Suppose I need to scan an articles table:

create articles(art_id number primary key, art_text varchar2(4000))

using full-fledged regexp (it's strange but it is a customer prerequisite). In
MySQL it would be trivial:

select art_id, art_text from articles where art_text rlike :pattern

But as far as I know Oracle does not have rlike operator. Do you think that
Oracle might introduce this feature in some future releases? Meanwhile I have to
do a workaround. I was thinking of using GNU regexp Java package and writing a
Java stored procedure (actually a function) to use this stuff. Then my query
would look:

select art_id, art_text from articles where regexp(art_text,:pattern)=0

This works fine the problem is however - performance.
Regexp, just as PL/SQL, is really done in two stages: compile (preferably once)
and execute (many times reusing compiled structure). So if my articles table
contains 10000 entries regexp function would be called 10000 times, pattern
would be compiled 10000 times and the match executed 10000 times (assuming
Oracle would use full table scan for that query). It could be better than that.
So I use a lookup hashtable inside my function (match pattern->regexp compiled
object) to find a compiled-in structure and reusing it if already there or
compiling and installing in the cache if not found. But I am not sure whether
this is thread-safe (is compiled structure used in read-only fashion in the
execute stage?). I was also thinking about function-based and/or
application-based indices but after a while I figured out it was impossible to
index something that would be regex-searched.

But my question: is there a better (faster) way to do it right in Oracle? Maybe
some dbms_* package?

3. Many SQL manuals maintain that modelling entities with 1-1 relationship is
strange and should be avoided (by merging those entities into one). But I have a
database model when it seems impossible (or impractical) to avoid it. Correct me
if I am wrong, please.

The following should serve an example:
(This project is already working in production and has seen 4 successful
deployments. I just want to make sure that I did the best possible in my
design).

There is an intranet application where users can enjoy (among others not
relevant here) the following services:
1. instant messaging (1-1, 1-n communication device)
2. forum (1-all communication device)
3. library (admins-all communication device)

All of these services can have optional file attachments (unlimited number
thereof).

So I group the attachments into an aggregate: attachment set (through
referential integrity).
Then each of: message, thread_message (in forum) and library_item can optionally
point to the attachment set (also referential integrity but nullable). So in
fact I have an 1-1 relationship between each of: message, thread_message,
library_item and attachment_set. Moreover attachment_set is a degenerated table
consisting only of attachment_set_id column (index=data so IOT again). But the
pure benefit of this approach is that my business logic (attachment set edition)
is implemented just once for all of the above as opposite to merging
attachment_set and message for example. Question: Is there something wrong with
it? Could you think of a better way to model that particular application? Could
you comment on use(ful|less)ness of 1-1 relationships in general?

4. A word on select. In one of the followups on your website I found your
description of how a select works. Example: table_with_million_rows and fetching
the result set. You said that server just fetches one row and hands it out to
the application as a response to the fetch call (rs.next() in Java,
$sth->fetchrow() in Perl, etc). Server just gives it out, takes a note in the
cursor object and forgets. Perhaps a new block in the cache being also a
side-effect of it. But this was just a simple query. What about queries with
order by, group by clause and what about nested queries? For instance:

select * from table_with_million_rows order by name;

Isn't it so that the server must first gather all the results and sort them
before giving them to the application? Or does it sort them on the fly (kinda
insert or rather outsert sort :-)?

Thank you in advance,
Many regards

Piotr
and we said...
1) search for

paging on the web

The first hit:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:76812348057
shows my approaches to this. Asktom uses the first approach there for paging
through its results.

2) search for

regex

on this site. I even deal with the "compile once" "use many issue"

3) couldn't picture your model in my head.   you don't give me the actual model
as far as I could tell (or it would take far too many re-reads to figure it out
on my own with a high degree of possible "mistaken assumption" on my part.

as far as I can tell -- I don't see the relationship between message,
thread-message for example.

4) It depends.

is there an index on name?
is name the primary key in an IOT (and hence already sorted on disk)?

There are more then one way to crack a nut.  In *general* we give you the rows
on the fly as much as possible -- using TEMP only when we need to (i always said
"in general" -- people assume that we "copy" the data up front and serve that
out -- that is wrong)
Review & Followup
Rating: 3
More data July 09, 2002
Reviewer: Piotr from Braunschweig, Germany
For 1. and 2. I should have searched your archives before... Sorry :-<

Answer for 4. is satisfying.

As far as 3. is concerned I will give you some more details:

                 1-1                  1-n
MESSAGE           ->
THREAD_MESSAGE    ->  ATTACHMENT_SET   <-  ATTACHMENT
LIBRARY_ENTRY     ->

Each entity has lots of attributes and I don't want to obscure the whole
picture. The most important things here are on the above figure (relationships
and their cardinalities).

Each of MESSAGE, THREAD_MESSAGE, LIBRARY_ENTRY are completely independent. One
thing that they have in common is the ATTACHMENT_SET (optional). ATTACHMENT_SET
if (exists) may contain 1 to many individual file attachments.

So, for example: message has an attribute aset_id that can point to an existing
attachment_set or may be empty. It is kind of 1-1 relationship (one message may
point at most to one attachment set). ATTACHMENT_SET exists merely as common
subexpression (if you let me use the mathematical analogy) and it has only one
attribute it's unique ID. Now my common business logic cares only of the
ATTACHMENT_SET-ATTACHMENT entities. When it's done the resulting aset_id is
associated with any of MESSAGE, THREAD_MESSAGE, LIBRARY_ITEM.

So my concern is: because ATTACHMENT_SET is so pathologically small and it does
have 1-1 relationship with e.g. MESSAGE is there a way to phase it out of the
model? Or would leave it as is here?

I hope I reduced the likelihood of your "mistaken assumption" :-).

Thanks for your answer and waiting impatiently for your follow-up.

Regards,
Piotr

Followup:
ah, ok -- here is a good time for a one to one relationship that uses two
tables.

That is when an object (attachment set) is 1-1 with MANY other objects (message,
thread_message, library_entry).

Now, question -- can an attachment set below to a MESSAGE, THREAD_MESSAGE and or
LIBRARY_ENTRY (your model certainly *permits* it but do you allow it)

What you have done is fine and in fact necessary if you wanted to use
declaritive integrity (eg: a fkey from attachement back to attachment_set and
then a fkey from message|thread_message|library_entry to attachment_set).

Rating: 4
July 10, 2002
Reviewer: Piotr from Braunschweig, Germany
> Now, question -- can an attachment set below to a
> MESSAGE, THREAD_MESSAGE and or LIBRARY_ENTRY (your model
> certainly *permits* it but do you allow it)

Sorry but I do not understand this question.
Anyway I have made this model, written SQL & PL/SQL and written application
business logic in Perl so I could change everything without much (if any) impact
on other people - web designers. But you have said it is fine so I have much
more confidence I had done a good design. I could leave out attachment_set if I
gave up the referential integrity, it is clear, but I did not want to. It is
better to delegate that job to Oracle.

Thanks galore!
Regards,
Piotr

July 09, 2002 Rosidi -- Thanks for the question regarding "Compatibility", version 7.3.4
You Asked
Hai,
My 7.3.4 oracle was not compatibility configured. ( did set compatible in
init.ora file). So its defaulted to 7.0.0 .
As a result I can not resize my datafile ( alter database datafile 'filename'
resize 200M) which required compatability to be set to 7.2 or higher.
My question is what are the effects/problems of changing the compatibile to 7.2.
and we said...
It is recommended to do a backup BEFORE changing compatible.

Changing compatible will enable new features, new data structures to be used.
File formats will change (redo log file structures for example will change).  It
could cause product issues that have not manifested in your implementation to
"appear".  You need the ability to "go back" (hence the backup).

You would want to set compatible to 7.3, not 7.2.
Review & Followup
Rating: 4
Any philosphy on how to set it? July 09, 2002
Reviewer: Doug from Southbury, CT USA
I just noticed I cannot do a dynamic alteration of freelists on an 8.1.7
database because the compatability is set to 8.1.5.  (needs to be 8.1.6)  Do you
have a philosophy on how to set compatability?  As far up as you can?  To my
understanding, one of the main points in allowing it to be backleveled is to
make use of older features (things you need to be "compatable" with, therefore
shouldn't it be set as close to the real version of the database as possible
with proper testing?

Followup:
well, when you upgrade, you might leave it at the old level (since it permits
file format changes to happen as well -- eg: compatible affects the format of
the redo logs -- if you set it up -- you cannot back out of an upgrade).

Once tested and comfortable with the upgrade, backup -- up it to the current
level and test again (realizing that you may restore that backup in the event of
something "bad" happening)

July 09, 2002 Piotr -- Thanks for the question regarding "ORA-00604 - what does it mean", version 9.0.1.0.0
You Asked
Hi,

I have the following problem:

SQL> select * from t;
select * from t
              *
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-04092: cannot COMMIT in a trigger
ORA-06512: at line 12
ORA-00942: table or view does not exist

And table `t' does not exist.
But this happens no matter what user I am, no matter what I do but only if there
are errors (assuming that it would normally give only the last error).

I was looking for the trigger that might do commit but there is no single one in
any schemas in our database.

What does this ORA-00604 mean?
Does it have something to do with recursive SQL queries?
Is this some kind of internal problem?
How can I get rid of it?

Thanx in advance,
Piotr
and we said...
someone created an "after servererror" trigger and is committing in it.
consider:

ops$tkyte@ORA9I.WORLD> drop user a cascade;

User dropped.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> grant create session to a identified by a;

Grant succeeded.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> @connect a/a
ops$tkyte@ORA9I.WORLD> set termout off
a@ORA9I.WORLD> REM GET afiedt.buf NOLIST
a@ORA9I.WORLD> set termout on
a@ORA9I.WORLD> select * from t;
select * from t
              *
ERROR at line 1:
ORA-00942: table or view does not exist

a@ORA9I.WORLD>
a@ORA9I.WORLD> @connect /
a@ORA9I.WORLD> set termout off
ops$tkyte@ORA9I.WORLD> REM GET afiedt.buf NOLIST
ops$tkyte@ORA9I.WORLD> set termout on
ops$tkyte@ORA9I.WORLD> drop table log;

Table dropped.

ops$tkyte@ORA9I.WORLD> create table log( msg varchar2(255) );

Table created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> create or replace trigger error_trigger
  2  after servererror on database
  3  begin
  4     insert into log values ( 'hi' );
  5     commit;
  6  end;
  7  /

Trigger created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> @connect a/a
ops$tkyte@ORA9I.WORLD> set termout off
a@ORA9I.WORLD> REM GET afiedt.buf NOLIST
a@ORA9I.WORLD> set termout on
a@ORA9I.WORLD> select * from t;
select * from t
              *
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-04092: cannot COMMIT in a trigger
ORA-06512: at line 3
ORA-00942: table or view does not exist

a@ORA9I.WORLD>
a@ORA9I.WORLD> @connect /
a@ORA9I.WORLD> set termout off
ops$tkyte@ORA9I.WORLD> REM GET afiedt.buf NOLIST
ops$tkyte@ORA9I.WORLD> set termout on
ops$tkyte@ORA9I.WORLD> select owner, trigger_name
  2    from dba_triggers
  3   where trigger_type = 'AFTER EVENT';

OWNER                          TRIGGER_NAME
------------------------------ ------------------------------
SYS                            AURORA$SERVER$STARTUP
OPS$TKYTE                      ERROR_TRIGGER

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> drop trigger error_trigger;

Trigger dropped.

Use that last query to find the offending trigger and either make it an
autonomous transaction so it can commit or remove the commit.
Review & Followup
Rating: 4
It just takes to know right things May 24, 2002
Reviewer: Piotr Jarmuz from Braunschweig, Germany
Now all is clear. I understand the flow of locus of control. Actually I knew
about system level triggers but not about `after servererror' so actually I
could not associate it with this message. I used an autonomous transaction and
now it all works well. And I also traced down the culprit who or rather what set
this trigger up.

Thank you again.

Rating: 3
can commit cause problem in Logoff trigger July 09, 2002
Reviewer: Dilip from NY
In before logoff trigger, I am updating flag like this:

..before logoff on database..

If <condition> THEN
      Update table
         set col = releaseflag ;
==>   COMMIT ;
End if ;
..
Can Commit in this trigger cause the database to shutdown?
Thanks!
DilipM

Followup:
it should not.

Rating: 5
What about Forms 6i January 03, 2003
Reviewer: Luis Soto from Dominican Republic
In forms 6i every time I shutdown and restart Personal Oracle 8.1.5 in Win98Se
Forms give me this error when I try to commit my chages. I tried it in SQL*Plus
and it Works well.  If I compile the form again it runs well.  What drive me
crazy is that it happen just with one form the others works well.

Any Help Will be really apreciate

Luis Soto

Rating: 5
superb answer for after servererror May 07, 2003
Reviewer: jignesh from india
u r simply gr8

July 09, 2002 Chandra -- Thanks for the question regarding "problem in connecting to 9i from oc4j appln server as a sysdba", version 9i
You Asked
Class.forName("oracle.jdbc.driver.OracleDriver");

Connection con =
DriverManager.getConnection("jdbc:oracle:thin:@net:1521:ora9i","system AS
SYSDBA", "chandu" );

The above code is not working fine and it's not giving me the connection object
from oracle 9i backend when I tried to connect as a sysdba.  I kept the above
code in oc4j container.  And it's giving the message as "java.sql.SQLException:
ORA-01017: invalid username/password; logon denied".

But this code works fine(in both 8i and 9i) if the statement is as follows.

Connection con =
DriverManager.getConnection("jdbc:oracle:thin:@net:1521:ora9i","system",
"chandu" );

But I would like to connect to 9i only and should be able to access all the
schemas of 9i database.

Thanks in advance if somebody can help me.
and we said...
see
http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/java.901/a90211/basic.htm#1006248
shows how to use the internal_logon property
July 09, 2002 Ed -- Thanks for the question regarding "Cursor Expressions", version 8.1.7
You Asked
Tom,

I have been asked to write a procedure (in 8.1.7.2.0) that returns a REF CURSOR
(to a calling PHP script) using a SQL statement with a cursor expression.

It's along the lines of:

   OPEN p_cc_rep FOR
   SELECT dep.name,
          CURSOR(SELECT cc.code,
                        cc.description,
      ... etc

When I try to compile my package I get "PLS-00103: Encountered the symbol
"SELECT" when expecting one of the following: " etc.

The SQL statement works fine when called as a standalone statement.

Now it's not a big deal, as I can code it using a join (which is not an issue
with the quantity of data we're dealing with).  Alternatively I can get the PHP
routine to use the SQL statement directly (though I'd rather avoid this, as we
use packages for all interface between PHP and Oracle at the moment).

But why does this happen?  Is there some incompatability between SQL and the
PL/SQL engine? And can I use dynamic SQL to get round this?

Also when I try to get SQL*Plus to run the standalone SQL statement, I get
"ERROR: ORA-00932: inconsistent datatypes".  Is there a problem with cursor
expressions in SQL*Plus, or is it just because I'm using an old version -
3.3.2.0.2 (don't ask why :-()?

Are there many practical uses for cursor expressions (except for where the
'parent' record has huge quantities of data)?  I couldn't find any references to
them in my Oracle bible (your book).

Regards,

Ed (I'd rather be using 9iAS anyway)
and we said...
Ed -- if you see this, your supplied email is not working, just FYI...
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3027089372477
the other thing -- sqlplus -- is because sqlplus at that release didn't
understand that a select could select a cursor (functionality did not exist in
7.3)

I actually do reference them, page 908.  There is just so much in Oracle, it's
impossible to cover everything in detail ;)
Review & Followup
Rating: 5
Doh! July 10, 2002
Reviewer: Ed from Cheshire, UK
How frustrating!  I get a chance to ask a question, and it's already been
covered!

I did try searching for "Cursor Expressions", though.

I haven't had a chance to read your book from cover to cover ... yet!

Ed

July 09, 2002 Robert -- Thanks for the question regarding "Application Design Principles", version 8.1.7
You Asked
Tom,

Can you please give us some of your core application design principles?
(assuming an Oracle database).

Also, I remember a book (I think it may have had Oracle's endorsement) about
table design, including normalization, which used an airline reservation system
as its model to explain and illustrate the various principles. Do you remember
such a book, and if so would you recommend it?

Thanks,

Robert.
and we said...
Well, I actually wrote a book on that topic:
http://www.amazon.com/exec/obidos/ASIN/1861004826
lots of reviews are there, if you goto asktom.oracle.com, you can click on a
link to read a sample chapter.  It explains my "core principles".

I don't remember that other book -- I'll publish this so others can comment if
they have seen it.
Review & Followup
Rating: 4
Application Design Principles July 09, 2002
Reviewer: Scott LeBlanc from Calgary, Alberta Canada
The book you are refering to is
Case Method - Entity Relationship Modelling.  Written by Richard Barker.

ISBN - 0-201-41996-4

Rating: 5
Thanks Tom and Scott. July 09, 2002
Reviewer: Robert from Memphis, TN

Rating: 5
Very nice Book about E-R Modelling July 10, 2002
Reviewer: pasko from Hamburg,Germany
I think That's a Great Book about E-R Modelling and Design.
But may be not so for people wanting Denormalized databases..

For instance, i remember to have read from it that , if you have  Table that is
not linked to other tables, then your Design is flawed-but is that True?

Plus, Oracle is now Objects-Relational - But that is not covered  from this
Book.

Otherwise,It's a Great Book because iit even has the stuff we learned in college
 about Relational Database Management Systems ( RDBMS ) - Boyce Codd rules , and
all Normalization/Denormalization  principles-Step-by-Step.

Regards

July 09, 2002 Susan -- Thanks for the question regarding "public synonym", version 8.1.7
You Asked
Hi Tom,

We are using public synonyms to access certain procedures across projects.
Unfortunately, on some projects the procedure also is owned by the schema
project.  So, we have test_proc in one schema as a public synonym and test_proc
in the schema project.  Our ASP page just calls test_proc -- which test_proc is
being called?  Who has precedence?  Thanks Tom for all the help.
and we said...
we will always look in the current_schema first, then public.

So, if user X has a procedure TEST_PROC and there is a public synonym TEST_PROC
that points to SCOTT.TEST_PROC and user X executes "test_proc", they will run
"X.TEST_PROC"

This demonstrates this fact:

ops$tkyte@ORA817DEV.US.ORACLE.COM> set linesize 70
ops$tkyte@ORA817DEV.US.ORACLE.COM> desc all_objects
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 OWNER                               NOT NULL VARCHAR2(30)
 OBJECT_NAME                         NOT NULL VARCHAR2(30)
 SUBOBJECT_NAME                               VARCHAR2(30)
 OBJECT_ID                           NOT NULL NUMBER
 DATA_OBJECT_ID                               NUMBER
 OBJECT_TYPE                                  VARCHAR2(18)
 CREATED                             NOT NULL DATE
 LAST_DDL_TIME                       NOT NULL DATE
 TIMESTAMP                                    VARCHAR2(19)
 STATUS                                       VARCHAR2(7)
 TEMPORARY                                    VARCHAR2(1)
 GENERATED                                    VARCHAR2(1)
 SECONDARY                                    VARCHAR2(1)

ops$tkyte@ORA817DEV.US.ORACLE.COM> create view all_objects as select * from
dual;

View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> desc all_objects;
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 DUMMY                                        VARCHAR2(1)

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop view all_objects;

View dropped.
Review & Followup
Rating: 5
May 15, 2003
Reviewer: Helena Marková from Bratislava, Slovakia

July 09, 2002 Martin -- Thanks for the question regarding "attributes of an object definition", version 8.1.7
You Asked
Tom,

given the following object type definition:

CREATE OR REPLACE TYPE O_Object1 AS OBJECT (
      attribute1          NUMBER(15),
       attribute2       NUMBER
);

I understand, one can find an Object Type(O_Object1) in USER_OBJECTS.

I would like to know if there is a way to retrieve attributes(attribute1,
attribute2) of an object type from the data dictonary as well.

Thanks for your assistance.
Regards,
Martin Kachel
and we said...
             USER_TYPES
             USER_TYPE_ATTRS
             USER_TYPE_METHODS

are the tables you are looking for.
July 09, 2002 Michael -- Thanks for the question regarding "Graph validation", version 8.1.7
You Asked
Hello Tom!

I have the following tables:

CREATE TABLE NODES
( ID NUMBER(38) PRIMARY KEY,
  Name VARCHAR2(40)
);

CREATE TABLE GRAPH
( Parent_ID REFERENCES NODES,
  Child_ID REFERENCES NODES,
  PRIMARY KEY (Parent_ID, Child_ID),
  CHECK (Parent_ID != Child_ID)
);

This tables model a graph = an many-to-many-relationship between nodes.
Now i want to ensure with triggers, that there isn't a loop in the graph:

NODES:
ID     Name
================
 1     AA
 2     BB
 3     CC
 4     DD
 5     EE

GRAPH:
Parent_ID   Child_ID
=======================
 1           2   (OK)
 1           3   (OK)
 2           4   (OK)
 2           4   (OK)
 4           1   (Not allowed! - Should be rejected)

I tried the following approach:

- To prevent the simple direct loop (parent and child are the same) i introcued
the CHECK constraint.

- To prevent the complex indirect loop (like in the example) i implemented the
following package and triggers:

CREATE PACKAGE MY_MEMORY_PKG
    TYPE IDArray IS TABLE OF ROWID INDEX BY BINARY_INTEGER;
    UsedMemory RowIDArray;
    EmptyMemory RowIDArray;
END;

CREATE TRIGGER GRAPH_BS_TRIG
BEFORE INSERT OR UPDATE ON GRAPH
BEGIN
    MY_MEMORY_PKG.UsedMemory := MY_MEMORY_PKG.EmptyMemory;
END;

CREATE TRIGGER GRAPH_AR_TRIG
AFTER INSERT OR UPDATE ON GRAPH
FOR EACH ROW
BEGIN
   MY_MEMORY_PKG.UsedMemory(NVL(MY_MEMORY_PKG.UsedMemory.LAST, 0) + 1) :=
:NEW.RowID;
END;

CREATE TRIGGER GRAPH_AS_TRIG
AFTER INSERT OR UPDATE ON GRAPH
DECLARE
     Dummy NUMBER; Loop_found EXCEPTION;
     PRAGMA EXCEPTION_INIT (Loop_found, -1436);
BEGIN
     LOCK TABLE GRAPH IN EXCLUSIVE MODE NOWAIT;
     FOR i IN MY_MEMORY_PKG.UsedMemory.FIRST .. MY_MEMORY_PKG.UsedMemory.LAST
LOOP
        SELECT NULL INTO Dummy
        FROM GRAPH
        START WITH GRAPH.RowID = MY_MEMORY_PKG.UsedMemory(i)
        CONNECT BY PRIOR GRAPH.Child_ID = GRAPH.Parent_ID;
     END LOOP;
EXCEPTION
     WHEN Loop_found THEN
         RAISE_APPLICATION_ERROR(-20200, 'Loop in Graph.');
END;

Now my questions:

1. Is this a good solution?

2. I don't know how to avoid the LOCK TABLE IN EXCLUSIVE MODE. I think i need
it, because if another session makes (uncommited) changes/inserts, i (the AFTER
STATEMENT TRIGGER) can't see them. So the changes from the other session
combined with the changes/insert from my session could result in a loop.

GRAPH before:
Parent_ID   Child_ID
=======================
 1           2
 1           3
 2           4
 2           4

t = 0: Session 1: Doing something else.
t = 1: Session 2: INSERT INTO GRAPH VALUES (5, 1); (Isolated OK)
t = 2: Session 1: INSERT INTO GRAPH VALUES (4, 5); (Isolated OK)
t = 3: Session 2: COMMIT;
t = 4: Session 1: COMMIT;

GRAPH after:
Parent_ID   Child_ID
=======================
 1           2
 1           3
 2           4
 2           4
 4           5
 5           1

Now without LOCK TABLE there would be a loop ...

Is there a possibility to avoid the LOCK TABLE without losing data integrity?

3. Is it a good idea to create an index on the Child_ID to speed up the
SELECT... CONNECT BY?

Thanks in advance.
and we said...
1) well, personally - i might put this logic into a stored procedure and not try
to be so fancy with the triggers.

Good is sometimes in the eye of the beholder, I would not necessarily do it like
that -- but it works.

2) Well, that lock table is a deadlock just waiting to happen.

If two sessions do this at about the same time, they will both fire the row
level trigger (and will have gained some row locks on the table).  Then the
first one to the after trigger will block on the lock (cannot lock that table,
someone else is using it).  Then when the other session gets to the after
trigger -- there will be a deadlock.  One of them will get an error -- the other
will continue to block until the session that got the error releases its locks.

I don't see anyway of not serializing at the table level in this case.  You must
wait for each session to finish their work before starting your work in order to
validate your rules.

If this model is actually an "example" model and the table is "bigger" (eg: not
just parent/child but some other attribute to identify the specific GRAPH) we
could serialize at the GRAPH level using dbms_lock instead.

3) given that you:

 CONNECT BY PRIOR GRAPH.Child_ID = GRAPH.Parent_ID;

the index should be on PARENT_ID, not child_id.  You take the Prior child_id
(the row you just retrieved, you have that) and look up the matching parent_id
records.
Review & Followup
Rating: 5
July 11, 2002
Reviewer: A reader
Thanks!

July 09, 2002 Sameer -- Thanks for the question regarding "Replication of Designer 6i Repository", version 8.1.7
You Asked
We are usign Designer 6i versioning feature. We upload Forms in designer
repository and then Do check - in after uploading the forms.

We want to Replicate Designer Repository at Another Location by creating both
the sites as Master Sites, so that user can upload the forms at either sites.

Is it Possible to Replicate Repository? As Replication is done on Database
objects , will uploaded form will also get replicated?

Thanks in advance
and we said...
No, you cannot replicate a designer repository -- it is 100% impossible to
replicate, especially for update anywhere, an application that was not designed
for replication from day 1.

You could possibly (maybe) create a read only replica but it would involve
modifying every trigger and lots of other stuff.

The capability to update anywhere -- not a chance.
July 09, 2002 Lon -- Thanks for the question regarding "Does PL/SQL support composite types in 8.1.7", version 8.1.7
You Asked
Tom,

We have an application that needs to keep a list of integers inside a PL/SQL
record. The records definition is as follows:

   Type tblFNOs is table of integer index by binary_integer;
   Type rOwnershipAttrInfo is record(
          iTableCNO integer,
          sTableName varchar2(128),
          sOwnerID1FieldName varchar2(128),
          sOwnerID2FieldName varchar2(128),
          sOwnerCNO1FieldName varchar2(128),
          sOwnerCNO2FieldName varchar2(128),
          iFNOCnt integer := 0,
          tFNOs tblFNOs);

I know this is possible in 9i, but we cannot demand that our customers be on 9i
at this time. Is there any way in 8.1.7 to do this?
Or maybe a workaround?

Thanks for your support.
and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2     Type tblFNOs is table of integer index by binary_integer;
  3     Type rOwnershipAttrInfo is record(
  4            iTableCNO integer,
  5            sTableName varchar2(128),
  6            sOwnerID1FieldName varchar2(128),
  7            sOwnerID2FieldName varchar2(128),
  8            sOwnerCNO1FieldName varchar2(128),
  9            sOwnerCNO2FieldName varchar2(128),
 10            iFNOCnt integer := 0,
 11            tFNOs tblFNOs);
 12  begin
 13          null;
 14  end;
 15  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle8i Enterprise Edition Release 8.1.7.3.0 - Production
PL/SQL Release 8.1.7.3.0 - Production
CORE    8.1.7.0.0       Production
TNS for Solaris: Version 8.1.7.3.0 - Production
NLSRTL Version 3.4.1.0.0 - Production

Yes it does (as do all versions pretty much).  It is TABLES of TABLES (arrays of
arrays) that is new in 9i.
July 10, 2002 Gert -- Thanks for the question regarding "mapping an oracle.exe process to an instance on NT", version
You Asked
On NT 4.0 performancemonitor, All Oracleprocesses are
displayed as ORACLE73. Is there a tool that can tell me what
instance in meant by what ORACLE73-process?
and we said...
You can use "TLIST.EXE" from the Windows NT Resource Kit to
determine
which process ID is associated with which Oracle Instance.

The tool "TLIST.EXE" lists memory usage and commandline
information for a
given process. Do the following:

1) Go to the Windows NT task manager and select the processes
tab.

2) If the PID column isn't present, go to the View pulldown menu
   and choose "Select Columns".

3) Check the box for PID (Process Identifier).

4) Now find your "ORACLExx.EXE" processes and their associated
PIDs.

You can also find the PID from the command line as opposed to
Task Manager.
To find Oracle Related processes from the command line, using
TLIST,
you can do the following:

C:\>tlist | grep -i oracle
 269 ORACLE80.EXE
 235 ORACLE73.EXE
 123 PLUS80W.EXE       Oracle SQL*Plus
 217 CMD.EXE           Command Prompt - grep -i oracle
 227 oracle80.exe

Note that the "GREP.EXE" command is also available as part of
the
Windows NT Resource Kit.  The GREP makes TLIST look for
Processes
that have Oracle in their name or in their description.

To find information about a certain PID, just run TLIST and the
PID:

C:\>tlist 269 | grep -i CmdLine
   CmdLine: d:\ora805\bin\oracle80.exe OR85
C:\>tlist 227 | grep -i CmdLine
   CmdLine: d:\ora804\bin\oracle80.exe OR84
                                       ^^^^^ there is the SID
Review & Followup
Rating: 4
"mapping an oracle.exe process to an instance on NT", version July 09, 2002
Reviewer: S.Sivakumar from India
Hi Tom,

Can you elaborate more on how to find the memory consumed by
oracle processes (ie. background processes) using tlist or other utility.

Thanks in advance

Regards,
Siva

Followup:
Task Manager will show you much of this information (there is only one
process/instance on NT)

Perfmon can give you more details.

v$sesstat in the database is what I would look at (pga/uga memory) -- that way,
no matter the platform, I can tell.

July 10, 2002 Godwin -- Thanks for the question regarding "Writing Trigger or Procedure on a table to update/insert record(s) in another table.", version 7.3.2
You Asked
Tom thanks for your previous answers. am still working around it to fix that
problem.
As if today is my day with you, I just run into another problem.
Here we go...
There is a Human resource database designed some years back but fails to capture
some information and am trying to incorporate them into the database. The
question is there is a table called personel that contain details about staff
say personel(paanum number PK,rank number,dept varchar2,...). The rank field
takes values like 1,2,3... with different descriptions.
The other table which am deriving is
RANK_ARRANG(code number,rank_type varchar2,paanum number UNIQUE)

Now,q1. I need to write a procedure which will select records (already inserted
into personel)from personel and base on some conditions will insert matching
records into RANK_ARRANG table eg.

select paanum,rank from personel
 if rank in (1,2,3) then
   insert into RANK_ARRANG
   values(rank,'SM',paanum);
 elsif rank in (4,5,6) then
  insert into RANK_ARRANG
   values(rank,'JS',paanum);
end if;
This procedure can be run once a while.

q2. Write a trigger on the same problem but this time whenever a record is being
created in the personel table the insertion into the corresponding RANK_ARRANG
table must be done at the same time.

Can please give me a simple code to implement the above problem.
Thanks for your precious time.
and we said...
why would you do that at all?

why not just select out

decode( rank, 1, 'SM', 2 'SM', 3, 'SM', 4, 'JS', '5, 'JS', 6, 'JS', null )

and be done with it -- I would not have a column at all for this obviously
derived data.
Review & Followup
Rating: 2
Need additional comments July 04, 2002
Reviewer: Godwin from Ghana
Tom,I mean the table personel has already in it more than 3000 records. Now the
reason am creating another table to capture rank filed is this:
the rank that contains values such like
1 stands for Senior Member Academic
2 stands for Senior Member Non-Academic
... ... .... ..........................
4 stands for Junior Member Academic
5 stands for Junior Member  Non-Academic
.......................................

Now management want a report of all staff by rank type
and am designing this report using Report 6.
I know the simple query could have been

select * from personel where rank in (1,2,3)--- for senior members
and
select * from personel where rank in (4,5,6)--- for senior members

Now writing this query in Report by passing a parameter is not accepting
multiple values e.g
select * from personel where rank in :P_1 --- for senior
so that in passing the parameter one will type 1,2 but it does not work that
way.
Therefore, I decided to create another table which will group the ranks into
different categories thus
for 1,2,3 will go for 'SM' i.e Senior Members
and 4,5,6 will go for 'JS' i.e Junior Members

So because the table personel already contains many records without the field
that groups the rank into SM and JS, I want to create a seperate table to take
care of that so that the procedure will pick records from personel into that
RANK_ARRANG table base on those conditions.
Hope u understand my point so please help me out.
Thanks.

Followup:
create view v
as
select p.*, decode( rank, 1, .... ) RANK
  from personel p
/

use V in the report, it'll appear as if RANK exists.

Rating: 5
So whu not do like that... July 04, 2002
Reviewer: Visitor from Japan
select * from personel where rank in (:P_1,:P_2,:P_3)

and do the asignment the way you want.

Rating: 4
Thanks for the answer July 08, 2002
Reviewer: Godwin from Ghana
Hi Tom,
Your suggestion works perfectlty. This is what am looking for.

Rating: 4
Executing this statement in forms July 09, 2002
Reviewer: Godwin from Ghana
Hi Tom,
Back again with some worry. I want to execute this DDL statement in forms 6 but
getting some compilation error:
e.g
FORMS_DDL('create view Rank_V
as select paanum,decode(
paapcde,1,'SM',2,'SM',3,'SM',4,'SM',5,'SS',6,'JS',7,'SM',8,'SM',9,'SM',10,'SM',11
,'SS',12,'CA',13,'JS',14,'DE',15,'TA',16,'VS',17,'GA',20,'PT',25,'PE',27,'NS',30,
'HA',35,'EE') rank
from paapr1');

but get the error message "Encounted the symbol "SM" when expecting one of the
ff .(),*@%$/=.+</> is not range...

Does it have to do with the Character SM and how do i got by it.
Thanks.

Followup:
FORMS_DDL('create view Rank_V
as select paanum,decode(
paapcde,1,''SM'',2,''SM'',3
          ^^  ^^   ^^  ^^   use TWO quotes inside of a character string literal
to
                            get a single quote

July 10, 2002 Shivaswamy -- Thanks for the question regarding "Average of time", version 817
You Asked
Tom,

Could I ask for your help for the following task.?

I have a table which stores the start & end time of tasks. I need to find out
the average of time taken for a given task, when executed multiple timees.

SQL> desc swamy.t;
 Name                                                                     Null?
  Type
 ------------------------------------------------------------------------
-------- -----------------
 TASK
  VARCHAR2(20)
 SDT
  DATE
 EDT
  DATE

SQL> select * from swamy.t;

TASK                 SDT       EDT
-------------------- --------- ---------
eating               08-JUL-02 08-JUL-02
reading              08-JUL-02 08-JUL-02

2 rows selected.

SQL> compute avg of Times on report
SQL>  select task,
  2   trunc(edt -sdt) || ' - ' ||
  3   trunc(mod((edt - sdt) *24, 24))  || ':' ||
  4   trunc( mod( (edt- sdt)*24*60, 60)) || ':' ||
  5   trunc( mod( (edt-sdt)*24*60*60, 60)) "Times"
  6   from swamy.t
  7  /

TASK                 Times
-------------------- --------------------
eating               0 - 0:0:34
reading              0 - 0:0:50

Is converting all the times in to seconds and then avg it and convertback to
hh:MI is the ony solution?

TIA,
Shivaswamy
and we said...
You cannot really use sqlplus to compute the "average time" like you want.  You
can use group by rollup however:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( task varchar2(20), sdt date,
edt date );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t
  2  select dbms_random.string('a',20), sysdate,
sysdate+dbms_random.value(1,1000)/24/60+dbms_random.value(0,1)
  3    from all_users
  4  /

41 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> column times format a20
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select nvl(task,'Tot Avg'),
  2         trunc( avg(edt-sdt) ) || ' - ' ||
  3         to_char(trunc(mod((avg(edt-sdt)) *24, 24)),'fm00')  || ':' ||
  4         to_char(trunc( mod( (avg(edt-sdt))*24*60, 60)),'fm00') || ':' ||
  5         to_char(trunc( mod( (avg(edt-sdt))*24*60*60, 60)),'fm00') Times
  6    from t
  7  group by rollup(task);

NVL(TASK,'TOTAVG')   TIMES
-------------------- --------------------
AXXaMWOQkU\Ke`r^kTqA 0 - 06:00:28
Bh`_P]_VNKJUNF^`iOrk 1 - 06:02:14
CGKmjJeaks^p[PZn`Otm 0 - 13:09:35
EKGHr\FRVZq\\TJs]KiS 0 - 09:34:38
EOVGM^odLKGajcKWrKRd 0 - 14:14:15
EYf_tEcJ_fcftC\XhX\R 0 - 17:56:01
E_pChbmOtYSD\BkG_a_A 0 - 18:24:51
FGHoc]EBM_nJRs_beZiT 0 - 06:40:37
GbQ[SFNNOE_tD`rFmHrX 0 - 12:54:20
I`WKNF^TWpBI\NstgTJn 0 - 04:40:19
IfnSDUZmU\DU\F^TPTJt 0 - 19:24:15
KJppDGfisJA^VSdAcDls 1 - 12:07:44
LL_inhjtDNKJOITp\FFh 0 - 10:40:56
MZ[RGXTf\sanSEkY_hNU 0 - 19:25:44
PeOBDTaNDTbdtAph\I`a 0 - 16:16:00
QraSleYgXFdOQSKI[_mg 1 - 06:40:42
RI[q`pn]knT`kSljWpYX 0 - 10:27:22
R[B^Zi[aIMeTA\SBeZIV 1 - 02:30:39
Rd_AOqTkiTdpgfJ`iqla 0 - 02:51:51
Uog[]d_dskqOMfaPoLqZ 0 - 20:53:01
UtNogTkJEIMIARcL_beA 0 - 04:53:47
YG`npbjLSZFgbkGYB_gi 0 - 20:01:27
ZRQFbeRgCV]ch^UoTXLq 0 - 13:35:08
[PjRppBcRnOt^\FU_ObN 0 - 19:30:17
[kUUiEAqHiiHYQKfi]kA 1 - 00:50:45
^KoTgnFaTYJDjs`ZmIhC 1 - 00:33:28
_nF^LmPiVQiB\YoWiK]L 0 - 21:10:46
`hI_TASHpTGFnEn[QPm[ 0 - 17:06:14
`qKb^GqE]UVDbhtESFIi 0 - 14:14:11
dpOfEcrOX^pZMbI[BmTf 0 - 20:38:50
flabFErKYfYkQpXRtmZc 0 - 16:55:15
ha]_Lb[pMC`DtSXjAP`a 1 - 07:23:05
iIOnp^qDP]p\FrANUZ^F 1 - 00:21:43
jprtXZgNlpZNh[MJTkDo 1 - 09:05:13
lqck^nlM`OTfF[lrc]cR 1 - 09:11:22
oJLVEIh_pit\iJHQBOJA 1 - 07:54:35
oPG[mNL[gDFJQdhQ]mN[ 0 - 13:49:40
orsc]ZSro_ZjAsDWTjoF 1 - 01:05:07
qNIGqG\sQelttEjWIZkJ 0 - 21:44:38
rWH_ZMoPl_RCqr`_\iOf 0 - 03:47:53
ssSrSsUiL]_FsVJ_ojX\ 1 - 00:32:19
Tot Avg              0 - 18:42:57

42 rows selected.

That shows the average time was 18 hours, 42 minutes and 57 seconds...
Review & Followup
Rating: 4
July 09, 2002
Reviewer: shivaswamy from Dallas, TX, USA
Tom,

This query will help me to get a running average of all tasks, very well. But I
need to find average time taken for those identical tasks which are executed
more than once.

Is there any better way than converting all the times in to seconds, average it
and convert back to hh:mi?

Followup:
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t
  2  select mod(rownum,5), sysdate,
sysdate+dbms_random.value(1,1000)/24/60+dbms_random.value(0,1)
  3    from all_users
  4  /

41 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> column times format a20
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select task,
  2         trunc( diff ) || ' - ' ||
  3         to_char(trunc(mod(diff *24, 24)),'fm00')  || ':' ||
  4         to_char(trunc( mod( diff*24*60, 60)),'fm00') || ':' ||
  5         to_char(trunc( mod( diff*24*60*60, 60)),'fm00') Times,
  6         decode( avg_diff, null, null,
  7         trunc( avg_diff ) || ' - ' ||
  8         to_char(trunc(mod(avg_diff *24, 24)),'fm00')  || ':' ||
  9         to_char(trunc( mod( avg_diff*24*60, 60)),'fm00') || ':' ||
 10         to_char(trunc( mod( avg_diff*24*60*60, 60)),'fm00') ) avg_diff
 11    from (
 12  select task,
 13         edt-sdt diff,
 14         decode( lead(sdt) over (partition by task order by sdt),
 15                 NULL, avg(edt-sdt) over ( partition by task ), NULL )
avg_diff
 16    from t
 17         )
 18  /

TASK                 TIMES                AVG_DIFF
-------------------- -------------------- --------------------
0                    0 - 23:36:53
                     0 - 04:37:53
                     0 - 20:48:48
                     0 - 20:53:01
                     0 - 21:04:32
                     0 - 07:03:08
                     0 - 06:30:19
                     0 - 15:27:44         0 - 15:00:17

1                    1 - 02:50:47
                     0 - 23:43:03
                     0 - 19:52:20
                     1 - 11:43:57
                     0 - 10:36:28
                     0 - 19:23:31
                     0 - 11:02:07
                     1 - 08:36:33
                     0 - 13:47:50         0 - 21:30:44

2                    0 - 21:49:58
                     1 - 02:39:28
                     0 - 17:17:54
                     1 - 03:06:10
                     0 - 13:29:28
                     0 - 18:01:11
                     0 - 17:21:13
                     0 - 09:40:38         0 - 18:55:45

3                    0 - 18:32:19
                     0 - 08:44:00
                     1 - 08:20:14
                     0 - 17:38:52
                     1 - 11:26:36
                     0 - 16:03:25
                     0 - 22:03:07
                     0 - 17:33:28         0 - 21:02:45

4                    1 - 02:09:31
                     0 - 22:35:00
                     0 - 11:50:50
                     1 - 09:46:33
                     0 - 16:40:43
                     0 - 17:11:17
                     0 - 20:23:57
                     1 - 00:24:50         0 - 21:37:50

41 rows selected.

Rating: 5
July 10, 2002
Reviewer: Shivaswamy
Great !!
Tom, Thank you **So much**
(This is for second follow up..)

July 10, 2002 Jon -- Thanks for the question regarding "Generating XML doc seems slow & Cursor Results Sets", version 8.1.7
You Asked
Tom,

2 questions:

1) We have the latest version of the XDK for PL/SQL installed into an 8.1.7
database on Solaris.  Generating an XML doc using the supplied XMLGEN package
(or DBMS_XMLQUERY) appears slow compared with the speed I can generated an XML
doc "manually" using BULK COLLECT.  For example:

-- Using XMLGEN
declare
    vXML clob;
    vBuff varchar2(4002);
    vTime number;
begin
    vTime := dbms_utility.get_time;
    vBuff := 'select OWNER, TABLE_NAME, TABLESPACE_NAME,
               CLUSTER_NAME, IOT_NAME, PCT_FREE, PCT_USED, INI_TRANS,
               MAX_TRANS, INITIAL_EXTENT, NEXT_EXTENT, MIN_EXTENTS,
               MAX_EXTENTS, PCT_INCREASE, FREELISTS, FREELIST_GROUPS,
               LOGGING, BACKED_UP, NUM_ROWS, BLOCKS
              from all_tables';
    XMLGen.setRaiseException(true);
    vXML := XMLGen.getXML(vBuff);
    vTime := 10*(dbms_utility.get_time-vTime);
    dbms_output.put_line('XMLGEN: '||vTime||' msecs');
    dbms_lob.freeTemporary(vXML);
end;

XMLGEN: 3200 msecs

-- Using BULK COLLECT
declare
    NL constant char(1) := chr(10);
    type TXMLRows is table of varchar2(4000);
    vXMLRows TXMLRows;
    vXML clob;
    vRow varchar2(4002);
    vTime number;
begin
    vTime := dbms_utility.get_time;
    select '<OWNER>'||OWNER||'</OWNER>'
        ||'<TABLE_NAME>'||TABLE_NAME||'</TABLE_NAME>'
        ||'<TABLESPACE_NAME>'||TABLESPACE_NAME||'</TABLESPACE_NAME>'
        ||'<CLUSTER_NAME>'||CLUSTER_NAME||'</CLUSTER_NAME>'
        ||'<IOT_NAME>'||IOT_NAME||'</IOT_NAME>'
        ||'<PCT_FREE>'||PCT_FREE||'</PCT_FREE>'
        ||'<PCT_USED>'||PCT_USED||'</PCT_USED>'
        ||'<INI_TRANS>'||INI_TRANS||'</INI_TRANS>'
        ||'<MAX_TRANS>'||MAX_TRANS||'</MAX_TRANS>'
        ||'<INITIAL_EXTENT>'||INITIAL_EXTENT||'</INITIAL_EXTENT>'
        ||'<NEXT_EXTENT>'||NEXT_EXTENT||'</NEXT_EXTENT>'
        ||'<MIN_EXTENTS>'||MIN_EXTENTS||'</MIN_EXTENTS>'
        ||'<MAX_EXTENTS>'||MAX_EXTENTS||'</MAX_EXTENTS>'
        ||'<PCT_INCREASE>'||PCT_INCREASE||'</PCT_INCREASE>'
        ||'<FREELISTS>'||FREELISTS||'</FREELISTS>'
        ||'<FREELIST_GROUPS>'||FREELIST_GROUPS||'</FREELIST_GROUPS>'
        ||'<LOGGING>'||LOGGING||'</LOGGING>'
        ||'<BACKED_UP>'||BACKED_UP||'</BACKED_UP>'
        ||'<NUM_ROWS>'||NUM_ROWS||'</NUM_ROWS>'
        ||'<BLOCKS>'||BLOCKS||'</BLOCKS>'
    bulk collect into vXMLRows
    from all_tables;
    dbms_lob.createTemporary(vXML, true, dbms_lob.call);
    for i in 1..nvl(vXMLRows.Last,0) loop
        vRow := vXMLRows(i)||NL;
        dbms_lob.writeappend(vXML, length(vRow), vRow);
    end loop;
    dbms_lob.freeTemporary(vXML);
    vTime := 10*(dbms_utility.get_time-vTime);
    dbms_output.put_line('Bulk Collect: '||vTime||' msecs');
end;

Bulk Collect: 1270 msecs

With the system I am working on, generating XML docs quickly is important.  The
supplied XDK packages appear to get exponentially slower as the number of rows
and/or columns increase.  Using bulk collect, however, results in a linear
performance drop off as rows/columns increase.  The difference between these two
approaches in favour of bulk collect can be huge (20+ secs) with some queries.
The problem with using bulk collect is that it is tedious for developers to
manually construct XML docs like this, and it doesn't work if an XML "row" is >
4000 bytes.

Is the something wrong with XMLGEN implementation to be this slow?  Is it better
in 9i (unfortunatley upgrading is not an option at the moment)?  Is there some
way of getting this faster?  And btw, are we suppose to be using XMLGEN or
DBMS_XMLQUERY?

2) When a ref cursor is passed back to a client application (VB, SQLPlus etc.)
the tool in some way has access to the columns and their datatypes when
displaying the result set.  Within PL/SQL, when a ref cursor is passed to a
procedure, can we access that same information - essentially the structure of
the cursor?  You can probably see where I am going with this in light of
question 1...

Thanks for your assistance.
Regards,
Jon Waldron
and we said...
Specific code is almost certainly ALWAYS going to be faster then generic code --
almost certainly.  As you point out also, your "optimal" approach will vary by
the data -- you cannot always do the concatenation.  It is not a generic
approach but rather very specific to the data itself.

I did run a test in 9iR1 and and then R2 and this was the output:

ops$tkyte@ORA9I.WORLD> declare
  2      vXML clob;
  3      vBuff varchar2(4002);
  4      vTime number;
  5          vCtx number;
  6  begin
  7      vTime := dbms_utility.get_time;
  8      vBuff := 'select OWNER, TABLE_NAME, TABLESPACE_NAME,
  9                 CLUSTER_NAME, IOT_NAME, PCT_FREE, PCT_USED, INI_TRANS,
 10                 MAX_TRANS, INITIAL_EXTENT, NEXT_EXTENT, MIN_EXTENTS,
 11                 MAX_EXTENTS, PCT_INCREASE, FREELISTS, FREELIST_GROUPS,
 12                 LOGGING, BACKED_UP, NUM_ROWS, BLOCKS
 13                from all_tables';
 14
 15          vCtx := dbms_xmlgen.NEWCONTEXT( vBuff );
 16      vXML := dbms_XMLGen.getXML(vCtx);
 17      vTime := (dbms_utility.get_time-vTime)/100;
 18      dbms_output.put_line('XMLGEN: '||vTime||' secs, ' ||
dbms_lob.getlength(vXML) || ' bytes');
 19      dbms_lob.freeTemporary(vXML);
 20  end;
 21  /
XMLGEN: 1.64 secs, 325847 bytes

PL/SQL procedure successfully completed.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> declare
  2      NL constant char(1) := chr(10);
  3      type TXMLRows is table of varchar2(4000);
  4      vXMLRows TXMLRows;
  5      vXML clob;
  6      vRow varchar2(4002);
  7      vTime number;
  8  begin
  9      vTime := dbms_utility.get_time;
 10      select '<OWNER>'||OWNER||'</OWNER>'
 11          ||'<TABLE_NAME>'||TABLE_NAME||'</TABLE_NAME>'
 12          ||'<TABLESPACE_NAME>'||TABLESPACE_NAME||'</TABLESPACE_NAME>'
 13          ||'<CLUSTER_NAME>'||CLUSTER_NAME||'</CLUSTER_NAME>'
 14          ||'<IOT_NAME>'||IOT_NAME||'</IOT_NAME>'
 15          ||'<PCT_FREE>'||PCT_FREE||'</PCT_FREE>'
 16          ||'<PCT_USED>'||PCT_USED||'</PCT_USED>'
 17          ||'<INI_TRANS>'||INI_TRANS||'</INI_TRANS>'
 18          ||'<MAX_TRANS>'||MAX_TRANS||'</MAX_TRANS>'
 19          ||'<INITIAL_EXTENT>'||INITIAL_EXTENT||'</INITIAL_EXTENT>'
 20          ||'<NEXT_EXTENT>'||NEXT_EXTENT||'</NEXT_EXTENT>'
 21          ||'<MIN_EXTENTS>'||MIN_EXTENTS||'</MIN_EXTENTS>'
 22          ||'<MAX_EXTENTS>'||MAX_EXTENTS||'</MAX_EXTENTS>'
 23          ||'<PCT_INCREASE>'||PCT_INCREASE||'</PCT_INCREASE>'
 24          ||'<FREELISTS>'||FREELISTS||'</FREELISTS>'
 25          ||'<FREELIST_GROUPS>'||FREELIST_GROUPS||'</FREELIST_GROUPS>'
 26          ||'<LOGGING>'||LOGGING||'</LOGGING>'
 27          ||'<BACKED_UP>'||BACKED_UP||'</BACKED_UP>'
 28          ||'<NUM_ROWS>'||NUM_ROWS||'</NUM_ROWS>'
 29          ||'<BLOCKS>'||BLOCKS||'</BLOCKS>'
 30      bulk collect into vXMLRows
 31      from all_tables;
 32      dbms_lob.createTemporary(vXML, true, dbms_lob.call);
 33      for i in 1..nvl(vXMLRows.Last,0) loop
 34          vRow := vXMLRows(i)||NL;
 35          dbms_lob.writeappend(vXML, length(vRow), vRow);
 36      end loop;
 37      vTime := (dbms_utility.get_time-vTime)/100;
 38      dbms_output.put_line('Bulk Collect: '||vTime||' secs, ' ||
dbms_lob.getlength(vXML) || ' bytes');
 39      dbms_lob.freeTemporary(vXML);
 40  end;
 41  /
Bulk Collect: 1.71 secs, 350442 bytes

PL/SQL procedure successfully completed.

ops$tkyte@ORA920> declare
  2      vXML clob;
  3      vBuff varchar2(4002);
  4      vTime number;
  5  begin
  6      vTime := dbms_utility.get_time;
  7      vBuff := 'select OWNER, TABLE_NAME, TABLESPACE_NAME,
  8                 CLUSTER_NAME, IOT_NAME, PCT_FREE, PCT_USED, INI_TRANS,
  9                 MAX_TRANS, INITIAL_EXTENT, NEXT_EXTENT, MIN_EXTENTS,
 10                 MAX_EXTENTS, PCT_INCREASE, FREELISTS, FREELIST_GROUPS,
 11                 LOGGING, BACKED_UP, NUM_ROWS, BLOCKS
 12                from all_tables';
 13
 14      vXML := dbms_XMLGen.getXML(vBuff);
 15      vTime := (dbms_utility.get_time-vTime)/100;
 16      dbms_output.put_line('XMLGEN: '||vTime||' secs, ' ||
dbms_lob.getlength(vXML) || ' bytes');
 17      dbms_lob.freeTemporary(vXML);
 18  end;
 19  /
XMLGEN: 1.29 secs, 342798 bytes

PL/SQL procedure successfully completed.

ops$tkyte@ORA920>
ops$tkyte@ORA920> declare
  2      NL constant char(1) := chr(10);
  3      type TXMLRows is table of varchar2(4000);
  4      vXMLRows TXMLRows;
  5      vXML clob;
  6      vRow varchar2(4002);
  7      vTime number;
  8  begin
  9      vTime := dbms_utility.get_time;
 10      select '<OWNER>'||OWNER||'</OWNER>'
 11          ||'<TABLE_NAME>'||TABLE_NAME||'</TABLE_NAME>'
 12          ||'<TABLESPACE_NAME>'||TABLESPACE_NAME||'</TABLESPACE_NAME>'
 13          ||'<CLUSTER_NAME>'||CLUSTER_NAME||'</CLUSTER_NAME>'
 14          ||'<IOT_NAME>'||IOT_NAME||'</IOT_NAME>'
 15          ||'<PCT_FREE>'||PCT_FREE||'</PCT_FREE>'
 16          ||'<PCT_USED>'||PCT_USED||'</PCT_USED>'
 17          ||'<INI_TRANS>'||INI_TRANS||'</INI_TRANS>'
 18          ||'<MAX_TRANS>'||MAX_TRANS||'</MAX_TRANS>'
 19          ||'<INITIAL_EXTENT>'||INITIAL_EXTENT||'</INITIAL_EXTENT>'
 20          ||'<NEXT_EXTENT>'||NEXT_EXTENT||'</NEXT_EXTENT>'
 21          ||'<MIN_EXTENTS>'||MIN_EXTENTS||'</MIN_EXTENTS>'
 22          ||'<MAX_EXTENTS>'||MAX_EXTENTS||'</MAX_EXTENTS>'
 23          ||'<PCT_INCREASE>'||PCT_INCREASE||'</PCT_INCREASE>'
 24          ||'<FREELISTS>'||FREELISTS||'</FREELISTS>'
 25          ||'<FREELIST_GROUPS>'||FREELIST_GROUPS||'</FREELIST_GROUPS>'
 26          ||'<LOGGING>'||LOGGING||'</LOGGING>'
 27          ||'<BACKED_UP>'||BACKED_UP||'</BACKED_UP>'
 28          ||'<NUM_ROWS>'||NUM_ROWS||'</NUM_ROWS>'
 29          ||'<BLOCKS>'||BLOCKS||'</BLOCKS>'
 30      bulk collect into vXMLRows
 31      from all_tables;
 32      dbms_lob.createTemporary(vXML, true, dbms_lob.call);
 33      for i in 1..nvl(vXMLRows.Last,0) loop
 34          vRow := vXMLRows(i)||NL;
 35          dbms_lob.writeappend(vXML, length(vRow), vRow);
 36      end loop;
 37      vTime := (dbms_utility.get_time-vTime)/100;
 38      dbms_output.put_line('Bulk Collect: '||vTime||' secs, ' ||
dbms_lob.getlength(vXML) || ' bytes');
 39      dbms_lob.freeTemporary(vXML);
 40  end;
 41  /
Bulk Collect: 1.46 secs, 462291 bytes

PL/SQL procedure successfully completed.

(hey, maybe generic code isn't so bad after all).  The big difference here is
that in 9i, the XML layer is written natively in C under the covers.  That can
lend a huge performance boost.

2) You need to use a DBMS_SQL cursor with that.  DBMS_SQL has the ability to
describe a dbms_sql cursor.  A ref cursor is not describable in PLSQL and there
is no method to dynamic fetch from it with different numbers of columns (eg:
fetch cursor_name into variable_list is well known at COMPILE time -
variable_list is an explicit list).

That and you cannot bulk collect from a dynamically opened ref cursor in 8i.

So, you would need to use dbms_sql.
Review & Followup
Rating: 4
Can we use the C version? July 09, 2002
Reviewer: Jon Waldron from Sydney, Australia
Tom, thanks for your prompt response.  Is there some way we can link in the 9i C
library used by XMLGEN?

Followup:
only by using a DBLINK from 8i to 9i ;)

It is part of the kernel, you either are using 9i or you are not.

July 10, 2002 Rama -- Thanks for the question regarding "Connect Internal", version 8.1.6
You Asked
Hi Tom,

In the Server Manager, while we connect as the internal user, we are not giving
any password.  While remaining connected as the internal user, if I query the
v$session view, I get to know that I am connected as the sys user.  Why is this
so?  Further, is it not required that the internal user also be authenticated
through a password?  Because, the internal user is capable of doing anything and
everything with the database.

Rama Subramanian G
and we said...
Connect internal can be password protected on NT but on UNIX any member of the
DBA group (that is the default group, it can be changed upon install) can
connect internal without being challenged.  On UNIX, you would preclude people
from the DBA group and they will not be able to connect internal

That internal shows as SYS is normal.  Even when you use sysdba it'll be SYS:

ops$tkyte@ORA817DEV.US.ORACLE.COM> connect / as SYSDBA
Connected.
ops$tkyte@ORA817DEV.US.ORACLE.COM> show user;
USER is "SYS"
ops$tkyte@ORA817DEV.US.ORACLE.COM> connect /
Connected.
ops$tkyte@ORA817DEV.US.ORACLE.COM> show user;
USER is "OPS$TKYTE"

That is expected -- there is no "internal" user really.

In actuality, you should get out of the habit of using internal as it is
deprecated and in 9i "goes away", does not exist anymore.
Review & Followup
Rating: 4
Startup & Shutdown Of Database July 10, 2002
Reviewer: Rama Subramanian G from Bangalore, India
Thanks Tom.

Typically, in our production environment, we require to grant the privilege of
database startup and shutdown alone and no other privileges of the dba user to
some users.  With your reply in perspective, is that not possible?

Rama Subramanian G

Followup:
grant SYSOPER to those people (that lets them startup, shutdown)

do NOT put them in the DBA group (assuming unix here).

Now, they can connect "username/password AS SYSOPER" and startup/shutdown.  The
cannot "connect internal", they cannot connect as sysdba.

You most certainly can achieve your goal.

Rating: 4
July 10, 2002
Reviewer: Ivan from Germany
We have significant amount of scripts with contain like:

spool %DB_SCRIPTFILES_PATH%\InstallKernel.log
-- here internal-user create environment and other users
disconnect
connect internal/oracle@&1
@@creTbsKernel.sql &1 &2 &3 &4 &5 &6 &7
@@creAppUser.sql &1 &2 &3 &4 &5 &6 &7
-- than concrete-user
connect user1/pwd1@&1
...

What is best way by your opinion to adopt our scripts for 9i?
Do we have to inspect and change every script manually?
Thanks

Ivan

Followup:
internal as a user does not exist in 9i (i believe is was version 8.0 we started
saying that -- 4 or 5 years ago).

You will have to "connect some_user/some_pw@&1 AS SYSDBA"

July 10, 2002 Amit -- Thanks for the question regarding "Authentication methods for batch jobs running against a database in a Win2000 environment.", version 8.1.6 on Win2000
You Asked
Hi Tom.

Consider the following setup (all servers run win2000):

 * In a firewall protected DMZ:

  Web servers-  Running IIS 5, using Oracle Objects 4 OLE (OO4O)
                with connection pools for DB access.

 * On the internal LAN:

  Batch server- Running batch jobs (written in java) as windows
                services.
  DB server-    Running Oracle 816.

The web application uses an OO4O connection pool to logon to
the database using a regular database user (APP_MAIN). The
APP_MAIN
schema contains non-sensitive data.

Additionally, there are a few sensitive tables in the database,
which
reside in another schema (APP_SECURE), and there are a few
stored-procedures in APP_SECURE (which have GRANT EXECUTE for
APP_MAIN)
through which data can entered by user APP_MAIN.

The data in APP_SECURE is processed by batch jobs running on
a dedicated batch server- hence the batch process needs to
access APP_SECURE's tables in a secure manner.
Batch job processes are java programs configured to run
as NT services .

The problem:
Which is the most secure way to implement connection
authentication for the batch programs to the database ?

I am considering implementing one of the following methods for
this (assuming SQL*NET is encrypted)-

1. Create a user DOMAIN\SECUREUSER in the windows domain, and an
appropriate
   one in the database (identified externally). The services
will be
   configured to run as this user.

   PRO: In this option DOMAIN\SECUREUSER password is managed in
the DOMAIN.
   CON: If someone manages to impersonate / simulate being
logged on to
        the domain as DOMAIN\SECUREUSER he can fool the database
and gain
        access to the secure data.

2. Create a configuration file on the batch server's local disk
and
   set its OS permissions so that only DOMAIN\SECUREUSER can
read this file
   and retrieve the password. The batch services will run as
DOMAIN\SECUREUSER.

   PRO: A password based login is used against the database- so
impersonation
        is not an issue.
   CON: I don't like the fact that there is a text file
containing the
        password to the secure schema (even though it is
protected by operating
        system permissions & password).

Questions:

1. For option #1: How is NTS implemented, and can the protocol
prevent
  hackers from faking the DOMAIN\USER passed to Oracle (for
example
  by hacking the thin JDBC protocol to simulate logon and
sending queries etc..) ?

2. Are there any other alternatives for implementing connections

   for batch programs ?

3. Which method would you choose, and why ?

Thanks-
Amit.
and we said...
NTS is implemented as an ASO adapter, actually. The database
kernel asks NT for the identity of the user on the other end of
the connection and is independent of the protocol. So, unless
the user spoofs Windows they can't spoof us.

OS Authentication over a network is a little safer in Windows NT
then in Unix (in unix, using remote os authentication is a BIG
mistake unless you "own" the entire networking infrastructure).
The user would have to get NT to believe they were who they
purported to be.  If you trust your NT security, this would be
the way to go.

Another option is to write the batch applications in PLSQL or
Java and have them run as jobs in the database directly.  You
would log in as the user who should run the batches and use
DBMS_JOB to run these processes on a recurring basis.  Since
they run IN the database, BY the database -- there is no
authentication taking place at runtime -- we use the user of the
person who submitted the job in the first place when we goto run
these jobs.  This would be by far the most secure and failsafe -
the jobs will only run if and when the database is available and
there are NO authentication worries....
Review & Followup
Rating: 4
Authentication July 10, 2002
Reviewer: Abdal-Rahman Agha from Saudi Arabia
Hi Tom,

  I have a problem arround this. When I execute a job (backup for example) from
the OEM, the job pane shows that the job fails because of: Authentication error:
VNI-2015.

Although I created a user in the win2000 and granted him "Logon as a batch job"
privilege. What will be the problem. My oracle is 817.

Followup:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1331402545512
it has trouble shooting steps.

July 10, 2002 Santosh -- Thanks for the question regarding
      "Disabling Statement Caching of PL/SQL engine when executing PL/SQL
      procedure.", version 8.1.7

      You Asked
Hi,

As I understand, when I execute a PL/SQL stored procedure(SP), a implicit cursor
is created & its put in OPEN_CURSOR list in Oracle.
For performance reasons, this remains open even after all the static SQLs are
executed in the SP.

But if I execute the SP with all the static SQLs converted to Dynamic SQLs, the
Statement Caching doesn't happen.

I have tried following statement, but that doesn't seems to be effective.

SQL> ALTER SESSION SET close_cached_open_cursors=TRUE

Can you please suggest me a way to programatically DISABLE  the statement
caching by PL/SQL engine when we execute the SP?

Thanks,
Santosh

      and we said...
WHY ?????

why in the world would you want to do this?

You do understand that if

a) you run plsql that caches 100 statement
b) you have open cursors set to 100
c) you try to open another cursor

that we will automagically really close one of them in order to open your
statement -- that the cursors are held "open" but "replacable" -- so that we can
get great performance (reused cursors) and not hit "max open cursors"

No, you cannot do this.

No, you do not want to do this.

close_cached_open_cursors was obsoleted when we changed this method in 8i.
Review &
      Followup

      Rating:
      5
  July 09, 2002
Reviewer:
      A reader

      Rating: 3
Reply -
      Statement Caching  July 10, 2002
Reviewer:
      Santosh  from India
I agree that its not a good practise to disable Statement Caching of PL/SQL.

The SP is used by the Web application.
When we perform the load test, we get following message:
ORA-01000: maximum open cursors exceeded

I guess the primary reason for this is the Statement caching.

Please suggest...

          Followup:

Your JAVA (guessing that) application is leaking cursors then!

(or you really don't have enough cursors configured)

search this site for ora-01000.

I have yet to see a case where this was not a bug in the java app (with it
leaking statement handles and not closing the cursors -- just letting the
variables go out of scope)

July 10, 2002 Riaz -- Thanks for the question regarding "Few
      Questions", version 8.1.5

      You Asked
Hi Tom !

Thankx for your quick response. I've been your fan since last 3 years. I really
learned through asktom.oracle.com. That's a great forum.

I've 2 questions:

Q.1) I have the table having information like this:

S#                                Name
--                                ----
1                                 aa
2                                 ali
4                                 bb
6                                 tt
7                                 cc
10                                xx

I want to find the missing serial nos. (that are 3,5,8,9). How can i do that?

Will the query be same if data is like:

s#
--
1
1
2
5
7
10
1
11
90
12

Q.2) I have table holding information about offices. I want Serial No. to be
unique with respect to offices and each new day the serial no. should be
re-started (i.e from 1). For this i created sequence for each office (through
database trigger "after Insert"). Is this right way to do that? How can i use
these sequences in Forms? I want something like:

select 'seq_'||:office_code.nextval from dual;

i.e; select next value (or current one) of the sequence whose name is based upon
the office_code column. How can i do that ?

And how can i have sequences automatically re-started every day?

Thanks

Riaz

      and we said...
q1) read

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3225867788098
q2) YUCK, double yuck.  why would you want to do that.  Sigh....

You would have to use dynamic sql in your trigger

  execute immediate
   'select seq_' || :office_code || '.nextval from dual' into whatever;

You would have to write a dbms_job or something to "reset" your sequence
(although there truly is not a "reset sequence").   see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1119633817597
but I would strongly advise you to reconsider your approach entirely.  Use a
single sequence (no dynamic sql) and don't reset it, just let it increment.
Review &
      Followup

      Rating:
      4
  July 09, 2002
Reviewer:  Riaz
      Shahid  from PRAL, Islamabad, Pakistan
Hello Tom!

You misunderstood the way...through trigger (after delete/insert on offices
table), i just drop/insert sequences and nothing more. Definitiely i will use
execute immediate for selecting nextval from sequences.

The reasons for having separate sequences & resetting the sequence each day are
quite obvious and are necessary. There are approx. 500+ offices using that S/W
and entering about 35,000 daily. Ecach office has its own serial. Can u imagine
abut using a single sequence in this scenario ?  I think not... 35000 *  500 =
1750000 serails increament daily. What do u think ?

          Followup:

I think I would use a sequence number, just like i said.

Lets do the math here ok (then it'll be obvious there is no issue).

First, each office has it's own sequence right?  Well, why do you multiply by
500 then?  We just are incrementing a sequence by 35000 each day -- and have 500
sequences.  So the number / day for each sequence is 35k

Now, how many days can we do that for?

ops$tkyte@ORA817DEV.US.ORACLE.COM> select 999999999999999999999999999/35000
days,
  2         999999999999999999999999999/35000/366 years
  3    from dual
  4  /

      DAYS      YEARS
---------- ----------
2.8571E+22 7.8064E+19

Hmmm, looks OK to me -- how about you?  will your software still be running in
Seventy-Eight quintillion Sixty-Four quadrillion Twelve trillion Four Hundred
Ninety billion Two Hundred Forty-One million Nine Hundred Ninety-Eight thousand
Four Hundred Thirty-Eight years??

Even if we use your 500 (or even BETTER 5000) and multiply 35k by that, you
won't exhaust the sequence for Fifteen quadrillion Six Hundred Twelve trillion
Eight Hundred Two billion Four Hundred Ninety-Eight million Forty-Eight thousand
Three Hundred Ninety-Nine years.  Seems fairly safe to me.

      Rating: 3
Your r
      Right  July 10, 2002
Reviewer:  Riaz Shahid
       from PRAL, Islamabad, Pakistan
Hay Tom !

Yes. You are absolutelty right. But what happens that soemtimes you have to work
opposite to what you are thinking (as with me). So i wouldn't waste your time in
discussing that whether it is right or wrong...because all the problem here is
that i will have to do that (even if i don't like such approach).

Tom ! i wouldn't use execute immediate  for the selection of nextval from
sequence no. I want a function that returns the nexval of seqeuce formed based
upon the value of Offcode like this:

create or replace function select_seqno(offcode in varchar2) return number is
a number;
begin
select 'seq_'||offcode||'.nextval' into a from dual;
return a;
end;

But it is not working properly. Please suggest what to do...

Regards

          Followup:

disagree with your premise 10000% or more

I am frequently asked "how do I do...." and I respond -- tell me WHY you believe
you want to do such a thing.  I won't answer.  When I discover the reason (many
times it is "because we did it that way in Sybase" or something) I explain HOW
to do the same thing in Oracle -- correctly, properly and efficiently.

So, I will ALWAYS state my mind -- It is not a waste of time to say "what you
are doing is a truly horrible idea, it stinks".

In answer to your question....  Well, if you won't use EXECUTE IMMEDIATE (or its
next of kin DBMS_SQL), you WON'T be doing your dynamic select (which will by the
way -- decrease the performance of your system, increase the amount of resources
you need and be a truly horrible idea).

You *need* to use dynamic sql in order to accomplish your stated end goal -- to
dynamically (at runtime, not compile time) pick the sequence you want to fetch
from.

create or replace function select_seqno(offcode in varchar2) return number is
a number;
begin
execute immediate 'select seq_'||offcode||'.nextval from dual' INTO A;
return a;
end;

is the way to code that very bad idea.

      Rating: 1
now i know why
      tom displays always displays this :  July 10, 2002

Reviewer:  Mohammed Ahmed  from Arabia
"Sorry I have a large backlog right now, please ask a question later "

I see most of his time goes down the drain on issues like this.
====================================================
For such a requirement, its business need and logic is to be strictly
questioned.
Tom,
Can you do the community a favor? by considering a method to put a close/lock to
such a threads?

          Followup:

I choose not to edit these.

Actually, the reason you see that message is because on a "good" day I can take
maybe 30-40 questions, max.

      Rating: 5
I agree with
      the above  July 10, 2002
Reviewer:  JULIA
       from Israel
Tom, excuse me for being so straightforward, but I think you should reformulate
your site dynamics. You waste too much time with thigs that are useless and we,
on the other hand, lose the opportunity to ask relevant questions. Why don't you
just ignore such questions and reviews and only answer questions you find
interesting ?

Thank you

          Followup:

because in this case -- I wanted to take the opportunity to try and educate.
When I see something really bad, like this, I want to say "stop!  think about
this!  what you are doing is *wrong*"

Otherwise, what will happen is -- they will implement this bad idea, then say
"hey, this oracle stuff is junk, look how slow it is.  they don't scale".

Believe it or not but you do not actually see 2 out of every 3 questions that
come through.  In the last 2.5 years, I've gotten over 16,000 questions, of
which you can see only about 5,500.  I try to answer everyone who posts (as long
as they have an email address that works) -- even if to simply say "sorry, don't
know"

      Rating:
      5
  July 10, 2002
Reviewer:
      A reader
These guys sometimes sucks ...looks like they don't know or understand what they
are talking about....meanwhile...wasting Tom's precious time

      Rating: 5
More
      comments  July 10, 2002
Reviewer:  JULIA
       from Israel
Tom, me again... I have observed that lately your site is getting "vertical".
Your pages are getting longer and longer. One of the reasons is people are
always getting the "Sorry, I have a large backlog" message. Then they try to ask
questions through the reviews. They search for a review relative to want they
want to ask and you end up answering. I suppose that was not your intention, at
least initially. Have you ever imagined how this is going to wind up if you
don't arrange your site differently ? I myself get lost sometimes with such
LOOOOOONG pages. It was not like this before you implemented reviews. And come
on, Tom, these are not "reviews" so to speak... these are people asking
questions and saying things. Some of these things get directly to the review
without censoring, unlike your regular questions.

      Rating: 5
It's useful
      anyway..  July 10, 2002
Reviewer:  Jan van
      Mourik  from Houston, TX
Even seeing questions about trying to do "obviously" bad things still help me.
Except from being told how to do something, it's good to be reminded NOT to do
something every now and then. Threads like this one help me in that respect.

Sad to see though that after all our Y2K problems Tom is blatantly ignoring a
possible Y-Fifteen quadrillion Six Hundred Twelve trillion
Eight Hundred Two billion Four Hundred Ninety-Eight million Forty-Eight thousand
Three Hundred Ninety-Nine-K problem in this case...

July 10, 2002 Chris -- Thanks for the question regarding
      "Extracting Table/Column Comments as XML", version 9.0.1

      You Asked
Hi Tom.

I have been exploring the supplied DBMS_METADATA.GET_XML function, which is very
nice, but I'm suprised it doesn't extract the USER_TAB_COMMENTS and
USER_COL_COMMENTS along with the rest of the metadata.

Am I missing something?  Is there a simple way to extract the comments as an XML
doc?

Thanks.
cg

      and we said...
I asked Sean Dillon, our locl XML guru, to take a look at this and here's what
he had to say:
----------------------

Hi cg,

In Oracle9i Release 1, COMMENT is not a supported object type in DBMS_METADATA.
The package is enhanced in Oracle9i Release 2.  9i R2 has MANY more object types
available for "export" to DDL or XML via the DBMS_METADATA package, so that's an
option to look at if you are using that package heavily in your environment.

Let's look at both options.  In 9i R2, use the following commands to test the
capabilities:

* CREATE A TEST TABLE:
ops$sdillon@FUNK92.US.ORACLE.COM> create table t (some_int number)
  2  /
Table created.

* CREATE A COMMENT FOR THE TABLE:
ops$sdillon@FUNK92.US.ORACLE.COM> comment on table t is
  2  'This is a sample table to try and show off the functionality of
DBMS_METADATA';
Comment created.

* WE'LL USE THIS PROCEDURE TO PRINT OUT THE XML RESULTS:
ops$sdillon@FUNK92.US.ORACLE.COM> create or replace procedure print_clob(
  2    p_clob in clob)
  3  is
  4    l_idx pls_integer := 1;
  5    l_amt pls_integer := 255;
  6  begin
  7    if p_clob is null then return; end if;
  8    debug.f('clob is %d bytes',length(p_clob));
  9    for i in 1 .. ceil(length(p_clob)/l_amt) loop
 10      dbms_output.put_line(substr(p_clob,l_idx,l_amt));
 11      l_idx := l_idx + l_amt;
 12    end loop;
 13  end print_clob;
 14  /
Procedure created.

* NOW WE'LL USE DBMS_METADATA TO SPECIFY THE TYPE OF OBJECT WE WANT TO
RETRIEVE...
* (COMMENT), THE TABLE NAME VIA A FILTER, THE SCHEMA OWNER VIA A FILTER, AND
THEN
* WE'LL RETRIEVE THE QUERY AND THE XML:
ops$sdillon@FUNK92.US.ORACLE.COM> declare
  2    l_handle number;
  3    l_clob   clob;
  4    l_xml    sys.xmltype;
  5  begin
  6    l_handle := dbms_metadata.open('COMMENT');
  7    dbms_metadata.set_filter(l_handle, 'BASE_OBJECT_NAME', 'T');
  8    dbms_metadata.set_filter(l_handle, 'BASE_OBJECT_SCHEMA', 'OPS$SDILLON');
  9    print_clob(dbms_metadata.get_query(l_handle));
 10    print_clob(dbms_metadata.fetch_xml(l_handle).getClobVal());
 11  end;
 12  /

* Here are the results from the dbms_metadata.get_query call:
SELECT VALUE(KU$) COMMENT_T FROM SYS.KU$_COMMENT_VIEW KU$ WHERE
KU$.BASE_OBJ.NAME='T' AND KU$.BASE_OBJ.OWNER_NAME='OPS$SDILLON';

* Here is the resulting XML, note the comment text in
\ROWSET\ROW\COMMENT_T\COMNT\text():
<?xml version="1.0"?>
<ROWSET>
 <ROW>
  <COMMENT_T>
   <VERS_MAJOR>1</VERS_MAJOR>
   <VERS_MINOR>0</VERS_MINOR>
   <OBJ_NUM>31942</OBJ_NUM>
   <BASE_OBJ>
    <OBJ_NUM>31942</OBJ_NUM>
    <DATAOBJ_NUM>31942</DATAOBJ_NUM>
    <OWNER_NUM>63</OWNER_NUM>
    <OWNER_NAME>OPS$SDILLON</OWNER_NAME>
    <NAME>T</NAME>
    <NAMESPACE>1</NAMESPACE>
    <TYPE_NUM>2</TYPE_NUM>
    <TYPE_NAME>TABLE</TYPE_NAME>
    <CTIME>10-JUL-02</CTIME>
    <MTIME>10-JUL-02</MTIME>
    <STIME>10-JUL-02</STIME>
    <STATUS>1</STATUS>
    <FLAGS>0</FLAGS>
    <SPARE1>6</SPARE1>
    <SPARE2>1</SPARE2>
   </BASE_OBJ>
   <COLNO>1</COLNO>
   <COLNAME>SOME_INT</COLNAME>
   <CMNT>This is a NUMBER column in table t to show off DBMS_METADATA</CMNT>
  </COMMENT_T>
 </ROW>
</ROWSET>

PL/SQL procedure successfully completed.

So that covers us for Oracle9i Release 2.  Since Oracle9i Release 1 does not
support COMMENT object types, we'll have to generate the XML using the Oracle
XML Developer's Kits. You can learn more about the XDK here:
http://otn.oracle.com/tech/xml/content.html
Use the following query to get your comments in XML in 9i Release 1:

ops$sdillon@FUNK92.US.ORACLE.COM> select dbms_xmlquery.getxml(
  2    'select * from sys.ku$_comment_view cv ' ||
  3    ' where cv.base_obj.name = ''T'' and
cv.base_obj.owner_name=''OPS$SDILLON''')
  3    from dual;

DBMS_XMLQUERY.GETXML('SELECT*FROMSYS.KU$_COMMENT_VIEWCVWHERECV.BASE_OBJ.NAME=''T
--------------------------------------------------------------------------------
<?xml version = '1.0'?>
<ROWSET>
  <ROW num="1">
    <VERS_MAJOR>1</VERS_MAJOR>
    <VERS_MINOR>0</VERS_MINOR>
    <OBJ_NUM>31951</OBJ_NUM>
    <BASE_OBJ>
      <OBJ_NUM>31951</OBJ_NUM>
      <DATAOBJ_NUM>31951</DATAOBJ_NUM>
      <OWNER_NUM>63</OWNER_NUM>
      <OWNER_NAME>OPS$SDILLON</OWNER_NAME>
      <NAME>T</NAME>
      <NAMESPACE>1</NAMESPACE>
      <TYPE_NUM>2</TYPE_NUM>
      <TYPE_NAME>TABLE</TYPE_NAME>
      <CTIME>7/10/2002 10:34:33</CTIME>
      <MTIME>7/10/2002 10:34:33</MTIME>
      <STIME>7/10/2002 10:34:33</STIME>
      <STATUS>1</STATUS>
      <FLAGS>0</FLAGS>
      <SPARE1>6</SPARE1>
      <SPARE2>1</SPARE2>
      </BASE_OBJ>
    <CMNT>This is a sample table to try and show off the functionality of
DBMS_METADATA</CMNT>
  </ROW>
</ROWSET>

You'll notice I used KU$_COMMENT_VIEW instead of USER_TAB_COMMENTS and
USER_COL_COMMENTS.  Obviously, by changing the SQL you pass to DBMS_XMLQUERY you
can create XML from whatever view/query you prefer.  Hope that helps!

_smd_

Sean Dillon
_____________________________________________________________________
Sean Dillon - Senior Technologist, Oracle Corporation
Author "Beginning Oracle Programming"
http://www.amazon.com/exec/obidos/ASIN/186100690X
Hope that helps!  Good luck!

July 10, 2002 Nikhil -- Thanks for the question regarding
      "SQLPLUS does not execute java function from a remote client", version
      8.1.7

      You Asked
Tom,

I have a very interesting problem

I have a function run_cmd which calls a method of one of the java classes loaded
into the database : Util.class. My Oracle 8.1.7 database is on HPUX-11.0.
The definition of my function is as follows:
function RUN_CMD(p_cmd in varchar2) return number as language java name
'Util.RunThis(java.lang.String[]) return integer';

When I execute the function from SQLPLUS 8.1.7 client on the Oracle server as:
select run_cmd('/bin/sh $HOME/Script_name.bat') from dual, it works

Connected to:
Oracle8i Enterprise Edition Release 8.1.7.0.0 - 64bit Production
JServer Release 8.1.7.0.0 - 64bit Production

SQL> select run_cmd('/bin/sh $HOME/Script_name.bat') from dual;

RUN_CMD('/BIN/SH$HOME/SCRIPT_NAME.BAT')
------------------------------------------
                                         0

SQL>

The zero indicates that the function has executed successfully.

However when I try to execute the same from a remote client(SQLPLUS 9.0 from
Windows 2000) connecting to the Oracle Server with the same user login as in the
first case, it does not work.

select run_cmd('/bin/sh $HOME/Script_name.bat') from dual

Connected to:
Oracle8i Enterprise Edition Release 8.1.7.0.0 - 64bit Production
JServer Release 8.1.7.0.0 - 64bit Production

SQL> select run_cmd('/bin/sh $HOME/script_name.bat') from dual;

RUN_CMD('/BIN/SH $HOME/SCRIPT_NAME.BAT')
--------------------------------------------------
134

Even more surprising is that when I run the following command from the remote
client
select run_cmd('/bin/ls /home') from dual

SQL>  select run_cmd('/bin/ls /home') from dual;

RUN_CMD('/BIN/LS/HOME')
-----------------------
                      0

SQL>

the command works. It looks like because of the remote login, I am unable to run
a function when it makes specific operating system calls using a Java class
method

I would appreciate your feedback.

Thanks
Nikhil

      and we said...
$HOME -- what do you believe $HOME is set to?

Suggest you select run_cmd( 'echo $HOME' ) from dual -- and have your run_cmd
print out what it gets back.  You might be surprised.  Don't use environment
variables, you don't have any control over them!

Review &
      Followup

      Rating:
      2
Reply  July 03, 2002
Reviewer:  Nikhil
      Goray  from NY, USA
Let me clarify on the $HOME setting.I have used the exact value of the variable
and not $HOME. The $HOME was just for illustration. I have used the exact values
of variables and not just the variable itself.

The problem is that when I execute it from an SQLPLUS client on the same machine
as where the Oracle server resides, it throws out no errors. However it throws
out errors when I execute it from an SQLPLUS client on my local windows machine.

Could you please advise again? Thanks

          Followup:

Well, the problem is the same problem. Even though you didn't tell me $HOME
wasn't really $HOME (eg: you changed the example between the cut and paste
apparently -- which of course will lead people down the wrong path -- why do
people do that???)

When you run the script using run_cmd from your dedicated server (not over the
net), it is running as you, it is picking up bits and pieces of YOUR environment
-- and the environment is *all* important when running shell scripts.

Your shell script is simply not able to function in the currently environment.
Let me explain with an example.  I'll run a script over dedicated server, show
it fail over the network and then show WHY it failed over the network and
correct it.

Let us start with the simplest of scripts:

$ ls -l /tmp/env.sh
-rwxrwxrwx   1 tkyte           9 Jul  3 10:53 /tmp/env.sh

$ cat /tmp/env.sh
printenv

$ /tmp/env.sh
CLASSPATH=/export/home/ora817/jdbc/lib/classes111.zip:/export/home/tkyte/j:/expor
t/home/ora817/sqlj/lib/runtime.zip:.
HOME=/export/home/tkyte
HZ=100
LARCH_PATH=.:/export/home/tkyte/lclint-2.4b/lib
LCLIMPORTDIR=/export/home/tkyte/lclint-2.4b/imports
LD_LIBRARY_PATH=/export/home/ora817/lib
LOGNAME=tkyte
MAIL=/var/mail/tkyte
MANPATH=/usr/openwin/share/man:/usr/man:/opt/gnu/man:/opt/GCC2723/man
NLS_LANG=AMERICAN_AMERICA.WE8ISO8859P1
ORACLE_HOME=/export/home/ora817
ORACLE_PROC_MAKEFILE=/export/home/ora817/precomp/demo/proc/demo_proc.mk
ORACLE_SID=ora817dev
PATH=/opt/gnu/bin:/usr/local/bin:/usr/ucb:/usr/bin:/export/home/tkyte/bin:/usr/lo
cal/bin:/export/home/ora817/bin:/export/home/tkyte/src/sqlstuff:/usr/openwin/bin:
/usr/etc:.:/usr/ccs/bin:/usr/sbin:/usr/oracle/oraweb/local/bin
PW=tkyte/tkyte
PWD=/export/home/tkyte/j
SHELL=/usr/bin/csh
SQLPATH=/net/aria/export/home/tkyte/src/sqlstuff
TERM=vt100
TZ=US/Eastern
USER=tkyte
_INIT_PREV_LEVEL=S
_INIT_RUN_LEVEL=3
_INIT_RUN_NPREV=0
_INIT_UTS_ISA=sparc
_INIT_UTS_MACHINE=sun4u
_INIT_UTS_NODENAME=aria-dev
_INIT_UTS_PLATFORM=SUNW,Ultra-4
_INIT_UTS_RELEASE=5.7
_INIT_UTS_SYSNAME=SunOS
_INIT_UTS_VERSION=Generic_106541-20

so, that obviously

a) exists
b) is executable by all
c) works (or does it?)

Logging into sqlplus, let us see what happens:

rt_test@ORA817DEV.US.ORACLE.COM> connect rt_test/rt_test
Connected.   via dedicated server!

rt_test@ORA817DEV.US.ORACLE.COM> set serveroutput on size 1000000
rt_test@ORA817DEV.US.ORACLE.COM> exec dbms_java.set_output(1000000)
PL/SQL procedure successfully completed.

rt_test@ORA817DEV.US.ORACLE.COM> exec rc('/bin/ls -l /bin/sh /tmp/env.sh' )
-r-xr-xr-x   3 bin      root       91840 May 10 18:13 /bin/sh
-rwxrwxrwx   1 tkyte    other          9 Jul  3 10:53 /tmp/env.sh
return code = 0 from /bin/ls -l /bin/sh /tmp/env.sh

PL/SQL procedure successfully completed.

Let's start by first taking a look at the environment our java stuff will run
in:

rt_test@ORA817DEV.US.ORACLE.COM> exec rc('/bin/sh -c /usr/ucb/printenv' )
CLASSPATH=/export/home/ora817/jdbc/lib/classes111.zip:/export/home/tkyte/j:/expor
t/home/ora817/sqlj/lib/runtime.zip:.
HOME=/export/home/tkyte
HZ=100
LARCH_PATH=.:/export/home/tkyte/lclint-2.4b/lib
LCLIMPORTDIR=/export/home/tkyte/lclint-2.4b/imports
LD_LIBRARY_PATH=/export/home/ora817/lib
LOGNAME=tkyte
MAIL=/var/mail/tkyte
MANPATH=/usr/openwin/share/man:/usr/man:/opt/gnu/man:/opt/GCC2723/man
NLS_LANG=AMERICAN_AMERICA.WE8ISO8859P1
ORACLE_HOME=/export/home/ora817
ORACLE_PROC_MAKEFILE=/export/home/ora817/precomp/demo/proc/demo_proc.mk
ORACLE_SID=ora817dev
ORA_NET2_DESC=10,13
PATH=/opt/gnu/bin:/usr/local/bin:/usr/ucb:/usr/bin:/export/home/tkyte/bin:/us
r/local/bin:/export/home/ora817/bin:/export/home/tkyte
/src/sqlstuff:/usr/openwin/bin:/usr/etc:.:/usr/ccs/bin:/usr/sbin:/usr/oracle/oraw
eb/local/bin
PW=tkyte/tkyte
PWD=/export/home/tkyte/j
SHELL=/usr/bin/csh
SQLPATH=/net/aria/export/home/tkyte/src/sqlstuff
TERM=vt100
TZ=US/Eastern
USER=tkyte
_INIT_PREV_LEVEL=S
_INIT_RUN_LEVEL=3
_INIT_RUN_NPREV=0
_INIT_UTS_ISA=sparc
_INIT_UTS_MACHINE=sun4u
_INIT_UTS_NODENAME=aria-dev
_INIT_UTS_PLATFORM=SUNW,Ultra-4
_INIT_UTS_RELEASE=5.7
_INIT_UTS_SYSNAME=SunOS
_INIT_UTS_VERSION=Generic_106541-20
return code = 0 from /bin/sh -c /usr/ucb/printenv

PL/SQL procedure successfully completed.

that worked, now lets run the shell script version:

rt_test@ORA817DEV.US.ORACLE.COM> exec rc('/bin/sh /tmp/env.sh' )
CLASSPATH=/export/home/ora817/jdbc/lib/classes111.zip:/export/home/tkyte/j:/expor
t/home/ora817/sqlj/lib/runtime.zip:.
HOME=/export/home/tkyte
HZ=100
LARCH_PATH=.:/export/home/tkyte/lclint-2.4b/lib
LCLIMPORTDIR=/export/home/tkyte/lclint-2.4b/imports
LD_LIBRARY_PATH=/export/home/ora817/lib
LOGNAME=tkyte
MAIL=/var/mail/tkyte
MANPATH=/usr/openwin/share/man:/usr/man:/opt/gnu/man:/opt/GCC2723/man
NLS_LANG=AMERICAN_AMERICA.WE8ISO8859P1
ORACLE_HOME=/export/home/ora817
ORACLE_PROC_MAKEFILE=/export/home/ora817/precomp/demo/proc/demo_proc.mk
ORACLE_SID=ora817dev
ORA_NET2_DESC=10,13
PATH=/opt/gnu/bin:/usr/local/bin:/usr/ucb:/usr/bin:/export/home/tkyte/bin:/us
r/local/bin:/export/home/ora817/bin:/export/home/tkyte
/src/sqlstuff:/usr/openwin/bin:/usr/etc:.:/usr/ccs/bin:/usr/sbin:/usr/oracle/oraw
eb/local/bin
PW=tkyte/tkyte
PWD=/export/home/tkyte/j
SHELL=/usr/bin/csh
SQLPATH=/net/aria/export/home/tkyte/src/sqlstuff
TERM=vt100
TZ=US/Eastern
USER=tkyte
_INIT_PREV_LEVEL=S
_INIT_RUN_LEVEL=3
_INIT_RUN_NPREV=0
_INIT_UTS_ISA=sparc
_INIT_UTS_MACHINE=sun4u
_INIT_UTS_NODENAME=aria-dev
_INIT_UTS_PLATFORM=SUNW,Ultra-4
_INIT_UTS_RELEASE=5.7
_INIT_UTS_SYSNAME=SunOS
_INIT_UTS_VERSION=Generic_106541-20
return code = 0 from /bin/sh /tmp/env.sh

PL/SQL procedure successfully completed.

and that worked,  now -- lets do it over the net:

rt_test@ORA817DEV.US.ORACLE.COM>
rt_test@ORA817DEV.US.ORACLE.COM> disconnect
Disconnected from Oracle8i Enterprise Edition Release 8.1.7.3.0 - Production
With the Partitioning option
JServer Release 8.1.7.3.0 - Production

rt_test@ORA817DEV.US.ORACLE.COM> connect rt_test/rt_test@ora817dev
Connected.  via listener!

rt_test@ORA817DEV.US.ORACLE.COM> set serveroutput on size 1000000
rt_test@ORA817DEV.US.ORACLE.COM> exec dbms_java.set_output(1000000)

PL/SQL procedure successfully completed.

rt_test@ORA817DEV.US.ORACLE.COM> exec rc('/bin/ls -l /bin/sh /tmp/env.sh' )
-r-xr-xr-x   3 bin      root       91840 May 10 18:13 /bin/sh
-rwxrwxrwx   1 tkyte    other          9 Jul  3 10:53 /tmp/env.sh
return code = 0 from /bin/ls -l /bin/sh /tmp/env.sh

PL/SQL procedure successfully completed.

file is there, we can see it -- our process runs (rc that is)

rt_test@ORA817DEV.US.ORACLE.COM> exec rc('/bin/sh -c /usr/ucb/printenv' )
HOME=/export/home/ora817
HZ=100
LD_LIBRARY_PATH=/export/home/ora817/lib:/export/home/ora817/ctx/lib:/usr/ccs/lib:
/export/home/ora817/webdb/lib:/usr/openwin/lib:/us
r/lib
LOGNAME=ora817
MAIL=/usr/spool/mail/ora817
MANPATH=/usr/openwin/share/man:/usr/man:/opt/gnu/man:/opt/GCC2723/man
ORACLE_HOME=/export/home/ora817
ORACLE_PROC_MAKEFILE=/export/home/ora817/precomp/demo/proc/demo_proc.mk
ORACLE_SID=ora817dev
ORA_NET2_DESC=20,23
PATH=/opt/gnu/bin:/usr/local/bin:/usr/bin:/export/home/ora817/bin:/usr/local/bin:
/export/home/ora817/bin:/export/home/ora817/src/sq
lstuff:/usr/bin/X11:/usr/etc:.:/usr/ccs/bin:/usr/sbin:/usr/oracle/oraweb/local/bi
n:/usr/openwin/bin
PWD=/export/home/ora817/network/admin
SHELL=/usr/bin/csh
SQLPATH=/aria-export/home/tkyte/src/sqlstuff
TERM=vt100
TNS_ADMIN=/export/home/ora817/network/admin
TZ=US/Eastern
USER=ora817
return code = 0 from /bin/sh -c /usr/ucb/printenv

PL/SQL procedure successfully completed.

but -- look at that environment.  In particular, pay attention to the PATH.
Notice how /usr/ucb/ in particular is NOT in the path.  This it the environment
the database listener is running as, NOT the environment our dedicated server
had!!!

Very very important

rt_test@ORA817DEV.US.ORACLE.COM> exec rc('/bin/sh /tmp/env.sh' )
return code = 1 from /bin/sh /tmp/env.sh

PL/SQL procedure successfully completed.

rt_test@ORA817DEV.US.ORACLE.COM>

bummer, failure.  So, what now?  I rewrite the script as:

rt_test@ORA817DEV.US.ORACLE.COM> host echo /usr/ucb/printenv > /tmp/env.sh

rt_test@ORA817DEV.US.ORACLE.COM> host chmod a+rwx /tmp/env.sh

rt_test@ORA817DEV.US.ORACLE.COM>
rt_test@ORA817DEV.US.ORACLE.COM> exec rc('/bin/sh /tmp/env.sh' )
HOME=/export/home/ora817
HZ=100
LD_LIBRARY_PATH=/export/home/ora817/lib:/export/home/ora817/ctx/lib:/usr/ccs/lib:
/export/home/ora817/webdb/lib:/usr/openwin/lib:/us
r/lib
LOGNAME=ora817
MAIL=/usr/spool/mail/ora817
MANPATH=/usr/openwin/share/man:/usr/man:/opt/gnu/man:/opt/GCC2723/man
ORACLE_HOME=/export/home/ora817
ORACLE_PROC_MAKEFILE=/export/home/ora817/precomp/demo/proc/demo_proc.mk
ORACLE_SID=ora817dev
ORA_NET2_DESC=20,23
PATH=/opt/gnu/bin:/usr/local/bin:/usr/bin:/export/home/ora817/bin:/usr/local/bin:
/export/home/ora817/bin:/export/home/ora817/src/sq
lstuff:/usr/bin/X11:/usr/etc:.:/usr/ccs/bin:/usr/sbin:/usr/oracle/oraweb/local/bi
n:/usr/openwin/bin
PWD=/export/home/ora817/network/admin
SHELL=/usr/bin/csh
SQLPATH=/aria-export/home/tkyte/src/sqlstuff
TERM=vt100
TNS_ADMIN=/export/home/ora817/network/admin
TZ=US/Eastern
USER=ora817
return code = 0 from /bin/sh /tmp/env.sh

PL/SQL procedure successfully completed.

and wah-lah, problem solved.

Your script is failing because the environment in which it is running is NOT
CORRECT for it to run.  You need to either

a) fix the listeners environment (not suggested)
b) not rely on any environmental variables (suggested)
c) supply the correct environment at the head of your script (eg: set the PATH)

      Rating: 4
Clear
      explanation  July 03, 2002
Reviewer:
      A reader  from ca, usa

      Rating: 5
Excellent
      explanation  July 09, 2002
Reviewer:  Nikhil
      Goray  from NY, USA
Gaining confidence from what u said , I have got the damned thing to work,
setting the environment and then calling SQLLDR statement . Everything works
good if I execute the SQLLDR statement with

sqlldr $ORUSER LOG=/home/gernern/LOAD_backflush
CONTROL=/home/gernern/LOAD_backflush

Where $ORUSER is for the sake of illustration(I have used the actual variable
value).

However, when I execute the same statement with the DIRECT=TRUE option,

sqlldr $ORUSER LOG=/home/gernern/LOAD_backflush
CONTROL=/home/gernern/LOAD_backflush DIRECT=TRUE

I get the following error:

ORA-02352: Direct path connection must be homogeneous

Could you please explain why this is happening and a potential workaround for
the above situation? I need to use the DIRECT=TRUE option so that load times are
quick, I refered to one article which I found in ur archive, but that wasn't
very helpful.

Thanks

Nikhil

          Followup:

$ oerr ora 2352
02352, 00000, "Direct path connection must be homogeneous"
// *Cause: The user-side to kernel-side connection is heterogenous.
// *Action: Make the connection be between identical hardware and O/S's.

apparently either the OS you are running sqlldr from is different from the OS of
the database

or

the version of sqlldr you are running is different from the version of the
database you are loading into.

Note: I hope you fully and totally understand the ramifications of a direct path
load before you use it.  Read the server utilities guide.  It is generally used
for a first time load, rarely for incremental loads (unless they are huge
increments).  I cannot imagine wanting to do a direct path load from a stored
procedure personally.

      Rating:
      3
  July 10, 2002
Reviewer:
      A reader
Tom,

The conditions certainly are homogenous since when I execute the same batch
file(which has the SQLLDR command) directly from the operating system, without
using a 'JAVA BASED PROCEDURE', it works and does not throw out any error.
However, when I execute the same batch file (which has the SQLLDR command) using
'JAVA', I get the error which I have mentioned. So honestly, the same batch file
is being executed in two scenarios.

1) Executing it from the operating system of database or from a sqlplus session
started on the Operating system itself(HPUX)
2) Executing it from a client from Windows which uses either ODBC or JDBC to
connect to Oracle and executes a procedure which in turn calls the SQLLDR batch
file.

Given such a scenario, SQLLDR with the DIRECT=TRUE option should work without a
problem.

Could you please clarify on this?

Thanks

Nikhil

          Followup:

check out the ENVIRONMENT please.  As pointed out -- the ENVIRONMENT is
different.

Perhaps you are not running the sqlldr you think (try being explicit).

Compare the ENVIROMENTS the two things run in (from the command line vs from the
server itself) and see what is different and correct that.

      Rating: 5
Practical
      solution presented  July 10, 2002
Reviewer:
      Nikhil Goray  from NY, USA
Tom,

Thanks a million. Everything works perfectly now. No problems with SQLLDR now.
Ur response was really cool.

I have one last question which is stopping us from using Java in our Oracle
8.1.7 Datawarehouse. Whenever I execute an OS-Command from a java function such
as

select run_cmd('/bin/sh') from dual,

 the command hangs up and creates a zombie process on the server which we cannot
kill. The only option which we have used until now is bouncing the database. We
cannot move to production till we have a solution for a known problem like this.

On the other hand if we execute a command such as
select run_cmd('/bin/sh /home/nikhil/Load_exe.bat') from dual, the function
returns and everything looks good. Similarly on Windows too, I have seen that if
I execute a function such as

select run_cmd('c:\winnt\notepad.exe') from dual,
it hangs up. The notepad application also does not open. Are there some
parameteres which we can set to solve the above problem?

Thanks a million again..........Hope u keep up the good work

Nikhil

          Followup:

That is NOT a known problem (other then you made a "bad" choice).

Think about what happens when you run /bin/sh -- out there in cyber space
somewhere is a little $ prompt waiting for someone to type EXIT.

Same with NOTEPAD

The problem you have here is that you are letting people run /bin/sh and
cmd.exe.

You should

a) only let them run that which you let them run (eg: DON'T LET THEM RUN
NOTEPAD, don't dbms_java.grant_permission on that one)

b) DON'T let them run things that can run other things (eg: DO NOT
dbms_java.grant_permission on cmd.exe, /bin/sh -- do it on the scripts/programs
ONLY that you want to run)

then the problem is solved....

July
      10, 2002 kiran -- Thanks for the
      question regarding "Difference between count(1) and count(*)", version
      8.1.6

      You Asked
Hi Tom,

Its a nice feeling to post a question in this web site for the first time (Thank
God ! I am able to post a question after so much of wait !!).

I have a question, please answer it or atleast let me know some URL's where I
can do some study regarding this.

What is the difference between

1) SELECT COUNT(*) FROM ALL_TABLES;
and
2) SELECT COUNT(1) FROM ALL_TABLES;

The following are statistics with query 1 [count(*)]

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

The following are statistics with query 1 [count(*)]

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

I am really unable to find any difference between the statistics.
Also, the execution paths are absolutely same
(Though, I cannot see the execution path for a query using Dictionary objects, I
have run similar query against one user table).

Please help me in knowing which is faster and the tuning implications. Also,
please let me know whether is there a difference in which both are executed. ?

Thanks a lot
Srirama Kiran Kumar

      and we said...
bummer that you waited so long when a search for:

count(*) count(1)

on this site would have found an article entitled:

difference between count(1) and count(*)

that immediately would have answered this question
Review &
      Followup

      Rating:
      1
  July 09, 2002
Reviewer:
      A reader

      Rating: 5
Say it ain't
      so!  July 09, 2002
Reviewer:  Mark A.
      Williams  from Indianapolis, IN USA
"A Reader" rates it 1 star...

Why can't people be bothered to use the search facility, do you think?  It isn't
exactly hard to find or use...  same reason they don't read the Oracle docs?

Maybe you should put a link to the Oracle docs online on your main page?  But,
would it help?

Anyway, another unsolicited $0.02...

- Mark

      Rating: 5
Perfect
      Answer  July 09, 2002
Reviewer:  Jan

to reader 1 and others like he:

This page is also a kind of Information System. It means - System about
information, not just a storage of anything. Search before. If something is
already responded, don`t ask again.
Use 'modular approach' - reusability.

      Rating: 3
Thank you
      Tom  July 09, 2002
Reviewer:  Kiran
       from India
Hi Tom,

I have went through the series of Q/A's resulted due to the search which you
suggested. I have found it very useful. Also, I realize that Searching the forum
is important before I post.
I expected a different explaination, i.e in terms of the way count(*) and
count(1) are executed i.e how blocks are read in both cases. Please do respond
if you feel its worth.

Thanks
kiran

          Followup:

Ok, read this

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1156159920245
which is the article "difference between count(1) and count(*)".

It starts with:

nothing, they are the same, incur the same amount of work -- do the same
thing,
take the same amount of resources.

they are in fact *the same thing*

      Rating: 4
Some
      comments  July 10, 2002
Reviewer:  Karl
       from Austria
Tom,

Thank you for this very useful site.

As for the Original question, I think they were more interested in having the
pleasure of posting it than really asking the question itself.

However, it's needless to say that some myths are almost unbreakable. And some
are even written down and signed off by Oracle, for example in Oracle Press
books. So, don't blame us poor mortals for being confused sometimes.

For example, Mr. Jason Couchman in his Oracle8i Certification Exam Guide
objectively states:

- "Use COUNT(1) instead of COUNT(*) because of its performance benefits."
- "The ORDER BY columns have to be in the SELECT clause."
- "The password is encrypted and stored in the Oracle database"

... among others.

          Followup:

VBG

they are of course all wrong (and the second one is trivial to prove wrong,
cannot imagine how that got past the editors!)

      Rating:
      4
  July 10, 2002
Reviewer:
      A reader
In the above article, there is an unresponded post by Dulimin in China where he
posted trace details of count(1) and count(*) on 8.1.7. Curious to know your
response to it. Thanks.

          Followup:

write it off to a side effect of the cartesian product.

      Rating: 2
How is password
      stored in the database?  July 10, 2002
Reviewer:
      A reader
This is related to Karl's third observation.... How is the password stored then,
I mean what is wrong in that statement?

          Followup:

It is not encrypted.  Encrypted implies there would be a decrypt.  Encrypt
implies there is a key that would unlock the passwords -- there is not.

It is a one way message digest, very very different.

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:95412348059
for example.

      Rating:
      4
  July 10, 2002
Reviewer:
      A reader
Hi Tom,

I've seen in varous environments where, when a count(1) was changed to count(*),
the performance improved dramatically, sometimes by a hundred fold. What would
be the possible reasons for this?

thanks

          Followup:

i give up, use count(*) ;)

It is what I use.

The old "myth" was the count(1) was faster, better -- that you should avoid
count(*).

Use count(*), it is more appropriate anyway.

      Rating: 5
Gee
      !  July 10, 2002
Reviewer:  A reader

I don't believe... no slam on this last one ?

      Rating:
      5
?  July 10, 2002
Reviewer:
      A reader
Didn't he read the top of the page ?

      Rating: 4
How about
      this?  July 10, 2002
Reviewer:  Mr. 420
       from Like a mouse in the cage, I am trapped inside the cubicle

I am pretty sure that SELECT COUNT(ROWID) FROM a_very_large_tbl is faster than
using COUNT(*). I would say, I am 99% sure. Tom, can you be kind enough to
confirm this?

      Rating: 5
RE: How about
      this?  July 10, 2002
Reviewer:  Mark A.
      Williams  from Indianapolis, IN USA
PMFJI...

In regard to count(rowid) - just benchmark it!

I created a table (t) as select * from all_objects, and then repeated insert
into t select * from t until I had 3330304 rows in the table.

Here's the relevant trace/tkprof results:

select count(*)
from
 t

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.00          0          1          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      4.34      13.88      40967      42239          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      4.35      13.88      40967      42240          0           1

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 23

Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                       2        0.00          0.00
  db file scattered read                       2605        0.07         10.44
  db file sequential read                         2        0.00          0.00
  SQL*Net message from client                     2        5.99          5.99
********************************************************************************

select count(rowid)
from
 t

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.02       0.00          0          1          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      4.22      13.71      40967      42239          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      4.24      13.71      40967      42240          0           1

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 23

Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                       2        0.00          0.00
  db file scattered read                       2605        0.07         10.29
  db file sequential read                         2        0.00          0.00
  SQL*Net message from client                     2       10.26         10.26
********************************************************************************

My laptop hard drive isn't terribly fast, so ignore the waits.  The thing is,
13.88 and 13.77 don't seem too different.  Do the same benchmark.  Make the
table bigger if you want.  Play with it.  Have fun.  See what happens.

- Mark

      Rating: 5
Mark's
      response:  July 24, 2002
Reviewer:  Mr. 420
       from from Like a mouse in the cage, I am trapped inside the
      cubicle
Mark, Thank you very much for that benchmark test, and clearing my doubts. I
have to learn how tkprof/trace utilities work. They seem very useful! Thanks
again.

      Rating:
      5
PMFJI...????????  July 24, 2002
Reviewer:
      A reader
Mark, what does that mean?

      Rating: 5
RE: Mark, what
      does that mean?  August 17, 2002
Reviewer:  Mark
      A. Williams  from Indianapolis, IN USA
PMFJI == (P)ardon (M)e (F)or (J)umping (I)n

Since I jumped into the middle of the thread, I was trying to be a bit polite
about it...  and this response is only a month late, but I just read the
question!

HTH (Hope This Helps)...

- Mark

      Rating: 5
count(1) and
      count(field)....why NULL is being counted?  November 12,
      2003
Reviewer:  Bhavani  from India
Hi Tom,

ops@florida> create table t (f integer);

Table created.

ops@florida> insert into t values(1);

1 row created.

Execution Plan
----------------------------------------------------------
   0      INSERT STATEMENT Optimizer=CHOOSE

ops@florida> set autotrace off
ops@florida> insert into t values(1);

1 row created.

ops@florida> insert into t values(2);

1 row created.

ops@florida> insert into t values(3);

1 row created.

ops@florida> select count(1) from t;
/c cls  COUNT(1)
----------
         4

ops@florida> select count(f) from t;
/c cls  COUNT(F)
----------
         4

ops@florida> insert into t values(NULL);

1 row created.

ops@florida> commit;

Commit complete.

ops@florida> select count(f) from t;
/c cls  COUNT(F)
----------
         4

ops@florida> select count(1) from t;
/c cls  COUNT(1)
----------
         5

Why "select count(f) from t;" is not counting the row with NULL?

Thanks
Bhavani

          Followup:

that is the very essence of count

count(*) = count all rows

count( <expression> ) = count all NON NULL rows

count( distinct <expression> ) = count unique non null values

count(1) is the wrong way to code that, count(*) is correct and proper (btw)...

July 10, 2002 --
      Thanks for the question regarding "Question on Windows Compression ",
      version 8.1.7

      You Asked
Hi Tom,

1) I use hot backup. To gain some disk place, i wonder if there is a hidden bad
effect with using directory "zipped at Windows level" for my target directories
of backup ? I try it (on db test ...) and the restore / recover worked !! But
please tell me if i can use safely on the real life ...
Same question with using winzip for the backuped files.

2) Is there any way to make a loader with nested tables of 2 (or plus)
dimensions ?

Thanks for all the time you pass on this forum. It helps me a lot ...

      and we said...
1) the only side effect would be that

   a) you think you have 1gig of space (cause windows tells you that)
   b) you can only copy 750meg of backup there (cause the compression wasn't as
      good for those files)

Since the backups are "static" files, using a compressed drive is OK.  Using a
compressed drive for real datafiles would be bad because the file starts life as
a very very compressable things (mostly ZEROES).  Over time as you add data, the
compressionability of the file drops dramatically.  So a file that fit today on
your disk -- might not fit on the SAME EXACT disk tomorrow -- since the
compressability of the file decreased.  You'll get IO errors, we'll think "uh-oh
-- corrupt file" and you'll be in a bind.

2) in 817, nested tables have but one dimension??  you cannot have a 2
dimensional table to load in 817.
Review &
      Followup

      Rating:
      3
  July 09, 2002
Reviewer:  Juan Carlos
      Reyes  from Bolivia
I had been using compression in windows several years.
1)
I didn´t lost any file using this windows feature ( I always was up to date in
servicepacks)
2)
I had seen is faster for files in the networks, I suppose because it is already
compressed before sending to network.
3)
in SCSI disks you lost performance and SCSI compressed is like and IDE in my
experience.
4)
Verify if you are getting something in compression
In file property window compare the size and size in disk.
You can´t see this in folder properties.
5)
I had used compression in several database, backups and online database and it
works OK.
6)
I never detected enabling compression causes an important overhead in windows
nt,
nt compression is really nice.
P.D.
Beware you datafile grows to much. a datafile can have 20GB uncompressed and 1Gb
compressed. And the problem you can have is when trying to backupit, but you can
compress too with winrar before backup.

          Followup:

1) didn't say you would lose a file -- reread my comments.

Tell me -- say you have a compressed drive.  You put a 100meg "empty" datafile
on it.  Now the disk is full (adding another file would get "insufficient
disk").

Now, you starting adding data to that file.  What will happen as the compression
ratio goes down from the 5 or 10 to 1 that it started at (as binary zeroes) to 2
to 1?  ANSWER: file does not fit, IO error, now you are in trouble.

The fact that you haven't it it YET (and keyword here = YET) matters not.

2) Now you make no sense whatsoever.

a) if you are using network mounted filesystems for datafiles, you are asking
for BIG TIME trouble

b) the data is uncompressed as it leaves the disk.

3) that, in addition to not making total sense syntatically, is incorrect.

I'll just say this:

use compressed drives as your own risk, it is not a good idea, it would not be
recommended and it will lead to serious issues when the disk nears capacity and
the amount of compressability of the file decreases.

      Rating: 3
My
      experience  July 10, 2002
Reviewer:  Juan Carlos
      Reyes Pacheco  from Bolivia.
Sorry, I don´t know if I misunderstanding something,

I only want to share my experience
letme share my experience about ntfs windows compression.

We had used NTFS compressed disks (no FAT compressed disk ) for more than 6
years, some times with 8 databases, every database is in average 1 Gigabyte, in
two disk of 2 Gigabytes we never had problems.
we had been working hard with that compression drives restoring backups,
develping and never had problems.

Our seven customer, some of them has in average 3 databases more than 1.5
gigabytes in the same machine, in average had worked for more than 3 years with
NTFS compressed drives.
They did their backups in that disks too, one every day.
Never had problems with compression and I never had detected a compression
overhead.

Maybe I am wrong but I´ll suggest you to use NTFS compression.
I´ll not suggest to use FAT compression.

Maybe it is not good for you but I suggest you, apply all servicepacks and give
it a try.

Thanks.

          Followup:

do this for me.

a) create a database on a NTFS compressed disk -- with a 500meg EMPTY
tablespace.
b) fill up the rest of the drive -- get it to the point that it says "no more
room" for the smallest of files
c) create a table in that empty tablespace and fill her up.

Tell me what happens -- ok.

      Rating: 4
Not a good
      idea  July 10, 2002
Reviewer:  Jim Kennedy
       from OR , USA
I was at a customer site and it would crash every so often.  I found out that
accidently someone had compressed the Oracle data files on the drive. (NT OS
with NTFS as the file system) When the system started and people would use the
system NT would use almost all the RAM for caching the datafiles so it could
uncompress and compress the datafiles.  All told the datafiles were many time
the size of available  RAM and swap.

Once we cleanly shutdown the database and uncompressed the files the system
stopped crashing, performance was much better and we had a reasonable amount of
available RAM on the server. (not 3K available)

I would not compress a datafile; a false savings.  Use datafiles that
automatically extend (with a maximum limit) and make sure if they all extend to
the limit you do not run out of room.

          Followup:

Excellent -- that is exactly what I was talking about !!  Exactly.

      Rating: 3
I agree with
      you  July 10, 2002
Reviewer:  Juan Carlos
      Reyes
Please, I´m not telling what you say is wrong.

the only thing I tell is that for us is perfect.
we have 8 database like the following

SQL> SELECT NAME, BYTES FROM V$DATAFILE;

NAME                                                        BYTES
-------------------------------------------------- --------------
E:\ORASOAB\DATAFILES\DFL_SYS_SOAB                     340,787,200
E:\ORASOAB\DATAFILES\DFL_UNDO_SOAB                    314,572,800
E:\ORASOAB\DATAFILES\DFL_USER_SOAB                  1,415,577,600
D:\ORASOAB\DATAFILES\DFL_INDX_SOAB                    209,715,200
D:\ORASOAB\DATAFILES\DFL_OVERFLOW_SOAB                209,715,200
E:\ORASOAB\DATAFILES\DFL_HI_USER_SOAB                 262,144,000
D:\ORASOAB\DATAFILES\DFL_HI_INDX_SOAB                 104,857,600
E:\ORASOAB\DATAFILES\DFL_LOB_SOAB                      52,428,800

8 filas seleccionadas.

SQL> CONN ADM@SBBV.WORLD
Conectado.
SQL> SELECT NAME, BYTES FROM V$DATAFILE;

NAME                                                        BYTES
-------------------------------------------------- --------------
E:\ORASBBV\DATAFILES\DFL_SYS_SBBV                     340,787,200
E:\ORASBBV\DATAFILES\DFL_UNDO_SBBV                    314,572,800
E:\ORASBBV\DATAFILES\DFL_USER_SBBV                  1,153,433,600
D:\ORASBBV\DATAFILES\DFL_INDX_SBBV                    209,715,200
D:\ORASBBV\DATAFILES\DFL_OVERFLOW_SBBV                209,715,200
E:\ORASBBV\DATAFILES\DFL_HI_USER_SBBV                 262,144,000
D:\ORASBBV\DATAFILES\DFL_HI_INDX_SBBV                 104,857,600
E:\ORASBBV\DATAFILES\DFL_LOB_SBBV                      52,428,800

8 filas seleccionadas.

NAME                                                        BYTES
-------------------------------------------------- --------------
E:\ORASAFI\DATAFILES\DFL_SYS_SAFI                     340,787,200
E:\ORASAFI\DATAFILES\DFL_UNDO_SAFI                    314,572,800
E:\ORASAFI\DATAFILES\DFL_USER_SAFI                    996,147,200
D:\ORASAFI\DATAFILES\DFL_INDX_SAFI                    209,715,200
D:\ORASAFI\DATAFILES\DFL_OVERFLOW_SAFI                209,715,200
E:\ORASAFI\DATAFILES\DFL_HI_USER_SAFI                 262,144,000
D:\ORASAFI\DATAFILES\DFL_HI_INDX_SAFI                 104,857,600
E:\ORASAFI\DATAFILES\DFL_LOB_SAFI                      52,428,800

8 filas seleccionadas.

SQL> CONN OYM@OYM.WORLD;
Conectado.
SQL> SELECT NAME, BYTES FROM V$DATAFILE;

NAME                                                        BYTES
-------------------------------------------------- --------------
E:\ORAOYM\DATAFILES\DFL_SYS_OYM                       571,473,920
E:\ORAOYM\DATAFILES\DFL_UNDO_OYM                      314,572,800
E:\ORAOYM\DATAFILES\DFL_USER_OYM                      524,288,000
E:\ORAOYM\DATAFILES\DFL_HI_USER_OYM                   262,144,000
E:\ORAOYM\DATAFILES\DFL_LOB_OYM                        52,428,800
D:\ORAOYM\DATAFILES\DFL_INDX_OYM                      209,715,200
D:\ORAOYM\DATAFILES\DFL_OVERFLOW_OYM                  209,715,200
D:\ORAOYM\DATAFILES\DFL_HI_INDX_OYM                   104,857,600

8 filas seleccionadas.

And we don´t have problems.
We work in nt4server sp6a, w200server sp2
With oracle standard edition

Our customers too and they did´nt have problem for more than 3 years.

we didn´t suffer overhead neither our customer suffer it, because after
compressing their process were as fast as before.

Now I iagree that in other configurations and situations could be very
dangerous.

Thanks

          Followup:

You do not have a problem today.

You have a problem lying in wait for you.

I wish you luck.  You see -- if you have the room on your disks compressed, you
have the room uncompressed.  Your disks must not be at or near capacity.  They
will be one day, then you too will be in the same boat at the reader above.

It is dangerous in *any* configuration.  disk can be pretty cheap -- is your
data? is your time?  Think about it.

      Rating: 5
The important
      thing is...  July 10, 2002
Reviewer:  Mark A.
      Williams  from Indianapolis, IN USA
To me the important thing is this:

<quote>
Caution:
Do not store database files on a compressed drive. This can result in write
errors and decreased performance.
</quote>

- Oracle Database Administrator's Guide for Windows

It does not matter to me that I might be able to get by with something under
some circumstances without getting burned (even for a long period of time),
rather, what matters is that I have the potential to get burned by something.
Storing database files on a compressed drive will not necessarily result in
errors, but it may, so why take the chance?

- Mark

      Rating: 5
NT compression
      can be useful but !!!!  July 11, 2002
Reviewer:
      Raghu  from AZ
My 2 cents on our experience with compression during the last 3 years.
We have 2 Oracle Database servers.

First database server:
 Oracle7 and Oracle8
 Operating system = Windows NT
 2 GB RAM.
 Hard drive size = 80 GB
 oracle Database Size varies from a minimum of approximately 3.5 GB to 20 GB
 We have production copies of 10 customer Databases
 running at any given time.

Second database server:
 Oracle 8.1.6 and 8.1.7
 Operating system = Windows 2000
 3GB RAM.
 Hard drive size = 80 GB
 We have production copies of 15 customer Databases
 running at any given time.

We have been using NT compression on both these database servers.
The only folders that are compressed contain Oracle datafiles.
We use these databases for additional developments for that customer, bug fixes,
performance tuning, upgrades and other general support activities.

Some of the advantages and disadvantages that we have seen during this period:

Advantages:
1) Based on the current number of databases We would require huge amounts of
   disk space to have all these databases running.
2) Compression works great as long as we can afford to LOSE the database and its
contents.
   (We have backups that are again compressed)

Disadvantages:
1) IO errors.
2) Memory errors
3) Cannot do real performance tuning.

I guess its probably OK to use compression in an environment that I have, where
a database
is accessed randomly by 2-3 users every week depending on which customer has an
issue.
This would definitely be a problem in real time PRODUCTION environment.

July 11, 2002 Omar -- Thanks for the question regarding
      "intermedia Text", version 8.1.7

      You Asked
Hi Tom
I am trying to get a clear picture a bout intermedia before I get started using
it's options to develope a library system( searching for books by title, author
, subject, full text search in thesis...etc) , what we have here is a library
containing english and arabic books. Can intermedia create indexes on arabic
words by default or I need to create what is called Knowledge base, but when I
read the 8.1.7 tech. overview I read the following
"It may not be possible to create effective knowledge bases for languages
with extensive compounding, such as Dutch or German, or with many
word forms, such as Hebrew or Arabic."
what is ment by  effecive knowledge base. Did I need to create my own knowledge
base in order to use all the option in intermedia for arabic text or just for
Thesaurus only and theme search.

thanx in advance

      and we said...
I asked Joel Kallman, our resident interMedia guru for assistance on this
question and he says:

...
The knowledge base of interMedia Text is used when performing
linguistic analysis on text.  This analysis is performed when
generating themes of a document, performing a query using the
ABOUT operator, etc.  Rather than searching for documents that
contain specific words or phrases, users can search for documents
that are about a certain subject, even if that subject is not
mentioned explicitly in the document

To create an interMedia Text index and be able to perform
queries using the entire spectrum of interMedia Text operators
does not required a knowledge base.

BTW, since you're going to be storing documents from multiple
languages within the same table, you will want to consider using
the MULTI_LEXER (the lexer is the piece of software which breaks
text into individual tokens).  You can read more about this at:

http://otn.oracle.com/sample_code/products/text/htdocs/text_samples/Lite/Samples/imt_816_techover.html#LXR
....

Review &
      Followup

      Rating: 1
hyperlink was
      not working  March 01, 2002
Reviewer:  gino
       from UK
the hyperlink was not working

          Followup:

seems to have moved to:

http://otn.oracle.com/products/text/x/Tech_Overviews/imt_816.html#LXR
found by clicking on that link, got the "no page", plugged MULTI_LEXER into
search....

      Rating: 3
Intermedia text
      - multi lingual  July 05, 2002
Reviewer:
      Siva  from INDIA
Hello TOM,
        We are using intermedia text for picture searching using german
language. It is working fine. But Our customer told that we have the possiblity
to store and retrieve multi language in the same table (Deutsch, french, english
etc).
 If it is possible can you please give the steps what i have to follow like
setting the NLS Character set , creating the table with NVARCHAR (or
VARCHAR(CHAR 10)) and rebulding the indexes with different lexical etc.

Thank you and expecting your reply.

with regards,
siva

      Rating: 4
Multi-lingual
      support  July 11, 2002
Reviewer:  siva
       from INDIA
Hello TOM,
      I sent this message to Joel. He refered meadialink.oracle.com. But We
don't have customer support Identified. We have only developper suite.
 We allowed only to browse the following site.
Thank you for requesting your account information.
Please use the following Username and Password to access the following services:

        my.oracle.com...
http://my.oracle.com/
        Oracle.com...
http://www.oracle.com/
        Oracle Technology Network...
http://otn.oracle.com/
        Oracle Applications Network...
http://appsnet.oracle.com/
        Oracle E-Business Network...
http://www.oracle.com/ebusinessnetwork
         Oracle Learning Network...
http://www.oracle.com/education/oln
What i have to do.

 IS THERE ANY SITE TO GO THRU THIS ORACLE TEXT and Multilingual things?.

Thank you and expecting your reply.

with regards,
siva

This is the old message  I'm sending again,

        We are using intermedia text for picture searching using german
language. It is working fine. But Our customer told that we have the possiblity
to store and retrieve multi language in the same table (Deutsch, french, english

etc).
 If it is possible can you please give the steps what i have to follow like
setting the NLS Character set , creating the table with NVARCHAR (or
VARCHAR(CHAR 10)) and rebulding the indexes with different lexical etc.

Thank you and expecting your reply.

with regards,
siva

          Followup:

try the discussion forums on

http://otn.oracle.com/

July
      12, 2002 John -- Thanks for the
      question regarding "Multiple statements in a file", version 8.0.6

      You Asked
Hi, Tom

I wonder if you can give me a reason for the following:
I have a Unix shell script which says:

SQLPLUS........
START file1

File1.sql has the following:
DELETE TABLE a;
CREATE TABLE A
  (........);
INSERT INTO TABLE A
  (SELECT.......);
UPDATE TABLE a
  SET field1 =
    (SELECT.......);
UPDATE TABLE a
  SET field2 = field2 + 1
  WHERE .........
/

The problem is that when I run the shell script, the last statement gets run 3
times;
When I enter native SQL, and START file1 the last statement gets run twice;

I solved the problem by putting a COMMIT after the update, but I don't know the
reason for the final statement being run more than once.

Thanks

John

      and we said...
You need EITHER a "/" or a ";" -- but not both.

update t set x = 5;

will run the statement.

update t set x = 5;
/

will run the statement once and then the slash will run it again.

Also, I would hope you don't really have a script like you have above.  It
should just be:

drop table a;
create table a as select ......

with no updates, no inserts.  You should be able to do all of the work in a
single sql statement....
Review & Followup

      Rating: 4
Anonymous
      Block  July 11, 2002
Reviewer:  SR
       from USA
If you have a combination of SQL statements and anonymous blocks in a file then
you will need a slash to execute the anonymous block, correct?
Example:
select sysdate from dual;

begin
dbms_output.put_line('Hi!');
end;
/

In the above case SqlPlus only executes the select statement once even though it
has a ";".  Can you elaborate on this?

Thanks!

          Followup:

';' can only be used to terminate and run statements that cannot have ';' in
them naturally .

PLSQL -- has ';' in it naturally, hence when SQLPlus sees "begin" or "declare"
or "create procedure", "create package", "create function"  and so on -- it will
IGNORE the ';' until it sees the slash (or whatever character you defined as
being "run")

SELECT -- does not have ';' in it naturally.  Hence, sqlplus will run the
command when it sees a ';' -- since that is not part of that language.

July 12, 2002 Vikas -- Thanks for the question regarding "Ref
      cursor", version 8.1.7

      You Asked
Hi tom,
Q 1. I have a problem in using ref cursor for bulk bind. Following is the
procedure.

    create or replace procedure px is
     type x is table of number(10);
     type refcur is ref cursor;
     vcur    refcur ;
     veno    xt;

    begin
      open vcur for select empno from emp ;

        fetch vcur  bulk collect into veno;

    close vcur;
     dbms_output.put_line(veno.count);
 end;
/
Procedure created.

scott@ORCL.DEL1>exec px;
14

PL/SQL procedure successfully completed.

this is fine. but when i change it to follows:
  1  create or replace procedure px is
  2  type x is table of number(10);
  3  type refcur is ref cursor;
  4  vcur refcur ;
  5  veno x;
  6  vs varchar2(1000) := 'select empno from emp';
  7  begin
  8   open vcur for vs ;
  9
 10   fetch vcur  bulk collect into veno;
 11  close vcur;
 12* end;
scott@ORCL.DEL1>/

Procedure created.

scott@ORCL.DEL1>exec px;
BEGIN px; END;

*
ERROR at line 1:
ORA-01001: invalid cursor
ORA-06512: at "SCOTT.PX", line 10
ORA-06512: at line 1

What is the cause of this error. I have gone through your book pg 711 onwards
but could not get answer.

      and we said...
It is not documented clearly but you cannot bulk collect from a weakly typed ref
cursor (the one you can dynamically open).  A strongly typed one:

scott@ORA817DEV.US.ORACLE.COM> create or replace procedure px
  2  is
  3          cursor c is select empno from emp;
  4          type x is table of number(10);
  5          type refcur is ref cursor return c%rowtype;
  6          vcur refcur ;
  7          veno x;
  8  begin
  9   open vcur for
 10   select empno from emp;
 11
 12   fetch vcur  bulk collect into veno;
 13  close vcur;
 14  end;
 15  /

Procedure created.

scott@ORA817DEV.US.ORACLE.COM> show err
No errors.
scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> exec px

PL/SQL procedure successfully completed.

will work but not a dynamically opend, weakly typed ref cursor.

This will change for you in version 9i however.  In Oracle9i and up:

scott@ORA9I.WORLD> create or replace procedure px is
  2  type x is table of number(10);
  3  type refcur is ref cursor;
  4  vcur refcur ;
  5  veno x;
  6  vs varchar2(1000) := 'select empno from emp';
  7  begin
  8   open vcur for vs ;
  9
 10   fetch vcur  bulk collect into veno;
 11  close vcur;
 12    dbms_output.put_line( veno.count );
 13  end;
 14  /

Procedure created.

scott@ORA9I.WORLD> show err
No errors.
scott@ORA9I.WORLD>
scott@ORA9I.WORLD> exec px
14
Review &
      Followup

      Rating: 4
pls explain
      Internal Cause  June 18, 2002
Reviewer:  Vikas
      Sharma  from Delhi - India
hi tom,
thanks for your answer. can you please tell what happens internally.why it does
not work with weak ref cursors. please elaborate more.

          Followup:

it just wasn't supported until Oracle9i release 1 and up.  It just "didn't work"
until then.

      Rating: 5
do we have any
      performance gain in strongly typed ref cursor  July 11, 2002

Reviewer:  john
tom,

is there performance gain using strongly typed ref cursor over weekly typed or
both are one and same.

          Followup:

using the trivial test harness:

http://asktom.oracle.com/~tkyte/runstats.html
anyone could run this (hint hint hint)

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table emp;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table emp as select * from scott.emp;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      type rc1 is ref cursor;
  3      type rc2 is ref cursor return emp%rowtype;
  4
  5      l_cursor1 rc1;
  6      l_cursor2 rc2;
  7
  8      l_rec   emp%rowtype;
  9
 10      l_start number;
 11  begin
 12      insert into run_stats select 'before', stats.* from stats;
 13
 14      l_start := dbms_utility.get_time;
 15      for i in 1 .. 1000
 16      loop
 17          open l_cursor1 for select * from emp;
 18          loop
 19              fetch l_cursor1 into l_rec;
 20              exit when l_cursor1%notfound;
 21          end loop;
 22          close l_cursor1;
 23      end loop;
 24      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 25
 26
 27      insert into run_stats select 'after 1', stats.* from stats;
 28      l_start := dbms_utility.get_time;
 29      for i in 1 .. 1000
 30      loop
 31          open l_cursor2 for select * from emp;
 32          loop
 33              fetch l_cursor2 into l_rec;
 34              exit when l_cursor2%notfound;
 35          end loop;
 36          close l_cursor2;
 37      end loop;
 38      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 39
 40      insert into run_stats select 'after 2', stats.* from stats;
 41  end;
 42  /
193 hsecs
186 hsecs

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value-a.value run1,
c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11   order by abs( (c.value-b.value)-(b.value-a.value))
 12  /

NAME                                 RUN1       RUN2       DIFF
------------------------------ ---------- ---------- ----------
LATCH.active checkpoint queue           0          1          1
latch

LATCH.done queue latch                  0          1          1
LATCH.ksfv messages                     2          3          1
LATCH.ncodef allocation latch           1          0         -1
LATCH.undo global data               4001       4000         -1
STAT...consistent gets              18011      18012          1
STAT...enqueue releases                 3          2         -1
STAT...cursor authentications           2          1         -1
STAT...parse count (hard)               2          1         -1
STAT...physical reads                   1          0         -1
STAT...messages sent                    0          1          1
STAT...calls to get snapshot s       5006       5005         -1
cn: kcmgss

LATCH.transaction branch alloc          1          0         -1
ation

LATCH.session switching                 1          0         -1
LATCH.job_queue_processes para          1          0         -1
meter latch

STAT...enqueue requests                 4          2         -2
STAT...free buffer requested            4          2         -2
LATCH.cache buffers lru chain           3          6          3
STAT...redo entries                    16         13         -3
LATCH.messages                          4          7          3
LATCH.checkpoint queue latch            6         10          4
STAT...parse time cpu                   4          8          4
STAT...db block changes                27         22         -5
LATCH.row cache objects                76         70         -6
STAT...session logical reads        30035      30029         -6
STAT...db block gets                12024      12017         -7
LATCH.redo allocation                  14         25         11
STAT...recursive cpu usage            140        124        -16
LATCH.shared pool                    2151       2127        -24
LATCH.library cache                 10325      10292        -33
LATCH.cache buffers chains          60095      60132         37
STAT...redo size                    21440      21392        -48

32 rows selected.

I would say the differences are neglible AT BEST.

July 12, 2002 RB
      -- Thanks for the question regarding "IN versus OR in SUBQUERY", version
      8.1.7

      You Asked
Tom:
I have a query ( dynamically created ) with more than 2500 search entries. Right
now my Where clause contains 2500 OR statements ( X=a1 OR X=a2 OR ......OR
X=a2500 - just for clarity I have used these variables).  It's taking more than
5 minutes to search a table with more than 30000 rows of data.  I have tried
with IN clause but their is limit of 1000 entries in the list - got ora
exception.

Two questions:

1) What SQL statement will help me to query this table with a faster response
time.

2) What are the major differences between IN and OR? - What's really happening
interanlly when I execute a query with IN and OR?

TIA
RB

      and we said...
YOU NEED TO USE BIND VARIABLES.

It is probably taking 4.99 minutes to parse the query (but a tkprof would tell
us that)

Suggestion:

create global temporary table t ( x int );

Your problem will ARRAY insert it's 2500 values into this table (USING BIND
VARIABLES OF COURSE) and then simply:

select * from your_table where x in ( select x from t )

or

select * from your_table, t where your_table.x = t.x;

Review &
      Followup

      Rating: 4
Beware large
      SQL...  July 12, 2002
Reviewer:  AB
       from UK
At my last place a third-party app was generating a massive IN string for every
customer logging in. Eventually we ran out of enough contiguous space in the
library cache (and our library cache was big) to actually store the SQL and its
plan and had to bounce the database to "defrag" it and clear it up. Not very
good. I suggested using arrays/GTTs to the app developers but whether they ever
took it on board is anyone's guess.

I would always question why people need to generate huge dynamic SQL statements
when suggestions such as yours make so much sense...

July 12, 2002 Jeff -- Thanks for the question regarding "Last
      Index REBUILD", version 8.1.5.0.0 or 8.1.7.0.0

      You Asked
Is there a data dictionary view where I can find out the
timestamp of the last index REBUILD?  I looked at
dba_objects.last_ddl_time and that didn't seem to correctly
reflect the information.

      and we said...
This is fixed in 8.1.7:

tkyte@DEV2.THINK.COM> exec print_table( 'select * from
user_objects where object_name = ''T_IDX''' );
OBJECT_NAME                   : T_IDX
SUBOBJECT_NAME                :
OBJECT_ID                     : 77094
DATA_OBJECT_ID                : 77094
OBJECT_TYPE                   : INDEX
CREATED                       : 05-feb-2001 16:48:23
LAST_DDL_TIME                 : 05-feb-2001 16:48:23  <<----
TIMESTAMP                     : 2001-02-05:16:48:23
STATUS                        : VALID
TEMPORARY                     : N
GENERATED                     : N
SECONDARY                     : N
-----------------

PL/SQL procedure successfully completed.

tkyte@DEV2.THINK.COM> alter index t_idx rebuild;

Index altered.

tkyte@DEV2.THINK.COM> exec print_table( 'select * from
user_objects where object_name = ''T_IDX''' );
OBJECT_NAME                   : T_IDX
SUBOBJECT_NAME                :
OBJECT_ID                     : 77094
DATA_OBJECT_ID                : 77098
OBJECT_TYPE                   : INDEX
CREATED                       : 05-feb-2001 16:48:23
LAST_DDL_TIME                 : 05-feb-2001 16:49:10  <<----
TIMESTAMP                     : 2001-02-05:16:48:23
STATUS                        : VALID
TEMPORARY                     : N
GENERATED                     : N
SECONDARY                     : N
-----------------

PL/SQL procedure successfully completed.

tkyte@DEV2.THINK.COM>

Prior to that -- there is no way to determine when an index was
last rebuilt.

You could put an ALTER trigger on and capture this yourself in
the meanwhile, for example:

tkyte@ORACLE8I> column extra format a40
tkyte@ORACLE8I> create table log
  2  ( extra     varchar2(4000),
  3    timestamp date );

Table created.

tkyte@ORACLE8I>
tkyte@ORACLE8I> create or replace trigger ddl_trigger
  2  after alter on SCHEMA
  3  begin
  4       insert into log
  5       select sql_text, sysdate
  6         from v$open_cursor
  7        where upper(sql_text) like 'ALTER%REBUILD%'
  8         and user_name = USER;
  9  end;
 10  /

Trigger created.

tkyte@ORACLE8I>
tkyte@ORACLE8I> drop table t;

Table dropped.

tkyte@ORACLE8I> create table t as select * from all_users where
rownum < 100;

Table created.

tkyte@ORACLE8I>
tkyte@ORACLE8I> create index t_idx on t(username);

Index created.

tkyte@ORACLE8I>
tkyte@ORACLE8I> alter index t_idx rebuild;

Index altered.

tkyte@ORACLE8I>
tkyte@ORACLE8I> select * from log;

EXTRA                                    TIMESTAMP
---------------------------------------- ----------
alter index t_idx rebuild                05-02-2001
Review &
      Followup

      Rating: 5
Good use of
      PL/SQL  March 26, 2002
Reviewer:  Mondo Tofu
       from New England
I'm finding that more programmatic control of gathering statistics, validating
structures, checking for index rebuilds is becoming necessary.

Thanks for sharing the trigger code for tracking such events.

      Rating: 5
What is the
      data_object_id  July 12, 2002
Reviewer:  A
      reader  from USA
Tom,

As always your reponse was to the point and illustrated with example. However, I
noticed that after the rebuild the DATA_OBJECT_ID value was changed 77094 to
77098. Why is that? And what is the DATA_OBJECT_ID anyway?

Thanks a lot in advance.

          Followup:

It is the dictionary object number of the segment that actually contains the
object now.  It starts as the same as the object_id -- but is assigned the new
number after the rebuild.  The new number is the segment number of the temporary
segment that was created and used during the rebuild.  Just a level of
indirection, so the object_id doesn't have to change.

July 13, 2002 Om
      -- Thanks for the question regarding "Regarding Export and Import ",
      version 8.1.6

      You Asked
Hi Tom,

Thanks very much for your kind support and very useful suggestions.

My Quest to you now is on Import and export.

Now a days we have to export and import tables of size 1GB and more.
And this generally this process is very time consuming.For tables more
tahn 1 GB I think there is a need to split the .dmp file.I have tried
hard to make use of more than 1 file name to do that,but it does not work.

1) Can you please write the exp & imp statement which should be used to
export and imp a table of size 1 GB with parameters like buffer and other
relevant parameters which I should use to reduce the process time.

2) Is there any split option which can be used to take a export of the
large tables ?

3) Is it true that while taking the import of BIG tables we should import
the structure first followed by rows-y option in the next import statement.

I will be glad if you can write the statements with parameters and the values
which should be used for these parameters as I have tried different options
without any great improvement in the process.

Many Thanks and Warm Regards,
Om

      and we said...
Well, I would question WHY you need to imp/exp tables of one gig or more.  Why
do you do that?  Its not a backup, there are easier ways to get the data from
point a to point b (eg: sqlplus COPY command is awesome).

You say "I have tried
hard to make use of more than 1 file name to do that,but it does not work."

Well, I cannot address issues you provide no information.  I don't know how many
times a day i get "but it does not work" and no other information!!   What does
"it does not work" mean -- do you get an error, if so , what .  describe what
you did and what happened. Else, you'll get nowhere.

It works -- with 8i, you use the FILESIZE= and FILE= to specify multiple files
to export to.  It is easy, it most certainly does work.  (if you have my book, i
show how to do it and how to use a named pipe on unix to introduce compression)

1) direct=y would be the parameter you are looking for on the export.

2) see the filesize and file parameters

3) false, wrong, bad.  if you import the structure, it'll import the empty table
and put all of the constraints/indexes/etc on it.  They, you import with rows=y
into a table with INDEXES and such on it.  That is slower then importing the
table, INDEXING the table and so on.

Review &
      Followup

      Rating: 2
didn't say what
      sqlplus COPY command is  July 12, 2002
Reviewer:
      Dave Archbell  from San Diego, CA
Your response says "(eg: sqlplus COPY command is awesome)"
but I've checked the docs and don't find reference to
this in the SQL reference.  I'd have liked to see a
pointer to it or an example of syntax.

          Followup:

you didn't look in the sqlplus guide then I support (sort of thought by saying
"the sqlplus COPY command -- it would have been a pointer..)

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a82950/ch8.htm#1028839
....

July
      13, 2002 SHANMUGAM -- Thanks for the
      question regarding "Multiplex a control file in Primary and Standby
      server.", version 8.1.7

      You Asked
Hi Mr.Tom,

        Thanks for answering my previous questions.
We have setup the Primary and standby database server.
Q1)  How to multiplex the Primary server control file? Is any impact at standby
server? Is anything  shall I do in standby server?

Q2) As per Oracle document, Any structural changes we made on Primary server we
need to implemnet on standby server also. For eg)  ADD a Datafile etc.

But Suppose I need increase the one Datafile size or Rollback segmemet  size,
then What are steps I need to follow on Standby server?

Thanks in advance
shanmugam

      and we said...
q1) the way you've always done it -- just add more the the control_files
init.ora parameter.  Nothing needs be done on the standby.

q2) autoextends/resizes will carry over by themselves.  You need to ensure
sufficient disk space exists on the standby!

A rollback segment is just that -- a segment.  Like every other segment (table,
index, cluster, etc) it'll propagate normally.
Review &
      Followup

      Rating: 4
Multiplex a
      control file in Primary and Standby server  August 20, 2001

Reviewer:  Shanmugam  from Singapore
The answer is very useful to me. Thank you very much.

      Rating: 5
Another
      solution  August 22, 2001
Reviewer:  Pavel
      Levin  from UKRAINE
If you can close your database for the short time,
I can suggest another solution.
You copy all files of closed database to hard disk
and open database.(This stage can be done by hand or
automatically).
Then files are copied to standby(reserved) server
(automatically, by cron under UNIX).
All new (after copying) arc-files are copyed
to standby server after their appearance.
These scheme allows restore your production and standby
database any moment.
All changes in your production database will
be automatically in standby database as well.
You can debugg your soft on the standby database
without influence on the production one.
You can open and close reserved database any time.
Having reserved copies of database files, you can restore production and standby
databases any moment.

      Rating: 4
Multiplex a
      control file in Primary and Standby server  September 20,
      2001
Reviewer:  Shanmugam  from Singapore
Hi  Pavel Levin,

       Thanks. Can i have your mail id.

      Rating: 4
Standby
      Database control files  July 12, 2002
Reviewer:
      Oct
Hi Tom,
Please let us know
1.Is Undo and temp tablespace/datafile are also reqd. to copy on standby
database together with all other production datafiles.
2.Is multiplexing of standby control file is reqd. on standby database.
3.what about redo log?
Db=Oracle 9.0.1.1.1
Thanks
Oct

          Followup:

Better then that:

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/server.901/a88808/toc.htm
is everything you ever wanted or needed to know about data guard and standby
databases....

They should be PHYSICAL bit for bit, byte for byte copies of eachother (i mean
-- you want to use them for failover right?  -- well not really you don't WANT
to failover but their purpose is to failover to -- In that case, they need to be
as resiliant upon failover as production was right?  so yes, you want all of
that stuff there, ready to go.  You are not saving space for if you do not have
the disks sitting there ready and raring to go -- you by defintion don't have a
failover site!

9iR2 introduces the concept of a logical standby -- in which case, things can be
different BUT you'll still have undo, temp, redo -- multiplexing, the whole
thing.

July 13, 2002 --
      Thanks for the question regarding "Checking the data?", version 8.0.5.0.0

      You Asked
Hi Tom,

I have a varchar2 field and I want to change it number.
I want to check the all data in that field is all that data numbers (like
IsNumber() function in C language).

Thanks,

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function isNumber( p_string
in varchar2 ) return number
  2  as
  3          x number;
  4          not_a_number exception;
  5          pragma exception_init( not_a_number, -6502 );
  6  begin
  7          x := to_number( p_string );
  8          return 1;
  9  exception
 10          when not_a_number then
 11                  return 0;
 12  end;
 13  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select 'a', isNumber( 'a' ) from dual
  2  union all
  3  select '1', isNumber( '1' ) from dual;

' ISNUMBER('A')
- -------------
a             0
1             1

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review &
      Followup

      Rating: 5
Thanks
      Tom  July 13, 2002
Reviewer:  A reader

Thats what we was looking for.
Thanks

July 13, 2002 NIRMALKUMAR -- Thanks for the question regarding
      "How can set audit for select statement", version 7.3.1

      You Asked
Hi tom,

Hope that my question may be meaningless. Anyhow i want to know the solution,
whether it's possible or not.

We have a table which is having very sensitive data. This table having dynamic
data can be used by very few users.

Our management needs to audit this table for select statement. Is't possible? If
so how?.

We tried by writing trigger on copied SYS.AUD$ table in different schema, say
system.aud$ as table and writing trigger on it. But this method is failed with
us.

And Is any package are avaible for encrypt and decrypt as like
dbms_obsubcation_toolkit in oracle 7.1.3?. Or we should user our own encrypt
algorithm?.

thanks.

Nirmal.

      and we said...
Encryption - Decryption = Oracle8i and up.

Fine Grained Auditing (auditing selects) is an Oracle9i new feature.  It is
called Fine Grained Auditing.

This is one of those rare cases where I say -- you really truly need to upgrade
to achieve your goals.
July 13,
      2002 Shivaswamy -- Thanks for the
      question regarding "StatsPack", version 817/NT

      You Asked
Tom,

I use statspack with stale option. Of late, I find the job of statspack running
for a very long time.(Hours together, say 5 to 8 hours)I have Huge tables
800+Million to 2+ Billion rows on some critical (4 to 6 )tables.(Partitioned).
The statspack queries against such tables are very very long running..
Eventhough I do not see much CPU overhead on account of this, I have ended up
out of TEMP(on my abt 14Gig and about 7 to 10 concurrent users with small
demand) and ORA-155 on RBS.(2gig each  14 number RBSes..) While I understand, on
account of huge tables its demand may be more for TEMP & RBS, it is disturbing
to see query running well over 8 hours. Also my user jobs do not complain of
TEMP or RBS.

How best I can address this?(This is more of DSS database)
Shivaswamy

A query under sys which is mapped to the statspack job# keeps running.. The
nature of the query will be like
SELECT    /*+ */
    count(*),
count("field1"),
    count(distinct "field1"),
    sum(vsize("field1")) ,
    min("field1"),
    max("field1"),
....
for all fields of the tables.
The job has been failing for the past few days, only a few partitions are
analyzed in the same period.

I am happy to give any more info, if required.

      and we said...
There is no such thing as a statspack "stale" option  -- ok -- I asked:

statspack stale option?

or do you mean dbms_stats with the gather stale option.

I have to assume you mean the second one - dbms_stats.

What options do you use with dbms_stats to analyze your tables, the query I see
there is for computing histograms (very very intensive and takes alot of space
for nearly distinct columns).  Perhaps you need to look at what options you are
using with dbms_stats and gather ONLY the stats you really need/want to.
Review &
      Followup

      Rating:
      5
  July 13, 2002
Reviewer:
      A reader
Tom,
I express my sincere sorry, for not being specific. Your assumption was correct
and I actually meant DBMS_STATS.GATHER_SCHEMA_STATS with stale option. I will
look into the details of the options.

Thanks once again,
Shivaswamy

July 13, 2002 Charlie -- Thanks for the question regarding "How
      to store Double.POSITIVE_INFINITY in database?", version 8.1.7.0.0

      You Asked
Hi Tom,

In java, we can use Double.POSITIVE_INFINITY to specify positive infinity.  The
question is how we store
Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN in Oracle
database?  Using Number datatype?

Thanks,

      and we said...
Quick Answer: you don't -- they are not representable in an Oracle Number truly.

What I might consider:

NULL -- for all three.  They quite simply all mean "there isn't a value for this
number".  In SQL -- that would be NULL.

Review &
      Followup

      Rating: 3
NULL may not be
      good  July 13, 2002
Reviewer:  Charlie

NULL may not be good.  Since if I really want to store NULL in this column and
retrieve it later, how do I know it represents a Double.NEGATIVE_INFINITY,
Double.POSITIVE_INFINITY, Double.NaN, or a real NULL?

thanks,

          Followup:

You'll have to use two columns then. One tells you +/- inifinity or remains
null.

As a programmer myself, I never frankly saw the need for neg/pos infinity.  They
are both "not a number" really.

July 14, 2002 Hsiao -- Thanks for the question regarding "log
      miner", version 8.0.5

      You Asked
Dear Tom:
I am trying to do logminer on oracle 8.0.5 (on unix)so I copied dbmslmd.sql and
dbmslm.sql from a NT oracle 8.1.6.
SQL> @dbmslmd.sql;

Package created.

Package body created.

No errors.

Grant succeeded.

SQL> @dbmslm.sql;

Package created.

Grant succeeded.

SQL> exec dbms_logmnr_d.build('orcldict.ora', '/tiadata/iaoracle');

PL/SQL procedure successfully completed.

SQL> exec  dbms_logmnr.add_logfile('/tiadata/iaoracle/8.0.5/admin/IAORCL/archive
/1_759.dbf', dbms_logmnr.new);
begin dbms_logmnr.add_logfile('/tiadata/iaoracle/8.0.5/admin/IAORCL/archive/1_75
9.dbf', dbms_logmnr.new); end;

*
ERROR at line 1:
ORA-04068: existing state of packages has been discarded
ORA-04067: not executed, package body "SYS.DBMS_LOGMNR" does not exist
ORA-06508: PL/SQL: could not find program unit being called
ORA-06512: at line 1

"HOWEVER WHEN I"

SQL> desc dbms_logmnr;
PROCEDURE ADD_LOGFILE
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 LOGFILENAME                    VARCHAR2                IN
 OPTIONS                        BINARY_INTEGER          IN     DEFAULT
PROCEDURE END_LOGMNR
PROCEDURE START_LOGMNR
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 STARTSCN                       NUMBER                  IN     DEFAULT
 ENDSCN                         NUMBER                  IN     DEFAULT
 STARTTIME                      DATE                    IN     DEFAULT
 ENDTIME                        DATE                    IN     DEFAULT
 DICTFILENAME                   VARCHAR2                IN     DEFAULT
 OPTIONS                        BINARY_INTEGER          IN     DEFAULT

What could go wroing? When didn't it let me execute this procedure
'dbms_logmnr.add_logfile'?

      and we said...
You cannot simply take functionality from a higher (816) release of Oracle and
put it in a lower (805) release.

That'll NEVER work.  The supporting code needed to do logminer just did not
exist

You need an 8i instance in order to run 8i code.
Review &
      Followup

      Rating:
      5
  July 14, 2002
Reviewer:  Hsiao
      Chen  from Taipei, Taiwan

July 14, 2002 Sajjad -- Thanks for the question regarding "want
      to read Users Password as a Text", version 7.3

      You Asked
hi

i want to know is there any way to make users password readabkle in ver. 7.3 ,i
mean  if i issue this statment

select password from dba_users ;
it gives me some codes i dont want to change pasword , i just only want to
see/read the password..
is that possiable??
& if it is kindly please tell me in detail because i am new in oracle.

      and we said...
read

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:95412348059
to get a glimpse into how we store the passwords (they are NOT encrypted and
they are not "reversible")

What you ask for would be what most people would consider to be "a security hole
large enough to fly a rather large plane through"
July 14, 2002 Justin -- Thanks for the question regarding
      "Normalization.....", version 8.1.7

      You Asked
Hi Tom,

Am not sure there are how many normal forms..And to normalize the database how
many normal forms should be achieved...Tom can you give some examples...

Thanks..

Justin..

      and we said...
I've never really worried about such nuances.  Personally, I don't know or
really care too much about the "normal forms". I studied them once upon along
time ago but day to day, they just form part of my "knowledge base".

Here is my take on it -- limit data redundancy as much as possible - given that
performance is a very serious goal.  Design the schema to be easy to use for
your application -- not according to some rules in a book.  Use common sense and
your intimate knowledge of how the data will be utilized to design optimal
structures to hold your data.

Review &
      Followup

      Rating:
      4
Normalization  July 14, 2002
Reviewer:
      PAblo
Tom:
     If you use more normalization to represent your business rules you´ll have
less redundancy but you need to use more joins. If you need more joins in your
queries your system performance decrease,isnt' it?.In my work there're many
views with 4 or 5 joins and sometimes they need a lot of times to answer to a
simple count(*), i can´t redesign this system so i have to improve this views
with some database setting. What do you suggest it to me?, maybe clusters?, any
database setting?. We user 8.1.5 with NT and Linux.

Thank in Advance
PAblo R.

          Followup:

Herein lies my key words "Use common sense"

I cannot answer your question (it takes quite a bit of background - that
intimate knowledge of how the data is actually used) to asnwer those questions.
4 or 5 joins -- that is nothing, JOINS are not evil, joins are not bad, joins
are good.

Consider something like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select (select
decode(extent_management,'LOCAL','*','') from dba_tablespaces where
tablespace_name =
  2  b.tablespace_name) || nvl(b.tablespace_name,
  3                           nvl(a.tablespace_name,'UNKOWN')) name,
  4             kbytes_alloc kbytes,
  5             kbytes_alloc-nvl(kbytes_free,0) used,
  6             nvl(kbytes_free,0) free,
  7             ((kbytes_alloc-nvl(kbytes_free,0))/
  8                                                    kbytes_alloc)*100
pct_used,
  9             nvl(largest,0) largest,
 10             nvl(kbytes_max,kbytes_alloc) Max_Size,
 11             decode( kbytes_max, 0, 0, (kbytes_alloc/kbytes_max)*100)
pct_max_used
 12  from ( select sum(bytes)/1024 Kbytes_free,
 13                            max(bytes)/1024 largest,
 14                            tablespace_name
 15             from  sys.dba_free_space
 16             group by tablespace_name ) a,
 17       ( select sum(bytes)/1024 Kbytes_alloc,
 18                            sum(maxbytes)/1024 Kbytes_max,
 19                            tablespace_name
 20             from sys.dba_data_files
 21             group by tablespace_name
 22             union all
 23        select sum(bytes)/1024 Kbytes_alloc,
 24                            sum(maxbytes)/1024 Kbytes_max,
 25                            tablespace_name
 26             from sys.dba_temp_files
 27             group by tablespace_name )b
 28  where a.tablespace_name (+) = b.tablespace_name
 29  order by &1
 30  /
old  29: order by &1
new  29: order by 1

             %
                                                             %
MaxPoss    Max
Tablespace Name        KBytes         Used         Free   Used      Largest
 Kbytes   Used
---------------- ------------ ------------ ------------ ------ ------------
*DRSYS                 35,840       34,304        1,536   95.7        1,536
*RBS_TS_01             30,824       30,824            0  100.0            0
*RBS_TS_02            153,704      153,704            0  100.0            0
*RBS_TS_03             25,704       25,704            0  100.0            0
*RBS_TS_04             27,752       27,240          512   98.2          512
*RBS_TS_05             25,704          104       25,600     .4       25,600
*TEMPORARY            387,584      387,584            0  100.0            0
*TEST_TEMP              4,096        4,096            0  100.0            0

*USERS                645,632      498,688      146,944   77.2       94,208
SYSTEM                321,536      188,624      132,912   58.7       63,072
XXX                   102,904            8      102,896     .0      102,896
              ------------ ------------ ------------
sum                 1,761,280    1,350,880      410,400

11 rows selected.

Elapsed: 00:00:00.02

Now, considering that DBA_FREE_SPACE itself is a union all of two thee table
joins - DATA_DATA_FILES is a 4 table join -- and so on....

You just need to apply good old fashioned query tuning techniques -- remember --
a view was created for a purpose -- that view may not be the best view for you
to be using in your case, another -- better view may be called for!

      Rating:
      5
  July 15, 2002
Reviewer:
      A reader
Great reply.

I too agree with tom that there is no hard and fast rule about this
normalization.

July 14, 2002 Jayson -- Thanks for the question regarding
      "delete using multi sub-queries / lpad", version 8.1.7

      You Asked
Dear Tom:
When I run the following statement:

(  SELECT lpad(t.mdl,3,0) ,
         lpad(t.qu,2,0),
         lpad(t.st,2,0)
   FROM table1  t

   MINUS
   SELECT g.mdl , g.qu , g.st
   FROM table2 g   )

(table2 seems to be lpadded already)
327 rows are selected.
I want to delete these, but when I add the following code:

DELETE
FROM table1 t
WHERE  (  lpad(t.mdl,3,0) , lpad(t.qu,2,0) , lpad(t.st,2,0) )  in

(  SELECT lpad(t.mdl,3,0) ,
         lpad(t.qu,2,0),
         lpad(t.st,2,0)
   FROM table1  t

   MINUS
   SELECT g.mdl , g.qu , g.st
   FROM table2 g   )

4,658 rows are deleted.  I'm not sure what's wrong?
Can you help?

(mdl, qu, and st are being used together as a composite key to find
unique values, but just until the table is set up with the correct
data, then a primary key will be added)

(There are no constraints on either table)

Also, is there any way to permanently lpad a table column?

Thanks.
Jayson

      and we said...
Minus de-dups (it does a sort/distinct)

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t1 ( x int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t2 ( x int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t1 select 1 from all_users where
rownum <= 5;

5 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t1 select 2 from all_users where
rownum <= 5;

5 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t2 values (2);

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t1 MINUS select * from t2;

         X
----------
         1

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t1 where x in ( select * from
t1 MINUS select * from t2 );

         X
----------
         1
         1
         1
         1
         1

ops$tkyte@ORA817DEV.US.ORACLE.COM>

See -- the minus says "t1 minus t2" is 1.  The in says "there are 5 rows in T1
such that X is in the MINUS of T2 from T1"

mdl, qu, and st must have lots of repeats.  Not a good candidate for a "key".

Try doing the SELECT instead of a delete -- to SEE what you are getting.
July 14, 2002 Munzer -- Thanks for the question regarding
      "delete procedures", version 8.1.7

      You Asked
Tom:

1.  Can I run a delete statement to delete procedures/functions/packages in a
certain schema.

2.  ALso, deleteing all objects (trigger,procedure,sequence, etc.)?
  Would you do using ALTER tablespace drop ....

3.  I have read that a note that  says delete a datbase using dbassist and then
delete */dbf files using unix and the home directory. where can i find dbassist?
is it a unix or windows program?

4.  Does oracle application server support running JSP programs or other web
development tools like coldfusion/php/asp.  I know that database support java
stored procedures.

Thank you,

      and we said...
1) you do not "delete" procedures etc -- you "drop" them

Yes you can.

  execute immediate 'drop procedure schema.procedure';

Note that the OWNER of the procedure will need DIRECT privs to do this (eg: drop
any procedure).  See

http://asktom.oracle.com/~tkyte/Misc/RolesAndProcedures.html
for details on that

2) As a tablespace is not associated with a user, umm -- no, i wouldn't in
general.  You can if you want to.

3) it is a java app.  It's in $ORACLE_HOME/bin

4) Yes, iAS fully supports JSP, running php.  The others -- coldfusion and asp
-- not really.  Not sure of coldfusion would or would not run under iAS.
Review &
      Followup

      Rating: 5
delete
      procedures  July 14, 2002
Reviewer:  Munz
       from Reston, USA
Tom:

1. Does this drop all procedures. Do you run it in SQL*PLUS prompt.

execute immediate 'drop procedure schema.procedure';

2.  can i apply this also to tables, triggers etc like

execute immediate 'drop table schema.table';

3.  I get confused on when do you use execute immediate.  Do you use this to run
SQL commands in SQL/PLUS program?

4. Is it true that IAS is faster than IBM websphere and BEAS weblogic.  I think
the others though have larger market share? any reason why?

Thank you,

          Followup:

1) no, it would drop A procedure, you have to iterate over them (eg: select
'drop ' || object_type || ' ' || object_name from user_objects where object_type
in ( 'PROCEDURE', 'PACKAGE', 'FUNCTION' ))

2) sure.

3) you use it to do dynamic sql in plsql.  DDL must be done dynamically.

4) see

http://ecperf.theserverside.com/ecperf/
and decide for yourself.

July 15, 2002 Sulinder -- Thanks for the question regarding
      "Tunning Oracle connectivity With non Oracle systems", version 9.0.1

      You Asked
Hi Tom,
I had a a couple of questions about tunning Oracle Database which in turn uses
Teradata database thru hsodbc to fetch some data online.
Are there particularly any areas that we need to look into..?
or its same as tunning any other Oracle Database, I dont/cannot  wannna touch
teradata.

Currently i am pulling tables using create table as select * from table and then
creating indexes and analyzing, as there is quite a bit of processing done in
Oracle DB based on the data pulled.
1) How can i tune the create table as select statements ?
2) Is it possible to create a materalized view pointing to Non Oracle system >
3) The teradata tables are not loaded in dictionary, how do i get them loaded, i
cannot select using a column name from teradata.
I have to either create a view in oracle as create view x as select "column"
from table; or just give column names in double quotes.
4) There will be a lot of processing (update/Inserts)happening on the database,
and the database is in Archieve log mode , is there a posibilbity of the
database getting hung as redo are full and cannot start writing in Achieve logs.

Thanks a lot for creating this beautiful resource full site.

      and we said...
1) you might find the sqlplus copy command to be better at this.  You can tune
the array fetch size using "set arrraysize" and "set copycommit" and others.
See

http://asktom.oracle.com/~tkyte/Misc/MoveLongs.html
for an example of the copy command, also fully documented in the sqlplus guide

2) yes, the defining query can point to a non-oracle system

3) The easiest way to get them into the dictionary is as you described --
"create view V as select * from teradata_table".  If teradata has a data
dictionary you can query, you may use that as well.  For example, a dblink from
Oracle to Oracle will not incorporate the remote sites data dictionary into the
local one -- you would still have to query "select * from all_objects@dblink" to
see whats on the remote site.

4) there is but only if your DBA isn't very good.  Your DBA should have things
in place to monitor this and should be getting the archives OFF of the system as
soon as possible.

Also, you can help out by doing some operations in an unrecoverable mode (create
table as select UNRECOVERABLE, create index unrecoverable and so on).

It would be really good if you touched base with the DBA staff to

a) alert them to the possibility of additional, unexpected redo generation
b) the fact you might be using unrecoverable and they might need to take that
into consideration as far as backup and recovery go.

Review &
      Followup

      Rating: 3
Fetching tables
      into Oracle  July 06, 2002
Reviewer:
      A reader
Hi Tom,
Thanks very much for your view,
is the copy command available to use thru stored procedures, i gues i wasnt very
explicit in that question, but the pulling of table is going to be a daily
process kind a thing.

          Followup:

No, COPY is a sqlplus only command.  You would have to procedurally implement it
yourself in PLSQL.  At that point, it is better to use the insert into select *
from (insert /*+ append */ if you can.)

      Rating: 3
Acessing non
      oracel systems thry materalized views  July 15, 2002

Reviewer:  H.P.K.J.L.  from S'pore
Hi Tom,
I am trying to create a materalized view where in the query acesses the table in
non oracle systems

CREATE MATERIALIZED VIEW mv_wills
  AS
  SELECT  * test@nonoracle ;

i even tried with a view

CREATE MATERIALIZED VIEW mv_wills
  AS
  SELECT  * v_test ;

both the cases it comes out with
ORA-03113: end-of-file on communication channel

Normal selects from the table work fine.
Are there some specfic parameters which i need to set..?

          Followup:

contact support.  3113's -- the infamous EOF on communication channel -- fall
generally into the category of "bug"

      Rating:
      5
  July 22, 2002
Reviewer:  John
       from MA
SELECT  * FROM test@nonoracle
------------/\

need a from

July 15, 2002 Rajesh -- Thanks for the question regarding "Time
      Eastimation for Update/Uploading Data/Backup/Recovery/Export-Import",
      version 7.3.4 & 8.1.5

      You Asked

Hi Tom,
    I really appreciate your expertise on Oracle.Can you give me scripts for
rough time estimation for suppose
1) Updation of table with 100000 rows.If possible give me approximate time
estimation
for range of (Number of rows) of a table.e.g. 1-50000,50000-100000 so on.
2)Time estimation for uploading data into table using Sqlloader.
3)Time estimation for export/import considering different ranges of size of
database.
4)Time eastimation for Backup/Recovery considering different ranges of size of
database.
     I hope you got what i mean to say.I ask for scripts after seeing your reply
for question "Updation Time Span" on 20/03/2002.In that you showed the time
estimation for updation of table of 620000 rows.I really got impressed by it.

Hope to get reply.
--Rajesh

      and we said...
1) impossible.
1,000,000 one byte rows
1,000,000 thousand byte rows
1,000,000 rows with no indexes
1,000,000 rows with 15 indexes
IDE drives
SCSI drives
size of redo log files
size of redo log buffers
single user mode
heavily loaded system
over loaded system
really fast disks front ended with solid state ram cache
really slow disks

way way waaaayyyy too many variables.

You'll have to benchmark under your circumstances, using your equipment.

2) see #1

3) see #1

4) see #1

In that question, I was demonstrating how to compare two approaches using a
simple test harness:

http://asktom.oracle.com/~tkyte/runstats.html
you can use that to see what approach is best given two ways to do something but
it won't predicate "how long something will take"

July 15, 2002 -- Thanks for the question regarding "Select
      statement on 2 tables using group by...", version 8.1.6

      You Asked
Tom,

Long time listener, first time caller...

I need to generate a report that counts the number of each status type that is
in the Log table.

Status Table, contains the four possible status types for the app I'm
developing...

Status.status_name
------------------
Not Started
On Hold
In Process
Completed

Log Table, contains problem descriptions and the status of the problems (10
other columns too)...Here's my select statement:

select count(l.status) c_status, l.status, l.type_ind, s.status_name
from lm_log l, lm_status s
where l.type_ind = 'problems'
and s.status_name(+) = l.status
and to_char(l.date_reported, 'yyyy') in ('2002')
group by l.status, l.type_ind, s.status_name

Here's the output...

32 Completed   problem  Completed
20 In Process  problem  In Process
 8 Not Started problem  Not Started

Here's what I'm trying to get the ouput to look like...

32 Completed   problem  Completed
20 In Process  problem  In Process
 8 Not Started problem  Not Started
   On Hold     problem  On Hold

Are there any restrictions between group functions and outer joins,
thought I had read that somewhere.

Thanks for your help...Adam

I changed requests to problems in the select statement, the outer join was
misplaced too, my mistakes...

CREATE TABLE lm_log
 (
  log_id                     NUMBER NOT NULL,
  date_reported              DATE,
  requestor                  VARCHAR2(60),
  requestor_phone_num        VARCHAR2(20),
  reported_problem           VARCHAR2(1000),
  priority                   VARCHAR2(10),
  assigned_to                VARCHAR2(100),
  resolution                 VARCHAR2(1000),
  date_completed             DATE,
  type_ind                   VARCHAR2(30),
  create_date                DATE NOT NULL,
  modify_date                DATE,
  status                     VARCHAR2(20),
  closed                     VARCHAR2(2),
  assigned_due_date          DATE
 )

CREATE OR REPLACE TRIGGER bf_ins_lm_log
BEFORE INSERT
ON lm_log
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
Begin
  select lm_log_seq.nextval into :new.log_id from dual;
  :new.create_date:=sysdate;
End;
/

CREATE OR REPLACE TRIGGER bf_upd_lm_log
BEFORE UPDATE
ON lm_log
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
Begin
    :new.modify_date:=sysdate;

End;
/

CREATE TABLE lm_status
 (
  status_name                VARCHAR2(20) NOT NULL,
   )
/

Here are some inserts...

Insert into lm_log(date_reported, requestor, reported_problem, priority,
assigned_to, resolution, date_completed, type_ind, status, assigned_due_date)
values('15-JUL-02', 'Anonymous', 'Update email list page to include other
departments', 'High', 'Roger Sanchez', ' ',
' ', 'problem', 'Not Started', '31-JUL-02')

values('15-JUL-02', 'Anonymous', 'Development of short and long term plans,
timelines, resources, etc...', 'High', 'Roger Sanchez', ' ', ' ', 'problem', 'On
Hold', '31-JUL-02')

values('15-JUL-02', 'Anonymous', 'Automate problem resolution log and change
request log', 'High', 'Roger Sanchez', ' ',
' ', 'problem', 'In Process', '31-JUL-02')

      and we said...
Given that you seem to want all of the records from LM_STATUS and the respective
counts from LM_LOG -- if available -- you have the outer join backwards.

Here is how I might write that query:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(l.status) c_status, l.status,
l.type_ind, s.status_name
  2    from ( select *
  3             from lm_log
  4            where type_ind = 'problem'
  5              and date_reported between to_date('01-jan-2002')
  6               and to_date('31-dec-2002 23:59:59', 'dd-mon-yyyy hh24:mi:ss' )
  7         ) l,
  8         lm_status s
  9   where s.status_name = l.status(+)
 10   group by l.status, l.type_ind, s.status_name
 11  /

  C_STATUS STATUS               TYPE_IND                       STATUS_NAME
---------- -------------------- ------------------------------ ---------------
         0                                                     Complete
         1 In Process           problem                        In Process
         1 Not Started          problem                        Not Started
         1 On Hold              problem                        On Hold

July 15, 2002 Irshad -- Thanks for the question regarding
      "Locally Managed Tablespaces.", version 8.1.5

      You Asked
Hi,

I am running Oracle 8.1.5 and I have inherited a database with
dictionary managed tablespaces. I would like to convert all the
tablespaces to locally managed tablespaces. I have 2 questions:

1. How do I do that? I can't find DBMS_SPACE_ADMIN package in
8.1.5 but I can find DBMS_SPACE instead.

2. I read about the benefits of Locally Managed tablespaces like
...eliminates recursive SQL for space-management, uniform extent
sizes...etc. I want to know any drawbacks of locally managed
tablespace configuration as well.

Regards.

      and we said...
A dictionary managed tablespace cannot be migrated to a locally
managed tablespace (but we can migrate a locally managed to a
dictionary managed) in 8.1.5.

The reason is that each file of the locally managed tablespace
must set aside the first 64k for the bitmap.  The dictionary
managed tablespace will have data there.  It has to be moved.
We must rebuild the tablespace.

DBMS_SPACE_ADMIN.TABLESPACE_MIGRATE_TO_LOCAL was added in 8.1.6
-- it is not available in the version you have.  You cannot add
this functionality to 8.1.5.

In 8.1.6, they permit this migration however -- you must specify
a datafile with sufficient space to hold the bitmaps for all of
the existing files.  They will grab some space in there to hold
the bitmaps for all files instead of putting the bitmap at the
head of the file.

I have found no drawbacks as long as you remember to add 64k to
the size of each file (eg: if you create a locally managed
tablespace with uniform size 5m -- make sure every datafile is
sized to be:

  64k + 5m * N

where N is the number of extents you would like to be able to
put into that file.  If you created the datafile with just:

   5M * N

there would be always 5m-64k "left over" as wasted space.

Review &
      Followup

      Rating: 3
Why to convert
      to locally managed tablespace??  July 15, 2002

Reviewer:  sameer  from NJ, USA
Tom

I always have this question.
I am not expert on oracle.

Let say we have 100m initial in DMT. So at least we are guaranteed to have
contagious storage. Which reduces the head spins of disk drive.

In case of LMT if we have 1m uniform extents which can be stored thought the
disk. The probability of head spins is more.

I agree for the fact that acquiring a extent in DMT is very costly but given the
fact space is cheap & if one can estimate properly how frequently the table
might need to extain???

Dose I make any sense ??

$ameer

          Followup:

Don't even need to be an expert in Oracle to know the answer to this one.  You
just need to know that Peter Norton made a ton of money writing the "Norton
Speedisk" to defragment disks.  A 100meg file has never, will probably never,
ever be "contigous" physically.  Logically sure -- but block 1 might be a mile
away from block 2 on disk.  Files on disk are not congigous.

Additionally - with todays disk systems (raid, volume management, logical
volumes) AND THE FACT that you are a multi-user system (so the head never ever
stays where you left it!) -- these arguments don't hold an ounce of water.

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1056598515561
....

July
      15, 2002 -- Thanks for the question
      regarding "SQL", version generic

      You Asked
How to select the second maximum value in the column?Say i have
SALARY column in a table and i have values 3000,2000,1000,500 in
it ,so how to get 2000 value by querying i.e. the second maximum
in a column.

      and we said...
depends on the version of oracle.  I'll assume Oracle8i EE since
you did not say

scott@ORA8I.WORLD> select ename, sal, dense_rank() over ( order
by sal )
  2   from emp;

ENAME             SAL DENSE_RANK()OVER(ORDERBYSAL)
---------- ---------- ----------------------------
KING                5                            1
SMITH             800                            2
JAMES             950                            3
ADAMS            1100                            4
WARD             1250                            5
MARTIN           1250                            5
MILLER           1300                            6
TURNER           1500                            7
ALLEN            1600                            8
CLARK            2450                            9
BLAKE            2850                           10
JONES            2975                           11
SCOTT            3000                           12
FORD             3000                           12
KING             5000                           13

so, to get the 6'th highest salary we simply:

scott@ORA8I.WORLD> select * from  (
  2  select ename, sal, dense_rank() over ( order by sal )r
  3  from emp )
  4  where r = 6;

ENAME             SAL          R
---------- ---------- ----------
MILLER           1300          6

Review &
      Followup

      Rating: 5
Getting nTH
      result of a query with OVER  March 15, 2001

Reviewer:  Alan  from Caxias do Sul, RS BRAZIL

Hi!

That SQL is incredible!
That kind of thing is not known by the most people...

Very, very useful!

Best regards.

      Rating:
      5
SQL  March 15, 2001
Reviewer:  Eduardo
      Ferrari  from Caxias do Sul, RS Brazil
Hello!!! This SELECT kicks ass!!!! It's wonderful, most people do not know how
to do this.

      Rating:
      3
  March 15, 2001
Reviewer:
      A reader

      Rating: 3
How to find it
      if Version is 7.3.2  July 13, 2002
Reviewer:
      Riaz Shahid  from PRAL, Islamabad, Pakistan
Hi Tom !

I m using Oracle 7.3.2. How can i find the second maximum salary from emp table.

Regards

          Followup:

You won't find it efficiently in SQL, you will want to order by sal DESC, fetch
and discard the first row and then keep the second.

You can *do* it in sql, it just won't be

a) fast
b) the right way to do it (because of a)

Give it a whirl -- it is an interesting query to figure out (it can be done)

      Rating: 5
Can I do same
      with 8.1.5 Standard Edition  July 14, 2002

Reviewer:  Reddy  from Bangalore, India
Hi Tom

Very beautiful example. Same thing can i query in Oracle 8.1.5 Standard Edition.
In 8.1.5 dense_rank is not available.

How can I do with 8.1.5 SE.

Thanks

Reddy

          Followup:

see comment right above yours -- same answer as for 7.3

      Rating: 4
Will this Work
      ?  July 15, 2002
Reviewer:  Tejas  from
      INDIA
Will this Work for second maximum value ?

select * FROM emp
    where sal = (select max(sal) from emp
        where sal < (select max(sal) from temp));

          Followup:

that is one way, yes

  1  select * FROM emp
  2      where sal = (select max(sal) from emp
  3*         where sal < (select max(sal) from emp))
scott@ORA817DEV.US.ORACLE.COM> /

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM
DEPTNO
---------- ---------- --------- ---------- --------- ---------- ----------
----------
      7788 SCOTT      ANALYST         7566 09-DEC-82       3000
  20
      7902 FORD       ANALYST         7566 03-DEC-81       3000
  20

scott@ORA817DEV.US.ORACLE.COM> select sal from emp order by sal;

       SAL
----------
       800
       950
      1100
      1250
      1250
      1300
      1500
      1600
      2450
      2850
      2975
      3000
      3000
      5000

14 rows selected.

      Rating: 4
And what about
      this ?  July 15, 2002
Reviewer:
      A reader
Please comment on the Performance of following Query.

select ename, sal from emp where sal =
    (select sal from
        (select rownum rank, sal from (select distinct(sal)
              sal from emp order by sal desc))
    where rank = 6);

          Followup:

It is against emp, it will be blindingly fast because emp has 14 rows.

In general, that'll work just fine performance wise if

o sal is indexed
o sal is NOT NULL (else the index cannot be used for that query)
o you use the CBO

there is an optimization in 8i for TOP-N type queries like this -- it'll do it
rather quickly.

      Rating: 5
More Analytic
      Function...  July 15, 2002
Reviewer:  Sikandar
      Hayat Awan  from Pindi Bhattian - Pakistan
The dense_rank() function gave me initiative to find more functions. So here are
some valuable links for you guys,

http://www.akadia.com/services/ora_analytic_functions.html
http://www.quest-pipelines.com/newsletter-v3/0402_D.htm
I hope you will explore more as I am ....

      Rating: 2
Other way of
      doing the same thing...  July 16, 2002
Reviewer:
      Avinash Shivani  from INDIA
I think the same query can be written like this ( which will run on any version)

Select * from emp
where 1 = ( select count(*)
            from emp e
            where salary > emp.salary)

This will select the employee with the second
highest salary.

Hope it helps solve your problem.

Avinash

July 15, 2002 Bhavesh -- Thanks for the question regarding
      "owa_util.listprint Query", version 4.0.5

      You Asked
Hi Tom,
    Can you please help me in writing the following query :
The following works fine.

owa_util.listprint('select cod_country, nam_country, '||' decode(cod_country,
''USA'', ''SELECTED'', null) from country order by
cod_country', 'p_cod_country', 1, FALSE);

but when I take it in htp.tableData I just get trapped.

htp.tableData('owa_util.listprint('select cod_country, nam_country, '||'
decode(cod_country, ''USA'', ''SELECTED'', null) from country order by
cod_country', 'p_cod_country', 1, FALSE)');

I understand I am not able to put proper colons.
Thanks a lot in advance.
Regards

Bhavesh

      and we said...
listprint is a procedure, not a function.  since there is not tabledata
open/close procedures, you'll just emit the HTML yourself:

htp.p( '<td>' );
owa_util.listprint( .... );
htp.p( '</td>' );
Review & Followup

      Rating:
      5
owa_util.listprint   March 24, 2001

Reviewer:  Bhavesh  from Singapore
As usual, Ask Tom has made me sort out hours of job in few seconds. This is not
documented anywhere on how to use oracle utilities while working with table
data. Thanks a million.

      Rating: 5
READONLY
      LISTPRINT  March 24, 2002
Reviewer:  Munz
       from Reston, USA
Tom:

WIt hHTML you can add an attribute like "READONLY" or "DISABLED" for an input
text type to maket it readonly or disabled.

Now, I have several forms that use listrpint for data entry.  I am now  trying
to make a brwose only form with no edit so i want to add "READONLY" to the tag.
IS there a way to add this to the listprint and if not does this mean I have to
rewrite all of owa_util.listprint to <SELECT> tags with defined cursors.

Thank you,

          Followup:

what you see is what you get.  readonly and disabled must be relatively new
(i've not heard of them personally) and post date listprint by many years (i
know, I wrote listprint in the first place)...

You'll need to have your "own" listprint -- it is shipped in source code format,
easy enough for you to modify and make it your own.

      Rating: 5
Disabled is a
      new attribute  March 25, 2002
Reviewer:  Tom
       from UK
The reason that disabled and readonly are not well known is they are not fully
supported yet.  You'll be fine if you try to use it in IE4+ [I think] but
Netscape 4 certainly doesn't.  Hence it is not very useful unless in corporate
IE only environment

      Rating:
      5
listprint  July 15, 2002
Reviewer:
      Munz  from Reston, USA
Tom:

1.  Can i use an onchange javascript event handler with listrpint or not?

THanks

          Followup:

you'll need to Do It Yourself.

July 15, 2002 kamlesh -- Thanks for the question regarding
      "Preference of Cost based optimizer over rule based optimizer", version
      8.1.7

      You Asked
why do you prefer cost based optimizer over rule based optimizer?

      and we said...
because

a) in the next release of Oracle, RBO goes away
b) there are literrally dozens of features NOT AVAILBLE to you with RBO
   - function based indexes
   - index fast full scans
   - index skip scans
   - parallel query
   - bitmap indexes
   - index organized tables
   - etc
c) in the next release of Oracle, RBO goes away.
Review &
      Followup

      Rating: 5
What about hard
      coded /*+ rule */ hints?  July 15, 2002
Reviewer:
      A Reader  from USA
We tried to avoid rule RBO as much as possible.  However, there some queries
perform well only with /*+ RULE */ hint.  If RBO gese away in future, what
should we do about these hints when we do upgrade?

          Followup:

Our application developers have a simple rule they must follow

a) no hints
b) CBO only

all Oracle applications are delivered that way.  Have you tried the parameters
that most affect the optimizer -- setting them for your system.  Also, have you
looked at the way in which you analyze you objects.  Most people do neither.
Analyze (better yet, dbms_stats) has lots and lots of options to them!

      Rating: 3
no
      hints?  July 15, 2002
Reviewer:  Steve
       from Dallas
your developers use no hints at all? or just no rule hints?

If they are not using any hints why so? Is CBO always picking the best plan for
you without the need for any influence at all?

          Followup:

No hints = no hints.  /*+ rule */ is a hint.  No hints.

It is their coding standard.  As new features come out - the use of hints will
preclude in some cases the optimizer from using that better path.

They test and they tune when needed.

      Rating: 5
Common
      dbms_stats mistakes  July 15, 2002
Reviewer:
      Greg  from San Francisco, CA USA
Hi Tom,

In your reply you asked, Have you "looked at the way in which you analyze your
objects?," and you noted that dbms_stats "has lots and lots of options."

Apparently you find that people often set up a standard dbms_stats command and
then just assume they are gathering stats they need and that's the end of it.

It would be great if you would describe two or three common errors people make
when gathering stats that may cause serious problems, especially mistakes
involving the options available with dbms_stats.

Thanks for the excellent job you do on this forum.

      Rating:
      3
  July 15, 2002
Reviewer:  Wolfgang
      Breitling  from Calgary, Alberta Canada
I have been using the CBO for some time now and mostly it does a good job, but
sometimes, still too frequently, it gets it wrong. In my experience there are
mostly two reasons why it picks the wrong path - and that wrong path is almost
invariably a NL where it severely underestimated the cardinality executing the
inner rows source, maybe itself made up of a NL, thousands of times instead of
the estimated 1 time. I traced the origin for the underestimation to the
implicit premise that the predicates are independent, i.e. if predicate A has 10
distinct values and predicate B has 10 distinct values then predicate A and
predicate B will have 100 distinct values and therefore the extimated
cardinality is 1/100th of the original cardinality. That is often not the case
and the most up-to-date statistics do not overcome that premise. The other
premise is that column values are evenly distributed. You can overcome that
premise to some degree with histograms.

July 15, 2002 Mike -- Thanks for the question regarding
      "DBMS_SQL and Oracle Object REF", version 8.1.7.0

      You Asked
I may have been using Oracle for awhile, but this problem is new to me and I'm
having difficulty finding the right information to solve this problem.

When I attempt to create the procedure below using PL/SQL the error
returned is PL/SQL: Statement ignored
PLS - 00306 wrong number or types of arguments in call to '||'.

A change to another data type will allow the procedure to compile, but I just do
not understand how the REF is interpreted in the parse.

The question is, what do I have to do to allow dynamic sql to parse the
statement so that it includes the REF?

Please help..

CREate or replace procedure insert_dynamic
  ( start_date_in IN VARCHAR2,
    stop_date_in IN VARCHAR2,
    src_hw_in IN varchar2, probe IN VARCHAR2 )

 IS

 cursor_handle         INTEGER;
 execute_feedback     INTEGER;
 extension         varchar2(10);
 table_name         varchar2(64);

 src_node_ref  REF node_obj;
 start_date date;
 stop_date date;

 BEGIN

 extension  := '_trans_tbl';
 table_name := probe || extension;

 -- Insert source into the node object and return the reference
    BEGIN
        src_node_ref := get_node_ref(src_hw_in, 'NA');
        IF src_node_ref IS NULL
        THEN
            insert_node_type_proc(src_hw_in, 'NA');
            src_node_ref := get_node_ref(src_hw_in,'NA');
        END IF;
    END;
 -- Assign and format the variables
 start_date := to_date(start_date_in, 'MM/DD/YYYY HH24:MI:SS');
 stop_date  := to_date(stop_date_in, 'MM/DD/YYYY HH24:MI:SS');

 cursor_handle := DBMS_SQL.OPEN_CURSOR;

 DBMS_SQL.PARSE(cursor_handle, 'insert into ' || table_name  ||
      ' ( dt_start, dt_stop, src_node ) values( ' ||
                   '''' || start_date      || '''' || ',' ||
               '''' || stop_date       || '''' || ',' ||
               '''' || src_node_ref    || '''' || ')', DBMS_SQL.NATIVE);

 execute_feedback := DBMS_SQL.EXECUTE (cursor_handle);
 commit;
 DBMS_SQL.CLOSE_CURSOR( cursor_handle );
END insert_dynamic;
/

      and we said...
You have GOT to use bind variables -- not using bind variables is quite simply
"the single worst thing you can ever do in Oracle"

Never hard code (concatenate) literals into an insert statement -- NEVER.

Also, use native dynamic sql, much easier.

execute immediate
 'insert into ' || table_name ||
 ' values ( dt_start, dt_stop, src_node ) values ( :x, :y, :z )'
USING start_date, stop_date, src_node_ref;

Review &
      Followup

      Rating: 4
Follow
      on  July 15, 2002
Reviewer:  Mike Taldo
       from Linthicum, Maryland
Thank you for the insightful information. After looking at the sample you
provided the stored procedure has been changed to something like the following:

execute immediate
 'insert into ' || table_name ||
 '( dt_start, dt_stop, src_node ) '  ||
 ' values ( :A, :B, :C )'
 using start_date, stop_date, src_node_ref;

The changes helped overcome the problems with REF's and allowed the me to
dynamically determine the table name.

However, the stored procedure with the above changes ran 6 seconds slower while
processing 6000 records when compared to the regular insert statement. Is this
consistant with your findings? Could the six seconds be related to the parsing?

Thanks for your help,

Mike

          Followup:

since the "regular" insert never ran -- due to your inability to insert the ref
-- how can you compare?

This should run faster then an insert without bind variables.

July 15, 2002 Zen
      -- Thanks for the question regarding "Locally Managed Tablespace", version
      9.0.1.1.1

      You Asked
Hi Tom
Is local managed tablespace in auto allocate mode take care of allocating the
more space when defined tablespace size is full or not?
Say Tablespace "A" is of size 100 mb with one datafile.
and after some time its used 100%.now will we have to addd more space or it
managed itself.?
Thanks
Zen

      and we said...
Depends on whether you created it with AUTOEXTEND ON at the datafile level.

when you added the datafile to the tablespace, you had the option of saying:

autoextend on
NEXT xM
MAXSIZE yM

to let it "grow" as it needs.  Use my "free.sql"

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:506422022373
to see a report that'll show you how big each tablespace is now, the % utilized
and how BIG the tablespace could become if it grows.
Review &
      Followup

      Rating: 5
Locally Managed
      Tablespace  July 15, 2002
Reviewer:  Zen

Thanks Tom,
I got the answer,
we have to edit the datafile for autoextend feature when full.
Please let us know how to measure ,what size is appropriate for autoextend and
maximum size(or unlimited)when full.

Can we alter LMT from auto allocation to Unifom allocation mode?
Is uniform allocation have any advantage over auto allocation.?
Thanks

          Followup:

answer to the first part -- is up to you.  You have to tell me what is an
appropriate upper bound on your OS -- and what size file you want to deal with.

No, you cannot alter the type from auto to uniform.

The advantage of uniform -- you know exactly how big each and every extent will
be.  With auto, you don't have any idea as the algorithm is not documented and
not entirely predicable.


Subject | Article | Last Updated (East coast time)
backups | article only | Jul 16, 2002 07:18 am
Built in Functions -- bitand | article only | Jul 16, 2002 07:32 am
Materialized View | article only | Jul 16, 2002 07:38 am
Syncronization from Oarcle to sqlserver | article only | Jul 16, 2002 06:12 pm
Slow Response at Peak hour and some performance question | article only | Jul 16, 2002 10:04 pm
IP address | article only | Jul 17, 2002 10:20 am
using rownums & for clause in 9i | article only | Jul 17, 2002 10:22 am
Unable to bind to Oracle Management Server | article only | Jul 17, 2002 10:23 am
Re : 'Total required in Hierarchical query' | article only | Jul 17, 2002 10:24 am
how to replicate a BLOB field to another DB table. | article only | Jul 17, 2002 01:55 pm
Populating A column value for all colmns with NULL values | article only | Jul 17, 2002 03:24 pm
How to display Weleom Oracle Portal page instead of user defined page whe logout | article only | Jul 18, 2002 05:22 am
Features of 9i | article only | Jul 18, 2002 08:15 am
generate sql for sqlldr | article only | Jul 18, 2002 08:20 am
CONNECT BY CLAUSE | article only | Jul 18, 2002 09:31 am
Invalid dimension | article only | Jul 18, 2002 10:57 am
Does Spool, set pages 0 command works with perl Scripts. | article only | Jul 18, 2002 10:59 am
Validation | article only | Jul 18, 2002 02:07 pm
Unused Index(s) for a specific Time? | article only | Jul 19, 2002 07:22 am
Limit and conversion very long IN list : WHERE x IN ( ,,, ...) | article only | Jul 19, 2002 07:28 am
return result set from temporary table to java | article only | Jul 19, 2002 08:38 am
why do we need ORDBMS? | article only | Jul 19, 2002 08:49 am
contention for data blocks. | article only | Jul 19, 2002 09:31 am
Get a string as a BackReference.. | article only | Jul 19, 2002 09:32 am
Implict bind variables? Implicit cursors? | article only | Jul 20, 2002 09:50 am
PL/SQL | article only | Jul 20, 2002 12:33 pm
SQL Loader - CLOB/BLOB | article only | Jul 20, 2002 12:42 pm
Top N% of a set of data | article only | Jul 20, 2002 12:55 pm
read from flat file and load into a pl/sql table | article only | Jul 21, 2002 09:24 am
How to delete or update data in nested table? | article only | Jul 21, 2002 10:24 am
How to use Utlbstat & Utlestat | article only | Jul 21, 2002 10:28 am
Oracle 9i new feature EXP | article only | Jul 21, 2002 11:51 pm
High Parsing | article only | Jul 22, 2002 09:39 am
how to send file with any type to directory on the server | article only | Jul 22, 2002 09:40 am
When to create different schema for the users? | article only | Jul 22, 2002 09:50 am
binding "where in :" as an in-list | article only | Jul 22, 2002 10:05 am
Table Sizing | article only | Jul 22, 2002 06:27 pm
direct path load and sql loader | article only | Jul 22, 2002 06:36 pm
Application schema | article only | Jul 22, 2002 07:05 pm
NVL function with select clause | article only | Jul 22, 2002 08:08 pm
bitmap indexes | article only | Jul 22, 2002 08:13 pm
Querying instance names | article only | Jul 22, 2002 08:25 pm
Oracle8i connecting a IBM DB2 database | article only | Jul 22, 2002 08:38 pm
row(s) 1 - 43 of 43
July 16, 2002 mathew -- Thanks for the question regarding "backups", version 8i+
You Asked
hi,
   If the physical data files and the RMAN based backup are of the same size
what will you infer - the tablespace is almost full?
Is the RMAN backup always of smaller size as compared to the physical files?
What overheads does RMAN pose while taking backups to online databases? Also why
is it recomended to issue a "switch logfile" command after taking hot backups?

thanks,
mathew
and we said...
The tablespace could be EMPTY actually -- you can only infer that at SOME point
in the past, the tablespace was 100% utilized.  RMAN backs up every block that
was USED at some point. It may no longer be in use.

A hot backup will incur IO against the database.  That could affect other
running sessions.  However, the alternative of a cold backup is even worse --
the other running sessions don't exist.  Use HOT.

You generally do a switch logfile so you can gather together all of the archives
needed to take the backup you just made and make it "consistent".  You need all
of the archives/log generated DURING the hot backup to make the hot backup
consistent.  If you do a switch log file, add all of the archives to the backup
set -- you now have a hot backup + logs needed to restore it consistently to
another machine.
Review & Followup
Rating: 4
July 15, 2002
Reviewer: A reader
"RMAN backs up every block that was USED at some point. It may no longer be in
use."

Are you talking about high watermark here? Will RMAN copy the blocks that are
released (after a Truncate) because those blockes were in use at some point?
Could you kindly explain why does RMAN copy the previously used blocks? Thanks.

Followup:
talking HWM.

Rating: 4
July 15, 2002
Reviewer: mathew from India
<quote>
The tablespace could be EMPTY actually -- you can only infer that at SOME point
in the past, the tablespace was 100% utilized
</quote>

could you explain how a tablespace could be empty and 100% utilized? Are you
saying that at a point in time the tablespace was full with data but over a
period of time it got deleted?
If that is the case why does a full rman based hot backup those blocks which
don't have any records? Is that a bug or something? Does 9i also do the same
thing?

Followup:
insert into some_table select * from one_billion_record table;
commit;
delete from some_table;
commit;

tablespace that holds some_table is quite "full" looking -- yet -- it is
"empty".

When a block is used -- it becomes part of a data structure, the data structure
that is the table.  It'll be in the data structure until you truncate or drop
that structure.  As long as it is part of the structure -- it'll be backed up.

July 16, 2002 Krishna -- Thanks for the question regarding "Built in Functions -- bitand", version 8.0.4
You Asked
   What is the significance of the 'bitand' function used
    in the definition of the v_$ dynamic performance views.
and we said...
Its used in lots of the views (ALL_*, DBA_*, USER_*) as well as
the v$.  bitand is undocumented but used internally alot.

Bitand() is a bit wise manipulator of 32bit signed integers.  We
can use it to see if a specific "bit" in the signed integer is
ON or OFF.  For example (uses some base conversion functions I
have at
http://osi.oracle.com/~tkyte/hexdec/index.html
to show binary representations)

ops$tkyte@DEV8I.WORLD> select to_bin(1234) bin,
  2             bitand(1234,1)+0 bit1,
  3             bitand(1234,2)+0 bit2,
  4             bitand(1234,4)+0 bit3
  5    from dual
  6  /

BIN                BIT1       BIT2       BIT3
------------ ---------- ---------- ----------
10011010010           0          2          0

ops$tkyte@DEV8I.WORLD>

that shows that bitand could tell us if a given bit (2^n) is on
or off.  If its ON, the the return will be >0, if its off --
it'll be zero.
Review & Followup
Rating: 5
July 15, 2002
Reviewer: A reader
Tom,

 I am confused with bitand function. Can you explain in really simple words?
Where it can be used?

Thanks,

Followup:
http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/server.901/a90125/functions14.htm#1001968
....

      Rating:
      4
Undocumented?  July 15, 2002
Reviewer:
      Mark J. Bobak  from Ann Arbor, MI
Um, Tom, first you say it's undocumented, and then you give
a pointer to where it's documented?  Looks to me like it's
been documented since at least 8.1.7, though I'm aware it
existed well before that, 8.0, probably even 7.3....?

-Mark

          Followup:

It was documented starting in 817 -- sorry about that.  (answered the question
for 804 -- it gets a little hairy with all of the changes/additions in the
releases!)

      Rating: 5
is
      utl_raw.bitand the same as the bitand discussed here..  July
      16, 2002
Reviewer:  pasko
Hi Tom

I am trying to get the 5 LSB ( least significant bits) from each Byte of a RAW
value.

Is it OK if i use :

utl_raw.BIT_AND( my_raw_value  , raw_1F  )

given that my raw_1F  is defined as
 raw(16)  :=    HEXTORAW('1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F1F')  ;

Could i also use the other bitand?

Thanks.

          Followup:

You cannot use the other bitand -- it works on 32 bit signed integers.  Your raw
string (first of all it is raw, not number so that wouldn't work) appears much
larger then that.

You can use utl_raw. You could use:

utl_raw.bit_and( my_raw_value, hextoraw( rpad('1F',length(my_raw_value),'1F') )
);

that'll ensure that the mask 1f is as long as it needs be and no longer.  when
we use length on my_raw_value, my_raw_value will be turned into hex first so
we'll get the right number of 1F's

      Rating: 5
the Length
      trick is Great...  July 16, 2002
Reviewer:
      Pasko
Hi Tom,
The length trick is Great !
Actually i'm getting the my_raw_value from
the return value of
 dbms_obfuscation_toolkit.md5 using the seed as
sys_guid() and to_char(sysdate) and some other varchar2 value.

That way i can translate each byte to of the raw value to only 5 LSB's.

I think  dbms_obfuscation_toolkit.md5 returns only 16 byte RAWs ,  right?

How could i use mod( my_raw , 32 ) , i guess that would achieve the same result.

Thanks

      Rating:
      5
Whoops...  July 17, 2002
Reviewer:  Mark
      J. Bobak  from Dearboen Heights, MI
Sorry, Tom.  I didn't realize this was first posted more
than two years ago, and for 8.0.4.

July 16, 2002 vishal -- Thanks for the question regarding
      "Materialized View", version oracle8i version 8.1.5.0.0

      You Asked
1. what is the advantage of materialized view over a summary
table which is updated by database trigger in data warehousing ?

2. when i queried v$waitstat , the result is

CLASS                   COUNT       TIME
------------------ ---------- ----------
data block              10500       5180
sort block                  0          0
save undo block             0          0
segment header              2         15
save undo header            0          0
free list                   0          0
extent map                  0          0
bitmap block                0          0
bitmap index block          0          0
unused                      0          0
system undo header          0          0
system undo block           0          0
undo header               142         47
undo block                 24         41

14 rows selected.

how to interprete this ?

      and we said...
1)

o a MV is done for you (you write no code).  if you do it
yourself, you code it yourself.  Less code to write/maintain

o QUERY REWRITE.  You query the detail tables, we automagically
rewrite to use the summaries.  Add a new summary and people
start using it without being trained to use it.  Take away a
summary table and you don't have to tell anyone.  Summary tables
should be hidden from the end user -- with MV's they are.

o MV's can be updated either in real time or in a deferred
fashion.  Triggers either go in real time or not (more flexible)

2)

very hard to say without other information.  If the database in
question is heavily hit and has been up for a long time, the
fact that you had 52 seconds of waits is not relevant.  If the
database is moderately hit and/or hasn't been up long -- 52
seconds might be a long time.

The way to reduce "data block" waits is to increase free lists
on the affected objects (tables/indexes that are frequently
inserted into concurrently).  This wait is a wait for freelist
blocks in a segment.  In 8.1.5 and before, you have to RECREATE
the object to increase the free lists (alter table T move
storage ( ) .... or alter index I rebulid storage ( )....).  In
8.1.6, you can alter objects and add freelists.

Review &
      Followup

      Rating: 4
chen sun,
      HK  July 16, 2002
Reviewer:  chen sun
       from Hong Kong
Hi Tom,
   Can I create table MV like this:
   create materialized view as select empno, cursor(select count(*) from emp)
where .......

   My question is that can create a MV that the query included a cursor?

          Followup:

Nope --
scott@ORA920.US.ORACLE.COM> set echo on
scott@ORA920.US.ORACLE.COM> create materialized view X as select empno,
cursor(select count(*) from emp)
  2  from emp
  3  /
create materialized view X as select empno, cursor(select count(*) from emp)
                                            *
ERROR at line 1:
ORA-22902: CURSOR expression not allowed

scott@ORA920.US.ORACLE.COM> spoo off

(it is quite easy to test some of these things -- no?)

MV's are just tables in the end.  they can only use datatypes that can be used
in a real table.

July 16, 2002 Nagaraj -- Thanks for the question regarding
      "Syncronization from Oarcle to sqlserver", version 9.0.1.1.1

      You Asked
Hi Tom,

I have a requirement, when ever if application is populating the data into
master tables it should get populated into sql server . I had created
heterogeneous services of oracle  to connect to sqlserver. I had written a
trigger with pragma autonomous transaction . and I set auto commit on . I was
able to insert into sqlserver, I know it is not two phase commit, will work in
this scenario. As a test I had shutdown sqlserver and tried inserting into
oracle it
Worked In my exception block I am inserting into another table with commit. I
can’t suggest transparent gateways as we need to spend money on it.

i am setting set auto commit on other wise i get

ERROR at line 1:
ORA-00164: autonomous transaction disallowed within distributed transaction
ORA-04088: error during execution of trigger 'EMP_SQL'

CREATE OR REPLACE TRIGGER EMP_SQL
after INSERT  ON EMP
for each row
declare
pragma autonomous_transaction;
BEGIN
if inserting then
INSERT INTO EMP@Msql VALUES(:NEW.EMPNO,:NEW.ENAME);
end if;
commit;
exception
when others then
insert into emp_remote(:new.empno,:new.ename);
comit;
END;

My question is this a right way of doing or you have any other solution, any
ideas Will help me greatly.

what are potential problems u feel it may come up. i had tested with sqlplus not
with my application.

Thanks and regards
V.Nagaraj Rao

      and we said...
Well, lets put it this way -- you are surely heading towards a situation where
you will lose data integrity.  I can assure you of that.

Tell me -- what happens when the insert in oracle is rolled back?  Whoops
Tell me -- what happens when sqlserver is not available?  Whoops

Is the total lack of integrity with regards to the data worth the money you are
saving, I hope this data is next to meaningless (in which case you seem to be
spending alot of time doing something for nothing).

I can suggest perhaps a "better" way.  It is a 99% solution (you can end up
with the same syncronization problems -- I've just decreased the window of
opportunity and removed some of the possible errors).

Do not do the insert right away, rather, SCHEDULE the insert to take place
later, after a commit.  If the remote insert succeeds, the job will go away --
if the remote insert fails -- the job will fail--you'll get an error message in
the alert log and you can fix it (if it does not fix itself).  If the original
transaction rolls back -- the dbms_job submission will roll back as well.

Do something like this:

create table queue_table( job_id number primary key, empno number );

create procedure do_insert( p_job_id in number )
as
begin
   insert into emp@sql select empno, ename from emp where empno
     = ( select empno from queue_table where job_id = p_job_id );
   delete from queue_table where job_id = p_job_id;
end;
/

and in the trigger code:

declare
   l_job number;
begin
   dbms_job.submit( l_job, 'do_insert( JOB );' );
   insert into queue_table( l_job, :new.empno );
end;

Now, after you commit, this job (and any other jobs you have queued) will be run
in the background.  Do it the way I describe above (with the queue table --
passing JOB to your background job) as the is the only way to make use of bind
variables with jobs (very very very important)

Read about dbms_job in the Supplied Packages Guide:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76936/dbms_job.htm#999107

Review &
      Followup

      Rating: 3
Syncronization
      from oarcle to sqlserver  July 17, 2002
Reviewer:
      Nagaraj Rao  from INDAI, Banglore
Tell me -- what happens when the insert in oracle is rolled back?  Whoops

From application once the admin user selects save it is  commit their is no
rollback

Tell me -- what happens when sqlserver is not available?
  Whoops

In my triggering code i am inserting inta a table when ever
their is an exception and it populate the remote database with some background
job periodic check on this table.

But your way is the best one thanks it helped me a lot.

July 16, 2002 Keanu -- Thanks for the question regarding "Slow
      Response at Peak hour and some performance question", version 8.1.5

      You Asked
Hi Tom:

It's very happy that I can submit a question in here.
I have some questions about the Performance issue. Please help.

Dear all:

I have a OCI program connect to the Oracle Database Enterprise Edition 8.1.5 on
Windows NT server platform. This program pass execute a store procedure and some
update statment with bind variable used into the Database. The table includes
million of data. The table is normal heap table with some indexes created on the
column that frequently updated and using in the "where" clause.

During peak hours, almost ten DML statment pass through the OCI program.
however, in some time the response from the database would exceed more than ten
seconds. normally the response time is within one second. During to the business
rule, the response time needs to be within ten seconds.

I capture the statistics from the utlbstat/utlestat. I do not find any clue from
this report. Part of report.txt is attached.

Statistic                   Total        Per Transact Per Logon    Per Second
--------------------------- ------------ ------------ ------------ ------------
CPU used by this session         2827460        83.89        558.9       218.42
CPU used when call started         22931          .68         4.53         1.77
CR blocks created                    935          .03          .18          .07
DBWR buffers scanned               14374          .43         2.84         1.11
DBWR checkpoint buffers wri       129717         3.85        25.64        10.02
DBWR checkpoints                     105            0          .02          .01
DBWR free buffers found            14374          .43         2.84         1.11
DBWR lru scans                         4            0            0            0
DBWR make free requests                4            0            0            0
DBWR summed scan depth             16285          .48         3.22         1.26
DBWR transaction table writ         1520          .05           .3          .12
DBWR undo block writes             17224          .51          3.4         1.33
SQL*Net roundtrips to/from         56693         1.68        11.21         4.38
background checkpoints comp          105            0          .02          .01
background checkpoints star          105            0          .02          .01
background timeouts                14916          .44         2.95         1.15
branch node splits                     1            0            0            0
buffer is not pinned count        842771           25       166.59         65.1
buffer is pinned count            569554         16.9       112.58           44
bytes received via SQL*Net      50343633      1493.61       9951.3      3889.04
bytes sent via SQL*Net to c     16598903       492.46      3281.06      1282.26
calls to get snapshot scn:        772795        22.93       152.76         59.7
calls to kcmgas                    34497         1.02         6.82         2.66
calls to kcmgcs                      795          .02          .16          .06
change write time                   1128          .03          .22          .09
cleanouts and rollbacks - c           26            0          .01            0
cleanouts only - consistent          445          .01          .09          .03
cluster key scan block gets        11315          .34         2.24          .87
cluster key scans                   3581          .11          .71          .28
commit cleanout failures: b           17            0            0            0
commit cleanout failures: b          484          .01           .1          .04
commit cleanout failures: c            3            0            0            0
commit cleanout failures: c          658          .02          .13          .05
commit cleanouts                  199038         5.91        39.34        15.38
commit cleanouts successful       197876         5.87        39.11        15.29
consistent changes                  1218          .04          .24          .09
consistent gets                  2929184         86.9          579       226.28
cursor authentications               333          .01          .07          .03
data blocks consistent read          937          .03          .19          .07
db block changes                  806732        23.93       159.46        62.32
db block gets                    1110245        32.94       219.46        85.77
deferred (CURRENT) block cl       102533         3.04        20.27         7.92
dirty buffers inspected                4            0            0            0
enqueue releases                   78276         2.32        15.47         6.05
enqueue requests                   78281         2.32        15.47         6.05
enqueue timeouts                       8            0            0            0
enqueue waits                          4            0            0            0
execute count                     735706        21.83       145.43        56.83
free buffer inspected                  4            0            0            0
free buffer requested              40622         1.21         8.03         3.14
hot buffers moved to head o        52752         1.57        10.43         4.08
immediate (CR) block cleano          471          .01          .09          .04
immediate (CURRENT) block c        80868          2.4        15.98         6.25
leaf node splits                     144            0          .03          .01
logons cumulative                   5059          .15            1          .39
messages received                  43716          1.3         8.64         3.38
messages sent                      43717          1.3         8.64         3.38
no work - consistent read g       597009        17.71       118.01        46.12
opened cursors cumulative          68665         2.04        13.57          5.3
parse count (hard)                    23            0            0            0
parse count (total)                66142         1.96        13.07         5.11
parse time cpu                       733          .02          .14          .06
parse time elapsed                   758          .02          .15          .06
physical reads                     42638         1.26         8.43         3.29
physical reads direct               5717          .17         1.13          .44
physical writes                   138702         4.12        27.42        10.71
physical writes direct              8981          .27         1.78          .69
physical writes non checkpo        31396          .93         6.21         2.43
process last non-idle time     934070365     27712.29    184635.38     72156.85
recursive calls                   957770        28.42       189.32        73.99
recursive cpu usage                13180          .39         2.61         1.02
redo blocks written               213281         6.33        42.16        16.48
redo buffer allocation retr          147            0          .03          .01
redo entries                      438816        13.02        86.74         33.9
redo log space requests              164            0          .03          .01
redo log space wait time            6209          .18         1.23          .48
redo size                       92475240      2743.58     18279.35       7143.7
redo synch time                    67226         1.99        13.29         5.19
redo synch writes                  33725            1         6.67         2.61
redo wastage                 -4281632632    -127028.8   -846339.72   -330755.71
redo write time                    78178         2.32        15.45         6.04
redo writer latching time              1            0            0            0
redo writes                        37718         1.12         7.46         2.91
rollbacks only - consistent          909          .03          .18          .07
rows fetched via callback         104067         3.09        20.57         8.04
session connect time           934070365     27712.29    184635.38     72156.85
session logical reads            4039428       119.84       798.46       312.05
session pga memory             907440632     26922.23    179371.54      70099.7
session pga memory max         907494888     26923.84    179382.27     70103.89
session uga memory              25705468       762.64      5081.14      1985.74
session uga memory max          96751052      2870.44     19124.54      7474.01
sorts (disk)                          37            0          .01            0
sorts (memory)                      2546          .08           .5           .2
sorts (rows)                      147716         4.38         29.2        11.41
switch current to new buffe         1288          .04          .25           .1
table fetch by rowid              643086        19.08       127.12        49.68
table scan blocks gotten            7186          .21         1.42          .56
table scan rows gotten             17936          .53         3.55         1.39
table scans (short tables)          2659          .08          .53          .21
total file opens                     214          .01          .04          .02
user calls                        117182         3.48        23.16         9.05
user commits                       33706            1         6.66          2.6
write clones created in bac            4            0            0            0
write clones created in for         1367          .04          .27          .11

Event Name                       Count         Total Time    Avg Time
-------------------------------- ------------- ------------- -------------
SQL*Net message from client              56724       2938755         51.81
rdbms ipc message                         2528       2589880       1024.48
dispatcher timer                           211       1296560       6144.83
virtual circuit status                     421       1293488       3072.42
log file sync                            33889         67213          1.98
db file sequential read                  36937         64105          1.74
PL/SQL lock timer                           66         32813        497.17
log file switch completion                 164          6209         37.86
direct path write                         2989          3645          1.22
direct path read                           151           108           .72
file identify                                4            68            17
log buffer space                            11            37          3.36
SQL*Net message to client                56722            22             0
control file sequential read                24            11           .46
latch free                                  23             3           .13
file open                                   24             0             0
refresh controlfile command                  4             0             0

Questions:
============

1. How do I proceeded to troube shoot the problem ? why "redo wastage " is -ve ?
any doc describe every event in the report.txt ?
IF I trun on the SQL_TRACE, would it affect the performace of the database and
how do I using the information from SQL_TRACE?

2. As I know, using index would speed up the "select" and slow down the
insert/update/delete (due to update the index). When I trun on the auto trace. I
see that the insert/update/delete can use the index to serach the row and then
update it. So is index really slow down the DML operation ?

3. When I using the autotrace to see the execution plan

xxx colum has a index
select .... where xxx = 123, full table scan is performed.
select .... where xxx = '123', index is used.
the '' is really matter in the execution plan ?

Thank you for your help.

      and we said...
You should use statspack, not b/estat -- they are very old and contain not
enough useful information.

I see a large wait on log file sync, you are experiencing contention there, that
would require file system tuning, increasing the redo buffer size or committing
less frequently (you should only commit when your transaction is complete, never
before)

sql_trace will affect runtime performance.  It is good to tune a single program
with it.  do not turn it on at the instance level, you'll run really slow.  If
you have my book -- read chapter 10.

Indexes are a mixed bag with DML.  They will cause extra work when you update an
indexed column -- BUT -- without the index, the update itself might have to full
scan.  This is where common sense kicks in and says "hey, this index is worth
it". They slow it down, they speed it up -- it is a trade off.

If you have my book -- I answer your #3 question in "why isn't my index getting
used" in the chapter on indexes.   column XXX is a varchar2 column.  You are
comparing a string to a number.  So

select * from t where xxx = 123

is REALLY

select * from t where to_number(xxx) = 123;

you are applying a function to the column XXX implicitly.  That invalidates the
index usage.  When you code:

select * from t where xxx = '123'

you are comparing a string to a string and lo and behold -- no such conversion
takes place.

ALWAYS COMPARE STRINGS TO STRINGS, DATES TO DATES, NUMBERS TO NUMBERS.  do not
rely on implicit conversion -- ever!

Review &
      Followup

      Rating:
      4
  July 16, 2002
Reviewer:  Keanu From
      Hong Kong
Thank you very much !!!

Can I use the statpack with my Oracle version eariler than 8.1.6?

Yeah, I buy your book in last week. It's a great book. I'll look at the chapter
that you mention

          Followup:

yes, goto

www.oracle.com/oramag

and search for statspack

there are articles and downloads for doing this in 8.0 and 815

July 17, 2002 Thoi
      -- Thanks for the question regarding "IP address", version version
      8.0.4.0.0

      You Asked
I have got an Oracle version 8.0.4.0.0
I'd like to know how you get the IP address from users when they
have already logged on the Database?
Thanhs beforehand.

      and we said...
In 8.0 you can enabled auditing (set audit_trail=true in the
init.ora and restart).  then audit connect;

then you can:

  1* select comment_text from user_audit_trail where
sessionid=userenv('sessionid')
scott@ORA806.WORLD> /

COMMENT_TEXT
-----------------------------------------------------------------
-----------------------------------
Authenticated by: DATABASE; Client address:
(ADDRESS=(PROTOCOL=tcp)(HOST=138.2.5.206)(PORT=32850))

scott@ORA806.WORLD>

Review &
      Followup

      Rating:
      2
  July 17, 2002
Reviewer:
      m.p.kirankumar

      Rating:
      1
  July 17, 2002
Reviewer:
      A reader
I tom I have enabled audit_trail=true in init.ora , I am not getting any rows in
user_audit_trail...

          Followup:

then you

a) didn't restart the database (init.ora changes don't take effect till then)

or

b) did not "audit connect"

read the admin guide or the sql ref -- it documents fully how to enable
auditing.

      Rating:
      1
  July 17, 2002
Reviewer:  kiran
       from india
hi tom, what is connect audit...

          Followup:

it is audit connect and it audits connections - see the admin guide for details
on auditing.

      Rating: 5
To those people
      submitting low star ratings...  July 17, 2002

Reviewer:  DBA  from Columbus, OH USA
Tom is right. Auditing is the only way to reliably get client IP addresses from
the database.

If you are a DBA and you don't know what auditing functionality is already, you
probably shouldn't be a DBA.

If you are a developer, talk to your DBA.

Stop giving Tom 1 or 2 star ratings for the correct answer because he didn't
give you the answer you wanted.... the man is an Oracle god and you are lucky to
get any of his time.

Turning on auditing for connect only is not a big performance hit.

      Rating: 5
last one was
      good  September 05, 2003
Reviewer:  Mahesh
       from Sydney

July 17, 2002 sushant -- Thanks for the question regarding
      "using rownums & for clause in 9i", version 8.1.6

      You Asked
we can use rownums in a lot of queries.
say
SELECT *
        FROM   (SELECT * FROM my_table ORDER BY col_name_1 DESC)
        WHERE  ROWNUM < 10;
rownum clause works fine with < , <= , in operators
what i want to know is why cant we use rownum with a >,= or not in operator.

i just saw something uncanny in oracle i was using a for loop in 9i pl/sql . i
was using isqlplus. the moment i entered the a + operator in the body & clicked
the execute button i got an error. when i saw the error the + operator always
vanishes whenever i execute
the  code

  declare

   vsum   number := 0;
begin

    for i in 1..&n

loop

    vsum := vsum + i;
end loop;
dbms_output.put_line(vsum);
end;

      and we said...
rownum is incremented AFTER a row passes the predicate (and before any sorts
or aggregates). Hence, if you say "where rownum > 1" -- that will NEVER be true
since rownum starts at one and is incremented ONLY when the predicate is
satisfied -- and rownum > 1 will never be satisfied as long as rownum is one!!

Here is an example from another question I had once:

....
So, the query:

select l_id from prod.daily_def
 where rownum between 100 and 200;

is conceptually processed like this:

    rownum = 1
    for x in ( select * from prod.daily_def )
    loop
          if ( rownum between 100 and 200 )
          then
                  OUTPUT record
                  rownum := rownum + 1;
          end if;
    end loop

Since your predicate is NEVER satisfied, there is NO row 2 -- ever.

try:

   select * from ( select rownum r, * from t ) where r between 100 and 200;

instead.

.....

(me again)
Inline views are useful here.  Using your example, you can:

select *
from (
SELECT rownum rnum, a.*
        FROM   (SELECT * FROM my_table ORDER BY col_name_1 DESC) a
        WHERE  ROWNUM < 100
)
where rnum > 89;

for example to get rows 90..99 from a result set.

As for isqlplus, never used it -- don't have any facilities to replicate your
issue.  You can contact support for that one and open a TAR.

Review &
      Followup

      Rating: 3
very
      useful..but....  July 17, 2002
Reviewer:  arief
      zj  from Malaysia
i did follow ur advice but to certain query it works, but when it come to a more
complex when ORA-00918 appears :(..
i don't know to tell u bcoz u're quite bz nowadays..

however, i hope u could spend some times for me :)..these are my query:

SQL> select * from (
  2  select aa.*,rownum rnum from (
  3  select distinct
  4  rownum rn,
  5  swi.site_wide_id,
  6  swi.object_id,
  7  decode(sign(sysdate-swi.user_checkoff_date),1,1,0,1,0) as published_p,
  8  to_char(swi.last_modified, 'Mon FMDDFM, YYYY') as last_modified,
  9  u.first_names || ' ' || u.last_name as owner,
 10  u.user_id as owner_id,
 11  swi.one_line_description as name,
 12  swi.overview,
 13  decode(swi.overview_html_p,'t',1,0) as overview_html_p,
 14  swi.last_modified,
 15  (select nvl(round(avg(gc.reuse_points),2),0) from general_comments gc
 16            where
 17            gc.on_which_table = 'sn_objects' and
 18            gc.on_what_id = swi.object_id) average_rating,
 19  (select count(*) from general_comments gc
 20            where
 21            gc.on_which_table = 'sn_objects' and
 22            gc.on_what_id = swi.object_id and not gc.reuse_points is null)
number_ratings,
 23  sn_linked_object_list(swi.site_wide_id) as linked_object_ids
 24  from
 25  site_wide_index swi, users u
 26  where
 27  u.user_id=swi.original_author_id and
 28  swi.expiration_date > sysdate and
 29  swi.object_type_id = 152 and
 30  sysdate - swi.last_modified < 365
 31  order by
 32  swi.last_modified desc
 33  ) aa where rownum <= 100
 34  )
 35  where rnum >= 51;

but it returns error saying that:

select aa.*,rownum rnum from (
          *
ERROR at line 2:
ORA-00918: column ambiguously defined

i hope u could answer it for me and to others as well

          Followup:

select * from big_table where exists ( select
null from big_table bt2 where bt2.id = big_table.id );

your subquery has two columns with the same name -- it is ambigously defined.
Use aliases on the columns and it'll work

      Rating: 5
Thanx a lot
      man!!!  July 17, 2002
Reviewer:  arief zj
       from Malaysia
wow.. how careless i am, the problem solved already,
thanx tom .. you da man :))

July 17, 2002 Doug -- Thanks for the question regarding "Unable
      to bind to Oracle Management Server ", version 8.1.6

      You Asked
For our development environment we installed Oracle Management
Server on the same machine as our Oracle database. I was able to
log in thru OEM and everything was fine. I changed an .init parm
that caused me to bounce the database - then I couldn't login to
the OMS. I got the following messages when trying to get a
status of OMS:
- - - - - - - - - - - - - - - - - - - -
$ oemctrl status oms sysman/sysman
OEMCTRL for Solaris: Version 2.1.0.0.0
Copyright (c) 1998, 1999 Oracle Corporation.  All rights
reserved.
/local/u01/app/oracle/admin/orb/resources.ora is not found!
VXA-2010 : Unable to bind to Oracle Management Server paradise
oracle on paradise in /home/oracle
$ oemctrl stop oms dougj/dougj
OEMCTRL for Solaris: Version 2.1.0.0.0
Copyright (c) 1998, 1999 Oracle Corporation.  All rights
reserved.
Stopping the Oracle Management Server...
/local/u01/app/oracle/admin/orb/resources.ora is not found!
VXA-2010 : Unable to bind to Oracle Management Server paradise
oracle.sysman.emSDK.client.omsClient.IncompleteOMSInitException:
A service in Oracle Management Server has not been completely
initialized.
VXA-2012 : Failed to log in
oracle on paradise in /home/oracle
- - - - - - - - - - - - - - - - - - - - - - - - -

I have to re-boot the Unix box to get things back in sync... any
ideas on how to resolve this?? Thanks in advance.
 Doug J

      and we said...
There is an open bug, #1296538, on this issue with 8.1.6.  The
temporary workaround is as follows (if you shut down oms before
bouncing the instance, this will not happen):

Problem Description
-------------------

OEM 2.1 was running well and the repository database is 8.1.6.
Once shutdown repository database before stop OMS, now get the
error messages everytime running  "oemctrl status, start or stop
oms".

VXA-2010: Unable to bind to Oracle Management Server <hostname>

Solution Description
--------------------

1. ps -ef|grep mx32m to find all the processes with the name as:
   'jre -nojit -mx32m -DORA'

2. kill all these processes

3. startup oms
   'oemctrl start oms &'

Explanation
-----------

1. Since repository database has been restarted up when oms
process is still alive, oms lose connection to repository
database and hangs.

2. hanging oms process still using the port defined by oem
confiuration, we have to kill this process to free this port in
order to restart up oms. You can find the port number in

   $ORACLE_HOME/sysman/config/omsconfig.properties

3. The Oracle Management Server (OMS) is a jre process.
   Its process looks like

   /oem21/oracle/jre/1.1.8/bin/../native_threads/jre -nojit
-mx32m -DORA

Review &
      Followup

      Rating: 4
Very
      nice  July 09, 2002
Reviewer:  Abdal-Rahman
      Agha  from Saudi Arabia
Hi Tom,

   Thats good. But I have another problem with the oms. The oms on my server
win2000 oracle 817 does not start. It says the service did not return an error
but the time given to the service to start is finished. Also it reports there
may be a windows internal error or servie error.

   This problem always arises when I install Oracle 817 on Windows 2000 and
install OMS after that. It never works. Please help me.

Thanks.

          Followup:

Please contact support.  Open a tar -- they can assist with
installation/configuration issues.

      Rating: 4
From
      Agha  July 09, 2002
Reviewer:  Abdal-Rahman
      Agha  from Saudi Arabia
Hi Tom,
  I am sorry to appear again, but I want to give you the exact error I got
(concerning the OMS). It says:

Could not start OracleOMSManagementServer service on local computer.
Error 1053: The service did not respond to the start or control request in a
timely fashion.

and ...

VdbSession.processError: errorMsg >ORA-12640:Authentication adapter
initialization failed<
 [main][2002-7-9:4:15:11:531] VdbSession.processError: database connection error

Please any suggestions.
Thanks.

          Followup:

A simple search on metalink for "ORA-12640 Error 1053" would have very quickly
found this support note on the topic (hint hint hint)

Article-ID:         <Note:153954.1>
Circulation:        PUBLISHED (EXTERNAL)
Folder:             em.ManagementServer.V9i
Topic:              - - -    Known Problems
Title:              Starting OMS, Other Services, EM Tools Fails with Error
                    1053, ORA-12640
Document-Type:      PROBLEM
Impact:             MEDIUM
Skill-Level:        NOVICE
Updated-Date:       01-JUL-2002 11:21:10
Attachments:        NONE
Content-Type:       TEXT/PLAIN
Errors:             DBA-01200;  ODG-05014;  ODG-05151;  ODG-05271;  ODG-05577;
ODG-05638;  ODG-05837;  ORA-03113;  ORA-12640;  VTD-0123;  VXA-02018;  VXA-2028;

Products:           1;  100;  242;
Platforms:          GENERIC;

Fact:
MS Windows 2000
MS Windows NT
Oracle Server - Enterprise Edition 8.1.7
Oracle Server - Enterprise Edition 9.0
Oracle Server - Enterprise Edition 9.2
Oracle Enterprise Manager 2.2
Oracle Enterprise Manager 9.0
Oracle Management Server 2.2
Oracle Management Server 9.2
Performance Manager
Capacity Planner
Oracle Data Gatherer

Symptom:
Starting Oracle Management Server (OMS) fails
Error 1053 : The service did not respond to the start or control request in a
timely fashion.
Errors appear in oms.log file
VXA-02018: Unable to connect to OMS repository.
"Could not start management server. Initialization failure"
ORA-12640 Authentication Adapter Initialization Failed

Symptom:
Could not start the OracleOraHome81Management Server service on Local Computer.
The service did not return an error. This could be an internal Windows
error or an internal service error.  If problem persists,contact your system
administator.
Errors appear in oms.nohup file
VXA-2028: Unable to connect to OMS registry service
ORA-03113: end-of-file on communication channel

Symptom:
Launching Performance Manager / Overview chart fails
VTD-0123: Error connecting to service <service name>
DBA-01200: Error oracle login failed
ORA-12640: Authentication Adapter Initialization Failed
ODG-05271: Error message decode: vp_initiliaze_req: vppcallbb
ODG-05151: Error vppdgth failed in message decoding while not collecting

alert_dg.log contains errors
ODG-05638: Error: failed to find parameter
ODG-05577: Error: loader failed to process connect message
OCI Error -- ErrorCode(12640): ORA-12640: Authentication adapter initialization
failed
DBA-01200: Error: Oracle LOGON failed
ORA-12640: Authentication adapter initialization failed
ODG-05014: Error: call to collection cartridge failed
ODG-05271: Error: message decode: VP_INITIALIZE_REQ: vppcallbb failed
ODG-05151: Error: vppdgth: failed in message decode while not collecting
ODG-05837: Error: cartridge not ready will retry

Cause:
1.  If connecting to an 8.1.7 database or repository, see BUG:1397927 / 1522966
2.  Error connecting to an 8.1.7 database on MS Windows when
SQLNET.AUTHENTICATION_SERVICES is set to (NTS) for O/S authentication.

If connecting to a 9i database or repository, the machine may not be connected
to the network.

Workaround:
Remove the line sqlnet.authentication_services=(NTS)
from the ORACLE_HOME\network\admin\sqlnet.ora file on the client and server
or set sqlnet.authentication_services=(NONE)

Fix:
If NTS is required, for 8.1.7 databases, apply patchset 8.1.7.2.1, available
from Metalink: ID:1019595 Patch:2002298.

For 8.1.7 on Windows 2000, review Note:137529.1 Oracle8i Enterprise Edition
Release Notes Release 3 (8.1.7).

      Rating: 5
Oh, it
      works  July 09, 2002
Reviewer:  Abdal-Rahman
      Agha  from Saudi Arabia
Hi Tom,

   I appreciate your nice (and very quick) help to me. Thank you very very much.
Just I replace NTS with NONE and it works.

      Rating: 2
From Agha
      again  July 17, 2002
Reviewer:  Abdal-Rahman
      Agha  from Saudi Arabia
Hi Tom,

1)  As you informed, I went to Oracle Support side -MetaLink- and I downloaded
the patch you specified to solve the problem of using:

     sqlnet.authentication_services = (NTS)

    in the sqlnet.ora file instead of using the value NONE. But, unfortunately,
when I tried to restart the services on the windows 2000 the database service
gave the following error:
           Oracle.exe-Entry point not found.
           The procedure entry point pciffc could not be located in the dynamic
link library orapls8.dll.

           Could not start the OracleServiceTEST service on local computer.

   What will be the problem?

2) I had seen many patches for the oracle database on windows NT/2000 in the
MetaLink side. For what can I know all these patches designed? I mean is there
any place to know full description about the patches and buggs?

Thanks.

          Followup:

please contact support for issues like this ... this is what they do...

      Rating: 3
From
      Agha  July 20, 2002
Reviewer:  Abdal-Rahman
      Agha  from Saui Arabia
Hi Tom,
  I contacted Oracle Support, But I solved my problem before they reply. I
discovered that I do not need to download that patchset. My problem is solved.
It was concerning the way I assign the "Logon as a batch job" to the user in the
preffered credentials. I discovered that it is not enough to do that from Local
Security Policy from win2k, because my user is a domain user. It must be given
from the Domain Security Policy or from the Domain Controller Security Policy.
And finally, it works and now I am able to do my backup, export, import from the
EM.

July 17, 2002 Pertisth -- Thanks for the question regarding "Re
      : 'Total required in Hierarchical query'", version 8.1.6

      You Asked
Dear Tom,
This refernce to my last question regrading the 'Total required in Hierarchical
query'

I think I could not explained you the my requirement properly.

My test table has following data :
------------------------------------
MAIN           SUB          AMOUNT
------------------------------------
START          ACCOUNT                 //Department
  ACCOUNT        KULDEEP      10       //Employee
  ACCOUNT        VERMA        10       //Employee
  ACCOUNT        PRAGYA       10       //Employee
  ACCOUNT        VIPIN        10       //Employee
START          IT                      //Department
  IT             PERTISTH     10       //Employee
  IT             ANAND        10       //Employee
  IT             MOHAN        10       //Employee
  IT             DINESH       10       //Employee
START          MARKETING               //Department
  MARKETING      SALES                 //Department
    SALES          ANUPAM     10       //Employee
    SALES          KAPIL      10       //Employee
  MARKETING      CSC                   //Department
     CSC           MANOJ      10       //Employee
     CSC           SMITH      10       //Employee
  MARKETING      SERVICING             //Department
     SERVICING     DERIK      10       //Employee
                   THOMAS     10       //Employee
  MARKETING      YOGESH       10       //Employee

Note : The above hierarchical can expend upto any levels for example there may
be any number of department under production and may be more than one department
under each department of production.

Can I get output like this
--------------------------------------------
PARTICULAR            AMOUNT
--------------------------------------------
ACCOUNT                40
  KULDEEP              10
  VERMA                10
  PRAGYA               10
  VIPIN                10

IT                     40
  PERTISTH             10
  ANAND                10
  MOHAN                10
  DINESH               10

MARKETING              70
  SALES                20
    ANUPAM                10
    KAPIL                 10
  CSC                  20
    MANOJ                 10
    SMITH                 10
  SERVICING            20
    DERIK                 10
    THOMAS                10
  YOGESH               10
---------------------------------------

      and we said...
I assume that "THOMAS 10" above is really "SERVICING THOMAS 10" -- thats the
only way I can see the hierarchy getting constructed (else there is no linkages
to THOMAS).

If so:

tkyte@TKYTE816> select main,
  2         rpad(' ',(level-1)*2, ' ' ) || nvl(sub,main) sub,
  3         ( select sum(nvl(amount,0))
  4                 from test t2
  5                    start with t2.sub = test.sub
  6                  connect by prior t2.sub=t2.main) amt1
  7    from test
  8   start with main = 'START'
  9  connect by prior sub = main
 10  /

MAIN                 SUB                   AMT1
-------------------- --------------- ----------
START                ACCOUNT                 40
ACCOUNT                KULDEEP               10
ACCOUNT                VERMA                 10
ACCOUNT                PRAGYA                10
ACCOUNT                VIPIN                 10
START                IT                      40
IT                     PERTISTH              10
IT                     ANAND                 10
IT                     MOHAN                 10
IT                     DINESH                10
START                MARKETING               70
MARKETING              SALES                 20
SALES                    ANUPAM              10
SALES                    KAPIL               10
MARKETING              CSC                   20
CSC                      MANOJ               10
CSC                      SMITH               10
MARKETING              SERVICING             20
SERVICING                DERIK               10
SERVICING                THOMAS              10
MARKETING              YOGESH                10

21 rows selected.

does it
Review
      & Followup

      Rating: 2
"Re : 'Total
      required in Hierarchical query'"  July 16, 2002

Reviewer:  Alan Kirchoff  from Overland Park, KS USA

How does this work?  Don't you get the error ORA-01437?

Thanks,
Alan

          Followup:

there are no joins here?

No, i did not get a 1437, what you see is exactly what I got -- cut and pasted
from sqlplus in version 816

      Rating: 2
"Re : 'Total
      required in Hierarchical query'"  July 17, 2002

Reviewer:  Alan Kirchoff  from Overland Park, KS USA

I'm sorry I beg to differ....

There is a join on line 6 in the "start with" clause.

Take your query and edit it by placing my view in the query.  I am now thinking
my problem lies in my view.  I am working with three tables...  A hiearchy
table, hierarchy_nodes, a subscriber table, subscribers, and an amounts table,
subscribers_totals.

I thought by creating a view with the three tables I could get my hierarchical
relationship and amount in the same "table" and then do my "start with" and
"connect by" on the view.

I believe I understand my problem now and how to fix it.  If you really want to
take a look at it let me know and I'll provide more detail.

Thanks for your help.

Alan

          Followup:

beg to differ myself.

It is not a join, it is called a correlated subquery.

A join requires two tables (at least) in the from clause

      Rating: 4
This solution
      could be slow though, exponentially with the data
      volume...  March 12, 2003
Reviewer:
      A reader

Is it possible to implement this using analytical functions?

July 17, 2002 lin -- Thanks for the question regarding "how to
      replicate a BLOB field to another DB table.", version 8.1.6

      You Asked
I have 2 DB(Server0,Server1).
Both Server0 and Server1 have a same table article_img(id,lobs)
id is the PK,and lobs is BLOB field.

I want to replicate the article_img@server0 to article_img@server1
but use DBMS_LOB package(PL/SQL package),
it return ORA-22992 error tell me oracle can not get the Locator of BLOB object
from remote DB.
my test pl/sql block is below :

declare
v_rlob    BLOB;
v_dlob    BLOB;
begin
    SELECT lobs INTO v_rlob FROM article_img@server0
        WHERE article_id=2442 and name='t0901.gif' ;

        INSERT INTO article_img@server1
    VALUES('t0901.gif',1,empty_blob());

    SELECT lobs INTO v_dlob FROM article_img@server1
    WHERE article_id=1 and name='t0901.gif' FOR UPDATE;

    dbms_output.put_line('Now process BLOBs!');
        DBMS_LOB.COPY(v_dlob,v_rlob,DBMS_LOB.getlength(v_rlob),1,1);

end;
/

if it's inner error of oracle.
How can I do with the replication?

      and we said...
You can do this to copy a lob from server0 to server1 using a third database
(ora817dev is my third database, ora815 is server0, ora8i.world is server1)

scott@ORA817DEV.US.ORACLE.COM> create global temporary table t ( x int, y clob )
on commit delete rows;

Table created.

scott@ORA817DEV.US.ORACLE.COM> insert into t select * from clob_table@ora815;
1 row created.

scott@ORA817DEV.US.ORACLE.COM> insert into clob_table@ora8i.world select * from
t;
1 row created.

scott@ORA817DEV.US.ORACLE.COM> commit;
Commit complete.

The blob is now in ora8i.world.

Review &
      Followup

      Rating: 4
how to
      replicate a BLOB field to another DB table  July 17, 2002

Reviewer:  Geert van de Leur  from Nieuwegein, The
      Netherlands
It takes some time, but it works fine. I was trying to import a schema's objects
into a database running on an Alpha/VMS system and repeatedly got kicked out
because of insufficient process quota. As only one table (1.9 million rows, one
blob column and one clob column) caused the problem, I created the (empty) table
and copied the data from the source table. Using this method I was able to do
this in chunks of 100,000 rows. Yoy have to have time and patience, though ;)

          Followup:

you could have set the blob/clob to nologging, the table to nologging and did a

insert /*+ APPEND */ into t select * from another_t

(making sure there are no indexes on T) and bypass redo/undo.  then put them
back to logging and backup the tablespaces you loaded into.  That'll speed it up
(use PARALLEL even).  If you have my book -- I do have a full example of that
(actually, it is a conversion from long raw to blob but the concepts are
identical -- speed up a mass copy of really big stuff)

July 17, 2002 Omar -- Thanks for the question regarding
      "Populating A column value for all colmns with NULL values", version 8.1.7

      You Asked
Hi Tom,
I have a question considering PL/SQL,
What I have is

SQL> select policy_ref,event_no,posting_no,amount,product,branch
  2  from orafin_data
  3  where  policy_ref = '03500003749'
  4  and orafin_data.batch_id = orafin_data.batch_id
  5  and (orafin_data.event_no = 1 or orafin_data.event_no = 2)
  6  ;

POLICY_REF                      EVENT_NO POSTING_NO    AMOUNT PRODUCT    BRANCH
------------------------------ --------- ---------- --------- ----------
----------
03500003749                            1          1      16.5
03500003749                            1          2    -14.65 BANCASS    51
03500003749                            1          3   -1.3723
03500003749                            1          4      -.17
03500003749                            1          5    -.0879
03500003749                            1          6    -.2198
03500003749                            2          1      16.5
03500003749                            2          2    -14.65 HOUSEHOLD    50
03500003749                            2          3   -1.3723
03500003749                            2          4      -.17
03500003749                            2          5    -.0879
03500003749                            2          6    -.2198

12 rows selected.

SQL>

U will see that when the POSTING_NO = 2 the PRODUCT AND branch columns have a
valuse,
What I want to do is to put these values with all columns where the EVENT_NO is
the same,
I mean to have the following result

POLICY_REF                      EVENT_NO POSTING_NO    AMOUNT PRODUCT    BRANCH
------------------------------ --------- ---------- --------- ----------
----------
03500003749                            1          1      16.5 BANCASS    51
03500003749                            1          2    -14.65 BANCASS    51
03500003749                            1          3   -1.3723 BANCASS    51
03500003749                            1          4      -.17 BANCASS    51
03500003749                            1          5    -.0879 BANCASS    51
03500003749                            1          6    -.2198 BANCASS    51
03500003749                            2          1      16.5 HOUSEHOLD  50
03500003749                            2          2    -14.65 HOUSEHOLD  50
03500003749                            2          3   -1.3723 HOUSEHOLD  50
03500003749                            2          4      -.17 HOUSEHOLD  50
03500003749                            2          5    -.0879 HOUSEHOLD  50
03500003749                            2          6    -.2198 HOUSEHOLD  50

How could I get these data through using a PL/SQL statment?,I am using ORACLE
version 8.1.7,
I hope not to suggest me to use a function because I am going to build a view
based
on this table (ORAFIN_DATA) and the table is very corwded with records and using
a function
during building the view will make it very slow,
I hope u help me Tom,
Best Wishes and Regards,
Omar.

      and we said...
you can't get there from here without a function -- select a select.

you might be totally surprised at the speed.

select a.*,
       (select product from t t2 where t2.policy_ref = t.policy_ref
                 and t2.event_no = t.event_no and t2.posting_no = 2 ) product2
  from t a
/

Review &
      Followup

      Rating: 4
Good
      Answer  July 19, 2002
Reviewer:  Omar

I tried to work the way u told me and it is working, I did not want to use
Functions because I was afraid of the performance but the way u suggested was
good and the performance was OK,
Thanks Tom.

July 18, 2002 Louise -- Thanks for the question regarding "How
      to display Weleom Oracle Portal page instead of user defined page whe
      logout", version 1.0.2.2.a

      You Asked
Hi Tom,

I have developed my own portal page and set as default page.  But evey time I
click on logout link, the 'Welcome to Oracle Portal' page comes out.  When I
starts portal, I start with URL:
http://host:7778/pls/portal30/url/page/myPage
I have set global and other default page to
myPage.  I don't know how to replace this WELCOME TO ORACLE PORTAL to my own
page.

LT

      and we said...
I asked Kim Better, our resident portal guru to tackle this one and she says:

The answer to your question........

The logout page is the default page for the PUBLIC user - not your particular
user. From the Administrator's Home Page, navigate to the Administer Tab, and
then Edit User portlet. Edit the PUBLIC user. On the Preferences tab, change the
Default Home Page to another page that you choose for your logout page.

Hope this helps!

--kim
Review
      & Followup

      Rating: 4
How to replace
      Oracle default WELCOME TO ORACLE PORTAL with user defined
      page  July 18, 2002
Reviewer:  Louise
       from USA
This is a simple way to start your own portal site.

July 18, 2002 Prasanna -- Thanks for the question regarding
      "Features of 9i", version 8i

      You Asked
Hi Tom,
My application currently runs on 8i and we are planning to upgrade it to ORACLE
9i.
I would like to know few things before we go ahead,

1) What are the steps to be considered before migration from 8i to 9i. Most
documentations talks about Oracle 7 To 8i Migration in detail, but not about the
8i to 9i migration.

2) What enhancements are there in PL/SQL in 9i over 8i.
i.e any features not available / available but not allowed in PL/SQL in 8i and
allowed in 9i. Can you please provide me with the list of those features.

this can help me to decide whether we genuinely require 9i.

Regards
YERRA

      and we said...
1) for a migration -- it is purely evolutionary, no big changes like from 7 to 8
for an existing app (there was that whole rowid thing, absolute to relative file
numbering and so on).

An 8i app should (barring bugs or changes in the the expected behavior) run "as
is".  You can then go back and add 9i features to it as you see fit.

2) see

http://otn.oracle.com/docs/products/oracle9i/doc_library/901_doc/appdev.901/a89856/whatsnew.htm#967650
for "whats new in ps/sql"

perhaps the biggest "whats new" (i haven't played with it but have high hopes)
is Native compilation of PLSQL:

Improve performance by compiling Oracle-supplied and user-written stored
procedures into native executables, using typical C development tools. This
setting is saved so that the procedure is compiled the same way if it is later
invalidated.

Review &
      Followup

      Rating:
      4
  July 15, 2001
Reviewer:
      A reader

      Rating: 5
Migration from
      8i to 9i  June 06, 2002
Reviewer:  Munz
       from Reston, USA
Tom:

We have a html web application running as stored procedures in 8.1.7 database.

THe DBA wants to upgrade from 8.1.7 to 9.0.2 on IBM/AIX server.

Is there anything that we need to do or be aware of for the web applications.
Do you have to test the application again using the 9i database?

Thank you,

          Followup:

Well, anytime you change ANYTHING -- you need to test.  Period.

I would say an upgrade from 817 to 902 would deserve a "test" -- definitely,
100%, yes, certainly.

      Rating:
      5
Upgrade  June 06, 2002
Reviewer:
      A reader
And even an upgrade to 9.2.0 as I don't believe there is such a beast as 9.0.2
:)

          Followup:

sure there is.  There was a 9.0.2 patchset.

      Rating: 5
Do you have to
      test the application again using the 9i database?  June 06,
      2002
Reviewer:  Mark A. Williams  from Indianapolis,
      IN USA
Munz:

"Do you have to test the application again using the 9i database?"

What else would you do?  Go with "Well, gee, it compiled under 9i, so it *must*
be ok... no reason to do a proper job here"?

This isn't meant as a "slam", but of the many questions you've posed here,
surely you've learned enough to be able to answer that question after a little
thought...

- Mark

      Rating: 5
9.0.2
      Patchset  June 07, 2002
Reviewer:  Mark A.
      Williams  from Indianapolis, IN USA
Tom:

Which platform for the 9.0.2 patchset?  I'm looking in MetaLink and only see:

9.2.0.1
9.0.1.3
9.0.1.2
9.0.1.1
9.0.1.0

Thanks!

Mark

          Followup:

Contact support -- there is a 9.0.2 out there (ias and others are built on that
set of RSF's)

it's the stuff that comes with the current ias release.  There may not have been
a 9.0.2 standalone db release now that I peek around.

      Rating: 4
What's new in
      9i R2 ?  June 07, 2002
Reviewer:  Andre
       from Brazil
What can you say are the most important new features in 9i R2 compared to R1 ?
Right now, I'm studying R1, but the OU books often say something like "... in a
later release of 9i ..."

          Followup:

So far -- personal favorites are

o logical standby, as opposed to physical standby.  the logical standby has all
of the features of the current (9i and before) physical standby but it can also:

  - be on a different OS/platform (hetergenous)
  - be open for reading and even writing whilst being recovered to
  - supports the addition of additional structures against the data being
"guarded"
    -- such as additional indexes and materialized views (making it excellent
for
    offloading dss type work)

to name a few

o rename a column (yeah)

o DBA can grant on another schema's objects (yeah)

      Rating: 4
No more GATHER
      STALE needed ?  June 10, 2002
Reviewer:
      Andre  from Brazil
Is it true 9i R2 has a new feature with which I don't have to "download"
statistics generated by MONITORING using GATHER STALE any more ?

          Followup:

Sort of -- but not quite.  You still need to analyze...

see

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96533/sql_1016.htm#26070

      Rating:
      4
Continued  June 11, 2002
Reviewer:
      Andre  from Brazil
Well, it seems to me DYNAMIC SAMPLING is still just a worthy turnaround in some
cases. But I imagine you still recommend using DBMS_STATS or ANALYZE with jobs
or crontab. Right ?

          Followup:

Yes, where dynamic sampling might come in handy is....

o tables that frequently and continous change in size
o maybe useful with global temporary tables, but I haven't really played with
that as yet.

      Rating: 5
New
      features  June 12, 2002
Reviewer:  Andre
       from Brazil
Short answers would be fine...

1 - Is Data Guard sold separately from Oracle9i ?
2 - Can you give me or direct me to an overview of: EXPROC, create library, and
their use with create procedure/function ? What are "agents" in this context ?
(I know what a .so lib in UNIX is)
3 - I understand how an HS agent works internally. It seems to be used for
connecting the Oracle server to other systems. Can you give me an example of
such systems?

          Followup:

1) it is part of 9i EE (enterprise)

2) my book -- i spend 70 pages on extprocs in C....  Agents are new with 9i,
they let you run an extproc process for each library if you want.  this is so
that

  a) code you write can run as "user1", code we write can run as "user2"
(different
     OS users with different OS privs
  b) if your code crashes, it won't crash our extproc (and vice versa ;)

3) sqlserver, db2, any odbc compliant data source using the odbc gateway, etc
etc
   etc.

      Rating: 3
Oracle 9i new
      features details  July 08, 2002
Reviewer:  N

Hi tom.
Please point where to find O9i 9.0.1.1.1 features and its details
Thanks

          Followup:

General question to all you out there --

is the documentation really that hard to discover???
is otn.oracle.com an "unknown" resource???

(this is not a slam to "N" here, I just really am wondering if something is
totally broken).

goto
http://otn.oracle.com/
click on documentation.  Then, since you are interested in the database, pick
the release you want (901).  Then, to get an inclusive list of books click on
"list of books".  Then click on New

      Rating: 4
Can you explain
      "native" execution?  July 17, 2002
Reviewer:
      Doug  from Woodbury, CT USA
Tom - you said way up at the top that your favorite feature was native PL/SQL
execution.  I looked a the doc.  I'm confused.  What's so un-native about PL/SQL
in the first place? (I've always been confused by that term).

          Followup:

Native COMPILATION -- not execution.

Native compilation (ncomp for short) is the ability to have plsql compiled into
object (binary) code on the target operating system INSTEAD of it being turned
into OS independent pcode (psuedo code) that is intepreted at runtime.

It makes it run faster.

      Rating:
      1
  July 18, 2002
Reviewer:
      A reader

July 18, 2002 ted -- Thanks for the question regarding
      "generate sql for sqlldr", version 8i

      You Asked
tom, I am trying to use your sqlldr_exp with some modification

how do I extract corrected hh24.mi.ss  using your sqlldr_exp ?
    -i.e. how do I use decode to test date data_type and
generated corrected select statement.
thnx as always
ted chyn
========================================

prompt select
select    lower(column_name)||'||chr(124)||'
from    user_tab_columns
where    table_name = upper('$X') and
    column_id != (select max(column_id) from user_tab_columns
where
             table_name = upper('$X'))

      and we said...
Here is a revised SQLLDR_EXP script that does this:

$ cat sqlldr_exp
#!/bin/sh

if [ "$1" = "" ]
then
    cat << EOF
usage:        flat un/pw [tables|views]

example:    flat scott/tiger emp dept

description:    Select over standard out all rows of table or
view with
        columns delimited by tabs.
EOF
    exit
fi

PW=$1
shift

for X in $*
do
sqlplus -s $PW << EOF > /tmp/flat$$.sql
set    wrap off
set    feedback off
set    pagesize 0
set    verify off

prompt prompt LOAD DATA
prompt prompt INFILE *
prompt prompt INTO TABLE $X
prompt prompt REPLACE
prompt prompt FIELDS TERMINATED BY '|'
prompt prompt (
select    'prompt ' || decode(column_id,1,'',',') ||
                   lower(column_name ) ||
                   decode(data_type, 'DATE',
                   ' date ''DD-MON-YYYY HH24:MI:SS''', null )
from    user_tab_columns
where    table_name = upper('$X')
/
prompt prompt )
prompt prompt BEGINDATA

prompt    select
select    lower(column_name)||'||chr(124)||'
from    user_tab_columns
where    table_name = upper('$X') and
    column_id != (select max(column_id)
                from user_tab_columns where
             table_name = upper('$X'))
/
select    lower(column_name)
from    user_tab_columns
where    table_name = upper('$X') and
    column_id =
        (select max(column_id) from user_tab_columns where
             table_name = upper('$X'))
/
prompt    from    $X
prompt    /
prompt    exit
exit
EOF
sqlplus -s $PW << EOF
set    wrap off
set    feedback off
set    pagesize 0
alter session set nls_date_format='DD-MON-YYYY HH24:MI:SS';
set    verify off
set trimspool on
set linesize 5000
start /tmp/flat$$.sql
exit
EOF
rm /tmp/flat$$.sql
done

When you run this now, it'll generate:

$ sqlldr_exp scott/tiger emp
LOAD DATA
INFILE *
INTO TABLE emp
REPLACE
FIELDS TERMINATED BY '|'
(
empno
,ename
,job
,mgr
,hiredate date 'DD-MON-YYYY HH24:MI:SS'
,sal
,comm
,deptno
)
BEGINDATA
7370|SMITH|CLERK|7902|17-DEC-1980 00:00:00|800||20
7499|ALLEN|SALESMAN|7698|20-FEB-1981 00:00:00|1600|300|30
7521|WARD|SALESMAN|7698|22-FEB-1981 00:00:00|1250|500|30
7566|JONES|MANAGER|7839|02-APR-1981 00:00:00|2975||20
7654|MARTIN|SALESMAN|7698|28-SEP-1981 00:00:00|1250|1400|30
7698|BLAKE|MANAGER|7839|01-MAY-1981 00:00:00|2850||30
7782|CLARK|MANAGER|7839|09-JUN-1981 00:00:00|2450||10
7788|SCOTT|ANALYST|7566|09-DEC-1982 00:00:00|3000||20
7839|KING|PRESIDENT||17-NOV-1981 00:00:00|5000||10
7844|TURNER|SALESMAN|7698|08-SEP-1981 00:00:00|1500|0|30
7876|ADAMS|CLERK|7788|12-JAN-1983 00:00:00|1100||20
7900|JAMES|CLERK|7698|03-DEC-1981 00:00:00|950||30
7902|FORD|ANALYST|7566|03-DEC-1981 00:00:00|3000||20
7934|MILLER|CLERK|7782|23-JAN-1982 00:00:00|1300||10

Review &
      Followup

      Rating: 5
FLAT script
        April 15, 2002
Reviewer:  A reader

Tom, I just bought your new book BEGINGING ORACLE Programming

Great book, simple and lucid.

Iam trying to use the flat.sql script. It outputs the contents of a table to the
screen. But I want it to spool it to a file.

Can you suggest the changes.

          Followup:

redirection.  It works on all platforms.

FLAT was written to be run from the command line using redirection:

flat scott/tiger EMP > emp.dat

      Rating:
      5
  April 15, 2002
Reviewer:
      A reader
Tom, I fixed the above request I had put in the above comment.

Instead what Iwant to know is

in the below two queries what do the below conditions imply, why are they there

1.column_id != (select max(column_id)
                       from user_tab_columns
                      where table_name = upper('&1'))
2.column_id = (select max(column_id)
                      from user_tab_columns
                     where table_name = upper('&1'))

select lower (column_name)||'||chr(44)||'
  from user_tab_columns
 where table_name = upper('&1')
   and column_id != (select max(column_id)
                       from user_tab_columns
                      where table_name = upper('&1'))
/
select lower(column_name)
  from user_tab_columns
 where table_name = upper('&1')
   and column_id = (select max(column_id)
                      from user_tab_columns
                     where table_name = upper('&1'))
/
thank you

          Followup:

Just getting the commas right.  Wanted all but the last column -- then wanted
the last column.

Yes, there are 50 other ways to implement this query...

      Rating: 5
Very good
      script, but...  April 15, 2002
Reviewer:
      Tom  from USA
Tom,

I've tried to run the sqlldr_exp script, but I kept on getting the following
error:

$sqlldr_exp username/password@DB EMPLOYEE
LOAD DATA
INFILE *
INTO TABLE eai_lookup
REPLACE
FIELDS TERMINATED BY '|'
(
)
BEGINDATA
from    EMPLOYEE
*
ERROR at line 2:
ORA-00936: missing expression

Does anyone have the same problem?  Please help.

Thanks,
Tom

          Followup:

sqlldr_exp just writes sql to /tmp/

modify the script to NOT erase the generated script, check out the SQL and see
whats wrong with it.

      Rating: 5
Thank you, but
      I think it would have pay attention column order.  April 24,
      2002
Reviewer:  OMATA Ken-ichi  from Japan

Very very useful. thank you Tom.
But I have looked following case.
    ...
    FIELDS TERMINATED BY '|'
    (
    ,ename
    empno
    ,job
    ...
So, I think It would be append "order by column_id" phrase.
...
select    'prompt ' || decode(column_id,1,'',',') ||
                   lower(column_name ) ||
                   decode(data_type, 'DATE',
                   ' date ''DD-MON-YYYY HH24:MI:SS''', null )
from    user_tab_columns
where    table_name = upper('$X')
order by column_id              << here
/
...
select    lower(column_name)||'||chr(124)||'
from    user_tab_columns
where    table_name = upper('$X') and
    column_id != (select max(column_id)
                from user_tab_columns where
             table_name = upper('$X'))
             order by column_id  << here
/
...

      Rating: 3
Tom, it is
      useful  July 17, 2002
Reviewer:  Ramesh
       from CA, USA
Hey Tom,

I would like to know how to select few columns from the flat file.

There are 9 fields in the following dat file.

123|TEST|1200|12/12/2001|30000|4000|Testing|Valid|Request

Just I want to insert only 4 fields ( f1, f3, f6, f9)

Can you please reply.

Thanks in advance for your kind reply.

RM

          Followup:

use the FILLER keyword.

see

http://asktom.oracle.com/~tkyte/SkipCols/index.html

July 18, 2002 Parag -- Thanks for the question regarding
      "CONNECT BY CLAUSE", version 8.1.6

      You Asked
Hi Tom :

   I am trying to use Connect by clause with Joining of 2 tables say Emp & Dept.
It's giving me some Error.

    It means if u wanna to show All Employee & their Manager (Emp Table) with
Department Name (Dept Table) .. i can't show . Because it's giving some Oracle
Error.

   Exampel.

      Assume that i have standared EMP & DEPT table. Where Deptno is a Primary
Key of Dept Table & Forign Key of Emp Table.

      select empno , ename , mgr , d.dname  from emp , dept d
      where emp.deptno = d.deptno
      connect by mgr = prior empno
      start with mgr is null;

     ERROR at line 1:
    ORA-01437: cannot have join with CONNECT BY

    I am waiting for ur reply.

Thanks
- Parag

      and we said...
instead of trying to join a connect by - to TO the connect by:

scott@ORA8I.WORLD> l
  1  select lvl, empno, rpad(' ',lvl-1,' ') || ename ename, mgr, d.dname
  2    from ( select emp.* , level lvl
  3                from emp
  4               start with mgr is null
  5             connect by mgr = prior empno ) emp,
  6         dept d
  7* where emp.deptno = d.deptno
scott@ORA8I.WORLD> /

       LVL      EMPNO ENAME                  MGR DNAME
---------- ---------- --------------- ---------- --------------
         1       7839 KING                       ACCOUNTING
         2       7782  CLARK                7839 ACCOUNTING
         3       7934   MILLER              7782 ACCOUNTING
         2       7566  JONES                7839 RESEARCH
         3       7788   SCOTT               7566 RESEARCH
         4       7876    ADAMS              7788 RESEARCH
         3       7902   FORD                7566 RESEARCH
         4       7369    SMITH              7902 RESEARCH
         2       7698  BLAKE                7839 SALES
         3       7499   ALLEN               7698 SALES
         3       7521   WARD                7698 SALES
         3       7654   MARTIN              7698 SALES
         3       7844   TURNER              7698 SALES
         3       7900   JAMES               7698 SALES

14 rows selected.

Or, select a select and avoid joining all together:

  1  select empno, ename, mgr, (select dname from dept where dept.deptno =
emp.deptno )
  2    from emp
  3   start with mgr is null
  4* connect by mgr = prior empno
scott@ORA8I.WORLD> /

     EMPNO ENAME                  MGR (SELECTDNAMEFR
---------- --------------- ---------- --------------
      7839 KING                       ACCOUNTING
      7566 JONES                 7839 RESEARCH
      7788 SCOTT                 7566 RESEARCH
      7876 ADAMS                 7788 RESEARCH
      7902 FORD                  7566 RESEARCH
      7369 SMITH                 7902 RESEARCH
      7698 BLAKE                 7839 SALES
      7499 ALLEN                 7698 SALES
      7521 WARD                  7698 SALES
      7654 MARTIN                7698 SALES
      7844 TURNER                7698 SALES
      7900 JAMES                 7698 SALES
      7782 CLARK                 7839 ACCOUNTING
      7934 MILLER                7782 ACCOUNTING

14 rows selected.

Review &
      Followup

      Rating: 4
Query without
      join takes too long  July 18, 2002
Reviewer:
      Charanjiv  from India
Hi
the inner query (with the Connect by ) is a huge query returns about 27k rows.
With the join it comes to about 100. In this case the difference in time taken
by hte query is huge. Can u suggest any other way or preformace tips.

My SQL
SELECT at.componentid,
       at.instance,
       c.productclassid,
       c.sku,
       cd.bigheadings
  FROM (select componentid , componentofid , instance
         from tree_view
        where COMPONENTTYPE = 'slot'
        start with componentofid  = 'rkchn14'
      connect by prior componentid = componentofid ) at,
       Component c,
       ComponentDisplay cd
 where at.componentID = c.componentid
   AND c.productclassid = cd.productclassid
   and cd.languageid=1

I have an index on componentofid , componentid of hte base table and componentid
of component

Another thing the original table had a row where  componentofid = componentid
for each top level component thus i had to create view with a where clause of
componentofid <> componentid.

Oralce version 8.1.7

          Followup:

not knowing what "tree_view" is (have to assume a view right), cannot really
comment.

      Rating: 3
tree
      struc  July 18, 2002
Reviewer:  Charanjiv
       from India
oops missed that - it is a view from the tree table of the same structure.

COMPONENTID      NOT NULL VARCHAR2(10)
COMPONENTOFID    NOT NULL VARCHAR2(10)
INSTANCE         NOT NULL NUMBER(1)
COMPONENTTYPE    VARCHAR2(10)
ADDONFLAG      CHAR(1)
QUANTITY       NUMBER(10,2)

          Followup:

don't see anything obvious, short of a two step (insert join into global
temporary table, select hierarchy from global temporary table)

July 18, 2002 --
      Thanks for the question regarding "Invalid dimension", version 8.1.7.3

      You Asked
Tom,

I have created a time table defined as:

CREATE TABLE time (

curDate     DATE,
dayofweek   VARCHAR2(30),
week_num    INTEGER,
month       INTEGER,
YEAR NUMBER  );

The dimension is:

CREATE DIMENSION Time_dim

LEVEL curDate    IS time.curDate
LEVEL month      IS time.month
LEVEL year       IS time.year
LEVEL week_num   IS time.week_num

HIERARCHY calendar_rollup  (

curDate       CHILD OF
month         CHILD OF
year                    )

HIERARCHY weekly_rollup    (

     curDate          CHILD OF
     week_num           )

ATTRIBUTE curDate     DETERMINES  time.dayofweek
;

The materialized view is

 create materialized view sale_vw
       build immediate
        refresh on demand
      enable query rewrite
        as
        select s.customer_id, sum(s.sale_amount), time.week_num,time.year
        from sale s, time
        where s.curdate = time.curdate
    group by s.customer_id, time.week_num,time.year

I have query rewrite enabled to true and  and query rewrite integrity is
enforced.

How ever the query

select  time.month, time.year, s.customer_id,
        sum(sale_amount)
        from sale s, time
        where s.curdate = time.curdate
        and time.year=2002
   --     and time.week_num=15
        and rownum<5000
group by time.month, time.year, s.customer_id does not use sale_vw

how ever

select  time.year, s.customer_id,
        sum(sale_amount)
        from sale s, time
        where s.curdate = time.curdate
        and time.year=2002
   --     and time.week_num=15
        and rownum<5000
group by  time.year, s.customer_id does  use sale_vw

The aggreation to year and week_num is identified by sale_Vw however
with month i do not get used the sale_vw

I tried to change the dimesion

CREATE DIMENSION Time_dim

LEVEL curDate    IS time.curDate
LEVEL month      IS time.month
LEVEL year       IS time.year
LEVEL week_num   IS time.week_num

HIERARCHY calendar_rollup  (

curDate       CHILD OF
week_num child of
month         CHILD OF
year                    )

;

still does not use the sale_vw

Where am i going wrong?

The dbms_olap gives a recommendation to also use month aggregation in MV. should
i do that?

Regards

      and we said...
why do you think that week_num can be rolled up to MONTH?

You are asking for sales by MONTH and YEAR.

Your MV has sales by WEEK and YEAR.

A given week may span more then one month.  Hence, we cannot use the MV that is
at the WEEK/YEAR level to give you an answer by MONTH/YEAR.

Consider this month:

   July 2002
 S  M Tu  W Th  F  S
    1  2  3  4  5  6
 7  8  9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

Part of the first and last weeks are in the prev/next months.  Once you
aggregated to the WEEK/YEAR level, you lost the ability to see sales by
MONTH....

If you want to use the MV to answer that first query, you should listen to
DBMS_OLAP.  It might increase the size of the MV (since the first and last weeks
of JULY will each have two rows in the MV now -- one each for each month they
are in)

Review &
      Followup

      Rating:
      4
  July 01, 2003
Reviewer:
      A reader

July 18, 2002 Kalpesh -- Thanks for the question regarding
      "Does Spool, set pages 0 command works with perl Scripts.", version
      8.1.7.0.0

      You Asked
Tom,

I wrote a sql script that brings page header, page number ..,etc. But I
implemeted with perl scipts it does not work. Do you have any suggestion that I
could use.

1. SQL scripts.

ET FEEDBACK OFF;
SET ECHO OFF;
CLEAR COLUMNS
CLEAR BREAKS
CLEAR COMPUTES
COLUMN REPORT_REQUEST_COMPANY HEADING DEPARTMENT
COLUMN LAST_NAME HEADING 'LAST NAME'
BREAK ON DEPARTMENT_ID SKIP 1 ON REPORT
COMPUTE SUM OF SALARY ON DEPARTMENT_ID
COMPUTE SUM OF SALARY ON REPORT
SET PAGESIZE 65
SET NEWPAGE 0
SET LINESIZE 80
TTITLE CENTER 'Qwest Wireless Report as of sysdate' SKIP 2 -
LEFT 'The Following lists the email recipients of this Distribution List:' RIGHT
'PAGE:' -
FORMAT 999 SQL.PNO SKIP 2
BTITLE CENTER 'Lucent Technologies - Proprietary Use Pursuant to Company
Instructions'
select FULL_NAME, WORK_COMPANY_NAME as Company from REPORT_DISTRIBUTION_EMAIL
--where REPORT_REQUEST_COMPANY like 'V%'
where FULL_NAME is not null
/
spool off

2. Perl Scipts
#!/opt/exp/bin/perl
use strict;
use IO;
use warnings;
use DBI;
print "REPORT 0\n";
#my @data_sources = DBI->data_sources($driver_name);

my $arrayref = undef;
my $rows = undef;
my $arr  = undef;
my $PQINFO  = undef;
my ($reqname, $reqphone, $reqemail) = undef;
my ($hrid, $id, $pn, $last, $first, $middle, $mailto, $tel, $sname, $sname40,
$sname50, $dbuname, $tconame) = undef;
my ($DEPARTMENT_MANAGER_NAME,$EXECUTIVE_MANAGER_NAME, $BUSINESS_UNIT_NAME,
$COMPANY_NAME) = undef;
STDOUT->autoflush(1);

use lib qw(/home/jcdixon/myperl/lib /home/jcdixon/myperl/lib/sun4-solaris);

$ENV{ORACLE_HOME} = "/opt/oracle1/app/oracle/product/8.1.6";

my $dbuser =
qq{mad/mad@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=tooldb.ih.lucent.com)(PORT=1
521))(CONNECT_DATA=(SERVICE_NAME=p
roj1.ih.lucent.com))) };
my $db = DBI->connect("dbi:Oracle:", $dbuser);
if (not $db)
{
        print "DB connect FAILED!\n";
        exit 1;
}
$db->{RaiseError} = 1;
my $qqq = qq{"
spool test
select min(sysdate), max(sysdate)
from dual
"};
my $sqr1 = $db->prepare($qqq);
my $q = qq{
        spool REPORT
        select distinct REPORT_REQUEST_COMPANY, CUST_OR_ORG_NAME
                                FROM REPORT_DISTRIBUTION_EMAIL
        where REPORT_REQUEST_COMPANY is not null
        group by REPORT_REQUEST_COMPANY, CUST_OR_ORG_NAME
        order by REPORT_REQUEST_COMPANY, CUST_OR_ORG_NAME
};

my $sqr = $db->prepare($q);
$sqr->execute;
#$sqr->finish;
$arrayref = $sqr->fetchall_arrayref;

$rows = $sqr->rows;
if ($rows != 0)
{
    foreach $arr (@{$arrayref})
    {

print "$arr->[0]\n";
}
}
$sqr->finish;
##$rows = $sqr->rows;
##print "$arr->[0]";
$db->disconnect();

3. Perl error

REPORT 0
DBD::Oracle::st execute failed: ORA-00900: invalid SQL statement (DBD ERROR:
OCIStmtExecute) at qwest3.pl line 63.
DBD::Oracle::st execute failed: ORA-00900: invalid SQL statement (DBD ERROR:
OCIStmtExecute) at qwest3.pl line 63.

      and we said...
spool, set, etc are commands native to the interactive environment that is
SQLPlus.

They are not part of SQL.

In order to use them, you must be in sqlplus.

This would be a little like trying to use PERL inside of sqlplus (a whole LOT
like doing that).  SQLPlus is a "programming" environment sort of like perl.
The commands, spool, column, break, etc are all sqlplus directives and only
available there.
July 18, 2002
      Ed -- Thanks for the question regarding
      "Validation", version 8.1.7

      You Asked
Hi Tom,

I'm working on a system where users can import payment data, and there's also a
facility to load payment detail files from other systems.

I need to develop a utility for validating the data and reporting back if
validation has failed.

The trouble is, validation can be very different dependent on the payment
category, and there may be up to 100 different categories

I could code it as a huge series of IF .. THEN .. ELSIF statements for each
category, with the validation nested within.  The trouble is, this would result
in a huge amount of code, as I have to allow for 50 data items in each of the
100 possible categories

So I thought I could create a sort of validation data dictionary.  This is a
(very simplified) version of what I mean ...

SQL> desc payment_cat_validation
 Name                            Null?    Type
 ------------------------------- -------- ----
 PAYMENT_CAT                     NOT NULL VARCHAR2(2)
 COLUMN_NAME                     NOT NULL VARCHAR2(30)
 MANDATORY_IND                   NOT NULL VARCHAR2(1)
 FORMAT_MASK                              VARCHAR2(20)

SQL> select * from payment_cat_validation;

CH COLUMN_NAME                    M FORMAT_MASK
-- ------------------------------ - --------------------
AB REF_NO                         Y X999999
AB PAYEE                          Y
AB CHEQUE_NO                      N 999999
AB REQUEST_DATE                   Y DD-MON-YYYY
AK REF_NO                         Y 99999999
AK PAYEE                          Y
AK CHEQUE_NO                      Y 999999
AK REQUEST_DATE                   Y DD/MM/YYYY

The (simplified) table to be validated looks like this (the real one has about
50 columns):

SQL> desc payments_load
 Name                            Null?    Type
 ------------------------------- -------- ----
 PAYMENT_CAT                              VARCHAR2(2)
 REF_NO                                   VARCHAR2(12)
 PAYEE                                    VARCHAR2(80)
 CHEQUE_NO                                VARCHAR2(8)
 REQUEST_DATE                             VARCHAR2(20)

The fields are all 'VARCHAR2's, to try and ensure that all data is loaded using
SQL*Loader - all validation errors should be reported on within PL/SQL rather
than SQL*Loader

Now what I want to do is create a procedure taking a parameter of type
payments_load%ROWTYPE, and for each item in the record, use the validation data
in payment_cat_validation to do the validation.

I was going to create a private procedure to validate each field, and have this
called for each field in the imput parameter record.  This still means I'm going
to have 50 or so calls to the field validation routine within the record
validation routine.

Is there any better way of doing it than this?

Also, is it going to run like a dog with 3 legs if for each column value that
I'm validating, I'm querying my validation table to get the validation rules?

In answer to the request for additional information ...
How many records do we anticipate?

a) a single user uploading
Users will be entering data through a web application (PHP), so it will be one
at a time (my mistake - I wrote import, when I meant input!)

b) total -- in a typical day.
Typically, less than 1500

c) max -- in an atypical day.
There will be a number of daily feeds, plus a number of month-end imports, so a
worst case of 10,000

Thanks,

Ed

      and we said...
Very low bandwidth then.  Ok, my suggestion would be something like this:

CH COLUMN_NAME                    M FORMAT_MASK
-- ------------------------------ - --------------------
AB REF_NO                         Y to_number ( :x, 'X999999' )
AB PAYEE                          Y :x
AB CHEQUE_NO                      N to_number( :x, '999999' )
AB REQUEST_DATE                   Y to_date( :x, 'DD-MON-YYYY' )
.....

and then have a package like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t (
  2   id                                       number primary key,
  3   PAYMENT_CAT                              VARCHAR2(2),
  4   REF_NO                                   VARCHAR2(12),
  5   PAYEE                                    VARCHAR2(80),
  6   CHEQUE_NO                                VARCHAR2(8),
  7   REQUEST_DATE                             VARCHAR2(20)
  8  )
  9  /

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t2;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t2
  2  ( ch  varchar(2),
  3    column_name varchar2(30),
  4    mandatory   varchar2(1),
  5    format_mask varchar2(255)
  6  )
  7  /

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t2 values (
'AB','REF_NO','Y','to_number ( :x, ''999999'' )' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t2 values ( 'AB','PAYEE','Y',':x'
);

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t2 values (
'AB','CHEQUE_NO','N','to_number( :x, ''999999'' )' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t2 values (
'AB','REQUEST_DATE','Y','to_date( :x, ''DD-MON-YYYY'' )' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package demo_pkg
  2  as
  3     g_rec   t%rowtype;
  4
  5     procedure validate( p_id in number );
  6  end;
  7  /

Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package body demo_pkg
  2  as
  3
  4  procedure validate( p_id in number )
  5  as
  6  begin
  7      select * into g_rec from t where id = p_id;
  8
  9      for x in ( select * from t2 where ch = g_rec.payment_cat )
 10      loop
 11      begin
 12          execute immediate
 13          'declare
 14             l_dummy long;
 15           begin
 16             if ( demo_pkg.g_rec.' || x.column_name || ' IS NULL and
:mandatory = ''Y'' )
 17             then
 18               raise_application_error( -20001, ''Field is null'' );
 19             end if;
 20             l_dummy := ' || replace( x.format_mask, ':x', 'demo_pkg.g_rec.'
|| x.column_name ) || ';
 21           end;' using x.mandatory;
 22           dbms_output.put_line( x.column_name || ' checked out OK' );
 23      exception
 24          when others then
 25              dbms_output.put_line( '*** ' || x.column_name || ' sql error =
' || sqlerrm );
 26      end;
 27      end loop;
 28  end;
 29
 30  end;
 31  /

Package body created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1, 'AB', '123',
'hello', '21', '01-jan-2002' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec demo_pkg.validate( 1 );
REF_NO checked out OK
PAYEE checked out OK
CHEQUE_NO checked out OK
REQUEST_DATE checked out OK

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 2, 'AB',
'123456789012', 'hello', '21', '01-jan-2002' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec demo_pkg.validate( 2 );
*** REF_NO sql error = ORA-06502: PL/SQL: numeric or value error
PAYEE checked out OK
CHEQUE_NO checked out OK
REQUEST_DATE checked out OK

PL/SQL procedure successfully completed.

You can call that package either after inserting a row -- or in an after trigger
to verify a loaded row.

for the small number of records you are doing, this'll be more then efficient
enough.
Review
      & Followup

      Rating:
      5
Excellent  July 19, 2002
Reviewer:
      Ed  from UK
Tom,

Many thanks.  This is exactly what I wanted!

July 19, 2002 Sikandar -- Thanks for the question regarding
      "Unused Index(s) for a specific Time?", version 8.1.7

      You Asked
Hi Tom,
I want to drop those indexes which are not used in the last 2 months so to
reclaim space. For example

There are two indexes on scott.emp on the empno and the other is on deptno.
While the application is only using empno index but no use of deptno index.
After getting the info I will drop the deptno index and if required will
recreate the index.

Any script or any idea to get the above required info?

Regards,
Sikandar

      and we said...
No in 8i -- but in Oracle9i -- this is trivial.  You can "alter index
monitoring" to capture statistics about whether an index has been used in a
query or not.

Review &
      Followup

      Rating: 5
What about this
      script?  July 01, 2002
Reviewer:  Sikandar Hayat
      Awan  from Pindi Bhattian - Pakistan
http://www.think-forward.com/sql/bufferidx.htm

          Followup:

useless for this purpose.

Think about it.  If you insert a row into a table, what'll happen -- every index
on that table will be updated.  Now, did you use those indexes?  (yes, in a
way).  But -- did you REALLY use those indexes?  (no, you didn't).

You need to figure out how to determine whether you used an index as an access
path or just maintained it.  That script shows you the indexes that are
maintained as well as used as access paths (and you cannot tell the difference
between the two)

      Rating: 5
What about this
      script?  July 01, 2002
Reviewer:  Sikandar Hayat
      Awan  from Pindi Bhattian - Pakistan
set serverout on size 1000000
set verify off

column owner format a20 trunc
column segment_name format a30 trunc

spool bufferidx.lst

select distinct b.owner, b.segment_name
from x$bh a, dba_extents b
where b.file_id=a.dbarfil
and a.dbablk between b.block_id
and b.block_id+blocks-1
and segment_type='INDEX' and  b.owner not in ('SYS','SYSTEM')
/

spool off

http://www.think-forward.com/sql/bufferidx.htm

          Followup:

see above.

      Rating: 5
Your
      comments...  July 18, 2002
Reviewer:  Sikandar
      Hayat Awan  from Pindi Bhattian - Pakistan
For your comments on the following,

Mining Gold from the Library
By Donald K. Burleson

http://www.oracle.com/oramag/oracle/00-nov/index.html?o60dba.html

          Followup:

If you have my book -- I show a very very similar (but much more automated)
method that uses query plan stability.  We automagically capture all of the
plans for you with query plan stability.

It works -- but it is very cumbersome.  Less cumbersome then the above
referenced article but still....

I'll share a bit of it here from the book:

....
To See the Indexes Used

This is not actually one of the intended uses of stored outlines, more of a side
effect – but, hey, it works! A frequently asked question is 'I have lots and
lots of indexes in my database and I'm sure some of them are not being used, but
I'm not sure which ones. How can I tell?' Well, one way is via the stored
outlines – they'll list the name of every index they use in a query access plan.
If you use an ON LOGON trigger to enable automatic outline generation, run your
system for a while, and then disable it – you'll have a fairly inclusive list of
what indexes are used in the system (and by which queries). As we see  below,
all of the 'hints' that stored outlines use are stored in a data dictionary
table. It becomes very easy to see what indexes are used (and by what queries)
and which are not. For example, using the output of our two examples above, we
can see which queries use the index EMP_PK we have created in our database via:

tkyte@TKYTE816> select name, hint
  2  from user_outline_hints
  3  where hint like 'INDEX%EMP_PK%'
  4  /

NAME            HINT
--------------- --------------------
MYOUTLINE       INDEX(EMP EMP_PK)
FIRST_ROWS_EMP  INDEX(EMP EMP_PK)

We can use the NAME column from this query to go back to the actual SQL query
stored in USER_OUTLINES, to see the original query text that is making use of
this index.
.........................

      Rating: 5
about your
      book  July 18, 2002
Reviewer:  Sikandar Hayat
      Awan  from Pindi Bhattian - Pakistan
Dear TOM Your book in not available in Pakistan. I have check on www.amazon.com
and it is $49+shipment+taxes and it is about $60+. Very expensive due to
conversion. I wish that your book will be available in Pakistan very soon.

Currently I don't have your script/book so can't tell you about it. While the
script available in the above mentioned Article is under my testing.

          Followup:

Yes it is -- I've recieved the royalties.

Take the ISBN, goto a reputable bookstore, and they should be able to order it.

As a followup to the prov followup, here is a little more detail:

...
We could have used an ON LOGON database trigger, such as the following, to
achieve the same result:

tkyte@TKYTE816> create or replace trigger tkyte_logon
  2  after logon on database
  3  begin
  4     if ( user = 'TKYTE' ) then
  5        execute immediate
  6               'alter session set use_stored_outlines = hr_application';
  7     end if;
  8  end;
  9  /

Trigger created.

You need the privileges CREATE TRIGGER and ADMINISTER DATABASE TRIGGER in order
to create a LOGON trigger. Additionally, the owner of this trigger needs the
ALTER SESSION privilege granted directly to them, rather than via a role.
..........

Line 4 is something you would think about -- you probably don't need it -- you
want to generate outlines for every user.  Once you have that, the outlines will
be generated and you can use queries againt the document OUTLINE tables to see
what hints get placed in there and see what indexes the hints use.

Play with it -- try the alter session command in sqlplus, run some queries,
disable the query plan generation and query the resulting tables to see what
information you get.

      Rating:
      5
Excelent  July 19, 2002
Reviewer:
      Sikandar Hayat Awan  from Pindi Bhattian - Pakistan

SYSTEM > ALTER SESSION SET CREATE_STORED_OUTLINES = true;

Session altered.

SYSTEM > select * from scott.emp;
SYSTEM > select * from scott.emp where empno = 7566;

SYSTEM >  select name, hint
    from user_outline_hints
   where hint like 'INDEX%';
NAME                           HINT
------------------------------ ------------------------------
SYS_OUTLINE_020719211705845    INDEX(EMP EMP_PRIMARY_KEY)
SYS_OUTLINE_020719211713085    INDEX(O OL$NAME) <<<<<< ?
SYS_OUTLINE_020719211914610    INDEX(O OL$NAME)

after some other queries
NAME                           HINT
------------------------------ -------------------------------
SYS_OUTLINE_020719211705845    INDEX(EMP EMP_PRIMARY_KEY)
SYS_OUTLINE_020719211713085    INDEX(O OL$NAME)
SYS_OUTLINE_020719211914610    INDEX(O OL$NAME)
SYS_OUTLINE_020719212026173    INDEX(DTS)
SYS_OUTLINE_020719212026173    INDEX(TTS)
SYS_OUTLINE_020719212026173    INDEX(U I_USER1)
SYS_OUTLINE_020719212026303    INDEX(I I_IND1)
SYS_OUTLINE_020719212026303    INDEX(OBJAUTH$ I_OBJAUTH2)
SYS_OUTLINE_020719212026303    INDEX(O I_OBJ2)
SYS_OUTLINE_020719212026303    INDEX(U I_USER1)
SYS_OUTLINE_020719212041435    INDEX(EMP EMP_EMP_NO_PK)
SYS_OUTLINE_020719212050458    INDEX(O OL$NAME)

SYS_OUTLINE_020719212050458 << this is because I have not created outline?

Also reading the following link,

http://download-west.oracle.com/otndoc/oracle9i/901_doc/server.901/a87503/outlines.htm

      Rating: 5
very good idea
      to use outlines gathering in 8i to monitor indexes
      using  March 02, 2003
Reviewer:  MarkMal
       from Canada

July 19, 2002 Michael -- Thanks for the question regarding
      "Limit and conversion very long IN list : WHERE x IN ( ,,, ...) ", version
      Oracle 8.0.5

      You Asked
How many elements may be in the   WHERE x IN (,,,) list ?
I see 2 ways to overcome IN list limitation:

1) use  x=el_1  OR x=el_2 OR x=el_3 OR ...

2)  create temporary table , but another question arise here:
why
create table A( X INTEGER, Y VARCHAR(32) );
takes up to 15 seconds sometimes ?

Regards, Michael.

      and we said...
The same limit with IN is:

ERROR at line 42:
ORA-01795: maximum number of expressions in a list is 1000

DDL is very expensive and you should never dynamically create a
table at runtime -- there is no reason to.  You should just
create the table ONCE and then:

   insert into TEMP values ( ... );
   select * from T where (a,b) in (select x,y from temp);
   delete from TEMP;

Better yet, see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:146012348066
Please do not dyamically create a statement with hard wired IN
list values or OR'ed expressions.  That is the worst thing you
can do to us.  We'll spend more time parsing queries then
actually executing them!!

Either use the static temp table or use one of the object tables
or PLSQL tables as outlined in the link.
Review &
      Followup

      Rating:
      4
ORA-01795  March 28, 2002
Reviewer:
      sanjai_9@yahoo.com  from MI
Tom,

We have a select statement where in we dynamically bind the t.e (please see the
select statement below).The value of t.e depends on certain conditions. Using a
FOR loop we dynamically bind the values in the IN clause.

But whenever the value exceeds 1000 we get this error. I read this page. Do we
need to change our java code to handle this or is there a way to get around
this.

SELECT t.a, t1.b , t.c, t.d
FROM t, t1
WHERE t.a = t1.a1
AND ( t.e IN  (?, ?, ?, ?........ ) )

java.sql.SQLException: ORA-01795: maximum number of expressions in a list is
1000

Thanks a lot.

          Followup:

Well, as the message sort of implies -- the max number of elements in an in-list
(whether you BIND them or HARD code them -- please bind them) is 1,000.

You cannot exceed that.

Suggest you create a global temporary table, array insert your "in list" into
this table and use

select .... and ( t.e in ( select * from global_temp_table );

      Rating: 2
Could you
      please tell me how can i handle ?  July 19, 2002

Reviewer:  AdamWang  from China
Hi Tom,
I feel so trouble ,because my products of the CA must use "sleect * from a where
b in(1,2,3,.......)".Then could you tell me another way to handle it.

          Followup:

Look up cursor_sharing

search for it here, read about it in the docs, see chapter 10 in my Expert One
on One.

      Rating: 4
Another
      solution for java  July 22, 2002
Reviewer:
      Michael
This works quite good.

First:

CREATE TYPE NUMBERTABLE AS TABLE OF NUMBER;
/

Then in JAVA:

import java.sql.*;
import oracle.sql.*;
import oracle.jdbc.*;

public class ArrayBindTest {

    public static void main(String[] args) throws SQLException {

        long longArray[] = new long[3000];
        for(int i = 0; i < longArray.length; i++) {
            longArray[i] = 4500 + i;
        }
        System.out.println("Starting ...");
        DriverManager.registerDriver(new OracleDriver());
        Connection conn =
DriverManager.getConnection("jdbc:oracle:thin:@e2hp11:1521:destest", "scott",
"tiger");
        conn.setAutoCommit(false);

        ArrayDescriptor desc = ArrayDescriptor.createDescriptor("NUMBERTABLE",
conn);
        ARRAY array_to_pass = new ARRAY(desc, conn, longArray);
        OraclePreparedStatement pstat =
            (OraclePreparedStatement) conn.prepareStatement(
                "SELECT ENAME FROM EMP " +
                "WHERE EMPNO IN (SELECT * FROM TABLE (SELECT CAST(? AS
NUMBERTABLE) FROM DUAL))");
        pstat.setARRAY(1, array_to_pass);
        OracleResultSet rset = (OracleResultSet) pstat.executeQuery();
        while(rset.next()) {
            System.out.println(rset.getString(1));
        }
        pstat.close();
        conn.close();
        System.out.println("Finished ...");
    }
}

      Rating:
      4
  August 28, 2003
Reviewer:
      A reader

July 19, 2002 Charanjiv -- Thanks for the question regarding
      "return result set from temporary table to java", version 8.1.7

      You Asked
Hi
from a provedure i am returning a result set. the ref cursor is selected from a
Global Temp table. My Java prog gives the following error. Any way to avoid it
or an alternative

D:\tst>java curvar
 after exec
java.sql.SQLException: ORA-08103: object no longer exists

        at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:168)
        at oracle.jdbc.ttc7.TTIoer.processError(TTIoer.java:208)
        at oracle.jdbc.ttc7.Oall7.receive(Compiled Code)
        at oracle.jdbc.ttc7.TTC7Protocol.doOall7(Compiled Code)
        at oracle.jdbc.ttc7.TTC7Protocol.fetch(TTC7Protocol.java:889)
        at oracle.jdbc.driver.OracleStatement.<init>(OracleStatement.java:490)
        at oracle.jdbc.driver.OracleStatement.getCursorValue(OracleStatement.jav
a:2661)
        at oracle.jdbc.driver.OracleStatement.getObjectValue(OracleStatement.jav
a:4189)
        at oracle.jdbc.driver.OracleStatement.getObjectValue(OracleStatement.jav
a:4123)
        at oracle.jdbc.driver.OracleCallableStatement.getObject(OracleCallableSt
atement.java:541)
        at curvar.main(Compiled Code)

My Java File

import java.sql.*;
import java.io.*;
import oracle.jdbc.driver.*;

class curvar
{
  public static void main (String args [])
  throws SQLException, ClassNotFoundException
  {
      String query  =
           "begin pkg_tst.p_getfirstlvl( :1 ,:2 ); end;";
      DriverManager.registerDriver
          (new oracle.jdbc.driver.OracleDriver());

      Connection conn=
        DriverManager.getConnection
         ("jdbc:oracle:thin:@10.100.100.10:1521:test_ins",
          "xuser", "xuser");

      CallableStatement cstmt = conn.prepareCall(query);
      cstmt.setString(1,"46053-100");
      cstmt.registerOutParameter(2,OracleTypes.CURSOR);
      cstmt.execute();
      ResultSet rset = (ResultSet)cstmt.getObject(2);
      while( rset.next() )
        System.out.println( rset.getString(1) );
      rset.close();
      cstmt.close();
  }
}

the Package

 Procedure p_getfirstlvl
  (i_comp_ofid VARCHAR2,
   o_rset in out curtype )
 vdtl  int
 vstl_i int
 vtot int

 as
  Begin
  for x in (
    SELECT a.componentid,
           a.instance,
           c1.productclassid,
           c1.sku,
           cd.bigheadings,
           s.linkid,
           a.quantity
      FROM AssemblyTree  a,
           component  c,
           ComponentDisplay_Configurator  cd,
           SVGLink  s,
           ComponentGraphics  cg,
           component  c1
     WHERE a.componentofid=c.componentid
       And c.sku= i_comp_ofid
       And a.componentid=c1.componentid
       And cg.parent=c.componentid
       And s.imageid=cg.imageid
       And  s.productclassid=c1.productclassid
       And  s.instance=a.instance
       And c1.productclassid=cd.productclassid
       And a.addonflag='Y'
       AND a.instance = cd.instance
     order by TO_NUMBER(s.linkid) )
    loop
/*
processing code here
*/
 insert into tmp (
      COMPONENTID    ,
       dtl     ,
       stl_i,
      tot)
 Values
    (  i.COMPONENTID,
       vdtl     ,
       vstl_i,
       vtot)

    );
end loop ;
open o_rset for
     select
       COMPONENTID    ,
       dtl     ,
       stl_i,
      tot
   from tmp ;
end p_getfirstlvl ;

TMP is a Global TEmp table
with the struc
 COMPONENTID  VARCHAR2(10)
 DTL NUMBER
 STL_I NUMBER
 TOT NUMBER

Oracle Version 8.1.7

Charanjiv

      and we said...
Sure, the answer is simple -- this is being caused by the totally "stupid"
default setting of JDBC.  It has AUTOCOMMIT = TRUE (eg: every statement stupidly
issues a COMMIT, geez, why even buy a database...)

You want to either:

o make the global temporary table "on commit PRESERVE rows" so the object
doesn't disappear as soon as you select from it

o or better yet, make the FIRST line of code after connect of every java/jdbc
program you ever code be:

conn.setAutoCommit(false);

You should do the second thing "anyway" -- not matter what.
Review &
      Followup

      Rating: 4
perfect
        July 19, 2002
Reviewer:  Charanjiv
       from india
Put in the Auto commit false in the code .. and in the checklist of any further
forays into Java ;)
Thanks

July 19, 2002 Sajid -- Thanks for the question regarding "why
      do we need ORDBMS?", version 8.1.7

      You Asked
Hi Tom,
Could you please tell us why anyone would need Object Relational Database? What
is the need of it? Is there something that we cant do it RDBMS? Could you please
explain the difference?

One more question tom,
I have a table with 75 columns. And i want to write a simple select query to
output the record with "PIPE CHARACTER" as seperator. There are many ways of
doing it. But i want a SELECT statement only, please.
I am sorry if this is not genuine question.

Thanks in advance,

      and we said...
see

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/appdev.920/a96594/adobjint.htm#455472

select a || '|' || b || '|' || c || ....
  from t

see

http://asktom.oracle.com/~tkyte/flat/index.html
it'll generate the query for you easy enough.
Review &
      Followup

      Rating: 5
thanks a
      ton  July 19, 2002
Reviewer:  Sajid Anwar
       from London

July 19, 2002 Sateesh Kumar -- Thanks for the question
      regarding "contention for data blocks.", version 8.1.5

      You Asked
Hi Tom,

pri>select * from v$waitstat where class='data block';

CLASS                  COUNT      TIME
------------------ --------- ---------
data block             43619         0

It is very difficult to find out what causes block contention. I
increased the db_block_buffers, But still the contention is
there. Can u tell me what else can i do to reduce this
contention. It is not a bottleneck in our database. But i want
to zero them out ideally speaking.

Thanks
Sateesh

      and we said...
Well, these are contention for the SAME blocks -- would not
matter how many you cache -- these are people bumping into
eachother for the same exact things.

also, 43k is not a very large number if your database has been
up for a while.

Freelists are the answer to this (and one of the most overlooked
storage settings).

You'll want to increase the freelists settings on the affect
indexes and tables.  You'll look into v$session_wait and look
for buffer busy waits and look at p1, p2, and p3 from that
table.  You can then

SELECT * FROM v$datafile WHERE file# = p1

to find the affected file.  If only 1 object is in that file --
you are done, you know which one to fix.  Otherwise, you might
have to:

SELECT  segment_name ,  segment_type , owner , tablespace_name
  FROM  sys.dba_extents
 WHERE  file_id = p1
   AND  p2 BETWEEN block_id AND block_id + blocks -1

to identify the object.  In 8.1.5 and before as you have, the
only way to change the freelists is via a rebuild of the object.
 In 8.1.6 and up, you can alter the object to set the freelists
higher.

Review &
      Followup

      Rating:
      5
  May 09, 2001
Reviewer:  Anand
      Singh  from Delhi-India

      Rating: 4
When no entry
      in v$session_wait   July 18, 2002
Reviewer:
      Prince
Tom,

When we couldn't get the p1,p2, p3 from v$session_wait (say at this moment there
is no wait), can x$bh.tch or anything else we used to find the highly
active/contended tables?

I believe I have a couple of tables which are very active and I would like to
find whether they really have the contention problem. How do I determine it?
(Oracle version 8.1.7.4)

From v$waitstat, I know there are contentions.

Thanks,

          Followup:

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:865627341240
we can find it by file.

      Rating: 4
What if the
      datafile has lot of objects?  July 18, 2002

Reviewer:  Prince.
Thanks Tom for the pointer!

I already read the doc you pointed. But, how do I identify the particular object
if the datafile has many many objects. I have coouple of datafiles in the TS and
are having almost the same number of I/O.

          Followup:

you would start segmenting your objects into different tablespaces then.  We can
get you to the file.  If you have many objects using that file -- you'll need to
move them about to figure it out.

      Rating: 4
Very
      Interesting! But...  July 19, 2002
Reviewer:
      Robert  from Tennessee, USA
Tom,

I just checked our prod database and found these data block wait stats seemingly
very high. For one of our largest tables (120M rows) we had 14,368,500 of these
waits.
Our database has been up for 5 days.
I am going to show this to our head DBA.

Is there a downside to having too many freelists? ... can one shoot oneself in
the foot by setting this too high?

Thanks,

Robert

          Followup:

You may find you use slightly more space with multiple freelists.  A process
(connection) will only use space from ONE freelist -- so if it runs out of space
in its freelist, it'll grab a new extent, before reusing the space on the other
freelists.

In general -- in a high volume situation with lots of contention for a single
freelist -- this is OK and not an issue (since you are by definition consuming
the freelist space rapidly as it is!)

      Rating:
      5
  July 07, 2003
Reviewer:
      A reader

July 19, 2002 -- Thanks for the question regarding "Get a
      string as a BackReference..", version 8.1.7

      You Asked
Hi Tom

Thanks for a the chance to pose my Question..
I am pondering  how to do the Following using pl-sql :

From within a package :
  I have an Insert Procedure :  P
  i am generating some Random string :  X
  I store X in a Static  Table T , together  with other Columns .

 Now , i need to somehow track the Last generated String and use it in my next
insert in procedure P .

 P could be called by many sessions at a time .

 Could this be done in Oracle or is it the same story as trying to
generate gap free Sequences ?

Best regards

      and we said...
Yes it can be done and yes, it is the same story as trying to generate gap free
sequences to a large degree (eg: it is serialized).

If you are just interested in a random string, see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1235458156173
you might find it useful.  Else, you'll have to do something like this:

create table last_string( str varchar2(25) );

and have a function:

create function get_random_string return varchar2
as
   l_str varchar2(25);

   PRAGMA AUTONOMOUS_TRANSACTION;
begin
   select str into l_str from last_string FOR UPDATE;
   l_str := f(l_str);
   update last_string set str = l_str;
   commit;
   return l_str;
end;
/

It'll serialize but only "a little" -- just long enough to get the string,
modify it and put it back.
Review & Followup

      Rating: 5
is the Pragma
      there really necessary...  July 19, 2002
Reviewer:
      Pasko
Hi Tom ,

Thanks for a very prompt Response.

But , do i have to use
PRAGMA AUTONOMOUS_TRANSACTION  ?

Regards

          Followup:

If you do not -- you will serialize at the transaction level.

Using the autonomous transaction, you will serialize only at the function call
level (only one copy of the function will be running at any time -- the others
will block and serialize).

It is up to you.

July 20, 2002 Chuck -- Thanks for the question regarding
      "Implict bind variables? Implicit cursors?", version 8.1.7

      You Asked
Tom,
I have a database that is pretty much star shaped.
Views with subqueries don't work well for single parent-record
queries so I have created a bunch of functions like the one
below to help simplify programming of online queries.
Keep in mind that the real ones are a lot more complicated.

My questions are about the parsing and execution of these functions.

Eg:
CREATE OR REPLACE package my_stuff.parent_info as

  function child_total(ar_parent_id number) return number;
  PRAGMA RESTRICT_REFERENCES(child_total, WNDS, WNPS, RNPS);

  function child_total(ar_parent_id number)
  return number as
  ln_total number;

  begin
    select sum(value)
      into ln_total
      from tables.child_table c
     where c.parent_id = ar_parent_id
       and c.flag = 'Y';
    return ln_total;
    exception
       when others then
          return 0;
  end;
end;

It might then be used as follows in a program:

select p.parent_columns, mystuff.parent_info.child_total(p.parent_id),
....others
  from parent_table p
 where p.col1 = :b_value1
   and mystuff.parent_info.child_total(p.parent_id) > :b_value2;

1, What are the parsing implications of this?
2, Is ar_parent_id a bind variable?
3, Is child_total() executed once (I hope) or twice for each row?

4, To clarify from a question yesterday:
is
declare
  cursor c_cur is
   select bla
     from stuff;
begin
 for i_cur in c_cur loop
   do something;
 end loop;
end;

Is the cursor loop implicit or explicit?
I use this construct a LOT.
Thanks,
Chuck

      and we said...
I would be asking myself this:

0) what are the PERFORMANCE impliciations of this.

They can be horrible.  I never call PLSQL from SQL if I can do it in SQL using
CASE, DECODE or SELECT of a SELECT.

In your case, I would want to code:

select *
  from (
select p.parent_columns, (select sum(value)
                            from tables.child_table c
                           where c.parent_id = ar_parent_id
                             and c.flag = 'Y' ) val,
       ....others
  from parent_table p
 where p.col1 = :b_value1
       )
 where val > :b_value2;

(and I HATE when others, especially as you are using it -- you are invoking the
"please silently ignore all errors" option).

If it is the complexity of the SQL you are concerned with -- well then, that is
what views are very good at hiding.

1) there are no parsing implications
2) yes
3) probably twice or more.  It is not deterministic -- it could return one value
in the select list, another in the predicate.

4) i would call it IMPLICIT, no open/fetch/close.  Others would call it EXPLICIT
since you defined a cursor.  We are mincing terms at this point.

The real question is -- how does it perform (isn't it always ;)

So, comparing your implicity explicit cursor to a pure implicit cursor:

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      cursor c is select * from emp;
  3
  4      l_rec   emp%rowtype;
  5      l_start number;
  6      l_run1  number;
  7      l_run2  number;
  8  begin
  9      insert into run_stats select 'before', stats.* from stats;
 10
 11      l_start := dbms_utility.get_time;
 12      for i in 1 .. 2000
 13      loop
 14          for x in (select * from emp)
 15          loop
 16              null;
 17          end loop;
 18      end loop;
 19      l_run1 := (dbms_utility.get_time-l_start);
 20      dbms_output.put_line( l_run1 || ' hsecs' );
 21
 22
 23      insert into run_stats select 'after 1', stats.* from stats;
 24      l_start := dbms_utility.get_time;
 25      for i in 1 .. 2000
 26      loop
 27          for x in C
 28          loop
 29              null;
 30          end loop;
 31      end loop;
 32      l_run2 := (dbms_utility.get_time-l_start);
 33      dbms_output.put_line( l_run2 || ' hsecs' );
 34      dbms_output.put_line( 'run 1 ran in ' || round(l_run1/l_run2*100,2) ||
'% of the time' );
 35
 36      insert into run_stats select 'after 2', stats.* from stats;
 37  end;
 38  /
295 hsecs
318 hsecs
run 1 ran in 92.77% of the time

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value-a.value run1,
c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11   order by abs( (c.value-b.value)-(b.value-a.value))
 12  /

NAME                                 RUN1       RUN2       DIFF
------------------------------ ---------- ---------- ----------
....
STAT...redo size                    21460      21412        -48
STAT...recursive calls              30031      32031       2000

22 rows selected.

There is a benefit to the 100% pure implicit cursor (less recursive sql for
example)..

then comparing it to a pure explicit cursor:

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      cursor c is select * from emp;
  3
  4      l_rec   emp%rowtype;
  5      l_start number;
  6      l_run1  number;
  7      l_run2  number;
  8  begin
  9      insert into run_stats select 'before', stats.* from stats;
 10
 11      l_start := dbms_utility.get_time;
 12      for i in 1 .. 2000
 13      loop
 14          open c;
 15          loop
 16              fetch c into l_rec;
 17              exit when c%notfound;
 18          end loop;
 19          close c;
 20      end loop;
 21      l_run1 := (dbms_utility.get_time-l_start);
 22      dbms_output.put_line( l_run1 || ' hsecs' );
 23
 24
 25      insert into run_stats select 'after 1', stats.* from stats;
 26      l_start := dbms_utility.get_time;
 27      for i in 1 .. 2000
 28      loop
 29          for x in C
 30          loop
 31              null;
 32          end loop;
 33      end loop;
 34      l_run2 := (dbms_utility.get_time-l_start);
 35      dbms_output.put_line( l_run2 || ' hsecs' );
 36      dbms_output.put_line( 'run 1 ran in ' || round(l_run1/l_run2*100,2) ||
'% of the time' );
 37
 38      insert into run_stats select 'after 2', stats.* from stats;
 39  end;
 40  /
307 hsecs
314 hsecs
run 1 ran in 97.77% of the time

PL/SQL procedure successfully completed.

So that is a bummer, (i ran quite a few times, pretty consistent there).  The
implicitly explicit cursor is apparently the slowest of the three.

Now, you need to ask yourself BEFORE you go back to your code -- is that 7%
worth it?  Probably not in retrospect but maybe for the future.

July 20, 2002 Dinesh -- Thanks for the question regarding
      "PL/SQL", version 8.1.6

      You Asked
Hi I have Pl/sql procedures which does insert,update and delete records from the
table, i want to pass a parameter OUT to the application whenever the
insert,update or delete is sucessful or if not send a message or error code to
the application indicating the failure of the same
how do i send that? pl give a small ex which will help in understanding this
better,
Thanks in advance.
dinesh

      and we said...
Why bother with such parameters.  Just use exceptions and the client will get NO
ERROR (indicating success) or a SQL error and will have access to the sqlcode
and error message in the normal way.

Otherwise, sit down and read the PLSQL programmers guide - this is very very
basic functionality (using IN, IN OUT, and OUT parameters).

But again, I would quite simple "do nothing" and just let errors happen the way
they were designed and intended to happen -- as exceptions.
July 20, 2002 Natarajan -- Thanks for the question regarding
      "SQL Loader - CLOB/BLOB", version 8.1.5

      You Asked
Hi Tom,
In this forum, you have answered many questions regarding sql loader and
handling of LOBS.  You have given model scripts for extracting data from oracle
database into a flat file. If the table contains one CLOB and one BLOB column,
what will be the contents of the flat file.
Please answer assuming that the data can be stored either as inline or out of
line.
Thanks
N.Balasubramanian.

      and we said...
If you have my book -- i go into this in great detail.  Takes a bit of space to
cover but yes -- the CLOB data could appear inline or out of line (in the dat
file or in a separate file)

here is out of line:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:627223659651
here is in line:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1286201753718
BLOB data should appear OUT OF LINE as it is really hard for you to tell us
where it "ends".
July 20, 2002
      R -- Thanks for the question regarding "Top
      N% of a set of data", version 8.1.7

      You Asked
Tom:
    I was looking at the analytical functions of Oracle8i and saw few
of your answers related to Top N queries.  I have a similar qn but need some
help:

I have a set of data : Single table with two columns:

(id number, value number)

ex: (id ranging from 1-200 )

(1, 180)
(2, 100)
(3, 10)
(4, 1000)
(5, 20)
...
...
...

What I would like to find out is : Give me the top N% of the values

In this 5 rows of data: Top 20% data is (4,1000)

So, the query should accept the variable which represents the percentage ( in
this case 20 ) and it will return the entry.

If I have 200 entries and asking for Top 5%, then I have to show the top 10
entries from the list.

Please help

Thanks
Reji

      and we said...
Check out cume_dist()

  1* select id, value, cume_dist() over ( order by value desc ) from t
ops$tkyte@ORA817DEV.US.ORACLE.COM> /

        ID      VALUE CUME_DIST()OVER(ORDERBYVALUEDESC)
---------- ---------- ---------------------------------
         4       1000                                .2
         1        180                                .4
         2        100                                .6
         5         20                                .8
         3         10                                 1

Review &
      Followup

      Rating: 5
Very
      Good  September 20, 2001
Reviewer:  R
       from NJ, USA
Tom: How do I pass a variable to this query ( N %) and based on that get the
result

          Followup:

Just wrap it in an inline view:

  1  select *
  2    from (select id, value, cume_dist() over ( order by value desc ) cd from
t)
  3*  where cd <= 0.20
ops$tkyte@ORA817DEV.US.ORACLE.COM> /

        ID      VALUE         CD
---------- ---------- ----------
         4       1000         .2

      Rating: 5
how to
      eliminate a column from the display  September 20, 2001

Reviewer:  R  from NJ, USA
Tom: How to eliminate the last column ( cd) from the display?

      Rating: 5
how to
      eliminate the column ?   September 20, 2001

Reviewer:  R  from NJ, USA
Tom: I got it by specifying the id, value in the outer select.....
thanks

      Rating: 4
How to
      do?  September 20, 2001
Reviewer:  XE
       from NY, USA
I was looking for a similar soln - my data set is different
I have 6 columns in a table. Let's take an example for discussion purpose (id,
value1, value2, value3, value4, value5). sample data -
[1, 10, 2, 45, 30, 60]
[2, 90, 10, 90, 10, 89]
[3, 89, 12, 29, 11, 34]
[4, 60, 25, 190, 19, 30]
[5, 5, 89, 10, 100, 90]

I would like to show the top N% and bottom N% of the data. Because of the
different values in each record I have to group them by value1, value2, value3,
value4 and value5.

The display should look like for value1:
id value1 cd
2    90  .2
3    89  .4
4    60  .6
1    10  .8
5    5    1

similar thing for value 2 etc etc
Any help is really appreciated

          Followup:

I really don't know what you are looking for here?  It seems to me that you
would just run 5 queries -- or union all the 5 queries?

      Rating: 4
Sun
      HK  July 20, 2002
Reviewer:  CHEN SUN
       from Hong Kong
Hi Tom,
   I have a table and it includs some columns, and four of them are ac_yr_mth,
princial, clm_ind, net_grs_paid, rank which is coded like this : rank()over
(partition by ac_yr_mth, principal, clm_ind order by net_grs_paid), now I add a
new column called rank_ind to this table and which is used to store the value
'Largest' if rank is the top 50 records, 'smllest' if the rank is the bottom 50
records and others will be stored 'Others'. How can I update this rank_ind base
on rank and it should be grouped by ac_yr_mth, principal, clm_ind. Data like
this:
ac_yr_mth, Principal, clm_ind, rank, rank_ind
01/2002    ZH         NEW      1     Largest
.          .          .        2     Largest
.          .          .        3     Largest
.          .          .        4     Largest
.          .          .        5     Largest
.          .          .        .     Largest
.          .          .        .     Largest
.          .          .        .     Largest
.          .          .        .     Largest
.          .          .        50    Largest
.          .          .        .     others
.          .          .        .     others
.          .          .        .     others
.          .          .        1     smallest <-----Bottom
.          .          .        .     smallest <-----Bottom
.          .          .        .     smallest <-----Bottom
.          .          .        44    smallest <-----Bottom
.          .          .        45    smallest <-----Bottom
.          .          .        46    smallest <-----Bottom
.          .          .        47    smallest <-----Bottom
.          .          .        48    smallest <-----Bottom
.          .          .        49    smallest <-----Bottom
.          .          .        50    Smallest <-----Bottom

          Followup:

well, since you can get this value when you fetch -- and it will change with
each update -- i would hesitate to put it into the table itself (sort of like
storing a persons AGE in years, you should store their birthdate and compute
AGE)

but anyway, what is to happen if there are 50 records -- they are both the
largest and smallest in that case.  Seems to me you do not have a mutually
exclusive condition here.

July 21, 2002 SAM
      -- Thanks for the question regarding "read from flat file and load into a
      pl/sql table", version 8.1.5

      You Asked
Tom

I generate a flat file every day. I want to load that flat file into a pl/sql
table. Is that possible.

I'm not in a position to convince my dba to create a table so that I can user
sqlldr to load the flat file into the table.

So my need in specific is how to read the flat file and load it into a pl/sql
table.

Thanks

SAM in San Francisco

      and we said...
sure, you can use UTL_FILE to read a flat file.

see the supplied packages guide for details.  Your DBA will still have to help
you out -- by setting up the utl_file_dir and permitting you to use this
feature, so good luck.
Review & Followup

      Rating:
      3
  July 21, 2002
Reviewer:  Mike
       from NYC,NY
What are the differences of the SQL*LOADER and the UTL_FILE?

          Followup:

sqlldr is a high speed, optimized data loading tool.

UTL_FILE is a generic utility used by plsql programers to read and write text
(and in 9iR2 binary) files.

July 21, 2002 Bin
      -- Thanks for the question regarding "How to delete or update data in
      nested table?", version 8.1.7

      You Asked
I have a nested table as following:
-----------------------------------------
create type mytype as table of number(5,0);
create table test(
a number(5),
b mytype
)
nested table b store as test_b_tab;
---------------------------------------
And the table has the following data:

a b
---------
1 1
- 2
- 3

Q1. If I wanna delete b = 3 where a = 1. What should I do in SQL?
Q2. If I wanna set b = 8 where a = 1 and b = 3. What should I do in SQL?

Give me a reply, please.
And thank you first!

      and we said...
If you have my book "expert one on one", this is covered in the chapter on
tables.

You should also read

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a77069/04_colls.htm#20023
which has this info and more as well.

Here is a quick and dirty example:

ops$tkyte@ORA817DEV.US.ORACLE.COM> column b format a50
ops$tkyte@ORA817DEV.US.ORACLE.COM> select t.a, t.b b from test t;

         A B
---------- --------------------------------------------------
         1 MYTYPE(1, 2, 3)

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select t.a, t2.*
  2    from test t, table(t.b) t2
  3  /

         A COLUMN_VALUE
---------- ------------
         1            1
         1            2
         1            3

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> update TABLE(select t.b from test t where a =
1)
  2     set column_value = 8
  3   where column_value = 3
  4  /

1 row updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select t.a, t2.*
  2    from test t, table(t.b) t2
  3  /

         A COLUMN_VALUE
---------- ------------
         1            1
         1            2
         1            8

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> delete from TABLE( select t.b from test t
where a = 1)
  2   where column_value = 8
  3  /

1 row deleted.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select t.a, t2.*
  2    from test t, table(t.b) t2
  3  /

         A COLUMN_VALUE
---------- ------------
         1            1
         1            2

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review &
      Followup

      Rating: 4
Thank you very
      much!  July 21, 2002
Reviewer:  Yang Bin
       from China
I've never thought that you gave the answer so accurate,so quickly and so
detailed.It's very helpful to me.
I have no other ways to say "thanks" to you except buying that book "Expert one
to one" to give some support to your development. And at the same time, to
develop me.
In the end, thanks and thank you again, thank you very much!
Best wishes!

                                    Bin Yang

      Rating: 5
Great Stuff
      right there Tom !!  July 22, 2002
Reviewer:
      Pasko  from Hamburg,Germany

July 21, 2002 Manivachakam -- Thanks for the question regarding
      "How to use Utlbstat & Utlestat", version 9.0.1

      You Asked
Dear Tom,

How to use UTLBSTAT & UTLESTAT and EXPLAIN PLAN in Oracle9i.Please
guide me. Step by step procedure

Thanks,
Iyyappan.M

      and we said...
You don't want to even consider utlbstat/estat

You want to use STATSPACK.

If you have my book -- Expert one on one Oracle -- you want to read chapter 10
where I go into painstaking detail on

o tracing
o tkprof
o statspack

More then you ever thought you wanted to know -- but it takes about 50pages (not
appropriate in this forum)

short of that,

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/server.901/a87503/toc.htm
documents (step by step) explain plan (rather use autotrace personally) and
statspack along with lots more.
July
      21, 2002 Chunhai -- Thanks for the
      question regarding "Oracle 9i new feature EXP ", version 9.01

      You Asked
EXP system/manager tablespaces = Example
I did not see the index exported
Here is the detail.
C:\>Exp system/manager tablespaces=example

Export: Release 9.0.1.1.1 - Production on Fri Jul 21 14:50:44 2000

(c) Copyright 2001 Oracle Corporation.  All rights reserved.

Connected to: Oracle9i Enterprise Edition Release 9.0.1.1.1 - Production
With the Partitioning option
JServer Release 9.0.1.1.1 - Production
Export done in WE8MSWIN1252 character set and AL16UTF16 NCHAR character set

About to export selected tablespaces ...
For tablespace EXAMPLE ...
. exporting cluster definitions
. exporting table definitions
. . exporting table                      COUNTRIES         25 rows exported
. . exporting partition                  COSTS_Q1_1998      57879 rows exported
. exporting referential integrity constraints
. exporting triggers
Export terminated successfully without warnings.

      and we said...
it exported them tho, if you check the dmp file:

ops$tkyte@ORA920.US.ORACLE.COM> select segment_name, tablespace_name from
user_segments;

SEGMENT_NAME               TABLESPACE_NAME
------------------------------ ------------------------------
T                   TOOLS
T_IDX                   TOOLS

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> !exp userid=/ tablespaces=tools

Export: Release 9.2.0.1.0 - Production on Sun Jul 21 23:51:09 2002

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

Connected to: Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production
Export done in WE8ISO8859P1 character set and AL16UTF16 NCHAR character set

About to export selected tablespaces ...
For tablespace TOOLS ...
. exporting cluster definitions
. exporting table definitions
. . exporting table                              T          0 rows exported
. exporting referential integrity constraints
. exporting triggers
Export terminated successfully without warnings.

ops$tkyte@ORA920.US.ORACLE.COM> !imp userid=/ full=y show=y

Import: Release 9.2.0.1.0 - Production on Sun Jul 21 23:51:10 2002

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

Connected to: Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production

Export file created by EXPORT:V09.02.00 via conventional path
import done in WE8ISO8859P1 character set and AL16UTF16 NCHAR character set
. importing OPS$TKYTE's objects into OPS$TKYTE
 "CREATE TABLE "T" ("X" NUMBER(*,0))  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRA"
 "NS 255 STORAGE(INITIAL 65536 FREELISTS 1 FREELIST GROUPS 1) TABLESPACE "TOO"
 "LS" LOGGING NOCOMPRESS"
. . skipping table "T"

 "CREATE INDEX "T_IDX" ON "T" ("X" )  PCTFREE 10 INITRANS 2 MAXTRANS 255 STOR"
 "AGE(INITIAL 65536 FREELISTS 1 FREELIST GROUPS 1) TABLESPACE "TOOLS" LOGGING"
Import terminated successfully without warnings.

Actually, even in "normal" export, it doesn't say "hey, getting indexes" really
- for function based, bitmap and domain (extensible) it does -- but not for
"normal" ones:

exp userid=/ owners=ops\$tkyte

Connected to: Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production
Export done in WE8ISO8859P1 character set and AL16UTF16 NCHAR character set

About to export specified users ...
. exporting pre-schema procedural objects and actions
. exporting foreign function library names for user OPS$TKYTE
. exporting PUBLIC type synonyms
. exporting private type synonyms
. exporting object type definitions for user OPS$TKYTE
About to export OPS$TKYTE's objects ...
. exporting database links
. exporting sequence numbers
. exporting cluster definitions
. about to export OPS$TKYTE's tables via Conventional Path ...
. . exporting table                              T          0 rows exported
. exporting synonyms
. exporting views
. exporting stored procedures
. exporting operators
. exporting referential integrity constraints
. exporting triggers
. exporting indextypes
. exporting bitmap, functional and extensible indexes
. exporting posttables actions
. exporting materialized views
. exporting snapshot logs
. exporting job queues
. exporting refresh groups and children
. exporting dimensions
. exporting post-schema procedural objects and actions
. exporting statistics
Export terminated successfully without warnings.

Review &
      Followup

      Rating:
      5
EXP  July 22, 2002
Reviewer:  Chunhai
      He  from USA
I did not list in the EXP. However, it listed in IMP
Thanks!

July 22, 2002 PN -- Thanks for the question regarding "High
      Parsing", version 9.0.1

      You Asked
Hi Tom,
Thanks for all previous help.
Our database is in state of High parsing to execute ,please let us know where to
look and dos to reduce it.
Regards
PN

      and we said...
look in v$sql where parse_calls is near executions

select parsing_schema_id, sql_text
  from v$sql where parse_calls >= executions
   and parsing_schema_id <> 0;

would be a good place to start.  that'll get the SQL you can control -- sql you
"own" more or less.
Review & Followup

      Rating: 4
greater than
      executions?  July 09, 2002
Reviewer:  Doug
       from CT, USA
Tom - this captures not statements that were parsed and not executed many times,
it captures statements that were parsed more than they were executed?  What
causes such thing? Can EJB do this?

          Followup:

A bad "program" can do it -- some "database independent" or generic apps will
parse a "select * from t" to do a DESCRIBE of a table or query once to figure
out what it will return and then later parse it again in order to execute it.
(just inefficient code, they should have parsed, describe, execute instead they
parse/describe/close in one module, parse/execute/close in another)

      Rating: 5
Doug, You
      Already Have the Answer  July 09, 2002
Reviewer:
      Mark A. Williams  from Indianapolis, IN USA
Doug:

The answer to your question here has already been answered by Tom in a previous
question that appears to be posted by you...

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4032595293314
Sorry if that is not you...

      Rating: 4
hIGH pARSING
      due to DEV2k reports(2.5)  July 10, 2002
Reviewer:
      A reader
Thanks Tom,
From the out of scripts its found that most of the parsing is due to querys
executing during runnung reports (Dev2k).
As Dev2k Reports parameters not allowed bind variable ?
Is any solution?
Is reports 6i helps in this regards.
Thanks
PN

          Followup:

D2K most certainly supports bind variables, it is very good at it actually.

Check out the docs or post a very specific example.  Binds are fully and totally
supported -- although you can certainly choose to not use them as well.

      Rating: 4
hIGH pARSING
      due to DEV2k reports(2.5)  July 10, 2002
Reviewer:
      PN  from TH
Thanks Tom,
From the out of scripts its found that most of the parsing is due to querys
executing during runnung reports (Dev2k).
As Dev2k Reports parameters not allowed bind variable ?
Is any solution?
Is reports 6i helps in this regards.
Thanks
PN

      Rating: 5
Do insert from
      a sequence always parse?  July 11, 2002
Reviewer:
      A Reader  from USA
Hi Tom,
1.We have an insert like the following:
insert into table1  (Id,Desc,Type)
values  (SEQ1.nextval, :id, :Desc, :Type);
This statement parses as many times as it executes in the application program.

2. select *  from table2 where   Id = :Id
This query parses twice and executes once in the application pogram (total
number of parse_calls/executions=2).

The two are just examples, we have lot of queries like the above. Should and can
these be corrected? Should DBA or developers make any changes if they need to be
changed? The program is coded in Delphi.

Thank you very much!

          Followup:

1) no, your progammers are PARSING the statement more then once.  They have a
"bug" they need to fix.

INSERT INTO T
VALUES
 ( T_SEQ.NEXTVAL,:b1  )

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.01          0          0          0           0
Execute    100      0.03       0.00          0          1        114         100
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      101      0.03       0.01          0          1        114         100

that's proof of that... 1 parse, 100 executes, uses a sequence.

2) you are using some "generic" layer that was improperly implemented.  See my
very first followup/comment here -- I say that.

should they be corrected?  Let me ask you this - do you have a need to scale
higher, do you just want have efficient code or brute force, not efficient code.

I would have it fixed, yes (in fact -- i wrote a book that says "fix it, fix it,
fix it")

      Rating: 4
more than one
      sql in Sqlarea  July 22, 2002
Reviewer:  PN

Hi Tom,
When a check Sqlarea I found more than one similar sql at any time like
below,though sql executing through one application only D2k report 2.5.I review
the code in application but I found nothing like bug or so?Please let us know
how to avoide it duplication.

PARSING_SCHEMA_ID OPTIMIZER_COST USERNAM    LAST_LOAD_TIME    SQL_TEXT
98    34    WI    2002-07-22/08:39:44    SELECT 'All P',NULL FROM DUAL UNION
select distinct(pc),P_NAME  from  pot where pc in('F','P','R','S')   /*select
distinct(S.pc),Pn  from SP s, PS WHERE S.PC = Ps.PC */

2.We have one main user that is the owner on Database objects.and all other
users are accessing the data through public sysnonym created on base obj.
When go through SQLAREA THERE ARE SO MANY SQL WITH DIFF. USER NAME OTHER THAN
MAIN AND MOST OF WITH MAIN USERS. WHY IS DIFF. WHY NOT ONLY MAIN USERS.

thanks
PN

          Followup:

Not following you on #1, you don't show me any dups so I cannot really comment
on why.

#2 -- well, they log in as themselves right?

      Rating: 5
similar sql in
      sqlarea   July 22, 2002
Reviewer:  Ram

Hi Tom
Again a similar question,
We are using Dev2K report 2.5 for reporting purposes on Oracle 9.0.1.1.1
standard edition.
We are run time paramater form on all reports and have lov's for usrs to select
data to run report like;
1.SELECT 'All Plants',NULL FROM DUAL UNION distinct(plant_code),PLANT_NAME  from
 plants where plant_code in('F','P','R','S');

we are using 'All Plants' and UNION to list All Plants and
F,P,R,S  in run time parameters list to facilitate users.

Now in Data model part we are using DECODE to process above list as:-

WHERE
PLANT_CODE LIKE DECODE(:PLNT_CODE,'All Plants','%',:PLNT_CODE)

1.Now after running this report and then see sqlarea it shows five identical sql
stmt instedd of one.? why ?

we are using above methods in most of our reports and it will lead to high
optimer cost when base table is of very big (10000s or 100000s records due to
UNION with dual
)
if we fix the run time value then in case of new addition we have to change the
code again.

2.Please advice some methods to deal with type of query to facilitate high
performance and low cost.

Rgds

          Followup:

without actually seeing the data from v$sql -- one cannot comment.  My crystal
ball is in the shop this week.

July 22, 2002 belal -- Thanks for the question regarding "how
      to send file with any type to directory on the server ", version 817

      You Asked
hello Mr tom
I'm very glad to write to you

I've two questions :
1)I use local PC and i need to write a  pl/sql code that to move or copy a file
from my local pc to aspecified directory on server(c:\temp\) that defined by the
followin Statment:
create or replace directory my_files as 'c:\temp\';
if it possiple how can i do it?
-------------
2)i try to send mail with attachment using pl/sql with out java or other tools
so what possiple mime_type of file that i can send it as attachment

Thank you Mr Tom and really I'm very happy that i could write to you
bye

      and we said...
Not easily with 817 (with 9iR2 and updates to the UTL_FILE package -- you can).

Suggest however that you not use Oracle as a "ftp" server in this case.  Use the
OS networking facilities provided to "mount" c:\temp\ and just copy the file.
Much easier.

You can send an attachment of any type in the world you want to.  It is just an
attachment.  Search for

javamail

on this site for examples.
Review & Followup

      Rating: 1
I need not to
      use Oracle as a "ftp" server   July 21, 2002

Reviewer:  belal  from Israel
hello Mr tom
the reason of asking first question is that
i need to send mail with attachment and i success just using pl/sql code with
out using any java code but the problem is that when i try to send attachment i
need first to put  the attached file in the created directory in the database
serve....so i need the user to select the file from his local pc and internaly
by the oracle the file will saved in database directory and then the file will
be sent.....I need not to use Oracle as a "ftp" server ........thank u sir :o)

          Followup:

No you don't (need to put the file on the server)

You can send it directly.  See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1739411218448
for example (or even easier, use javamail)

In any case, you can use the UTL_FILE_DIR, set it to c:\temp\, and then proceed
to use UTL_FILE to write to a file on the server using your stored procedure.
UTL_FILE only supports TEXT files (eg: a .doc or .ppt file is binary and WON'T
work).  If you need support for binary files, you'll be using either a C
external procedure OR a java stored procedure (or you'll get 9iR2)

      Rating: 3
What is new
      with regards Binary File in R2  July 22, 2002

Reviewer:  A reader
Hi,

<Quote>
If you need support for binary files, you'll be using either a C
external procedure OR a java stored procedure (or you'll get 9iR2)
</Quote>

Can u pls eloborate on the above. What is new in R2 that allows me to read
Binary Files ??

Regards,
Ganesh R

          Followup:

In 9iR2 utl_file supports reading and writing binary files from PLSQL (there is
a get and put raw API)

as well as lots of other new functions in UTL_FILE.  goto otn.oracle.com and
read the supplied packages guide for 9iR2 for details

July 22, 2002 Abu
      Tarek -- Thanks for the question regarding "When to create different
      schema for the users?", version 8.1.7

      You Asked
Dear Tom,

When do you create schemas for users like customers or employees?

My company is a small one. There are only 30 employees. I have developed a
e-timesheet for our use. I have used two schemas one (named mis) to hold the
tables and other (named Timesheet) as the gateway to those tables through
synonyms and procedures. The employees are authenticated by a password table in
the mis schema through a procedure. One of my friend was telling me to create 30
schemas for the employees only with create session and some other privileges.

If we follow his instruction what would happen if our company gets 5000
employees?

Again think about banking system. Do we need to keep different schemas of
different clients here?

Actually I want to know when we should use different schema method and when the
password table.

Have a nice time,
Tarek

      and we said...
Personally, I like to let the database do what databases can do -- like
authenticate users, provide security, do the auditing and so on.

Using this common username for everyone and replicating the functionality of the
database as you are doing is something I've never been very fond of.  How do you
audit what people are doing?  How do you give different sets of privs to
different users (without writing a TON of code)?  And so on.

Me, I have this many schemas created in my database:

ops$tkyte@ORA8I.WORLD> select count(*) from all_users;

  COUNT(*)
----------
     48706

and let the database do all of the work for me (authentication, authorization,
auditing, access control).

Review &
      Followup

      Rating: 4
Good sanity
      check  July 21, 2002
Reviewer:  Mike
       from St. Louis, MO
Tom: it's reassuring to see your 48K+ user count as I've worried that thousands
of user accounts might not be appropriate but you dispelled that worry.

      Rating:
      4
  July 21, 2002
Reviewer:
      A reader
Dear Tom,

Thanks a lot. I'll follow your way in future.

My users can't select the data of other users. And can't perform DML on others
activity records. They can't connect to the database even with sql*plus. Only my
application allows them to connect to the database.

Take care,
Tarek

          Followup:

And how pray tell did you accomplish that in version 817?

      Rating: 3
Very high
      number of database user  July 22, 2002
Reviewer:
      Abhishek Sinha  from India/UK
If i have an application with oracle database and say there are 1 lakhs of
clients (say in banking system). Then would u still suggest to go for that many
Oracle users??

          Followup:

As long as a lakh is less then 2 billion, sure.

It is just data in a database

      Rating:
      4
  July 22, 2002
Reviewer:
      A reader
Lakh is the Indian term for 100,000. And Crore stands for 100 lakhs (10
million).

Bill Clinton learned these terms when he visited India.

July 22, 2002 Mark -- Thanks for the question regarding
      "binding "where in :" as an in-list", version

      You Asked
Hi Tom,
I am using Pro*C and I need to send an array of strings
in a "where <value> in :<value>" is this possible?
And if not what should I do?

      and we said...
In Oracle7, this is rather difficult.  You cannot bind in a
SINGLE value and expect it to be treated as MANY values (think
about how hard it would be to find something in an IN LIST that
had a "comma" in it for example).

In Oracle8.0 and up, this is easy with object types and nested
tables.  Just to see what that will look like, I'll show that -
then I'll show the v7 implementation.

ops$tkyte@8i> create or replace type myTableType as table of
number;
  2  /
Type created.

ops$tkyte@8i> create or replace function in_list( p_string in
varchar2 )
return myTableType
  2  as
  3      l_data             myTableType := myTableType();
  4          l_string       long default p_string || ',';
  5          l_n            number;
  6  begin
  7
  8          loop
  9                  exit when l_string is null;
 10                  l_data.extend;
 11                  l_n := instr( l_string, ',' );
 12                  l_data( l_data.count ) :=
                          substr( l_string, 1, l_n-1 );
 13                  l_string := substr( l_string, l_n+1 );
 14          end loop;
 15          return l_data;
 16  end;
 17  /

Function created.

So, in Oracle8 and up we can "select * from PLSQL_FUNCTION" when
the function returns a SQL Table type as follows:

ops$tkyte@8i>
ops$tkyte@8i> select *
  2    from THE ( select cast( in_list('1,2,3,5,12') as
            mytableType ) from dual ) a
  3  /

COLUMN_VALUE
------------
           1
           2
           3
           5
          12

Which means we can do that in a subquery:

ops$tkyte@8i> select *
  2    from all_users
  3   where user_id in ( select *
  4                      from THE ( select cast( in_list
                                            ('1,2,3,5,12') as
                                   mytableType ) from dual ) a )
  5  /

USERNAME                          USER_ID CREATED
------------------------------ ---------- ---------
SYSTEM                                  5 20-APR-99

In v7, we can do something very similar (works in 8.0 and up as
well) with plsql tables.  Its a little more work but not too
much.  It would look like this:

ops$tkyte@8i> create or replace package my_pkg
  2  as
  3   function in_list( p_string in varchar2, p_idx in number )

                         return varchar2;
  4          pragma restrict_references( in_list, wnds, rnds,
                                                  wnps, rnps );
  5
  6          function get_list_count(p_string in varchar2)
                         return number;
  7          pragma restrict_references( get_list_count, wnds,
                                    rnds, wnps, rnps );
  8
  9          pragma restrict_references( my_pkg, wnds, rnds,
                                          wnps, rnps );
 10  end;
 11  /

Package created.

ops$tkyte@8i>
ops$tkyte@8i> create or replace package body my_pkg
  2  as
  3  function in_list( p_string in varchar2, p_idx in number )
     return varchar2
  4  is
  5          l_start number;
  6          l_stop  number;
  7  begin
  8          if ( p_idx = 1 ) then
  9            l_start := 1;
 10          else
 11            l_start := instr( p_string, ',', 1, p_idx-1 )+1;
 12          end if;
 13
 14          l_stop := instr( p_string ||',', ',', 1, p_idx );
 15
 16          return ltrim( rtrim( substr( p_string, l_start,
                                          l_stop-l_start ) ) );
 17  end;
 18
 19  function get_list_count( p_string in varchar2 )
     return number
 20  is
 21          l_cnt number default 0;
 22  begin
 23     for i in 1 .. 1000 loop
 24         exit when nvl( instr( p_string, ',', 1, i ), 0 ) =
0;
 25         l_cnt := i+1;
 26     end loop;
 27
 28     return l_cnt;
 29  end;
 30
 31  end;
 32  /

Package body created.

So, now I can code a query like:

ops$tkyte@8i> variable bindVar varchar2(255)
ops$tkyte@8i>
ops$tkyte@8i> exec :bindVar := '1, 3, 44, 1000'

PL/SQL procedure successfully completed.

ops$tkyte@8i>
ops$tkyte@8i> select my_pkg.in_list( :bindVar, rownum )
  2    from all_objects
  3   where rownum <= ( select my_pkg.get_list_count( :bindVar )
from dual )
  4  /

MY_PKG.IN_LIST(:BINDVAR,ROWNUM)
-----------------------------------------------------------------
-----------
------------------------
1
3
44
1000

All we need for that to work is a table with MORE rows then we
have IN LIST
items -- all_objects is generally a good candidate.  It works by
using
rownum as an "index" into the plsql table.

So, now we can simply:

ops$tkyte@8i> select *
  2    from all_users
  3   where user_id in ( select my_pkg.in_list( :bindVar, rownum
)
  4                       from all_objects
  5                      where rownum <= ( select
y_pkg.get_list_count( :bindVar ) from dual )
  6                                    )
  7  /

USERNAME                          USER_ID CREATED
------------------------------ ---------- ---------
WEB$AZEILMAN                         1000 24-JUN-99

Review &
      Followup

      Rating: 5
Parameter
      receive Multiple values  March 16, 2001
Reviewer:
      Chandresh  from Pittsburgh, PA USA
Tom,

The solution provided is excellent. The only think  i was thinking is to use
DBMS_SQL. But the solution you provided help by using simple SQL and PL/SQL.

Highly appreciated for your help.

Thanks
Chandresh

      Rating:
      4
  July 19, 2002
Reviewer:
      A reader
Tom,

I'm using Oracle v734. I  generate a large number of values
based on a cursor and set the block property in forms dynamically. So for each
execution it will generate a different set of values. While using your function
I will have to set set_block_property('block name',
default_where, 'key in
 ( select my_pkg.in_list( :bindVar, rownum )
                         from all_objects
                        where rownum <= ( select
y_pkg.get_list_count( :bindVar ) from dual )
                                      ))

The number elements in the in list could be quite large( sometimes more than the
count of all_objects ).

Earlier I thought just dynamically creating the list
and assigning to the block. But since this would increase the parse cost I
abandoned that idea.

Please let me know whether this is a good approach and any other alternative.

          Followup:

If you are generating an inlist that exceeds the number of rows in all_objects
-- well, I think something is wrong right there.

Anyway, if you exceed the number of rows in all objects -- that'll not work
(you'll not get some of them in the subquery).

also, in 7.3 you'll be limited to a string of 2000 bytes in the sql query
(:bindVar will be 2k at most).  If you are exceeding the number of rows in all
objects, you've blown that limit for sure.

In any case -- with such a huge set -- can't you just plug the QUERY that gets
those values into the subquery itself????  I cannot imagine a user would be
picking thousands of elements from a list.

      Rating:
      4
  July 19, 2002
Reviewer:
      A reader
Tom,

I am forgoing the option to have the elemenents in the list
greater than the count of all_objects if so I have 2 options since I'm operating
in 734.

1. Create a temporary table and populate it on
forms runtime.Plug in the temporary table as sub query.
and each time I execute delete the table.

2. Use package option from ur site.

Please tell how you rate both the options.

          Followup:

what about 3) use a subquery against data in an existing table.

well, in any case -- I would do this.  benchmark it and see which performs best
for you.  They both have their ups and downs.

      Rating:
      3
  July 22, 2002
Reviewer:
      A reader
You mentioned there are ups and downs in using the following methods

1. Temporary Table
   1.Delete before insertion.
   2.Insert records

2. Use a Package to Return the subquery IN values

Could you give some of the ups and downs in both approach. I greatly determince
which I would want to follow.

Thanks

          Followup:

in order to populate the temporary table -- you need to run yet more sql.
You'll be in a transaction.
deleting from a temporary table will generate gobs of undo and hence redo.

using plsql is cpu intensive
it'll take memory
you need to write more code

things like that.  again -- benchmark it in your environment to see which works
best (and remember that neither is 100% superior or inferior to the other)

      Rating: 4
Replacing
      in-list with nested table  January 30, 2003

Reviewer:  Mark D. Fortenbery  from Huntsville, AL

We had a similar situation and we created a nested table to use instead of an
in-list.

Since the in-list varied in length, this significantly reduced hard parses.

But some of our queries were joins over three or more tables and in these cases
the execution plan often had Full Table Scans over 1 or more of these tables.

We tried using an in-clause as a subselect from the nested table as well as
joining on an in-line view over the nested table.

We have computed statistics and set optimizer_index_caching to 90 and
optimizer_index_cost_adj to 30.

We replaced the nested table with a temp table and this has improved things
quite a bit.

July 22, 2002 Nagaraju -- Thanks for the question regarding
      "Table Sizing", version 8.1.6

      You Asked
Tom,

 Greetings..

 in our new application, we designed the database and created the datamodel. we
even sized the tables came with storage parameters for each table.

but our DBA people tell that they will give us three tables sapces

1.data1 with uniform extent size 160K
2.data2 with uniform extent size 5MB
3.data3 with uniform extent size 160MB

now they are telling us to

1.create tables which are going to be less than 5MB in data1 with no storage
parametes for the table

2.create tables which are going to be less than 160MB in data2 with no storage
parametes for the table

3.create tables which are going to be more than 160MB in data1 with no storage
parametes for the table

same case with the indexes.

it doesn't look reasonable to me.

because for a table with an estimated size of 120MB we place it in data2. as we
create there it takes tablespace's storage parameters and it has to give 5MB 24
times to make it 120 as the table grows in size which i think is expensive.

and for tables with  say 2k size(even less) i have to keep in data1 with 160k,
where the rest of the space gets useless.

DBA claim that Numerous test by Oracle Corporation and others proved that this
design provides ultimate performance and prevents fragmentation.

first of all

my questions are

1. are they right doing so? or am i wrong
2. if they are not right i have to prove them with whatever they claim is
wrong(either thru documentation or thru queries which should show a solid proof
to make them convinced.). this we create good application.

as our application so complex we don't want any performance hitches.

please help.

thanks

      and we said...
It looks to me that they've been reading this site alot.

It matters not that a table has 1, 100, or even 1000 extents.  It is not only
reasonable -- it is absolutely AOK.

How many less then 2k tables do you have?  Given that the smallest table is a
block, and your block size is probably 8k, you might ask them for one "tiny"
tablespace but beyond that -- they are AOK.

Review &
      Followup

      Rating: 5
junk
      review  July 20, 2002
Reviewer:  anindya
       from INDIA
Tom,
    I think you should have a checking for junk reviews in your site.
anindya

          Followup:

I do, I try to read them all and I delete ones like the one to which you refer
(which no longer exists on this page ;)

      Rating: 3
i don't
      understand why  July 20, 2002
Reviewer:
      nagaraju
Tom,

thanks for the response.
you didn't tell me why are they ok??

for the tables which has to be of 120MB and if you place them in data2
tablespace, don't you think that allocating each time an extent of 5MB and it
happens 24 times, is a performance problem. i read in some oracle books that we
should put the estimated table size that is 120MB as its intial extent.

          Followup:

I did tell you EXACTLY why it is AOK:

It matters not that a table has 1, 100, or even 1000 extents.  It is not only
reasonable -- it is absolutely AOK.

That is why a table of 120m in a tablespace with 5m extents is OK -- because the
mythology that a table should optimally be in a single extent is just that --
mythology.

Having many extents is OK, it is fine, it is just dandy.

      Rating: 5
More in-depth
      discussion  July 21, 2002
Reviewer:
      A reader
You can find a more in-depth discussion of this very topic right here:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:730289259844

      Rating: 3
Sorry, but what
      is AOK ?  July 22, 2002
Reviewer:
      A reader
Thanks.

          Followup:

Sorry, American'ism I guess.

AOK means "perfectly all right, very good, not a problem"

      Rating:
      3
  July 22, 2002
Reviewer:  nagaraju

tom,

 i totally agree with you in saying that who cares on the number of extents.

but my doubt is:

1.isn't an extent ALLOCATION expensive? because i have more than 5 tables
estimated to 120MB. for each this extent allocation happens 24 times is that not
a problem.

2.if we don't have to give storage parameters on the table
  then what is the use of object level storage parameters.

i am not a DBA, i am a developer. so, please bear with me and explain.

thanks

          Followup:

1) NO, not with LMT's.  And even with DMT's -- 24 extent allocations in the
grand scheme of things is not even a blip on the radar screen.

2) I don't know -- I hate em, I don't use em.  Guess you could say "they are
there for backwards compatibility".

      Rating: 4
pctfree is a
      useful one  July 22, 2002
Reviewer:  andrew
       from ca, usa
I agree with using default storage parameters at the tablespace level, but
pctfree is one I've found useful at the table level to overcome chained rows
(set pctfree higher) or space wastage on big static tables (set pctfree lower).

          Followup:

Well -- two points:

I was thinking along the line of extent sizing -- object sizing

And, pctfree is not really a storage parameter.  Oh sure, it affects the
"storage" but it is not really a storage parameter.  It is a "physical
attribute" -- just like the storage clause it.

July 22, 2002 Nag
      -- Thanks for the question regarding "direct path load and sql loader",
      version 8.1.7

      You Asked
I.At what point we should start considering a direct path load , Iam expecting
the answer to be a figure, or is it possible to answer this question so
specifically (i.e. if the number of rows to be inserted is 10 million then we
should consider using direct path load  etc).

II.What is teh difference between a direct path load and sql loader.

Thank you

      and we said...
I.  Nothing is a number.  There is no number.  It depends on your requirements
more then anything.

If you have 10 million rows to load but need to apply a sql function to the
input data to convert it -- you won't be using a direct path load with sqlldr.

If you have 10 million rows to load but you need to allow users to perform DML
(writes) on the data as you are loading.  You won't be using direct path load.

If you have 1 million rows to load and you need to do it fast, and you meet the
restrictions for direct path loads -- go for it (to see the restrictions, read
the server utilities guide, there is a whole chapter on this topic).

Consider it anytime you have a large volume of data to load in a short period of
time.

So, no number from me.

II.  A direct path load is a FEATURE of SQLLDR.  SQLLDR is a tool that can do a
direct path load.  Its hard to "compare" them as one is a feature or option of
the other.  See the server utilities guide for info on exactly what takes place
during a direct path load.

Review &
      Followup

      Rating:
      4
  October 12, 2001
Reviewer:
      A reader
When I said direct path load , I meant the insert with the
/*+ append */ hint.

In some of your anserws you have referred to  them as direct path load( or was
it direct path insert).

          Followup:

Well, they are very similar EXCEPT that the direct path insert can use SQL all
of the way (making it much more flexible).

Both write blocks directly.  Both bypass UNDO for the table.  Both can bypass
REDO as well.

In 9i, with the addition of external tables ( the ability to query a FLAT FILE
with SQL) -- SQLLDR might be "moot".  Using the external table and the insert
/*+ append */  I can basically do a direct path load from file to database
without running a command outside of the database.

      Rating: 4
direct path
      load and sql loader  July 22, 2002
Reviewer:
      $ameer  from NJ ,USA
Tom

I have one confusion.

When you say SQLLDR in Direct path load 'can bypass REDO',
does this mean REDO is bypassed only if the table being imported has nologging
option??
Or the direct path load always bypass REDO??

Also how to check if a particular statement is using REDO / UNDO ?

Thanks in Advance

          Followup:

Depends on the mode of the database even -- archive log vs no archive log mode.

In no archive log, it'll bypass redo.
In archive log, it'll generate redo unless it is done unrecoverable (nologgin)

AUTOTRACE is very useful to see the redo generated by a statement.

      Rating:
      4
  May 08, 2003
Reviewer:
      A reader

July 22, 2002 Anton -- Thanks for the question regarding "
      Application schema", version all

      You Asked
Hi Tom.

Thanks for taking the time to answer this one.  I'm thinking aloud here.

Say I'm building a new app schema in Oracle.  I would create:

- the app schema to hold tables, indexes, packages/procedures to insert, update,
delete app tables
- individual user accounts
- roles that granted select on app tables and execute on packages/procedures
- grant roles to users
- set current_schema to app schema in application (or database login trigger)

My question is - should the individual user accounts be Oracle password
protected or OS authenticated ?
If someone gains unauthorised access at the OS level, they can use 'ps -ef' to
see any oracle passwords or
use 'sqlplus /' if OS authentication is used.  In either case they get in to the
Oracle database.
I could use 'hidepass' to hide the oracle password but is there any other way
round this ?

Also, hypothetically speaking of course, say we have an existing application
where everyone (users and app) log
in to Oracle using the application user/schema.  Suddenly we have a few
developers that decide
to do a bit of DBA work (eg. rebuild indexes unrecoverable and invalidate the
standby database).  The application
user password is known to the developers and hardcoded in backend scripts
everywhere (and may
even be hardcoded in the front end application).  What would be the best way to
correct this
situation ?  I assume (and not in any order):

- change the application user password
- create individual user accounts, roles etc.
- change back end scripts to use OS authenticated login to Oracle
- remove app user passwords from application and use set current_schema to app
schema
- audit application schema (in case developer manages to get access to app user
password)

Your thoughts ?  Thanks Tom.

      and we said...
Well, most people don't use sqlplus as an application interface but -- what I do
is this myself:

set the os_authent_prefix to say OPS$
create user ops$tkyte identified by password;

Now, on the local machine I can "sqlplus /" and over the network from my pc or
whatnot, I can "sqlplus ops$tkyte/password" -- best of both.

for the second one, you have this "public account" (eg: no security).  You want
to prevent developers from doing DBA'ish type things.  Well, you could use an
ALTER / CREATE / DROP trigger to prevent the app account from doing these things
-- making it so that ONLY a DBA in another account can do them.  I've seen
people do that just to audit, you can use this technique to prevent (note: 9ir2
lets the DBA set "force logging" to prevent just such a case as you point out)

Review &
      Followup

      Rating: 5
What
      if...  July 22, 2002
Reviewer:  Anton
       from Nw Zealand
Thanks for the reply Tom.

Say if you use OPS$ and someone hacks in to your OS account, then they can just
'sqlplus /' to get in to the database (once in they might not be able to do much
but they are in).  Given this possible scenario, would it not be better to use a
utility like 'hidepass.c' to obscure the Oracle password (say from a ps listing
in Unix) ?

          Followup:

If they hack into your OS account, you have much bigger problems don't you?

I use ops$ accounts on all of my database machines for all admin accounts -- to
avoid having passwords scattered about in a million scripts.

      Rating: 5
But what
      if...  July 22, 2002
Reviewer:  Anton
       from New Zealand
Hi Tom.

Yes, I agree.  But lets say John the data entry guy leaves his OS password lying
around and someone sees it.  Now this someone tells someone else who tells
someone else.....and Mr Hacker gets wind of this.  Eventually Mr hacker gets in
to the database server using John's OS password and does a simple 'sqlplus /' to
get in to the database.  Now say John's database account password was not using
OS authentication (ie. the database password was different from the OS password)
and was hidden from view using 'hidepass'.  This would provide an extra level of
security, possibly stalling Mr Hacker in this case.  So in this case, would it
not be better to use separate OS and database passwords (ie. not use OPS$) and
use 'hidepass' to stop passwords being viewed ?

          Followup:

lets start with

what if john the data entry guy leaves his DB password lying around.  Hacker
uses sqlnet to log in from the comfort of his own machine.

if John's password gets compromised -- thats it.

I'm not going to agree here -- like I said -- if a single PW is compromised,
they all probably are and even so the alternative is John the data entry guy
putting his database password into a script or shortcut to make it easier to log
in.  then we are right back into the realm of "it matters not".  If John is bad
enough to leave his OS PW out and about, he is also going to code his PW into a
script or shortcut.

Like I said -- you have bigger problems at that point. You have a hacker in your
system!

July 22, 2002 --
      Thanks for the question regarding "NVL function with select clause",
      version 8.0.5

      You Asked
Hi Tom,

I have the following scenario:

create table countries (country_code Number(3),
country_name  Varchar2(40),
rnk number(1));

create table states (state_code  Number(4),
state_name  varchar2(40),
country_code  number(3),
rnk number(1));

create table places (place_code  number(5),
place_name  varchar2(40),
state_code  number(4),
country_code  number(3),
rnk  Number(1));

insert into countries values (100, 'America', 1);
insert into countries values (101, 'Australia', 1);
insert into countries values (102, 'India', 2);

insert into states values (1001, 'Alabama', 100,null);
insert into states values (1002, 'Arkansas',100, 1);
insert into states values (1011, 'Tasmania', 101,1);
insert into states values (1021, 'Maharashtra',102,2);

insert into places values (10001, 'Mobil', 1001, 100, null);
insert into places values (10001, 'Huntsville', 1001, 100, 2);
insert into places values (10001, 'Bombay', 1021, 102, 2);
insert into places values (10001, 'Pune', 1021, 102, null);

I wrote the following query to get the desired output in my 816 database and it
works fine.
select place_name, nvl(rnk, (select nvl(rnk, (select rnk from countries
where places.country_code = countries.country_code)) from states
where states.state_code=places.state_code))  rank
from places;

Place_name Rank
Mobil  1
Huntsville 2
Bombay  2
Pune  2

I tried using the above SQL statement in 805 version but it comes out with error

ORA-00936: missing expression

Please advise if the above is possible in a single select statement in 805.
Thankx,
SB

      and we said...
Well, I used your example (thanks for the create tables and inserts!!!!! Many
thanks!!!  that makes it soooo easy) but modified the rnk so that places was in
single digits, states were in 10's and countries were 100s (so you can tell
whence the rank came from)

Based on that -- i used an outer join to get the same answer in 80 as you can in
8i with the new "select (select ...)" (new to 8i)

ops$tkyte@ORA817DEV.US.ORACLE.COM> select place_name, nvl(rnk, (select nvl(rnk,
(select rnk from countries
  2  where places.country_code = countries.country_code)) from states
  3  where states.state_code=places.state_code))  rank
  4  from places;

PLACE_NAME                                     RANK
---------------------------------------- ----------
Mobil                                           100
Huntsville                                        2
Bombay                                            2
Pune                                             20

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.place_name,
  2          nvl(a.rnk, nvl( b.rnk, c.rnk ) ) rnk
  3    from places a,
  4          states b,
  5          countries c
  6   where a.state_code = b.state_code(+)
  7      and a.country_code = c.country_code(+)
  8  /

PLACE_NAME                                      RNK
---------------------------------------- ----------
Mobil                                           100
Huntsville                                        2
Bombay                                            2
Pune                                             20

ops$tkyte@ORA817DEV.US.ORACLE.COM>

So, you can use the outer join in 8.0
Review &
      Followup

      Rating: 5
Thankx Tom
      !!!  July 22, 2002
Reviewer:  SB  from
      CA
Thankx for your prompt response, Tom.

July 22, 2002 Rob -- Thanks for the question regarding "bitmap
      indexes", version 8.1.7

      You Asked
Hi Tom:

I have a question related to the J. Lewis article reference on your homepage.

He says:
However, apart from keeping Oracle informed with this command, you also find
that the size of the index is very strongly affected by the clustering of the
data.

In my example, I have constructed the data to be as thinly scattered as possible
-- for example the town column rotates through the values 0 to 30. If I
restructure (effectively sort) the data so that ll the towns with code 0 are
together, followed by all the towns with code 1, the size of the index drops
from 40MB to a mere 7MB.

This dramatic variation in size is yet another reason for reviewing the claim
about "low cardinality." The potential benefit of a bitmap index varies with the
clustering of data (as does the potential benefit of a B*tree index, of course).
When you are considering bitmap indexes, do not be put off by a column which has
a "large" number of different values. If every value appears a "large" number of
times, and if the rows for each value are reasonably clustered, then a bitmap
index may be highly appropriate. In a table with 100M rows, a column with 10,000
different values could easily turn out to be a perfect candidate for a bitmap
index

My question:
I am unclear about the clustering of data. Does this mean that the data resides
on disk in adjacent locations. I know that you have commented on the fact that
data that might at first glance appear to be colocated is actually on many
physical drives due to modern drive arrays, RAID, volume managers etc.

I have seen articles on the benefits of presorting data before loading it. The
claimed benefit is that this is much more efficient if you have queries that are
performing index range scans on the data. I have never seen any article on your
site where you comment on this. What do you think?

Thanks as always for your time and great feedback.

Rob

      and we said...
here he is talking about the physical clustering of like values together.  So
that row after row has the same value -- physically -- on the blocks.  (the
blocks themselves may be physically far away from eachother on disk tho)

The way he loaded up his table -- he made row 1 have value 0, row 2 value 1, row
3 value2 and so on.  If he did it so that rows 1 ... N had the value 0, then
rows N+1 .. M had 1 and so on -- the data would be "clustered" or sorted by that
value and the resulting bitmap index would be much smaller.

Actually, if you search for

disorganized

on this site, you'll see some writeups on this.  I also cover it in my book on
the chapter on indexes.  It is really more about how many times you visit and
revisit a block then anything else.

In the case of the bitmap here, it is about what rows come next in a full scan
(regardless of the true physical storage of the blocks on the disk)

July 22, 2002 -- Thanks for the question regarding "Querying
      instance names", version 8.1.7

      You Asked
Hi,

Is it possible to QUERY all instance names configured in TNSNAMES.ora ??

Thank you for any input.

JC

      and we said...
No, that information isn't in the database (in fact, a tnsnames is typically on
the client -- far away from the database)

You would have to read and parse that file (should be easy, ignore lines with #
in them and only keep lines with something in column 1 -- those are the tns
connect string names)
July 22,
      2002 Luciano -- Thanks for the question
      regarding "Oracle8i connecting a IBM DB2 database", version oracle8i

      You Asked
Hi,

Sorry if I ask the same questions that someone did but I couldn´t find the
answer looking on sites that I selected.

I´m would like to connect to a non ORACLE database (IBM DB2) but I really don´t
know how can I do this (this is new for me) using a pl/sql application.

Can you help me giving me the way to do this creating a link, a procedure, or
whatever I have to do.

Thanks if you answer my questions

Regards

Luciano

      and we said...
There are a couple of answers

a)
http://www.oracle.com/gateways/
is the most complete.  distributed query, distributed transactions --
100% functionality.  Lets you treat DB2 as if it were an Oracle instance for all
intents and purposes.

b) generic connectivity.  If you have ODBC on the SERVER (oracle server) and can
use that to connect to DB2, you can use generic connectivity.  Less functional
then a)

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4406709207206
c) lastly, you can get their type4 (thin) jdbc (all java) drivers and load them
into Oracle.  Then, you can write a java stored procedure in Oracle that
accesses DB2 over jdbc.


Subject | Article | Last Updated (East coast time)
user login | article only | Jul 23, 2002 09:50 am
Script | article only | Jul 23, 2002 10:05 am
Transactions vs RBS / Keep Pool / | article only | Jul 23, 2002 10:25 am
Using Index for ordering the records | article only | Jul 23, 2002 10:32 am
Changing the column name of a table | article only | Jul 23, 2002 10:33 am
Trace SQL statements | article only | Jul 23, 2002 10:50 am
java mail | article only | Jul 24, 2002 10:29 pm
Understand the concepts but to define it to others | article only | Jul 24, 2002 10:53 pm
Copying Tables with Long columns | article only | Jul 24, 2002 10:55 pm
BY VALUES clause in ALTER USER | article only | Jul 24, 2002 11:02 pm
insertable and updateable view | article only | Jul 25, 2002 06:57 am
PL/SQL static SQL versus (pure) SQL | article only | Jul 25, 2002 06:58 am
Using connection pool in Java stored procedure | article only | Jul 25, 2002 10:15 pm
Sql plus Help -- if / then / else in SQL | article only | Jul 25, 2002 10:29 pm
Trusted connection to Oracle | article only | Jul 26, 2002 06:45 am
consistent reads | article only | Jul 26, 2002 06:47 am
i want to exit the user if the user is not e.g(SCOTT) | article only | Jul 28, 2002 02:45 pm
Parsing a varchar for use in a comparison test. | article only | Jul 28, 2002 02:58 pm
Select Rowids from different tables and then delete the rows where they match. | article only | Jul 29, 2002 07:22 am
What does the autotrace output mean | article only | Jul 29, 2002 07:22 am
Postgresql | article only | Jul 29, 2002 07:30 am
row(s) 1 - 21 of 21
July 23, 2002 -- Thanks for the question regarding "user login", version 8.1.x
You Asked
WHO i can FOUND THE LOGIN NAME OF PORTAL USER conected?

Can i found a book to portal developer?
and we said...
I asked our local resident Portal guru Kim Better and she says:

There are a couple of ways to answer your question:

If you're using PL/SQL, use WWCTX_API.GET_USER to get the username, and
WWCTX_API.GET_USERID to get the userid. These are located in the Portal schema.
In code, for example, an HTML portlet in Portal:

<HTML>
<HEAD><TITLE>Welcome User</TITLE></HEAD>
<BODY>
<ORACLE>
   declare
      theUser varchar2(30) := portal.WWCTX_API.get_user;
   begin
      if theUser = 'PUBLIC' then
         htp.p('<H1>Welcome! Please login.</H1>');
      else
         htp.p('<H1>Welcome ' || theUser || '</H1>');
      end if;
   exception
      when portal.WWCTX_API.NO_SESSION_EXCEPTION then
         htp.p('Error: Session not initiated.');
      when others then null;
   end;
</ORACLE>
</BODY>
</HTML>

If you are coding using the java and the JPDK, use the getName() API in
ProviderUser Class. In code (maybe a servlet or jsp), for example:

PortletRenderRequest portletRequest =
(PortletRenderRequest)request.getAttribute(HttpProvider.PORTLET_RENDER_REQUEST);
ProviderUser pUser = portletRequest.getUser();
String user_name = pUser.getName();
out.println("<BR><B><center>Welcome " + user_name + "<BR></CENTER></B><BR>");

As far as a suggestion for a book ---- I am not really impressed with the
current selection of Portal development books on the market. They are geared
towards the end user on how to use the front end GUI's and Wizards, not really
the code development that can be done with the product. The best place for
information on how to develop with portal is located at
http://portalcenter.oracle.com/
This site contains
a wealth of information related to Oracle Portal. The site contains
documentation, example code, as well as discussion forums that focus on specific
Portal topics. The discussions are monitored by the Oracle Portal Development
Group, so you're sure to get a timely response to any questions you might have.

Hope this helps!

--kim
July 23, 2002 atul -- Thanks for the question regarding "Script", version 8.0.4
You Asked
Hello sir,

I have written a script for daily hot backup..

Could you kindly help me in that

Script goes like this..

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

for i in `cat /tmp/sc1.lis`
do
echo "set heading off;\nset pages 0;\nALTER TABLESPACE '$i' BEGIN BACKUP
;\nexit"|sqlplus -s /
echo "set heading off;\nset pages 0;\nselect file_name from dba_data_files where
tablespace_name ='$i' ;\nexit"|sqlplus -s / > /tmp/
w32.lis
for l in `cat /tmp/w32.lis`
do
cp $l /backup/.
done
echo "set heading off;\nset pages 0;\nALTER TABLESPACE '$i' END BACKUP
;\nexit"|sqlplus -s /
done
---------------------------------------------------------------------------------
------------------------------

Where
vi /tmp/sc1.lis (Name of the tablespaces to be backed up)

TABLESPACE1
TABLESPACE2
TABLESPACE3

Actually we don't have development server so i can't test it..
so plz tell me will this script work??
or what changes should i do in it..

Thanks.
atul
and we said...
I am *not* a compiler.

I can recommend that if you do not have a test place to test the restore, you
might just as well skip doing backups -- you are not serious about it and it'll
only give you a false sense of security.

Testing is the only way to ensure that things work.
Having sufficient resources to implement what you need is mandatory.

Recommend you use RMAN instead of a script of your own as well.  Your script
doesn't have any checks for errors (did that cp work -- or not, did that alter
tablespace work -- or not).  Tools like RMAN will help you there.
July 23, 2002 Prince -- Thanks for the question regarding "Transactions vs RBS / Keep Pool /", version 8.1.7.4
You Asked
tom,

I have the following questions.

Q1:
===
Transactions vs RBS

I have read somewhere, its good idea to have one RBS per 4 transactions (as
starting point).

Now, my v$resource_limit there were max 8 concurrent transactions. According to
the v$wait_stat there are 30 undo header wait. What would be the cause of this
wait?

SQL> select * from v$resource_limit ;

RESOURCE_NAME                  CURRENT_UTILIZATION MAX_UTILIZATION INITIAL_AL
LIMIT_VALU
------------------------------ ------------------- --------------- ----------
----------
transactions                                     2               8        386
    386
sort_segment_locks                               0               8  UNLIMITED
UNLIMITED
max_rollback_segments                           26              26         78
     78

SQL> select * from v$wait_stat ;

CLASS                   COUNT       TIME
------------------ ---------- ----------
system undo block           0          0
undo header                30          2

Q2:
===
Suppose I move few objects I believe were hot and moved it to Keep Pool. Now how
do I verify that, it has any impact on the performance?
(by using statspack or by querying v$ tables)

Q3:
===
why there is log file parallel write, when the redo log group only has one file
(the disk is mirrored)?

Thanks for all your responses.
and we said...
q1) contention for the rollback segments.  All 8 of those transactions could
have (worst case) been using the same segment.

30 waits for 2/100 of a second -- that is not even "noise", not worth losing any
sleep over.  30 of anything, not worth it.

q2) statspack would be where I look.  do your waits go down.

q3) even though the disk is mirrored, you should let us multiplex them.  mirrors
are ok, but you want to multiplex them with us as well (prevent logical errors,
some raid arrays will return warnings to us when a mirror goes down -- that'll
down the instance, and so on).

we only track the write one way.  we are assuming the vast majority will have
multi-plexed redo logs.

You should.
Review & Followup
Rating: 3
About Q3: July 22, 2002
Reviewer: Prince
I wasn't worried much on the wait on the first case as I knew it was not very
high, but wondering why they end up in the same RBS.

Regarding Q3:
I see comparitively higer number of waits for that particular event (using
statspack). Could you please explain why this  is the case, when there is no log
to write in parallel?.

I totally agree with you on the Oracle side mirroring. I am planning to do that
soon.

Thanks.

Followup:
writes to the log are called "log file parallel write" regardless of the number
of logs.  It is the name of the statistic.

July 23, 2002 Subhash -- Thanks for the question regarding "Using Index for ordering the records", version 8.0.6
You Asked
Hi Tom,

   I am having a table with structure given below.

   Tree_struct
   ============
   PARENTNM   VARCHAR2(10)
   CHILDNM    VARCHAR2(10)
   DESCR      VARCHAR2(60)
   SERLNO     NUMBER(4)

   Now i write my query like
   Select Lpad(' ',6*(level-1)||progdesc
   START WITH LEVEL =1
   connect by prior CHILDNM = PARENTNM

   The SERLNO field denotes the serial nos in each level.I am using an INDEX on
fields PARENTNM and SERLNO for getting it ordered by SERLNO.
   But my problem is , the index is getting failed and am not getting it ordered
sometimes. I have my Primary Key on PARENTNM and CHILDNM.What all conditions the
index can fail and what can be done for that?

Regards
Subhash
and we said...
There is quite simply NO WAY to order a connect by reliably prior to Oracle9i --
it is not doable.

You can sometimes get it to work with a HINT but you are not assured of sorted
data.  It is just something you cannot do.

In Oracle9i, there is a new "order siblings by" in order to sort a hierarchical
query.
Review & Followup
Rating: 3
order hierarchical query by using packaged function instead of index July 15, 2002
Reviewer: Barbara Boehmer from Norwalk, CA USA
A long time ago, back in 1999, Solomon Yacobson posted a packaged function on
the pl/sql pipelines forums that seems to be a reliable method of ordering
siblings within a branch of a tree structure prior to Oracle 9i, without relying
on an index, even when there is a primary key on other columns.  The only limit
that I am aware of is the length of the resultant branch column.  Here is the
packaged function, followed by a usage example:

-- packaged function by Solomon Yacobson
CREATE OR REPLACE
 PACKAGE Hierarchy
  IS
        TYPE BranchTableType IS TABLE OF VARCHAR2(4000)
          INDEX BY BINARY_INTEGER;
        BranchTable BranchTableType;
        FUNCTION Branch(vLevel          IN NUMBER,
                        vValue          IN VARCHAR2,
                        vDelimiter      IN VARCHAR2 DEFAULT CHR(0))
                        RETURN VARCHAR2;
        PRAGMA RESTRICT_REFERENCES(Branch,WNDS);
END Hierarchy;
/
CREATE OR REPLACE
 PACKAGE BODY Hierarchy
  IS
        ReturnValue VARCHAR2(4000);
  FUNCTION Branch(vLevel        IN NUMBER,
                  vValue        IN VARCHAR2,
                  vDelimiter    IN VARCHAR2 DEFAULT CHR(0))
                  RETURN VARCHAR2
   IS
   BEGIN
        BranchTable(vLevel) := vValue;
        ReturnValue := vValue;
        FOR I IN REVERSE 1..vLevel - 1 LOOP
          ReturnValue := BranchTable(I)|| vDelimiter || ReturnValue;
        END LOOP;
        RETURN ReturnValue;
  END Branch;
END Hierarchy;
/

SQL> -- test table and data:
SQL> create table tree_struct
  2    ( parentnm    varchar2(10)
  3    , childnm     varchar2(10)
  4    , serlno      number(4)
  5    , progdesc    varchar2(10)
  6    , primary key ( parentnm, childnm ) )
  7  /

Table created.

SQL> insert into tree_struct
  2  values( 'projecta', 'projectb', 1, 'progdesc1' )
  3  /

1 row created.

SQL> insert into tree_struct
  2  values( 'projectd', 'projectg', 3, 'progdesc3' )
  3  /

1 row created.

SQL> insert into tree_struct
  2  values( 'projectc', 'projectd', 5, 'progdesc5' )
  3  /

1 row created.

SQL> insert into tree_struct
  2  values( 'projectb', 'projecte', 6, 'progdesc6' )
  3  /

1 row created.

SQL> insert into tree_struct
  2  values( 'projectb', 'projectc', 7, 'progdesc7' )
  3  /

1 row created.

SQL> insert into tree_struct
  2  values( 'projectc', 'projectf', 4, 'progdesc4' )
  3  /

1 row created.

SQL> insert into tree_struct
  2  values( 'projectd', 'projecth', 2, 'progdesc2' )
  3  /

1 row created.

SQL> select * from tree_struct
  2  /

PARENTNM   CHILDNM        SERLNO PROGDESC

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

projecta   projectb            1 progdesc1

projectd   projectg            3 progdesc3

projectc   projectd            5 progdesc5

projectb   projecte            6 progdesc6

projectb   projectc            7 progdesc7

projectc   projectf            4 progdesc4

projectd   projecth            2 progdesc2

7 rows selected.

SQL>
SQL> -- query without use of packaged function
SQL> -- may not return data in desired order within each branch:
SQL> column    progdesc format a35
SQL> select    lpad( ' ', 6 * ( level - 1 ) ) || progdesc as progdesc,
  2          parentnm, childnm, serlno
  3  from    tree_struct
  4  start with parentnm = 'projecta'
  5  connect by prior childnm = parentnm
  6  /

PROGDESC                            PARENTNM   CHILDNM        SERLNO

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

progdesc1                           projecta   projectb            1

      progdesc7                     projectb   projectc            7

            progdesc5               projectc   projectd            5

                  progdesc3         projectd   projectg            3

                  progdesc2         projectd   projecth            2

            progdesc4               projectc   projectf            4

      progdesc6                     projectb   projecte            6

7 rows selected.

SQL>
SQL> -- query with use of packaged function
SQL> -- returns data in order desired within each branch:
SQL> column   progdesc format a35
SQL> select   progdesc, parentnm, childnm, serlno
  2  from     ( select       lpad( ' ', 6 * ( level - 1 ) ) || progdesc as
progdesc,
  3                 parentnm, childnm, serlno,
  4                 hierarchy.branch( level, serlno ) as branch
  5          from       tree_struct
  6          start with parentnm = 'projecta'
  7          connect by prior childnm = parentnm )
  8  order by branch
  9  /

PROGDESC                            PARENTNM   CHILDNM        SERLNO

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

progdesc1                           projecta   projectb            1

      progdesc6                     projectb   projecte            6

      progdesc7                     projectb   projectc            7

            progdesc4               projectc   projectf            4

            progdesc5               projectc   projectd            5

                  progdesc2         projectd   projecth            2

                  progdesc3         projectd   projectg            3

7 rows selected.

Rating: 5
EXPLAIN PLAN July 23, 2002
Reviewer: Andrew Schonberger from Sydney, Australia
PMJI, ... this could be of public interest...

I am checking my query plans with an explicit hierarchical query.  I always
thought the order of siblings reflects the order in which the optimiser works.
Could the EXPLAIN PLAN output be misleading ?  May be I'm missing something - I
hope a hierarchical query in 8.1.7 returns siblings in the order they were
originally inserted. Or, some kind of natural ordering - otherwise EXPLAIN PLAN
would be unpredictable.

regards and thanks for the great forum,

Andrew

Followup:
siblings -- as with ALL DATA -- will be returned in some random order.

If and only if you use an order by will data be returned in some predicable
order.  Since there is no method at all to order siblings in 8i and before --
the siblings are returned in some non-specific order (definitely not the order
of insertion or anything like that).

The explain plan is extremely predicable -- the order of the results -- not so.

July 23, 2002 Mugdha -- Thanks for the question regarding "Changing the column name of a table", version Oracle 7.3
You Asked
I know that in Oracle 7.3 its not possible to change the name of
the column once the table is created. But is it possible to
change the name of the column by logging in as sys and changing
it directly in the data dictionary ?
and we said...
No, not in a manner that is supported or supportable.  There are
many places the column name may be referenced (in plsql code, in
triggers, in constraints and other places you are not aware of).

Updating the SYS.tables is wholly and totally unsupported and
actively discouraged.  Don't do it.  It might not affect you
today or tomorrow but 6 weeks from now.  It'll cause ORA-600
errors and other strange things.  It is just not a good idea.

This is what views are all about.  In order to "rename" a column
simply:

o rename T to T_old

o create view T as
  select old_name new_name, old_name new_name, ....
    from T_old

o run:
  select 'grant ' || privilege || ' on T to ' || grantee || ';'
    from user_tab_privs
   where table_name = 'T_OLD'
  /
  and save the output -- run those commands

o run:
  select 'revoke ' || privilege || ' on T_OLD from '
                  || grantee || ';'
    from user_tab_privs
   where table_name = 'T_OLD'
  /
  and save the output -- run those commands

You have renamed the column.  If you need to rename anymore,
just repeat step 2 above and you are done.

This will not affect performance at all.
Review & Followup
Rating: 5
Changing the column name of a table April 02, 2001
Reviewer: Chris Barr from Cambridge, MA USA
As usual, answered my question perfectly, with correct extra text about using
VIEWs and NOT updating SYS.* tables.

Rating: 4
July 22, 2002
Reviewer: A reader
if I want a job to have a specific job number how should I do it, using
dbms_job.isubmit or updating the data dictionary sys.job$

Followup:
if you even consider updating the data dictionary -- I want our software back.
That would be what is known as a hugely bad idea (updating the data dictionary).

Never do that, unless support is on the other end of the phone telling you
exactly what to type.

There is no documented method to assign a particular number to a job and it
would be a bad idea on your part as jobs are assigned numbers via the jobseq
sequence.  Hence, you could "break" the system by assigning a number that we
will be using later.

isubmit (internal submit) is used by import to put things back only

Rating: 5
How many ways does it have to be said? July 22, 2002
Reviewer: Randy from Texas
"That would be what is known as a hugely bad idea "

Tom, you've found yet another grammatically correct way to say "don't update the
confounded data dictionary!".

Rating: 4
July 23, 2002
Reviewer: A reader
is it ok to reset the jobseq sequence

Followup:
Lets see, jobseq is sys.jobseq.  things owned by sys are the data dictionary.
Back to:

if you even consider updating the data dictionary -- I want our software back.
That would be what is known as a hugely bad idea (updating the data dictionary).

Never do that, unless support is on the other end of the phone telling you
exactly what to type.

I guess.  NO, it is not ok

July 23, 2002 Marco -- Thanks for the question regarding "Trace SQL statements", version 8.1.6
You Asked
I should trace the flow of execution of SQL statements executed by a client of a
Web application.

The application architecture is:
- client front ends (browsers)
- application server (COM+ components)
- DB server (Oracle 8.1.6)

How can i filter/trace the SQL statements executed by a browser session ?

Thanks in advance!!
and we said...
enable sql_trace
http://asktom.oracle.com/~tkyte/tkprof.html
you might use sys.dbms_SYSTEM.set_sql_trace_in_session to do this or use an on
logon trigger to dynamically execute alter session set sql_trace=true
Review & Followup
Rating: 3
How to set sql_trace for instance without restarting database July 23, 2002
Reviewer: nadya from Russia
Tom,
From documentation (8i):
To enable the SQL trace facility for your instance, set the value of the
SQL_TRACE initialization parameter to true. Statistics are collected for all
sessions.

ALTER SYSTEM SET SQL_TRACE = true;

After the SQL trace facility has been enabled for the instance, you can disable
it for the instance by entering:

ALTER SYSTEM SET SQL_TRACE = false;

But when we wanted to change this parameter dinamicaly using login SYSTEM,  we
receive next error:
ORA-02095  specified initialization parameter cannot be modified
Does it mean it is impossible to set this parameter dinamically for the
instance?
Thanks.

Followup:
Yes, AND you don't want to do it for the INSTANCE (that'll generate so much
trace, you won't know what to do)

use sys.dbms_system.set_sql_trace_in_session to set sql trace on a particular
SESSION -- not the instance.

Rating: 3
Some other ways... July 23, 2002
Reviewer: andrew from ca, usa
To limit the scope of the trace (in a non-prod environment), I've included
execute immediate 'alter session set timed_statistics=true';
execute immediate 'alter session set sql_trace=true';
in the "elaboration code" section of a common package.

I've also tried "after logon" trigger and dbms_system.set_sql_trace_in_session()
too depending on what I'm after.

I guess it's also worth noting that connection pooling results in multiple
transactions going into the same trace file.

July 24, 2002 peter -- Thanks for the question regarding "java mail", version 8.1.7
You Asked
hi :
I've created the package following your instruction.
However I dont konw how to use "p_attachment_data => i.theBlob"
"p_attachment_type => 'application/winzip'"

Could you give me an example if I would like to add a pdf file into
Blob how to insert it ? and what should I use in "p_attachment_type"?

--original sample code----------------------------
2    ret_code number;
  3  begin
  4    for i in (select theBlob from demo )
  5    loop
  6      ret_code := send(
  7                    p_from => 'tkyte@oracle.com',
  8                    p_to => 'tkyte@oracle.com',
  9                    p_cc => NULL,
 10                    p_bcc => NULL,
 11                    p_subject => 'Use the attached Zip file',
 12                    p_body => 'to send email with attachments....',
 13                    p_smtp_host => 'aria.us.oracle.com',
 14                    p_attachment_data => i.theBlob,
 15                    p_attachment_type => 'application/winzip',
 16                    p_attachment_file_name => 'mail8i.zip');
 17      if ret_code = 1 then
 18        dbms_output.put_line ('Successfully sent message...');
 19      else
 20        dbms_output.put_line ('Failed to send message...');
 21      end if;
 22    end loop;
 23  end;
 24  /
Successfully sent message...
and we said...
Have you read the Lob Appplication Developers Guide?
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/index.htm
or searched for

load lob

on this site?  either will give you examples.  You can either programatically
load the load from a file (a temporary lob would do) or you can simply use
dbms_lob.loadfromfile to read a file into a lob.
Review & Followup
Rating: 3
an error occur July 24, 2002
Reviewer: peter from TW
hi
I got an error when using JAVA mail

 declare
      ret_code number;
    begin
      for i in (select theclob from demo )
      loop
        ret_code := send(
                      p_from => 'peteryeh@utechmedia.com.tw',
                      p_to => 'peteryeh@utechmedia.com.tw',
                      p_cc => NULL,
                     p_bcc => NULL,
                     p_subject => 'Use the attached Zip file',
                     p_body => 'to send email with attachments....',
                     p_smtp_host => '172.18.17.5',
                     p_attachment_data => i.theclob,
                     p_attachment_type => 'application/winzip',
                     p_attachment_file_name =>'tree.zip');--
       if ret_code = 1 then
         dbms_output.put_line ('Successfully sent message...');
       else
         dbms_output.put_line ('Failed to send message...');
       end if;
     end loop;
   end;

--error --
ERROR at line 6:
ORA-06550: line 6, column 21:
PLS-00306: wrong number or types of arguments in call to 'SEND'
ORA-06550: line 6, column 9:
PL/SQL: Statement ignored

SQL> select *   from demo
  2  /

        ID
----------
THECLOB
--------------------------------------------------------------------------------
         2
¡H?

Followup:
desc demo returns what?

Rating: 4
java mail July 24, 2002
Reviewer: peter
I can send mail without attach
but I indeed stored this file in demo table

    declare
      ret_code number;
      v_blob  blob;
    begin
       select theclob into v_blob from demo;
        ret_code := send(
                      p_from => 'peteryeh@utechmedia.com.tw',
                      p_to => 'peteryeh@utechmedia.com.tw',
                      p_cc => NULL,
                     p_bcc => NULL,
                     p_subject => 'test',
                     p_body => 'to send email with attachments....',
                     p_smtp_host => '172.18.17.5',
                     p_attachment_data =>v_blob,
                     p_attachment_type =>'zz',
                     p_attachment_file_name =>'tree.zip');--
       if ret_code = 1 then
         dbms_output.put_line ('Successfully sent message...');
       else
         dbms_output.put_line ('Failed to send message...');
       end if;
     end loop;

got Failed to send message...
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SQL> select id from demo
  2  /

        ID
----------
         1
I stroed tree.zip in it ...why It always failed?

Rating: 5
java mail July 24, 2002
Reviewer: peter from tw
select id,dbms_lob.getlength( theclob ) from demo
1,1054
means I stored it  successfully,

but it fails in mailging with attch
why?

July 24, 2002 sam -- Thanks for the question regarding "Understand the concepts but to define it to others", version 8.1.7
You Asked
Tom

1.I have used ref cursors, varrays, and nested tables.

But when Iam asked to define then Iam at a loss.

Can you define the above 3 for me( a small comparision example for varray and
nested table).

2.The same with nocopy parameter , actually the difference between in , out ,
inout and nocopy parameters. I know the examples and I have done it, but what I
want is definition. Without a computer and without a black board, how do you
define then( say when asked in an interview).

Thank you
and we said...
1) a ref cursor is a cursor -- its a pointer to a cursor but a cursor none the
less.

Their primary uses are:

o returning result sets from stored procedures to any client application
o performing dynamic SQL in plsql

Varrays and Nested Tables are close cousins of eachother.  When used in PLSQL
they behave much like an array would.  A varray is bounded (has a max number of
items) whereas a nested table is not (bounded more or less by RAM in PLSQL when
used as an array).

When used in CREATE TABLE statements for permanently stored objects, they allow
you to keep either an ordered array of data (varray's) or an un-ordered
collection of data (nested tables).  A varray is physically stored in a LOB as
packed data whereas a nested table is stored in a real physical child database
table.

2) NOCOPY says to pass that parameter by REFERENCE not by value.  IN only
parameters are ALWAYS passed by reference (a pointer to the parameter is passed,
not the parameter value).  This is because IN parameters cannot be written to,
hence passing the reference is "safe".

Normally IN OUT and OUT parameters are passed using "copy" semanatics.  An IN
OUT parameter is passed by copying the IN value into a temporary -- the
temporary is used in the procedure/function and then (upon SUCCESSFUL completion
of the routine) its value is copied back to the original parameter that was
passed (two copies).  An OUT parameter is not copied on the way into the
procedure -- however a temporary variable is still used in the routine and on
the way out (up SUCCESS) the temporary variable's value is copied back into the
parameter that was passed initially (one copy).

If you pass a varible with NOCOPY -- a pointer is passed.  For an IN OUT
parameter, this means that when you modify the parameter -- its value is
IMMEDIATELY modified, we do not copy to a temporary variable.  What this means
is that any modification you make to it in the routine is made to the parameter
right then -- not when the procedure succesfully completes.  This means side
effects from a failed procedure can be visible to the caller (and this is not
normally so).  If you have just a small routine such as:

  procedure p( x in out NOCOPY number ) is
  begin
     x := 55;
     raise program_error;
  end;

Then you call it like this:

 declare
    y number := 0;
 begin
    p(y);
 exception when others then
    dbms_output.put_line(y);
 end;

that will print 55 -- not 5 (it will print 5 if you do not use NOCOPY since the
values are ONLY copied upon SUCCESS).

It can be even more tricky with OUT parameters.  OUT parameters are ALWAYS
modified -- even if the procedure you called never touches them.  Simply change
P to be:

  procedure p( x out NOCOPY number ) is
  begin
     raise program_error;
  end;

that doesn't modify x at all, now if you execute:

declare
    y number := 0;
begin
    p(y);
exception when others then
    dbms_output.put_line('y='||y);
end;

that'll print "y="  -- y will be set to NULL.

So, you cannot just go in and put NOCOPY everywhere as this side effect can
change the existing behaviour of your code.  NOCOPY is ONLY useful on truly
large objects like tables and collections.  It doesn't make nearly as much sense
to use NOCOPY on a simple NUMBER/DATE type or small strings as it does on these
large variables (doesn't make sense on a LOB either as the lob locator itself is
small).
Review & Followup
Rating: 4
November 26, 2001
Reviewer: Marc Blum from Aachen, Germany
It's always the same: decided to lurk asktom and found some very interesting
article. Conclusion: spend 30min every day reading asktom as part of your
self-education-program

Rating: 4
No doubt! It is Sea-Knowledge of Oracle November 26, 2001
Reviewer: Samik Nath from Japan/Tokyo
Great Page

Rating: 5
Understand the concepts but to define it to others November 26, 2001
Reviewer: sanjai from MI,USA
Great page. I agree all of us should spend atleast 30 min reading askTom. I get
to learn the best of oracle stuff here.

Tom, thanks for all your help.

Rating: 5
April 24, 2002
Reviewer: A reader

Rating: 5
Cleared my confusion July 23, 2002
Reviewer: BK from Boston, MA USA
Great article ! some othere article suggested to search before asking new
question, that really worked. I'd confusion about SP parameters and was
wondering if the values are passed by reference or values. I've been developing
custom API's for oracle apps and make use of large Record types and had doubt
about performance implications of all this record types passed by value. If you
could give some more idea about performance implications of using Record types
when apssed as parameters and how to manage it in a better way then it would be
really helpful. API's are used for real-time integration between apps and
website so sub second improvement would also help. Is it better to use NOCOPY
when record types are passed as parameter ? If my record type being passed as
parameter to the SP has 10 attributes and I initialize 5 of them before passing
it to the procedure, then will it pass only initialzed attributes in a record
type or all(10) of them ?
Thanks,

Followup:
IN parameters  = REFERENCE. passed by reference (pointer)

OUT parameters = VALUE.     copy on output (temporary set up, filled in a copied
on success)

IN OUT         = VALUE.     copy to temporary, copy from temporary (passed by
value)

Use NOCOPY and all are passed by reference (search this site for nocopy to read
about the CAVEATS with that!  Better -- it depends.  Different is more like it.
It has SIDE EFFECTS)

Rating: 4
Thanks for the simple, solid definitions! July 24, 2002
Reviewer: Robert from Memphis, TN USA

Rating: 5
ToM, YOU THE MAN! July 24, 2002
Reviewer: A reader
:-) You are going to live 100 years, you have blessing from all of us! Great job
you are doing!

Rating: 5
sorry for the future generations :o( July 24, 2002
Reviewer: Robert Chin from Newtown, PA
>>:-) You are going to live 100 years, you have blessing from all of us!

Makes me feel sorry for the future generations...
(circa Oracle 38i ?)
who's gonna be crusading for Bind Variables ? who's gonna be pointing them down
the correct path to technical enlightenment in Oracle ?...who's gonna write
another book as excellent as "Expert 1-1"  ?

But for now...Tom, please confirm this:
If I miss just one and use literal,
out of n spots where BV should be used..
e.g.
Open myRefCur for 'select * from emp where empno = ' || x || ' and ename = :a
and sal > :b' Using myBV1, myBV2

This will totally negate the use of myBV1, myBV2, right ?
That is,this will be a total re-hardparse every time, right ?
Thanks

Followup:
pretty much -- assuming that EMPNO is the primary key, every query would be
unique.

July 24, 2002 Albert -- Thanks for the question regarding "Copying Tables with Long columns", version 8.1.7.1
You Asked
How do you copy a table that has a column formatted with a long datatype?
and we said...
http://osi.oracle.com/~tkyte/Misc/MoveLongs.html
Review
      & Followup

      Rating:
      4
  April 23, 2002
Reviewer:
      A reader
I got error when I ran the COPY command in the document.

SQL> copy from system/ph1ghr1skt@dev insert foo (the_whole_view, textlength, the
text ) using select view_name, text_length, text from all_views;

Array fetch/bind size is 15. (arraysize is 15)
Will commit when done. (copycommit is 0)
Maximum long size is 80. (long is 80)

select view_name, text_length, text from all_views
*
Error in SELECT statement: ORA-01002: fetch out of sequence

SQL>

Could you explain why? There is only implicit cursor involved here.

Thanks

          Followup:

Try it the other direction then.  Instead of a pull -- do a push.  Log into dev
and push it to the database you are connected to.

Else, contact support and open a tar. (when I've gotten this in the past,
reversing the direction of the copy fixed it up)

      Rating:
      4
ORA--3923194  July 24, 2002
Reviewer:  A
      Reader  from USA
Hi Tom,
When I use the COPY command, I get a error which I never saw before (and I
cannot find it from documentation):

Array fetch/bind size is 15. (arraysize is 15)
Will commit when done. (copycommit is 0)
Maximum long size is 100000. (long is 100000)

ERROR:
ORA--3923194: Message -3923194 not found;  product=RDBMS; facility=ORA

Thank you very much for your help.

          Followup:

gonna have to refer you to support on that one.

      Rating: 5
Answer from
      Oracle support  July 26, 2002
Reviewer:  A
      reader  from USA
Hi Tom,
Thank you for all the greate answers.  The following is the answer from Oracle
support about the error message, which I would like to share with all your
readers.

You are using the GUI version of SQL*Plus on a Windows PC and are
attempting to execute a copy command using syntax similar to:

The copy statement fails with the following error:

ERROR:
ORA--<RandomNumber>: Message -<RandomNumber> not found; product=RDBMS;
facility=ORA

Here the <RandomNumber> could be anything and varies between different systems

There is a bug in SQL*Plus that causes random errors and Dr. Watson's
when using the COPY command in the GUI based version of the tool. Until
the bug is fixed, there are several workarounds. Any of these solutions
will allow successful update to your table.

1. Execute the COPY command using the character based executable - i.e.
sqlplus.exe. This can be done by going to Start -> Run -> sqlplus.exe,
or by executing the command in an MS-Dos window.

Reference
=======

Bug 1504702, SQLPLUS COPY COMMAND FAILS WITH DR. WATSON, ORA-65535 OR
ORA--6449424

July 24, 2002 Andre -- Thanks for the question regarding "BY
      VALUES clause in ALTER USER ", version 8.1.6

      You Asked
I was reading your script 'su.sql' at:

http://govt.oracle.com/~tkyte/Misc/su.html
It changes from a DBA user to another user without him having to
supply the user's password.

Initially in the script you get the password column content from
dba_users.

Later on you reset the password with this statement:

alter user &1 identified by values '&pw';

What I do not understand is how can you reset a password that
was encrypted ?

I tried to locate the BY VALUES clause in the SQL Reference, but
it was not there.

So, from what I understand, the BY VALUES clause makes it
possible for you to "directly" input an already encrypted value
in a column. Is that right ?

Is the BY VALUES clause undocumented by the way ?

Thanks !

      and we said...
The password is never encrypted (that would imply we could
"decrypt" it but we cannot).  It is a one way "hash" (two
different users with the same password will have different
"hashed" password values).

the "identified by values 'hashed string'" syntax is not
documented however it is heavily used by utilities such as
export and import (that is how they manage to recreate user
accounts in other databases with the passwords intact.  The
server never needs to have been given the original password --
it just needs the hashed value.  Then, when a user attempts to
login and presents their username+password, the server will hash
that value and compare it to the hashed value in the dba_users
view to see if you gave the correct password or not.
Review &
      Followup

      Rating: 5
Importing
      Application password table from 7.3.4 to 8.1.7   July 24,
      2002
Reviewer:  Rajesh  from Denver,CO
Hi Tom,

I'm thinking to use your "digest" function logic for my application to store
hashed password and userid in a table(say customers) and use the same function
to compare the entered(in the application) userid+password with userid+password
in the table.

While testing the function in oracle 7.3.4 and 8.1.7 it is giving different
outputs(I hard coded userid and password  when calling "digest" function in
hash.sql file to test this. In other words  I used the same password string in
all the three cases).

Oracle7 Server Release 7.3.4.4.0 - Production
With the distributed, parallel query and Spatial Data options
PL/SQL Release 2.3.4.4.0 - Production

nolog> @hash

PL/SQL procedure successfully completed.

nolog> set serveroutput on
nolog> @hash
User: IMA_70                         digest: 3F61A1A4

Connected to:
Oracle8i Enterprise Edition Release 8.1.7.2.0 - 64bit Production
With the Partitioning option
JServer Release 8.1.7.2.0 - 64bit Production

IMA_70@IMATEST1:imatest1> set serveroutput on
IMA_70@IMATEST1:imatest1> @hash

User: IMA_70                         digest: 4ED11C33

Oracle8i Enterprise Edition Release 8.1.7.2.0 - 64bit Production
With the Partitioning option
JServer Release 8.1.7.2.0 - 64bit Production

IMA_70@IMADB01:imadog> @hash

PL/SQL procedure successfully completed.

IMA_70@IMADB01:imadog> set serveroutput on
IMA_70@IMADB01:imadog> @hash
User: IMA_70                         digest: 4ED11C33

So the hashed passwords didn't match form 7.3.4 to 8.17. What happens when I
import application tables(including customers) from 7.3.4 to 8.1.7 and run the
application from 8.1.7 database?. OR am I thinking/doing something totally
wrong!!!

I'm sorry!, If this is not the right place to post this question!

Thanks for your reply!!

Rajesh

          Followup:

they did change the HASH function between 7 and 8 -- in 8.x they use the same
hash function as the other database functions do.  In 7.x, the get hash routine
was "unique"

      Rating: 5
Thank you very
      much for your reply!!  July 25, 2002
Reviewer:
      Rajesh  from Denver,CO

July 25, 2002 Vikram -- Thanks for the question regarding
      "insertable and updateable view", version 8.1.6

      You Asked
Hi Tom,
       Back again.
Lets suppose we have a table t1

 x(primary  y(unique     z
    key)       key)
---------------------------
 a1        b1           c1
 a2        b2           c2
 a3        b3           c3

and table t2

 x(Foreign   p(pri. key) q
    key)
-------------------------
 a1        d11         e11
 a1        d12         e12
 a2        d21         e21
 a2        d22         e22

Now i created a view on these tables which looks like

 y         p          q           x
------------------------------------
 b1       d11         e11        a1
 b1       d12         e12        a1
 b2       d21         e21        a2
 b2       d22         e22        a2
 b3      (null)     (null)       a3

(NOTE: BUT THE FORM ON THIS VIEW WOULD SHOW ONLY THE FIELDS 'y',
'p' and 'q' NO 'x').

now how do i insert or update this view, like if i want to add a
new row with y=b1, p=d13, and q=e13

which add to the tables and the tables now look like

table t1
 x         y            z
---------------------------
 a1        b1           c1
 a2        b2           c2
 a3        b3           c3

and table t2

 x         p           q
-------------------------
 a1        d11         e11
 a1        d12         e12
 a1        d13         e13 ---> this is the new row added
 a2        d21         e21
 a2        d22         e22

      and we said...
we can do this with INSTEAD of triggers..  I'll demonstrate the
INSERT instead of trigger you need -- you can fill in the
update/delete as needed.  Here are the tables:

ops$tkyte@8i> create table t1
  2  ( x varchar2(5) primary key,
  3    y varchar2(5) unique,
  4    z varchar2(5) );

Table created.

ops$tkyte@8i>
ops$tkyte@8i> create table t2
  2  ( x varchar2(5) references t1,
  3    p varchar2(5) primary key,
  4    q varchar2(5) );

Table created.

and then the view:
ops$tkyte@8i>
ops$tkyte@8i>
ops$tkyte@8i> create or replace view v
  2  as
  3  select t1.y, t2.p, t2.q
  4    from t1, t2
  5   where t1.x = t2.x
  6  /

View created.

ops$tkyte@8i>
ops$tkyte@8i> create or replace trigger v_insert
  2  instead of insert on v
  3  begin
  4      insert into t2
  5      (x,p,q)
  6      select x, :new.p, :new.q
  7        from t1
  8       where y = :new.y;
  9
 10      if ( sql%rowcount = 0 )
 11      then
 12          raise_application_error
 13           ( -20001, 'That Y has no entry in T1' );
 14      end if;
 15  end;
 16  /

Trigger created.

Thats out logic for an insert into the view.  We'll insert the
P and Q columns and lookup the value of X based on Y (since Y is
a surrogate key into T1, this works).  If we cannot find a row
in T1 for that Y, we will fail.

ops$tkyte@8i> select * from v;

Y     P     Q
----- ----- -----
b1    d11   e11
b1    d12   e12
b2    d21   e21
b2    d22   e22

ops$tkyte@8i>
ops$tkyte@8i> insert into v (y,p,q) values ( 'b1', 'd13', 'e13'
);

1 row created.

ops$tkyte@8i>
ops$tkyte@8i> select * from v;

Y     P     Q
----- ----- -----
b1    d11   e11
b1    d12   e12
b2    d21   e21
b2    d22   e22
b1    d13   e13

ops$tkyte@8i>
ops$tkyte@8i> select * from t2;

X     P     Q
----- ----- -----
a1    d11   e11
a1    d12   e12
a2    d21   e21
a2    d22   e22
a1    d13   e13

ops$tkyte@8i>
Review & Followup

      Rating: 5
Insert/Update
      via View  July 25, 2002
Reviewer:  Eamonn
       from Hong Kong, Japan
Exactly what I was looking for except the coding has an error.  The insert
trigger did not insert the record into the T1 table causing the insert in T2 to
fail all the time.

          Followup:

There is no coding error here -- It just didn't do what you want, it was built
to a different set of requirements.

The supplied code does exactly what it was intended to do.  The requirement here
did not include inserting into T1.  In fact, given the requirements here, that
Y, P and Q are supplied -- inserting into T1 wouldn't make sense.  Y was the
surrogate key we could use to FIND X in t1, in order to put it into T2.  If the
row didn't exist in T1 -- the insert would fail (as documented above in bold)

July
      25, 2002 Steven -- Thanks for the
      question regarding "PL/SQL static SQL versus (pure) SQL", version 8.1.6

      You Asked
Dear Tom,
I am having a BIG problem with PL/SQL static SQL versus (pure) SQL.
I need to use the SQL CASE Expression and PL/SQL keeps choking on it:
LINE/COL ERROR
-------- -----------------------------------------------------------------
16/6     PLS-00103: Encountered the symbol "CASE" when expecting one of
         the following:
         ( ) - + mod not null others <an identifier>
         <a double-quoted delimited-identifier> <a bind variable>
         table avg count current exists max min prior sql stddev sum
         variance execute multiset the both leading trailing forall
         time timestamp interval date
         <a string literal with character set specification>
         <a number> <a single-quoted SQL string>

The same SELECT statement works just fine if I run it directly via SQL*Plus. I
need it in a PL/SQL Stored-Procedure.
I know that I could use Dynamic SQL, but that would "open a whole other can of
worms". I want to keep it static for %ROWTYPE etc.

Thanks,
Steven

      and we said...
Your choices in 8i are:

o create a view that "hides" the case from PLSQL
o use dynamic sql

the PLSQL sql parser quite simply does not understand certain constructs like

o order by in a subquery
o CASE
o group by rollup | cube
o select ( select ... ) from t;
o TO_LOB()

In 9i, the PLSQL and database engine share a common parser meaning that as
feaures are added to SQL, they'll instantly be avaialble in PLSQL.

You can use dynamic sql and use a %ROWTYPE using a "template" cursor.  Consider:

ops$tkyte@ORA817.US.ORACLE.COM> declare
  2      type rc is ref cursor;
  3
  4      cursor TEMPLATE is select ename, empno, rpad( '*', 30, '*' ) case_exp
from emp;
  5
  6      l_rec TEMPLATE%rowtype;
  7
  8      l_cursor rc;
  9
 10      l_job    varchar2(25) default 'CLERK';
 11  begin
 12      open l_cursor for
 13      'select ename, empno, case when sal between 0 and 500 then ''LOW''
 14                                 when sal between 501 and 1000 then ''MED''
 15                                 else  ''HIGH''
 16                            end
 17         from emp
 18       where job = :x'
 19       using l_job;
 20
 21      loop
 22          fetch l_cursor into l_rec;
 23          exit when l_cursor%notfound;
 24          dbms_output.put_line( l_rec.case_exp );
 25      end loop;
 26
 27      close l_cursor;
 28  end;
 29  /
MED
HIGH
MED
HIGH

PL/SQL procedure successfully completed.

you just need to make a static cursor (no WHERE clause is ever needed) that
selects representative columns.

followup to comment one

No -- the reason is simple.

In PLSQL, using the following block of code:

declare
   l_ename varchar2(5) default '%A%';
begin
   for x in ( select * from emp where ename like l_ename and sal > 55 )
   loop
    .....

the SQL submited will be:

SELECT * FROM EMP WHERE ENAME LIKE :b0 AND SAL > 55

PLSQL leaves constants along and just finds its variables and binds them.  In
SQLPLUS you would query:

select * from emp where ename like '%A%' and sal > 55;

Now, in sqlplus, if you want that to be "bound" for you -- in 816 and up you
can:

SQL> alter session set cursor_sharing=force;
SQL> select * from emp where ename like '%A%' and sal > 55;

Now what gets executed is

select * from emp where ename like :sys_bv_0 and sal > :sys_bv_1

using this "autobind" feature, you have have applications that do not bind (bad
application) and environments like SQLPlus use bind variables for you.

Review &
      Followup

      Rating: 4
common
      parser  July 10, 2001
Reviewer:
      A reader
I'm under the impression that In PLSQL -- when you use STATIC SQL -- everything
is bound for you already. Now, since both SQl and PL/SQL uses the same parser
does it mean that all our SQL statements (From SQL*PLUS)  will be binded too?

      Rating: 5
template
      cursors  May 13, 2002
Reviewer:
      A reader
I like that, mostly because I came up with that a few months ago for the exact
same reason and just stumbled upon your solution while looking at other related
topics.

This site is great!

      Rating: 3
SQL
      Books  July 25, 2002
Reviewer:
      A reader
Hi Tom,
Can u pls suggest any book for sql just sql, i mean which says these are the
ways u can write a query etc.
simple but very descriptive book on sql

Thanks

          Followup:

Have you read the SQL reference manual?  It is free

Joe Celko has a good "sql for dummies" book as well.  search amazon.com for it.

      Rating: 5
dummie,
      smartie..whatever  July 25, 2002
Reviewer:
      Robert  from PA
>>Joe Celko has a good "sql for dummies" book as well.  >>search amazon.com for
it.

Guess I got myself a wrong book...mine says "SQL for Smarties" ;)

      Rating: 5
RE: Guess I got
      myself a wrong book  July 25, 2002
Reviewer:
      A reader
In regard to "Guess I got myself a wrong book", perhaps you should invest in a
book on grammar instead?

July 25, 2002 -- Thanks for the question regarding "Using
      connection pool in Java stored procedure", version 8.1.7

      You Asked
Hello,

I want to use java stored procedure in Oracle 8.1.7 to connect using JDBC to
other database. Can I use java jdbc connection pool in that procedure (I have
clients connected to Oracle DB, client is running java stored procedure and I
want that java stored procedure is using jdbc connection from pool)?

Thanks,

R. Kazimir

      and we said...
The pool would be a pool of one -- for your session.  The pool is not a "global"
pool -- the other sessions cannot, will not see the same pool

It would not make sense to use a connection pool in the database.  Just use a
direct connection since you cannot share it with another session and should not
be using threading at all.
Review & Followup

      Rating:
      4
  July 26, 2002
Reviewer:
      A reader
I thought the same, I just wanted to make sure.

Thanks,

R. Kazimir

      Rating:
      4
  July 26, 2002
Reviewer:
      A reader

July 25, 2002 Servet -- Thanks for the question regarding "Sql
      plus Help -- if / then / else in SQL", version 3.3

      You Asked
Hi,

if a=1 then
b=1
else
b=2
end if

I need to implement the structure above with SQL.
I mean, For Example; I have a field in the table named
'Total'.And if it's value negative I Want it to be 0 in the
query results.But if its positive I want it to be itself in the
query results.
Thanks for your help...
Servet

      and we said...
select decode( sign(total), -1, 0, total ) from T

will do it.  sign will return -1 if total is less then 0, 0 if
total is zero and 1 if total is greater then 0.

decode is just like a case statement.  the above is functionally
equivalent to:

   switch (sign(total))
   {
      when -1: return 0;
      default: return total;
   }

Review &
      Followup

      Rating: 3
Leaves another
      question remaining . . .   July 25, 2002
Reviewer:
      Cynthia  from Colorado, USA
Is "switch" supported in SQL or PL/SQL?

          Followup:

CASE is supported in PLSQL starting with Oracle9i Release 1

CASE is supported in SQL starting with Oracle8i release 2.

July 26, 2002 --
      Thanks for the question regarding "Trusted connection to Oracle", version
      8.1.7

      You Asked
Hello,

I have a user client on Windows NT 4/2K and DB server Oracle 8.1.7 on VMS
platform. Is there a possibility to use trusted connection (user identified
externally) from Windows NT/2K workstation without setting remote_os_auth=true?

Thanks,

R. Kazimir

      and we said...
that would open a security hole the size of Texas (thats big).

All I would have to do is drop a windows machine on your network, create a user
"system" or "sys" and I'm in

NEVER set remote_os_authent!  Not unless you are in a 100% physically secured
environment and can be sure no one will be attaching a machine to your network
unless you want them to.
Review & Followup

      Rating:
      2
  July 26, 2002
Reviewer:
      A reader
I understood that setting remote_os_authent=true is security hole. But my
question is other: if I have remote_os_authent=false, can I have users in DB
identified externally and client on Windows NT/2K and DB server on other
platform (VMS, AIX)?

Thanks,
R. Kazimir.

          Followup:

No, you cannot, not as far as I know.

July 26, 2002 Alex
      -- Thanks for the question regarding "consistent reads", version 8.1.6.3

      You Asked
Hi

I have been wondering what are consistent gets and db block gets, according to
the docs consistent gets are reads from rollback segments. Now this puzzles me,
why everytime I excute any query consistent gets are never 0, even there arent
any DML activities? I have just did a test

1. I bounced the database
2. set autotrace on in sql*plus
3.
select * from dept;
Statistics
----------------------------------------------------------
        233  recursive calls
          5  db block gets
         42  consistent gets
          6  physical reads
          0  redo size
        727  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          4  sorts (memory)
          0  sorts (disk)
          5  rows processed

4. repeated query
select * from dept
Statistics
----------------------------------------------------------
          0  recursive calls
          4  db block gets
          2  consistent gets
          0  physical reads
          0  redo size
        727  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          5  rows processed

Why consistent gets is 2 when I am the only one using the database (I am testing
in my PC ok) and there isnt absolutely any DML going on (well since I am the
only one and I am not modifying anything...) the query reads from rollback
segments?

Cheers

(By the way I just asked you a question about session_cached_cursor and I won a
T-Shirt in Oracle Openworld because the support couldnt answered it ;D)

      and we said...
consistent gets are simply blocks read in consistent mode.   This will include
read asides to the rollback segment when necessary to rollback a block but is
not EXCLUSIVELY reads to the rollback segments.

Your query above isn't reading the rollback segments -- its reading the dept
table itself in this case.

followup to comment one below

in the above reports db block gets and consistent gets are counts of the number
of times a block was accessed from the buffer cache.  physical reads would be
the number of physical IO's (none in the above since there were all in the
buffer cache to begin with)
Review & Followup

      Rating: 2
Consistent
      Reads  July 06, 2001
Reviewer:  Sonia
       from India
Y will the table dept be read when it is in the buffer cache? There is no need
for block reads after the query was executed for one time

      Rating: 3
Consistent
      Gets...  May 01, 2002
Reviewer:
      A reader
Tom,

In Your explanation you have stated that Consistent gets do not necessary mean
that theu are RBS gets, then why this discrimination. Is'nt tis misleading.

Thanks.

Regards,
Ganesh R

          Followup:

consistent gets are blocks gotten in consistent read mode.  They are gotten AS
OF the point in time the query begin.

db block gets are CURRENT mode block gets.  They are gotten as the blocks exist
right now.

consistent gets MIGHT entail reading RBS to reconstruct the data.
current gets MIGHT entail waiting for some data.

That is the difference between them.  The distinction is very important.

      Rating: 3
Sorry
      !!!  May 01, 2002
Reviewer:  A reader

Tom,

Really Sorry but i am not able to place it...

<quote>
consistent gets MIGHT entail reading RBS to reconstruct the data.
current gets MIGHT entail waiting for some data.
</quote>

What do u mean by that capital MIGHT... If It might read the RBS what other
reason is there to increase the count of consistent gets in the trace output.

Sorry tom... I think i have got my brains switched off today.

Thanks.

Regards,
Ganesh R

          Followup:

We go for a block in the buffer cache.

If the block in the buffer cache is "good enough" (current enough and not too
current), we are done.

If the block is too current, we need to go to the RBS and roll it back.  This
will involve more READS (more logical/physical io).  This will increase the
consistent gets.

Read also:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:880343948514
which shows the direct correlation between the array fetch size and consistent
gets as well.

      Rating: 3
Why are "db
      block gets" and "consistent gets" all exist?  July 26, 2002

Reviewer:  Richard he  from China
When I repeat a same query on simple table,no other dml.I think only "consistent
get" occurs,but in fact, "db block gets" and "consistent gets" all occurs,why?

          Followup:

full scans, we get the segment header in current mode in order to figure out
what to scan.

      Rating:
      3
Reader  August 06, 2002
Reviewer:
      A reader
Tom,

If a block is in the Buffer Cache and the version of the
block (SCN and Seq# in Oracle 8i) is equal to the
logical time of the start of a query, this block is read
in "consistent mode" as shown by statistics. Is this
correct

Thanks

July 28, 2002 Naveed -- Thanks for the question regarding "i
      want to exit the user if the user is not e.g(SCOTT)", version 8.0

      You Asked
      hi tom
      i want to ask u that i have an icon of the sql plus
     on the desktop , i want to write the code in the
login.sql file that if the user using the icon for the sqlplus
  is connected with the scott/tiger then only he can connect to the
database  .
otherwise the session is disconnected and the sqlplus
 exit command execute in this situation

                               please help me

      and we said...
whenever sqlerror exit
variable n number
begin select 1 into :n from dual where user = 'SCOTT'; end;
/

will do it. bear in mind however, that once logged in -- nothing stops them from

connect system/manager

the login.sql is only run ONCE per sqlplus instance, not every time they
connect.  You can use the product user profile to stop them from being able to
issue the connect command.  See

http://technet.oracle.com/doc/oracle8i_816/sqlplus.816/a75664/ape.htm#623300
for details.
Review & Followup

      Rating:
      3
GOOD  July 26, 2002
Reviewer:  sagar
       from INDIA
WELL IT ALSO EXIT WHEN ANY OTHER SQL ERROR OCCURED AFTER LOGIN AS SCOTT..
HOW WE CAN STOP OR CHANGE SQLERROR AFTER SUCCESSFUL LOGIN

          Followup:

WHY ARE YOU SHOUTING SO LOUDLY.

if you checked out the sqlplus documentation, given that I showed you the
whenever command, you would find:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a82950/ch8.htm#1003483
which shows all of the variations of WHENEVER, including whenever sqlerror
CONTINUE, which is what you can put in after the check to reset the error
handling back to the way it was.

July 28, 2002 Peter -- Thanks for the question regarding
      "Parsing a varchar for use in a comparison test.", version 8.1.7

      You Asked
Hello Tom
I want to compare a value is a table with a certain threshold.

I have a table as follows

Name VARCHAR2(32);
Value NUMBER;
Threshold VARCHAR2(10);

The threshold is a number plus the character > or < or =.

One entry in this table could be:

Name  = 'People';
Value = 101;
Threshold = '>100';

I want to test the Value against the Threshold and have tried
to use execute immediate but do not get the result I expect.

string1 := 'Too many people';
execute immediate 'begin if ('||Value Threshold||') then
dbms_output.put_line('||string1||'); end if; end;';

Thanks for any help on this

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 'People', 101, '>100'
);

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 'Dogs', 11, '>1000' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace
  2  function eval( p_value in number,
  3                 p_threshold in varchar2,
  4                 p_when_true in varchar2,
  5                 p_when_false in varchar2 ) return varchar2
  6  as
  7      l_return varchar2(4000);
  8  begin
  9      execute immediate
 10      'select decode( count(*), 0, :when_false, 1, :when_true )
 11         from dual
 12        where :val ' || p_threshold
 13      into l_return
 14      using p_when_false, p_when_true, p_value;
 15
 16      return l_return;
 17  end;
 18  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> column outcome format a30
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select t.*, eval( value, threshold, 'Too many
people', 'Ok' ) outcome
  2    from t
  3  /

NAME            VALUE THRESHOLD  OUTCOME
---------- ---------- ---------- ------------------------------
People            101 >100       Too many people
Dogs               11 >1000      Ok

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review &
      Followup

      Rating: 5
Execute
      immediate - creating the statement from bind vars  July 23,
      2002
Reviewer:  Peter Jones  from Switzerland

Thanks for your help. I have identidified a common statement type and rather
than creating many procedures or sql statements I want to have one and give it 3
variables.
ObjectName, Operator (<,= or >) and Threshold.
I have checks like
NumberUsers < 5
Load > 12
State = 0 and there could be 100s like this, so I want
to manage one piece of common code only.

The following
procedure compiles but it gives an error when I run it probably because the
operator can not be a bind variable. However you managed to make this work with
execute immediate. I failed to make execute immediate work with a cursor so once
again I need your valuable help.

Many thanks in advance

create or replace procedure rule_check (RULE_NUMBER     in NUMBER ,
                        RULE_METRICNAME in VARCHAR2 ,
                        RULE_COMPARATOR in VARCHAR2 ,
                                        RULE_THRESHOLD  in NUMBER
                        )
is
type rc is ref cursor;
alarm_cur rc;
rec       perfmon_values%ROWTYPE;
begin
  open alarm_cur for 'select machine,timestamp,metricname,metricvalue
                     from perfmon_values p
                     where metricname=:RULE_METRICNAME and metricvalue
                     :RULE_COMPARATOR :RULE_THRESHOLD
                     group by machine,metricname,metricvalue,timestamp
                     having max(timestamp) = (select max(timestamp)
                                              from perfmon_values p2
                                              where p.machine=p2.machine
                                              and metricname=:RULE_METRICNAME);'

            USING
RULE_METRICNAME,RULE_COMPARATOR,RULE_THRESHOLD,RULE_METRICNAME;
 fetch alarm_cur into rec;
 loop
     BEGIN
        insert ....
    dbms_output.put_line('OK');
     EXCEPTION
              WHEN DUP_VAL_ON_INDEX THEN dbms_output.put_line('DUPLICATE');
              WHEN OTHERS THEN dbms_output.put_line('OTHER');
     END;
     fetch  alarm_cur into rec;
     exit when alarm_cur%NOTFOUND;
 end loop;
END;
/

          Followup:

  open alarm_cur for 'select machine,timestamp,metricname,metricvalue
                     from perfmon_values p
                     where metricname=:RULE_METRICNAME and metricvalue  ' ||
                     RULE_COMPARATOR || ' :RULE_THRESHOLD
                     group by machine,metricname,metricvalue,timestamp

      Rating: 4
Parsing a
      varchar for use in a comparison test  July 25, 2002

Reviewer:  Peter Jones  from Switzerland
Hi
Thanks fo your reply. I tried the changes you suggest but the code still gives
errors that I dont understand on the select statement.

Oracle8i Enterprise Edition Release 8.1.7.4.0 - Production
With the Partitioning option
JServer Release 8.1.7.4.0 - Production

SQL> @pete.sql

Procedure created.

SQL> execute pete
BEGIN pete; END;

*
ERROR at line 1:
ORA-00920: invalid relational operator
ORA-06512: at "PETE.PETE", line 11
ORA-06512: at line 1

SQL> l 11
 11*  open alarm_cur for 'select machine,timestamp,metricname,metricvalue
SQL>

create or replace procedure pete (RULE_NUMBER     in NUMBER default 1,
                                  RULE_METRICNAME in VARCHAR2 default 'l',
                                  RULE_COMPARATOR in VARCHAR2 default 'l',

                                  RULE_THRESHOLD  in NUMBER default 1
                                  )
is
type rc is ref cursor;
alarm_cur rc;
rec       perfmon_values%ROWTYPE;
begin
 open alarm_cur for 'select machine,timestamp,metricname,metricvalue
                     from perfmon_values p
                     where metricname=:RULE_METRICNAME and metricvalue '
                     || RULE_COMPARATOR || ' :RULE_THRESHOLD
                     group by machine,metricname,metricvalue,timestamp
                     having max(timestamp) = (select max(timestamp)
                                              from perfmon_values p2
                                              where p.machine=p2.machine
                                              and metricname=:RULE_METRICNAME);'
                    USING 'CpuIdle',90,'CpuIdle';
                    --USING RULE_METRICNAME,RULE_COMPARATOR,RULE_THRESHOLD,RULE_
METRICNAME;
 fetch alarm_cur into rec;
 loop
     BEGIN
             dbms_output.put_line('OK');

     EXCEPTION
                      WHEN DUP_VAL_ON_INDEX THEN dbms_output.put_line('DUPLICATE
');
                      WHEN OTHERS THEN dbms_output.put_line('OTHER');
     END;
     fetch  alarm_cur into rec;
     exit when alarm_cur%NOTFOUND;
 end loop;
END;

          Followup:

Look at the SQL you generate.

You are not passing a "comparator" -- you are just passing an expression.  The
sql you generate has a predicate like:

   .... and metricvalue 1 :rule_threshold ....

'1' is not a comparision operation -- I think rule_comparator should be
defaulted to '=' perhaps so that would be:

   .... and metricvalue = :rule_threshold ....

for example

      Rating: 2
Parsing a varch
      for ue in a comparison test  July 25, 2002

Reviewer:  Peter Jones  from Switzerland
Hello
I'm sorry, maybe I'm missing something. I have changed
the code as advised but I still get the error
SQL> execute pete
BEGIN pete; END;

*
ERROR at line 1:
ORA-00911: invalid character
ORA-06512: at "PETE.PETE", line 12
ORA-06512: at line 1
SQL> l 12
 12*  open alarm_cur for 'select machine,timestamp,metricname,metricvalue

          Followup:

SQL> set serveroutput on

and add some dbms_output.put_lines to your code to actually print out the sql
statement and debug your code!

      Rating: 5
pete,pete,pete
      your default = 1  July 25, 2002
Reviewer:  J
       from ma
procedure pete (RULE_NUMBER  in NUMBER default 1,
            RULE_METRICNAME in VARCHAR2 default 'l',
RULE_COMPARATOR in VARCHAR2 default 'l',      <<<<<<<<<<<<< default to '=' or
something                                                     RULE_THRESHOLD  in
NUMBER default 1
                                  )

      Rating: 3
Parsing a
      varchar for use in a comparison test  July 26, 2002

Reviewer:  Peter Jones  from Switzerland
Hello
I fear the problem is more fundamental. I have trimmed down the code in order to
debug and I cant use the dynamic cursor correctly. The syntax seems ok and I
have checked other examples for this.
Can you see where the parsing of the variable :x goe wrong?

create or replace procedure pete2
is
x number :=  9;
type rc is ref cursor;
alarm_cur rc;
rec       perfmon_values%ROWTYPE;
begin
 dbms_output.put_line('OK');
 open alarm_cur for 'select machine,timestamp,metricname,metricvalue from
perfmon_values where metricname = :x' using x;
 loop
     fetch  alarm_cur into rec;
     exit when alarm_cur%NOTFOUND;
     dbms_output.put_line('OK');
 end loop;
 close alarm_cur;
END;
/

SQL> exec pete2
OK
BEGIN pete2; END;

*
ERROR at line 1:
ORA-01722: invalid number
ORA-06512: at "PETE.PETE2", line 12
ORA-06512: at line 1

          Followup:

and is metricname a NUMBER???  I suspect not and you have fallen into the age
old trap of "i'm comparing a number to a string, or a date to a string or a
number to a date and getting an error, why?"  The error is you are converting
the string metricname to a number in order to compare to the number 9 and for
some row in the table, it is failing.

Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table perfmon_values
  2  ( machine varchar2(1), timestamp date, metricname varchar2(1), metricvalue
varchar2(1) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into perfmon_values values ( '1',
sysdate, '9', '9' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure pete2
  2  is
  3  x number :=  9;
  4  type rc is ref cursor;
  5  alarm_cur rc;
  6  rec          perfmon_values%ROWTYPE;
  7  begin
  8   dbms_output.put_line('OK');
  9   open alarm_cur for 'select machine,timestamp,metricname,metricvalue from
 10  perfmon_values where metricname = :x' using x;
 11   loop
 12       fetch  alarm_cur into rec;
 13       exit when alarm_cur%NOTFOUND;
 14       dbms_output.put_line('OK');
 15   end loop;
 16   close alarm_cur;
 17  END;
 18  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec pete2
OK
OK

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into perfmon_values values ( '1',
sysdate, 'A', '9' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec pete2
OK
OK
BEGIN pete2; END;

*
ERROR at line 1:
ORA-01722: invalid number
ORA-06512: at "OPS$TKYTE.PETE2", line 12
ORA-06512: at line 1

always compare dates to dates, numbers to numbers, strings to strings and
never any other way!!!

change:

  3  x number :=  9;

to

  3  x varchar2(20) :=  '9'

and try again.

      Rating: 2
Problem solved
        July 27, 2002
Reviewer:  Peter Jones
       from Switzerland
Hello Tom
Although I dont know why my last example fails I have managed to correct the
original problem. I had a ';' in my
select statement which is not needed.

July 29, 2002 Pranav -- Thanks for the question regarding
      "Select Rowids from different tables and then delete the rows where they
      match.", version 8.1.7

      You Asked
We have about 14 tables or so for each user and all of them have have 4 common
columns which are indexed.  I am to do a comparison on these tables based on a
given date and delete data that matches this criteria.  The average rows to be
deleted from each table are about 500,000.
My question is what would be the best way of accomplishing this.  I know how to
make it work in general for any user using execute immediate.  But I am trying
to look for the best query.  In the example below I am only getting the rowids
for 2 tables for test purposes.  Also for test purposes I have choosen a date
that gives very few results.

SELECT cc.rowid cc_rowid, ce.rowid ce_rowid
FROM
    ( SELECT tclmoff, tcdate, tseq, procdate, rowid
      FROM common_claim
    ) cc,
    ( SELECT tclmoff, tcdate, tseq, procdate, rowid
      FROM common_expense
    ) ce,
    ( SELECT tclmoff, tcdate, tseq, procdate, lprocdte
      FROM incident_expense
    ) ie
WHERE CC.procdate < '03-OCT-95'
AND   IE.lprocdte >= '03-OCT-95'
and   CC.tclmoff  = IE.tclmoff
AND   CC.tcdate   = IE.tcdate
AND   CC.tseq     = IE.tseq
AND   CC.procdate = IE.procdate
AND   CC.tclmoff  = CE.tclmoff
AND   CC.tcdate   = CE.tcdate
AND   CC.tseq     = CE.tseq
AND   CC.procdate = CE.procdate;

-----------------------------------------------------
Result:
CC_ROWID           CE_ROWID
------------------ ------------------
AABHfTAAGAAAACuAA3 AABHfaAAGAAAAxeAAC
AABHfTAAGAAADjnAA3 AABHfaAAGAAAAxeAAG
AABHfTAAGAAAACuAA4 AABHfaAAGAAAAxeAAF
AABHfTAAGAAAADAABx AABHfaAAGAAAAyjAAm
AABHfTAAGAAADjmACA AABHfaAAGAAAAxKAAv
AABHfTAAGAAADiXAAb AABHfaAAGAAACYUAAp

I want to save this data temporarily somehwre and then isssue delete statements
for all tables( in this case just two).  Such as:

Delete from common_claim where rowid in (select distinct cc_rowid from (A temp
table I guess));
Delete from common_expense where rowid in (select distinct ce_rowid from (A temp
table I guess));

So what be the best place to save the data temporaily and then perform the
delete statements??

      and we said...
First -- PLEASE USE 4 DIGIT YEARS.

is '03-oct-95' 1995 or 2095 or 1895?  Just use 4 digit years (we should all have
learned that somewhat painful lesson 2 years ago!).

Also, compare dates to dates and strings to strings, not dates to strings. Use
TO_DATE, use 4 digit years and ALWAYS use a date format.  You'll never be sorry
you did (you will only be sorry if you do not)

  to_date( '03-oct-1995', 'dd-mon-yyyy' )

you can

create global temporary table temp ( cc_rowid rowid, ce_rowid rowid );

and use a true temp table for temporary storage.

Review &
      Followup

      Rating:
      5
  March 14, 2002
Reviewer:
      A reader

      Rating:
      5
  March 14, 2002
Reviewer:
      A reader

      Rating: 2
Is rowid a good
      practice?  July 29, 2002
Reviewer:  Rahul
       from India
Hi Tom

Is it good to use ROWID in DML operations ?
Are you sure that it will give the consistent result forever?

Thanks

          Followup:

within a short term transaction -- rowids are fine, dandy -- ok.

There are only three cases where rowids change after a row is inserted:

o when you enable row movement in a partitioned table, update the partition key,
cause the row to physical moved from one partition to another.

o when you use an IOT (index organized table) and you update the primary key
(shame on you)

o when you use certain alter table commands such as MOVE

In a single shot transaction though (generate some rowids, do some work on them)
it is "safe". forms uses rowid heavily within a transaction for example.

July 29, 2002 Sajid -- Thanks for the question regarding "What
      does the autotrace output mean", version 8.1.7

      You Asked
Hi Tom,

Could you please explanation(very brief) the result:
e.g Whats recursive calls? whats db block gets? and so on.

Statistics
----------------------------------------------------------
          0  recursive calls
     202743  db block gets
      84707  consistent gets
          0  physical reads
          0  redo size
       2010  bytes sent via SQL*Net to client
        430  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          8  rows processed

Thanks in advance.

Regards,
Sajid Anwar
London

      and we said...
recursive calls = basically sql performed on behalf of your sql.  So, if we had
to PARSE the query for example, we might have had to run some other queries to
get data dictionary info.  that would be recursive calls.

db block gets = blocks gotten in "current" mode.  That is, blocks gotten as they
exist right now.  You'll see these for full table scans (segment headers are
read in current mode) and modification statements (we modify the block as it
exists "right now")

consistent gets = blocks gotten in consistent read mode.  This is the mode we
read blocks in with a select for example.  Also, when you do a searched
UPDATE/DELETE, we read the blocks in consistent read mode and then get the block
in current mode to actually do the modification.  A select for update will do
this as well.

physical reads = self explanatory, physical IO

redo size = self explanatory -- amount of redo generated

sorts (memory)/(disk) -- sorts done.

Review &
      Followup

      Rating: 5
Thanks a
      million.  July 26, 2002
Reviewer:  Sajid
      Anwar  from London
Hi Tom,
I was wondering how old questions appear in the list. I mean some are 200/300
days old or even higher. One more thing how somebody reviews the answer you
post. Say if you answer to A's query and send it A and A posts his review.
Then how come C comes in and submit his review?

I know this is not relevant place for this question. Please bare me.

Thanks in advance.

          Followup:

when I update a question, I update the timestamp and it bubbles back to the top
of the list.  So, the oldest quesiton in asktom could be at the top of the list.

I let anyone review a question

      Rating: 5
difference
      between block gets and consistent reads  July 26, 2002

Reviewer:  Doug C  from CT, USA
Tom - I'm not clear on the difference between block gets - those we get get as
they exist right now as in a full table scan.. and consistent reads.. as in a
select.
Suppose I did a select that required a full table scan? Then what?

Thanks.

          Followup:

if you did a select that required a full scan like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from dual;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'DUAL'

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

you would do a couple of current mode gets to read the segment header (4 in this
example) and then read all of the blocks from the table in consistent read mode.
 To understand that mode, read either "Expert One on One Oracle" (my book --
cover lots of these topics) or:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c01intro.htm#3956
which is about read consistency in Oracle.

      Rating: 5
To elaborate my
      above question  July 27, 2002
Reviewer:
      Doug  from CT, USA
Tom - I've noticed you have another question updated recently called "consistent
gets" - I guess my confusion is let's say we need some information as of "right
now".. so for each block we have to read to get this *consistent* information
including any rollback we have to get (if any), we charge a consistent get?  Is
there overlap between those consistent gets and db block gets and where are they
mutually exlusive if ever...

Thanks..

          Followup:

they are totally different.

Read:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c23cnsis.htm#17882
that explains what a consistent get is -- it is a point in time GET of the data.
 We might rollback data to a prior point in time in order to make it consistent
with the rest of your result set.

db block gets -- that is the data block as it exists right now, this instant.
consistent gets -- that is the data block as it EXISTED right then, at the point
in time it must be consistent as of.

      Rating: 3
redo
      size  July 29, 2002
Reviewer:  Nadya
       from Russia
Simple question: REDO SIZE - in bytes or in blocks OC?
Thank you for your site.

          Followup:

bytes

July 29, 2002 Al-KArim -- Thanks for the question regarding
      "Postgresql", version 8.1.7

      You Asked
Would you happen to have a database feature comparision matrix
between postgresql and oracle.
I would really appreciate if you could provide me with one.
thanks.

      and we said...
Nope.

You are going about this all wrong anyway.  What you need to do is develop a
list of what YOU desire in a RDBMS -- from features, to capabilities to options
to other things like is it available on my platforms -- all that I'm on now and
in the future, everything.

Then, you find the product you want based on your requirements.

When you go to buy a car, do you ask the BMW dealer to provide a feature by
feature comparision to Volvo?  No, you have a list of features you want -- you
test drive the cars that have those features and buy the one that satisfies you
the most.

Feature checklists for databases -- useless.  They are out of date tomorrow
(yesterday actually in all likelyhood).  Also, subject to massive
interpretation.

Review &
      Followup

      Rating: 3
Maybe I'm
      strange, but that's the way I shop for a car!  July 29, 2002

Reviewer:  Robert  from Memphis, USA
Tom,

Perhaps its because I'm on a limited budget and want to make sure I get the most
for my money... but I do like to see a checklist of features among different
cars which I may be consider buying! ;)

Thanks,

Robert.

      Rating: 5
Buy based upon
      cost per feature?  July 29, 2002
Reviewer:  Jim
      Kennedy  from Portland, OR USA
Tom,
Excellent response.

First poster.  So are you going to buy that car (BMW vs Volvo) based on who
lists the most features and divide by the price the dealer gives you?  UGH!  So
if a Volvo has a better parts to cost ratio than a BMW that makes a Volvo a
better car?  Or if you are a family of 4 and a BMW sportscar (eg M3) has a
better parts to cost ratio than a Volvo station wagon or SUV type then an M3 is
a better car?

(I don't want to start a car war, neither of these can I afford and I think they
are both excellent manufactures.)

      Rating: 5
Robert you are
      partially right,  July 30, 2002
Reviewer:
      Samosa Kachori  from Like a mouse in the cage...I am trapped
      inside my cubicle
>>Maybe I'm strange, but that's the way I shop for a car!

You are strange!
;-)


Subject | Article | Last Updated (East coast time)
Reverse Key Indexes | article only | Jul 30, 2002 01:32 pm
temporary tablespace | article only | Aug 05, 2002 11:25 am
Run away archival generation | article only | Aug 05, 2002 11:27 am
Statspack - negative execute to parse ratio - how to | article only | Aug 05, 2002 11:31 am
Pro*c Compilation | article only | Aug 05, 2002 07:27 pm
row(s) 1 - 5 of 5
July 30, 2002 Saibabu -- Thanks for the question regarding "Reverse Key Indexes", version 8.1.5
You Asked
Hi,I read that Reverse key Indexes are helpfull in OPS
environment where some insert's concentrate on few leaf blocks
and also in OLTP OPS environment.I didn't understand the
mailoffice example given in Oracle Documentation.I request you
to explain with some detailed example on how does Reverse key
indexes perform better.Does branch nodes bytes also reversed.
 Thanks
 Saibabu
and we said...
Lets say you are using a sequence to populate a primary key
field.  Sequences generate numbers like 1, 2, 3, 4, 5, .....
Everyone is hitting the same side of the index -- from every
node.  You have a terribly hot spot there where all insertions
need the same part of the index to work on.

A reverse key index is an attempt to spread the load out over
the entire index structure so people are hitting the left, the
right, the middle, all over the index -- not just adding an
increasing number to the one side.

Before the index entry is added to the index structure, all of
its bytes are "reversed" -- so yes, this affects all elements of
the index structure.

It is as simple as that -- instead of all inserts hitting one
side of the index, producing mass contention on one side of the
index -- a reverse key index will tend to distribute the inserts
throughout the entire structure more evenly.
Review & Followup
Rating: 2
Useful but leads to more questions... July 30, 2002
Reviewer: Mike Jones from England
Hi,

we seem to be having this problem on our product, and we have several tables all
linked in a kind of PO header, PO detail way (in fact there are 7 tables)

all of these tables have their PK populated from a sequence, and as they all
relate to each other all of the FK links are from a sequence as well.

My question is should we reverse sequence all of these?  and if so what are the
impacts?can you give any good advice for when to reverse key the index and when
not to?

Thanks,

Followup:
Get Jonathan Lewis's book "practical Oracle8i" -- great book and discusses this
in detail.  You won't be sorry you bought it for all of the other nuggets of
truth you'll find in it.

Rating: 4
Correct me if I'm wrong but.. July 30, 2002
Reviewer: DBA from Columbus, OH
Don't reverse key indexes prevent index range scans from occurring?

If so, you'd probably want to benchmark any queries that were previously using
range scans before using them in production...

Followup:
you are correct -- I do not thing reverse key indexes would be appropriate here
(not for that reason necessarily).  If you get practical Oracle8i -- you'll get
the reason why (takes a page or two to explain)

August 05, 2002 sunil -- Thanks for the question regarding "temporary tablespace", version 8.0.5
You Asked
hi tom,
   i have oracle 8.0.5 running on solaris, we have a script to
populate the data warehouse, now there is one table which had
about 2.1 million rows.  we usually create the table from the
views using " create table <tab> as select * from <view>" . now
creating this table usually we face the problem "cannot extend
the temp space <by some value> in <user tablespace>".
   now sometimes this doesnot happen everyday but in two or days
gap this do happen. what is the solution for this..
should i try to increse the temp tablespace size..user
tablespace size or partition the table or make the temp offile
then online before the processing(this is what we are doing
right now).

sunil.
and we said...
You need to make the temporary tablespace bigger -- period.

The view must have some aggregates or sorts it must build in
order to be answered.  These take space in temp.  If other users
are using some of temp -- we won't be able to finish the view
creation.

The only permanent solution is to size things appropriately for
your concurrent workload.  Make sure you are using a true
"temporary tablespace" (alter tablespace T temporary) to avoid
excessive space management calls and to prevent people from
putting objects in there permanently.

Another thing I do is to make my datafiles for temp
autoextensible (some people swear by this, some swear at it --
its up to you).  In this fashion, the datafiles can grow to some
maxsize (i always set a maxsize on them) and periodically I
"shrink" them back down as needed.  (in order to shrink a
temporary tablespace's datafile, you typically need to alter the
tablespace to be permanent and then back to temporary -- this
will make us un-allocate the extents.  In a true temporary
tablespace we do not deallocate the extents after we get them --
we manage them internally instead.).  The double alter can take
quite a while so be prepared to be patient with it and only do
it when you really need to (the overhead of reallocating those
extents all over again isn't something you want to keep going
through).
Review & Followup
Rating: 4
August 03, 2002
Reviewer: Rookie DBA from Bombay, India
Hey Tom,
I'm using the following query to find out which are the transactions that are
using temp tablespace.

==
break on report
compute sum of usage on report
--
select vss.sid, vss.serial#, vss.username, vsu.extents, vsu.blocks,
vsu.blocks*8192/1024/1024 usage
from v$session vss
,v$sort_usage vsu
where vsu.session_addr = vss.saddr
/
clear breaks
=====
does it mean whatever space I have allocated for temp tablespace - the sum that
I got in the above query is potentially available space for the next
transactions, so that this space can be used by them...?

please claryfy. appreciate your reply!

Followup:
the result of that query will show you how is using how much of temp at that
point in time.

When those people are done, that space will become available for others. Until
then -- it is in use.

August 05, 2002 Rookie DBA -- Thanks for the question regarding "Run away archival generation", version 8.1.7.1
You Asked
Hi tom,

Today our production database behaved funny! It started switching log files of
size 50M in about 15 seconds. While there is no activity at all on the database
still the log generation was too much, there by our archive log destination got
full and even when we tried to move, the space got filled up immediately, by
archival generation.

I would really like to know why this happens! are there any known problems in
8.1.7.1 running on HP Unix.

I had to shutdown the database, and bring it up, as soon as I bring it up it
started the same thing! One thing I obsrved is that there are snapshots jobs
that were getting submitted in an unusual manner. I had to get rid of all of
them by means of dbms_job.remove

after this the archival generation stopped, not really sure whether this took
care of the problem. Have you noticed this kind of problem before?

appreciate your quick response

regards,
_Anil
and we said...
Umm, "while there is no activity" and "massive redo log generation" do not
compute.  One does not happen without the other.

You just answered the question for us "there are snapshot jobs that were getting
submitted in an unusual manner" -- there is your activity.

Define "unusual manner" -- that has no meaning for me.  You do understand that a
snapshot refresh can generate lots of log as it generates lots of activity?
Review & Followup
Rating: 3
June 06, 2002
Reviewer: Rookie DBA from Mumbai, India
Well what I meant saying unusual manner, is that the jobs are scheduled at
various intervals, and I suspected that (may not be correct) they were getting
more than often, we have got only few snapshot jobs that are scheduled every
hour.
Also they were 4 snapshot processes agianst the log_archive_max_processes=4.
While I know that these can get submitted if the system demands it. just wanted
to know is there any catch here! Also the snapshot processes were dyind randomly
and taking hude amounts of system resources, specially CPU. are we hitting any
bugs here, just a thought!!! waiting for your reply...

thanks again,
_Anil

Followup:
"dying randomly" is vague -- they will record an error in the alert log and
generally create a trace file.

Way too early to tell if this is a bug (personally, i doubt it, sounds more like
a problem on your end at this point).

There is no relationship between the number of job_queue_processes and the
log_archive_max_processes at all.

What happened as best I can tell -- your snapshots were all trying to run, 4 of
them at a time can run, they generate gobs of redo (as expected), you were not
expecting them all to run at the same time -- hence you were surprised by the
amount of redo.  You had insufficient archive space configured, your database
got stuck archiving -- rather then freeing space (moving archives to tape) and
allowing the system to continue -- you shutdown.  This increased the problem as
upon restart the jobs (snapshots) would start OVER (not pick up where they left
off, start over) -- generating yet again ALL OF THE REDO they just generated.
The cycle continues...

Rating: 1
June 06, 2002
Reviewer: Rookie DBA from Mumbai, India
Also I would like to know is there any way to find out which are all the
processes (sids) are writing to the online redo logs/there by archive logs!!

appreciate your direction...

Followup:
look at v$transaction.

things that have transactions are things that are currently write to the redo
logs.

Rating: 5
Helpful reply / Ratings June 06, 2002
Reviewer: Jim from Australia
Funny how people seem to rate the replies not on how helpful
Tom's reply was but the level it agreed with them
eg A question submitter suggests the cause of a problem
   (usually stating they think it's a bug)

   If Tom's reply disagrees, saying that they perhaps are mistaken in their
diagnosis and he suggests ways to analyse the problem further ... Give it a low
rating!

Come on people the guy does this in his own time and
responds quicker and with a stack more advice then Oracle
support does, show him some appreciation.

I can't think of a reply he has given that deserves
a poor rating. Sometimes the answer he has given me
isn't what I wanted to hear, but it was still very informative and so far all
have been correct.

Rating: 5
Ratings June 06, 2002
Reviewer: Farid from Toronto
Hey Jim,
Agree with U. One has to think that this is the Service / Favor provided by Tom
and his Team. It is NOT their Job that If you are Using Oracle the whole Oracle
Corp. is become a Servant.

Rating: 4
June 06, 2002
Reviewer: sagi from India.
I agree Jim.

Mr. Rookie you need to learn appreciating others. Dont feel bad but what Jim
said is 100% correct.

Tom, Once again I take this opportunity to thank you on my and behalf of other
fans of yours.

Regards.

Rating: 5
Really helpful!! June 06, 2002
Reviewer: Rookie DBA from Mumbai, India
thanks again tom!

what happened today again was the same thing!! I refreshed a snapshot, and the
db started going generating all over again! it doesn't seem to end!!

if we get down to the numbers we have abt 100gigs of db, and the log size is
50M, on a normal day log file generation has been 80-100 logs. what I'm conerned
abt is that these snapshots used to run even before this happened. thats the
only thing I'm concerned abt!
sorry abt log_archive_max_processes, I meant job_queue_processes.

guys thanks for the astounding replies!! I do appreciate and infact admier this
guy how he could manage and get back in such a short time!!! sorry abt the
rating, my mistake!! to tell you the truth I did not look at the rating that I
was submitting, was thinking abt toms reply and nothing at all!!! thanks much
for pointing out!!

thanks again!!

Followup:
The amount of changes applied by the snapshot refresh will be a direct function
of the amount of changes to be applied.

What changed recently? What changed on your system?

What changed at the REMOTE SYSTEM (perhaps they reloaded the data for example,
perhaps it is doing a full refresh)

Rating: 3
June 07, 2002
Reviewer: Rookie DBA from India
Thanks again Tom.

Hmm nothing had changed recently in the system. Infact the snapshots are in the
same database, therefore no remote db is inolved in this. Only thing is that the
data selection from different data source takes huge amounts of time for online
reports, therefore we have opted for this solution, wherein we refresh the
snapshots at regular intervals and the users can see reports quickly online.

I'm trying to mine the log files and see what happened during that time. looks
like I haven't got lot of clues out of that. I do remember your first answer
that huge archival generation is a normal behaviuor, but it did not happen
before. sorry for being too repetitive.
Also what I have notice is that the log switch doesn't happen until the snapshot
refresh finishes. meaning the crazy log switches happen only after the snapshot
refresh finishes. any clues...

greatly appreciate your help.

thanks

Followup:
Then is it *not* the snapshot refreshes.  It is something else.

Search this site for "showsql", run that when it happens again and you'll see
what sql is running - from there, you can narrow it down.

Rating: 3
June 07, 2002
Reviewer: Rookie DBA from India
Hey Tom,
thanks for the quick reply! yup I have the similar query with me that does the
same thing as showsql. I will do that.

this thing happened three times by now, in three days. Immediately after the
refresh! that made me think that snapshot could be the problem.
I will investigate and get back to you!

thanks again!!

Rating: 4
Found the problem August 03, 2002
Reviewer: Rookie DBA from Bombay, India
Hey Tom,
I know its after a logn time; I guess I foudn the original reason behind the
problem! what I obsrved and replicated was that there was a bug in the
application code, which kept on generating huge number of sequence numbers about
6 million records got inserted into one of the tables. Essentially about 6
million times sequence.nextval was executed. and I was that there was a constant
lock on seq$ table.

does this make sense? I tried it in a test system selecting from a sequence and
I was that when there is a lock on this table, then the system generates huge
logs...

hope this clears everything!

regards

Followup:
It is not a lock on the seq$, rather it is the RECURSIVE SQL to update seq$ for
every 20 rows (by default, the cache size is 20) that did it.

So, you had hundreds of thousands of updates to seq$ not to mention the 6
million rows inserted.

That was the cause of your issue.

Rating: 5
It is a very interesting. August 06, 2002
Reviewer: Helena Markova from Bratislava, Slovak republic

Rating: 5
Thank you very much August 06, 2002
Reviewer: Rookie DBA from Bombay, India
Thanks Tom. Appreciate your reply.

Rating: 5
Good series June 26, 2003
Reviewer: EE from Georgia USA
Nice. Each cycle taught something

August 05, 2002 Leigh -- Thanks for the question regarding "Statspack - negative execute to parse ratio - how to", version 8.1.6
You Asked
Hi Tom,

The window of opporturnity has finally opened for me to ask a question, yah!!

(I have posted this to the newsgroup but only 1 response, please let me know
what's your opinion, thanks!)

While running statspack to diagnose a performance problem that my users
complained about, I found that the snapshots taken while this problem occured
(very slow switching between application screens while other users were running
reports or other processes) indicated a negative 'execute to parse' ratio:

Instance Efficiency Percentages (Target 100%)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            Buffer Nowait %:   99.98       Redo NoWait %:  100.00
            Buffer  Hit   %:   98.81    In-memory Sort %:   99.97
            Library Hit   %:   99.16        Soft Parse %:   98.67
         Execute to Parse %:  -33.17         Latch Hit %:  100.00
Parse CPU to Parse Elapsd %:   70.50     % Non-Parse CPU:   99.98

 Shared Pool Statistics        Begin   End
                               ------  ------
             Memory Usage %:   36.50   37.12
    % SQL with executions>1:   73.20   63.62
  % Memory for SQL w/exec>1:   67.10   62.24

-----------
From your previous Q&A in this forum, you wrote that
1) that means someone is parsing statements and NEVER executing them.   They are
just chewing up your CPU, latching the shared pool, killing your performance
....
and also:
There are tools (bad ones) that parse a statement but never execute it.  Or they
parse it to discover what inputs/outputs it takes and then later on parse it
again to execute it (so the parse/execute is 2:1).
It is commonly referred to as "inefficient coding techniques".  I see it with
some 3rd "generic" libraries.  They parse like there is no tomorrow -- works
great in a single user environment -- starts to stink with two users (or more).
-------

My question is: how to narrow down the culprit that cause this problem?

Where do I look in the rest of the statspack report? (Top 5 Wait Events, SQL
ordered by ..?)

The application is written in Powerbuilder, and the users access the
application through Citrix. Oracle version 8.1.6 on Window NT.

Thanks for your help,

Leigh
and we said...
You can query v$sesstat joined to v$statname to see what session has big "parse"
counts and low "execute" counts.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4  and lower(a.name) like '%' || lower('&1')||'%'
  5  and b.value > 0
  6  /
old   4: and lower(a.name) like '%' || lower('&1')||'%'
new   4: and lower(a.name) like '%' || lower('parse')||'%'

NAME                                VALUE
------------------------------ ----------
parse time cpu                         64
parse time elapsed                     75
parse count (total)                  4157
parse count (hard)                    182

Elapsed: 00:00:00.10
ops$tkyte@ORA817DEV.US.ORACLE.COM> @mystat execute
ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4  and lower(a.name) like '%' || lower('&1')||'%'
  5  and b.value > 0
  6  /
old   4: and lower(a.name) like '%' || lower('&1')||'%'
new   4: and lower(a.name) like '%' || lower('execute')||'%'

NAME                                VALUE
------------------------------ ----------
execute count                       35303

That'll be the culprit.

You cannot look in statspack to find the culprit as statspack is an aggregate
database view and you need to find the sessions.

POWERBUILDER is infamous for doing this.  You might want to set
"session_cached_cursors" just for them.  Or use the native Oracle driver if they
have one.  To be "data independent", they will parse "select * from t" to find
out what T looks like -- you or I would query a data dictionary table to get
that info.
Review & Followup
Rating: 5
Very Good May 09, 2002
Reviewer: kiro

Rating: 5
Ver 8.1.7.0 KEEP Pool statistics June 04, 2002
Reviewer: Dave
Hi,

We are observing the following Buffer Pool Statistics in the statspack report:

                                                      Free    Write     Buffer
       Buffer    Consistent    Physical   Physical  Buffer Complete       Busy
P        Gets          Gets       Reads     Writes   Waits    Waits      Waits
- ----------- ------------- ----------- ---------- ------- -------- ----------
D      71,413             0      71,202        492       0        0          0
K           0             0           0          0       0        0          0

Why are we seeing no details for the KEEP Buffer pool? I have assigned 15 tables
to the KEEP pool. I used

ALTER TABLE <table_name> STORAGE (BUFFER_POOL KEEP);

Is there anything else to be done to have the tables in the KEEP pool? Or is
there any other way to determine if my tables in the KEEP pool are really being
efficient?

I tried to query V$BUFFER_POOL_STATISTICS, and I get

PHYSICAL_READS DB_BLOCK_GETS CONSISTENT_GETS
-------------- ------------- ---------------
          1502             0               0

Thanks a lot for all your useful insights...

Followup:
and what does

SQL> show parameter buffer_pool

show?  did you configure a keep pool?

Rating: 4
KEEP pool June 05, 2002
Reviewer: A reader
SQL> show parameter buffer

NAME                                 TYPE    VALUE
------------------------------------ ------- ------------------------------
buffer_pool_keep                     string  4000
buffer_pool_recycle                  string
db_block_buffers                     integer 40000
log_buffer                           integer 1024000
use_indirect_data_buffers            boolean FALSE

Followup:
Well, I quite simply cannot reproduce -- ASSUMING you actually queried those
tables.

I altered my "big table"

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec show_space( 'BIG_TABLE' )
Free Blocks.............................2
Total Blocks............................14976
Total Bytes.............................122683392
Unused Blocks...........................7
Unused Bytes............................57344
Last Used Ext FileId....................7
Last Used Ext BlockId...................22921
Last Used Block.........................57

to be in the keep buffer pool, issued a select * from it and statspack shows me:

^LBuffer Pool Statistics for DB: ORA817  Instance: ora817dev  Snaps: 28 -29
-> Pools   D: default pool,  K: keep pool,  R: recycle pool

                                                      Free    Write     Buffer
       Buffer    Consistent    Physical   Physical  Buffer Complete       Busy
P        Gets          Gets       Reads     Writes   Waits    Waits      Waits
- ----------- ------------- ----------- ---------- ------- -------- ----------
D         104         1,533          59          0       0        0          0
K      14,991        87,390      14,991          0       0        0          0
          -------------------------------------------------------------

which is exactly what I expected.

Suggest you try a more "focused" test -- make sure the tables are in the keep
pool (query user_tables, buffer pool column).  Do a statspack.snap, run a full
scan or two, do another snap and see.

Rating: 5
SQL Text June 06, 2002
Reviewer: Nitin
The statspack report only shows partial SQL for the sql area. Is there some way
to get the entire SQL?

Example output:
 Physical Reads  Executions  Reads per Exec % Total  Hash Value
--------------- ------------ -------------- ------- ------------
             88            1           88.0     0.1    625421128
INSERT INTO STATS$SQLTEXT ( HASH_VALUE,TEXT_SUBSET,PIECE,SQL_TEX
T,ADDRESS,COMMAND_TYPE,LAST_SNAP_ID )  SELECT ST1.HASH_VALUE,SS.
TEXT_SUBSET,ST1.PIECE,ST1.SQL_TEXT,ST1.ADDRESS,ST1.COMMAND_TYPE,
SS.SNAP_ID   FROM V$SQLTEXT ST1,STATS$SQL_SUMMARY SS  WHERE SS.S
NAP_ID = :b1  AND SS.DBID = :b2  AND SS.INSTANCE_NUMBER = :b3  A

Thanks a lot for all the info.

Followup:
You'll have to go after the V$SQLTEXT_WITH_NEWLINES to get that.  with the
leading edge like that, you should be able to find the entire thing in there.

Rating: 4
Bug June 17, 2002
Reviewer: Nitin
I just found this information in Metalink:

SELECT name, (physical_reads / (consistent_gets + db_block_gets ) )  "HIT_RATIO"
      FROM V$BUFFER_POOL_STATISTICS
     WHERE ( consistent_gets + db_block_gets ) !=0
    ;

  (Note that this view shows 0 for CONSISTENT_GETS and DB_BLOCK_GETS in 8.1.7
and so the above select is useless in this release - See [BUG:1491213])

Rating: 4
Not following "Session culprit" August 04, 2002
Reviewer: Doug from Danbury, CT USA
Tom - I'm not following your statemenet "That'll be the culprit"..  The sid
isn't reported by your SQL, + the executes are higher than the parses.  Also,
isn't it odd that mystat only reports one row with executes > 0?

Followup:
yes, you'll need to add the SID (i just used a canned script I have to
demonstrate what to look for)

v$mystat should be replaced with v$sesstat. v$mystat has just your sessions row.

August 05, 2002 Kalpesh -- Thanks for the question regarding "Pro*c Compilation", version 8.1.5
You Asked
Hi Tom,
Is it possible to compile and link two Pro*c programs together.
eg. I have program1.pc and program2.pc
    I want to compile them together and want to link them.

If Yes then can you provide me some examples and sample make files.

Thank You
Kalpesh
and we said...
Yes, a pro*c program can be made of many source code files.

You will simply precompile each .pc file into a .c file and then compile and
link them as you would any C program.

See
http://asktom.oracle.com/~tkyte/proc_makefile/
for a simple makefile I use all of the time.  It simply finds ALL .pc files in
the current working directory, precompiles them -- compiles them and then links
them with the necessary Oracle libraries.  It names the resulting executable
after the current working directory.


Subject | Article | Last Updated (East coast time)
Re-compile Pro*c source from Oracle 7 to Oracle 9. | article only | Aug 06, 2002 04:02 pm
How to use htp.formHidden to display more than one page. | article only | Aug 07, 2002 08:24 am
Returning a Java Array into PL/SQL via a Java Stored Procedure and vice versa | article only | Aug 07, 2002 09:03 am
Indexing array | article only | Aug 07, 2002 10:03 am
Standby Database | article only | Aug 07, 2002 10:20 am
Saving rollup Info through MV | article only | Aug 07, 2002 02:22 pm
Dynamic SQL | article only | Aug 07, 2002 03:02 pm
cbo | article only | Aug 07, 2002 03:05 pm
Display a counter in forms while processing | article only | Aug 07, 2002 03:17 pm
Question about (asktom) Archive Questions | article only | Aug 07, 2002 06:49 pm
alternative approach to passing a java resultset to plsql as a ref cursor | article only | Aug 07, 2002 07:27 pm
Recipe to backup single 8.1.7 database NOARCHIVELOG | article only | Aug 08, 2002 07:52 am
how to format sql output | article only | Aug 08, 2002 08:38 am
Active X | article only | Aug 08, 2002 08:44 am
driving tables - reg | article only | Aug 08, 2002 09:12 am
SQL PLUS HELP COMMAND AND HELP TABLE | article only | Aug 08, 2002 09:21 am
Maintain Tables - how to rename a column | article only | Aug 08, 2002 01:03 pm
Sequence no's | article only | Aug 08, 2002 01:12 pm
session info/redo-archive | article only | Aug 08, 2002 01:28 pm
Best way to Ref a Cursor | article only | Aug 08, 2002 02:53 pm
SQL Plus | article only | Aug 09, 2002 08:09 am
about dbms_output.put_line and UTL_FILE.PUT_LINE | article only | Aug 09, 2002 08:46 am
Label format | article only | Aug 09, 2002 08:48 am
patchset 8.1.7.4 | article only | Aug 09, 2002 08:52 am
Multiple Instances of PL/SQL Procedures | article only | Aug 09, 2002 08:53 am
SQL Query Tuning and identifying Indexes | article only | Aug 09, 2002 08:55 am
Bind Variables with In Lists | article only | Aug 09, 2002 09:02 am
Stored Procedures and Space Fragmentation in System Tablespace | article only | Aug 09, 2002 09:19 am
Is group by equivalent to distinct | article only | Aug 09, 2002 12:45 pm
Modifying lob segments | article only | Aug 09, 2002 12:47 pm
How to get all user ids that are in the Portal sessions similar as V$session. | article only | Aug 09, 2002 12:56 pm
Capture the explain plan | article only | Aug 09, 2002 04:56 pm
How to change case of whole array | article only | Aug 09, 2002 05:11 pm
sql loader | article only | Aug 10, 2002 09:25 am
Hybrid OLTP-DSS application -- shared pool problems | article only | Aug 10, 2002 09:38 am
Before starting to TUNE a query | article only | Aug 10, 2002 10:41 am
differnt questions | article only | Aug 10, 2002 10:45 am
direct load not using temp tablespace | article only | Aug 10, 2002 04:50 pm
freelists on hash partitioned table | article only | Aug 10, 2002 05:03 pm
Date variable in sqlplus | article only | Aug 11, 2002 07:57 pm
about Import | article only | Aug 12, 2002 08:04 am
To do dml on a cursor records? | article only | Aug 12, 2002 08:07 am
Top 5 items | article only | Aug 12, 2002 08:07 am
Maximum number of cursors | article only | Aug 12, 2002 08:10 am
Extents Question | article only | Aug 12, 2002 08:24 am
SPF in 9i | article only | Aug 12, 2002 09:41 am
A question about OO4O | article only | Aug 12, 2002 10:02 am
No Data Found | article only | Aug 12, 2002 10:05 am
To optimize a program unit | article only | Aug 12, 2002 10:06 am
Parallel execution | article only | Aug 12, 2002 10:32 am
The Dreaded Table is Mutating Error | article only | Aug 12, 2002 10:35 am
HTML Form that Updates Table | article only | Aug 12, 2002 10:39 am
UTL_SMTP | article only | Aug 12, 2002 12:56 pm
Oracle Standards | article only | Aug 12, 2002 02:01 pm
error line# from exception | article only | Aug 12, 2002 05:49 pm
Unused OBJECTS | article only | Aug 12, 2002 06:48 pm
Execute executable | article only | Aug 12, 2002 07:48 pm
row(s) 1 - 57 of 57
August 06, 2002 Tim -- Thanks for the question regarding "Re-compile Pro*c source from Oracle 7 to Oracle 9.", version 9.0.1
You Asked
I have a few programs that were run on Oracle 7 on Solaris 2.5.1.
I want to upgrade then to Oracle 9 on Solaris 2.8.
I setup the environment and do the recompile on source and found the following
error. -xalias_level=weak -D_REENTRANT -DSS_64BIT_SERVER -DBIT64 -DMACHINE64 -K
-DPRECOMP -I/export/home/oracle/OraHome/precomp/hdrs
-I/export/home/oracle/OraHome/precomp/public
-I/export/home/oracle/OraHome/precomp/include
-I/export/home/oracle/OraHome/oracore/include
-I/export/home/oracle/OraHome/oracore/public
-I/export/home/oracle/OraHome/oracore/port/include
-I/export/home/oracle/OraHome/xdk/include
-I/export/home/oracle/OraHome/xdk/public
-I/export/home/oracle/OraHome/nlsrtl/include
-I/export/home/oracle/OraHome/slax/include -I. -DSLMXMX_ENABLE -DSLTS_ENABLE
-D_SVID_GETTOD -D_REENTRANT -L/export/home/oracle/OraHome/precomp/lib/
-L/export/home/oracle/OraHome/lib/ -R/export/home/oracle/OraHome/lib  -c" -b
-strconst -Xa -D__SunOS_5_8 -D__SUNPRO_C=0x510 -Dunix -Dsun -Dsparc -D__unix
-D__sun -D__sparc -D__sparcv9 -xarch=v9 -D__BUILTIN_VA_ARG_INCR -D__SVR4
-D__RESTRICT -D__PRAGMA_REDEFINE_EXTNAME -F -I/u01/opt/SUNWspro/WS6U1/include/cc
-O2 -D__unix -Dunix -D_solaris_ -DODETCP -D_REENTRANT -DSS_64BIT_SERVER -DBIT64
-DMACHINE64 -DPRECOMP -DSLMXMX_ENABLE -DSLTS_ENABLE -D_SVID_GETTOD -D_REENTRANT
-i tcsrv.c -o /tmp/acomp.12334.1.s -r /tmp/acomp.12334.0.ir
/u01/opt/SUNWspro/WS6U1/bin/iropt -O2 -xarch=v9 -xcache=generic -xchip=ultra3 -F
-xvector=no -I -o /tmp/iropt.12334.2.ir /tmp/acomp.12334.0.ir
rm /tmp/acomp.12334.0.ir
/u01/opt/SUNWspro/WS6U1/bin/cg -oo tcsrv.o -F -d -O2 -s -xarch=v9
-xcache=generic -xchip=ultra3 -xvector=no -xcode=abs44 /tmp/acomp.12334.1.s
/tmp/iropt.12334.2.ir
cg: assertion failed in file ../src/codegen/procgen.cc at line 967
cg: make_entry: no IR_ENTRY - v9 abi calling requires it
cg: 1 errors
rm /tmp/acomp.12334.1.s
rm /tmp/iropt.12334.2.ir
rm tcsrv.o
rm tcsrv.o
*** Error code 1
make: Fatal error: Command failed for target `tcsrv'

The "assertion failed in file ../src/codegen/procgen.cc at line 967" happened to
all of my programs when I do the recompile.
Can you please provide some hint to my problem?.

Thank you.

Regards,
Tim Hoang.
and we said...
This is a sun sparcworks compiler error:
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&threadm=36C9E980.7B020E19%40rorke.com&rnum=1&prev=/groups%3Fhl%3Den%26lr%3D%26ie%3DUTF-8%26selm%3D36C9E980.7B020E19%2540rorke.com
Has a bit about it.  The answer from Sun was "time to upgrade the compiler to
see if that fixes it".

You'll really need to contact sun on this one -- have you tried compiling ANY
programs at all (not just Oracle ones).

It could also be (but there isn't enough above) that you have /usr/ucb in your
PATH before the sparcworks stuff and you are using the WRONG cc

Here is a support note on this topic:

Article-ID:         <Note:1082095.6>
Circulation:        PUBLISHED (EXTERNAL)
Folder:             platform.Unix
Topic:              Miscellaneous
Title:              ERROR COMPILING SAMPLE1 WITH PROC
Document-Type:      PROBLEM
Impact:             LOW
Skill-Level:        CASUAL
Server-Version:     08.01.05.XX
Updated-Date:       23-JUN-2000 16:07:50
References:
Attachments:        NONE
Content-Type:       TEXT/PLAIN
Keywords:           CONFIGINFO;
Products:           5/RDBMS (08.01.05.XX);
Platforms:          453 (2.7);

Problem Description:
====================

You are attempting to compile the target sample1 for Pro*C and it fails with
the following error:

cg: assertion failed in file ../src/codegen/procten.cc at line 965

  %  cd $ORACLE_HOME/precomp/demo/proc
  %  make -f demo_proc64 sample1

cg: assertion failed in file ../src/codegen/procten.cc at line 965
cg: make_entry: no ir_entry-v9abi calling requires it
cg:1 errors

Solution Description:
=====================

Verify that you are using the correct C Compiler:

install the /opt/SUNWspro/bin/cc
or include in path statement

  %  which cc

INCORRECT: /usr/ucb/bin/cc
CORRECT: /opt/SUNWspro/bin/cc

If /usr/ucb/bin/cc is returned, you are using the incorrect version of the C
Compiler.  The correct version is Sun Microsystems C compiler and is located
in the  /opt/SUNWspro/bin/cc directory

If this directory does not exist, contact your systems administrator to install
the correct C Compiler.  If it does exist, modify the PATH environment variable
so that the Sun compiler is before the UCB compiler.

Solution Explanation:
=====================

The /usr/ucb/bin/cc is not a compatable compiler
Review & Followup
Rating: 4
Pro*C on recompile August 06, 2002
Reviewer: Tim Hoang from Hong Kong, SAR, Asia.
Thank you very much for your comments. That help me alot.

August 07, 2002 Richard -- Thanks for the question regarding "How to use htp.formHidden to display more than one page.", version 8.1.7
You Asked
Hi Tom,

I have two questions:

1. How can I get the next n pages to be displayed in the demo below? I suspect
it has to do with a hidden field but I can not get it to work when the user
types an ename to search and the query returns multiple pages to display.

2. How can I simplify the "select" statement? I will need to provide numerous
formating (left, right, and center, justified and in color) in the productions
version of the db. Any comments for improving the code will be appreciated.

Thanks,
Rich

 CREATE OR REPLACE package pkg2
as

/*
MISMATCH VARIABLES IN FORM NOT IN PROCEDURE:
NON-DEFAULT VARIABLES IN PROCEDURE NOT IN FORM: G_ENAME

Works for the first 4 rows...

*/

PROCEDURE startup ;

procedure Emp_Table(p_start in number default 0, g_ename in varchar2);

end;
/
CREATE OR REPLACE PACKAGE BODY pkg2
as

PROCEDURE startup is

g_ename varchar2(100) ;

BEGIN

HTP.HTMLOPEN;
--TITLE BAR
HTP.HEADOPEN;
HTP.TITLE('MY GENERIC WEB PAGE');
--HEADER
HTP.TABLEOPEN;
HTP.TABLEROWOPEN;
HTP.TABLEDATA('<H1><CENTER><I>Generic Web Page</H1></CENTER></I>');
HTP.TABLEROWCLOSE;
HTP.TABLECLOSE;
HTP.HEADCLOSE;

--MAIN BODY
HTP.PARA;
HTP.FORMOPEN('pkg2.Emp_Table?p_start=0');
HTP.TABLEOPEN;
HTP.TABLEROWOPEN;

HTP.TABLEDATA(HTF.BOLD('Enter Emp Name :'));
HTP.TABLEDATA(HTF.FORMTEXT('g_ename',cvalue => htf.escape_sc(g_ename)) ) ;

htp.formHidden( 'g_ename', cvalue => htf.escape_sc(g_ename) );

HTP.TABLEROWCLOSE;
HTP.TABLECLOSE;

htp.formSubmit(NULL, 'Find Emp' , NULL);
htp.formReset('Clear Form', NULL);

EXCEPTION
  WHEN OTHERS THEN
      HTP.P('<H4>');
      HTP.P('Error: ');
      HTP.P('</H4>');

END;

procedure Emp_Table(p_start in number,  g_ename in varchar2  )
is
l_more_data BOOLEAN  DEFAULT FALSE;
v_count     PLS_INTEGER ;
v_sql       VARCHAR2(4000) ;
v_page      PLS_INTEGER := 4 ;  -- Show only 4 rows per page..

v_job       emp.job%type :=  '' ;  -- get all jobs.

csr         INTEGER;  -- cursor

begin

 htp.formHidden( 'g_ename', cvalue => htf.escape_sc(g_ename) );

-- Debug message
HTP.P('<H4>');
HTP.P(g_ename);
HTP.P(p_start);
HTP.P('</H4>');

 -- Execute this only once. but how, where?
   select count(*) into v_count from emp ;

-- Can the select statement be simplified?
-- I'm trying different formating for future development...
v_sql := 'SELECT
             ''<DIV ALIGN = LEFT>  ''||''<font size = 4 >'' ||
htf.escape_sc(ename) ||''</font>''||''</DIV>'',
             ''<DIV ALIGN = RIGHT> ''||''<font size = 3 >'' || to_char(empno,
99999)|| ''</font>''||''</DIV>'',
             ''<DIV ALIGN = CENTER>''||''<font size = 2 >'' ||
htf.escape_sc(INITCAP(job))|| ''</font>''||''</DIV>'',
             ''<font size = 2 FACE="Arial,Helv,Helvetica" ><B><I>''
||to_char(mgr, 99999)||''</I></B></font>'',
             ''<font size = 4 FACE="Arial,Helv,Helvetica" ><font
color="#009922">
                 <B><I>'' || to_char(hiredate,
''MM/DD/YYYY'')||''</I></B></font></font>'' ' ||
         '  FROM emp '||
         ' WHERE ename LIKE UPPER(''%'' || :lastname || ''%'')'||
         '   AND job   LIKE UPPER(''%'' || :job || ''%'')'||
         ' ORDER BY job, ename' ;

csr := owa_util.bind_variables(
           TheQuery => v_sql,
           bv1Name  => 'lastname', bv1Value => g_ename,
           bv2Name  => 'job',      bv2Value => v_job) ;

htp.tableOpen ('border = 3 bgcolor=#ffffa0', NULL, NULL, NULL, 'CELLPADDING =
2');

htp.tableHeader ('Emp Name');
htp.tableHeader ('Emp No');
htp.tableHeader ('Job');
htp.tableHeader ('Manager');
htp.tableHeader ('Hire Date');

owa_util.cellsprint( csr,
                     v_page,
                     null,
                     p_start,
                     l_more_data );

 htp.formHidden( 'g_ename', cvalue => htf.escape_sc(g_ename) );

htp.tableClose;
htp.para ;

htp.anchor ('pkg2.Emp_Table?p_start=0', htf.img('/ows-img/active_first.gif ') );

if ( p_start > 0 ) then
   htp.anchor ('pkg2.Emp_Table?p_start='||(p_start - v_page),
htf.img('/ows-img/active_back.gif'));
end if;

if ( (v_count - v_page) > p_start) then
   htp.anchor ('pkg2.Emp_Table?p_start='|| (p_start + v_page),
htf.img('/ows-img/active_forward.gif'));
end if ;

htp.anchor ('pkg2.Emp_Table?p_start='||(v_count - v_page),
htf.img('/ows-img/active_last.gif'));

end;
end;
/
and we said...
Well, there are a couple of conceptual problems in the above.

That global variable g_ename is the first and foremost.  The PLSQL package will
*not* maintain a state from page to page.  You need to pass that as a formal
named parameter.

"counting" the result set is a terrible idea -- so bad that I will not even
begin to demonstrate how you might do it.  The SIZE if the result set can change
while you are paginating, it is a terrible overhead, and if the result set is
LARGE, you don't want to give them the ability to goto the last page!!!  it'll
take forever.  Just give them a prev/next -- nothing else.

You are trying to use hidden fields -- yet you have no "form".

So, I rewrote the package below.  I suggest you use a "view" to hide the
complexity of the sql:

scott@ORA8I.WORLD> create or replace view emp_view
  2  as
  3  select emp.*,
  4        '<DIV ALIGN = LEFT><font size = 4 >' ||
  5            htf.escape_sc(ename) ||'</font></DIV>' html_ename,
  6        '<DIV ALIGN = RIGHT><font size = 3 >' ||
  7            to_char(empno, 99999)|| '</font></DIV>' html_empno,
  8        '<DIV ALIGN = CENTER><font size = 2 >' ||
  9            htf.escape_sc(INITCAP(job))|| '</font></DIV>' html_job,
 10        '<font size = 2 FACE="Arial,Helv,Helvetica" ><B><I>' ||
 11            to_char(mgr, 99999)||'</I></B></font>' html_mgr,
 12        '<font size = 4 FACE="Arial,Helv,Helvetica" ><font color="#009922">
<B><I>' ||
 13              to_char(hiredate, 'MM/DD/YYYY')||'</I></B></font></font>'
html_hiredate
 14   from emp
 15  /

View created.

That'll make the procedure easier and isolates the formatting in the view.
Next, you'll need a little utility routine for your ANCHOR calls:

scott@ORA8I.WORLD> create or replace function urlencode( p_str in varchar2 )
return varchar2
  2  as
  3          l_tmp   varchar2(12000);
  4          l_len   number default length(p_str);
  5          l_bad   varchar2(100) default ' >%}\~];?@&<#{|^[`/:=$+''"' ||
chr(10);
  6          l_char  char(1);
  7  begin
  8          if ( p_str is NULL ) then
  9                  return NULL;
 10          end if;
 11
 12          for i in 1 .. l_len loop
 13                  l_char :=  substr(p_str,i,1);
 14                  if ( instr( l_bad, l_char ) > 0 )
 15                  then
 16                          l_tmp := l_tmp || '%' || to_char(ascii(l_char),
'fm0X');
 17                  else
 18                          l_tmp := l_tmp || l_char;
 19                  end if;
 20          end loop;
 21
 22          return l_tmp;
 23  end;
 24  /

Function created.

It makes strings "safe" to put into URLS -- sort of like htf.escape_sc does for
forms.

Now for the package itself:

scott@ORA8I.WORLD> CREATE OR REPLACE package pkg2
  2  as
  3      PROCEDURE startup ;
  4      procedure Emp_Table(p_start in number default 0, p_ename in varchar2);
  5  end;
  6  /

Package created.

note how emp_table takes TWO formal parameters.  We must pass two things from
page to page -- what row we are on and what the search criteria is.

Now for the package body:

scott@ORA8I.WORLD> set define off

scott@ORA8I.WORLD> CREATE OR REPLACE PACKAGE BODY pkg2
  2  as
  3      g_pagesize constant number := 4;
  4

there is an appropriate use of a global in a package on the web -- as a
constant.

  5  PROCEDURE startup
  6  is
  7
  8  BEGIN
  9      HTP.HTMLOPEN;
 10
 11      HTP.HEADOPEN;
 12      HTP.TITLE('MY GENERIC WEB PAGE');
 13      HTP.HEADCLOSE;
 14
 15      HTP.TABLEOPEN;
 16      HTP.TABLEROWOPEN;
 17      HTP.TABLEDATA('<H1><CENTER><I>Generic Web Page</H1></CENTER></I>');
 18      HTP.TABLEROWCLOSE;
 19      HTP.TABLECLOSE;
 20

tabledata stuff doesn't belong in the header, moved it out...

 21      --MAIN BODY
 22      HTP.PARA;
 23      HTP.FORMOPEN('pkg2.Emp_Table');
 24

you would never pass arguments in an "anchor tag" like fashion with a form --
removed that stuff...

 25      HTP.TABLEOPEN;
 26      HTP.TABLEROWOPEN;
 27
 28      HTP.TABLEDATA(HTF.BOLD('Enter Emp Name :'));
 29      HTP.TABLEDATA(HTF.FORMTEXT('p_ename', cvalue => htf.escape_sc(NULL)) )
;
 30
 31      HTP.TABLEROWCLOSE;
 32      HTP.TABLECLOSE;
 33
 34      htp.formSubmit(NULL, 'Find Emp' , NULL);
 35      htp.formReset('Clear Form', NULL);
 36
 37      htp.formClose;

you had never closed your form!!!

 38  END;
 39
 40  procedure Emp_Table(p_start in number default 0,
 41                      p_ename in varchar2  )
 42  is
 43      l_more_data BOOLEAN  DEFAULT FALSE;
 44      v_sql       long;
 45      v_job       emp.job%type :=  '' ;  -- get all jobs.
 46      csr         INTEGER;  -- cursor

removed unnecessary variables....

 47  begin
 48
 49      -- Debug message
 50      HTP.P('<H4>' || p_ename || ',' || p_start || '</h4>' );
 51
 52      v_sql := 'SELECT html_ename, html_empno, html_job, html_mgr,
html_hiredate
 53                  FROM emp_view
 54                 WHERE ename LIKE UPPER(''%'' || :lastname || ''%'')
 55                   AND job   LIKE UPPER(''%'' || :job || ''%'')
 56                 ORDER BY job, ename' ;

simplified SQL

 57
 58       csr := owa_util.bind_variables( TheQuery => v_sql,
 59                               bv1Name  => 'lastname', bv1Value => p_ename,
 60                               bv2Name  => 'job',      bv2Value => v_job) ;
 61
 62       htp.tableOpen ('border = 3 bgcolor=#ffffa0', NULL, NULL,
 63                       NULL, 'CELLPADDING = 2');
 64
 65       htp.tableHeader ('Emp Name');
 66       htp.tableHeader ('Emp No');
 67       htp.tableHeader ('Job');
 68       htp.tableHeader ('Manager');
 69       htp.tableHeader ('Hire Date');
 70
 71       owa_util.cellsprint( csr,
 72                            g_pagesize,
 73                            null,
 74                            p_start,
 75                            l_more_data );
 76
 77       htp.tableClose;
 78
 79      htp.para ;
 80      htp.anchor ('pkg2.Emp_Table?p_start=0&p_ename=' || urlencode(p_ename),
'First Page' );

that takes us to the first page -- note the need to pass the START and ENAME
and the need to urlencode ENAME

 81
 82      if ( p_start > 0 ) then
 83             htp.anchor ('pkg2.Emp_Table?p_start='||greatest((p_start -
g_pagesize),0) ||
 84                      '&p_ename=' || urlencode(p_ename), 'Prev' );
 85      end if;

Give them a page back (Prev) anchor if we are not on the first page

 86
 87      if ( l_more_data ) then
 88             htp.anchor ('pkg2.Emp_Table?p_start='|| (p_start + g_pagesize)
||
 89                        '&p_ename=' || urlencode(p_ename), 'Next' );
 90      end if ;

If we have not yet exhausted the result set, give them a page forward

 91  end;
 92
 93  end;
 94  /

Package body created.

scott@ORA8I.WORLD> set define on
scott@ORA8I.WORLD>

And that is it....
August 07, 2002 John -- Thanks for the question regarding "Returning a Java Array into PL/SQL via a Java Stored Procedure and vice versa", version 8.1.7
You Asked
Do you have any sample code to show how a Java Array can be
passed through PL/SQL and how a PL/SQL Array or record could be
passed to a Java Array via a Java Stored Procedure?

Thanks,

John Perkins
and we said...
For java passing to PLSQL see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:712625135727
For PLSQL passing to Java see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:331217279811
...
Review & Followup
Rating: 1
Arrays from pl/sql to java August 06, 2002
Reviewer: Peter
I think the question was about passing in, not using JDBC.

ie
myArray BigArrayType
begin
myJavaCall(myArray);
end;

Followup:
Question was:

a) how a java array can be passed to plsql.  That is exactly the example at:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:712625135727
b) how a plsql array could be passed to a java array via a java sp.  That is
exactly the example at:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:331217279811
Now the fact is -- if you want java to talk to plsql you sort of NEED JDBC.
That is part of the pie here.

August 07, 2002 gaurang -- Thanks for the question regarding "Indexing array", version 8.1.7
You Asked
Hi TOM,

 I am loding 500000 rows into an array. and search that array like table e.g

select * into value from TABLE(cast(tabls as mytable))
where column_name = ....

It is very slow process. Is there any way to create index of array in memory and
speed up operation.

 We have lots of RAM so memory is not problem.

Thanks.
and we said...
Why would you do that?

Don't do that.  Use a REAL table (don't load them up in the first place).  If
the "real" table is too burdensome to query -- use a indexed global temporary
table.

In 9iR2 there will be associative arrays -- where the index can be a string
instead of just a number.

Alternatively, if you have my book -- i demonstrate how to setup a hash table
today in Oracle8.0 and up easily.  If you select "where column_name = :value",
then all you need really is a hash table in your array instead of an array
indexed by a sequential number.

I think you might be utterly surprised at the amount of ram taken by your array
as well.
Review & Followup
Rating: 4
August 06, 2002
Reviewer: A reader
Hi Tom,

 Finally we got oracle 9i in our company.
 We have 9i R2.

 I am testing associative array and BULK COLLECT feature    together.

 ORGANIZATION_NAME                         NOT NULL VARCHAR2(110)

 I am getting errors

  1   declare
  2   type t_org  is table of varchar2(110) index by varchar2(110);
  3   t_org_arr     t_org;
  4   BEGIN
  5      select organization_name BULK COLLECT INTO t_org_arr
  6        from pubs.olv@renpqa1.world@pubs_link
  7      where delete_indicator = 0
  8         and main_point_flag ='Y';
  9       dbms_output.put_line('Oraganization :'||t_org_arr('Baker Rost'));
 10       dbms_output.put_line('Total :'||t_org_arr.count);
 11*  end;
SQL> /
    select organization_name BULK COLLECT INTO t_org_arr
                                               *
ERROR at line 5:
ORA-06550: line 5, column 48:
PLS-00657: Implementation restriction: bulk SQL with associative arrays with
VARCHAR2 key is not sup
ORA-06550: line 5, column 48:
PLS-00597: expression 'T_ORG_ARR' in the INTO list is of wrong type
ORA-06550: line 6, column 7:
PL/SQL: ORA-00904: : invalid identifier
ORA-06550: line 5, column 5:
PL/SQL: SQL Statement ignored

--Can you tell me where it is wrong?

-- In oracle 8i BULK COLLECT accross database link was not supported.Is it
supported in ORACLE 9i?

-- Above error tells that associative array is not supported with BULK COLLECT.
  Can you explain why it is not supported?.

  If it support bulk collect then populating array and   seraching array would
be the BEST.

 Thanks,

Followup:
I believe the error message itself is very telling:

PLS-00657: Implementation restriction: bulk SQL with associative arrays with
VARCHAR2 key is not supported

don't you??

Think about it -- look at your SQL, what *should* happen in your mind with the
above bulk collect?  I mean -- what should the "index" in the array be?  With an
index by binary_integer -- it is logical that row 1 goes into index 1, row 2
into index 2 and so on.  With an index by varchar2 -- when you fetch
organization name -- what INDEX should be used?????

Look at your example and tell me what would/should be returned by

   t_org_arr( 'Baker Rost' )

How would we know to use the string 'Baker Rost' as a subscript??

Here is how you would do this:

scott@ORA920.LOCALHOST> declare
  2     type temp   is table of varchar2(110) index by binary_integer;
  3
  4     l_key       temp;
  5     l_val       temp;
  6
  7     type t_org  is table of varchar2(110) index by varchar2(110);
  8     t_org_arr     t_org;
  9  BEGIN
 10     select username, created BULK COLLECT INTO l_key, l_val
 11       from all_users@ora920.us.oracle.com;
 12
 13     for i in 1 .. l_key.count
 14     loop
 15         t_org_arr( l_key(i) ) := l_val(i);
 16     end loop;
 17
 18     dbms_output.put_line('Created : '||t_org_arr('SCOTT'));
 19     dbms_output.put_line('Total   : '||t_org_arr.count);
 20  end;
 21  /
Created : 13-MAY-02
Total   : 34

PL/SQL procedure successfully completed.

You'll bulk collect the KEYs (indexes, subscripts) and the VALs.  You can do
this over a dblink as shown. You would then put them into the associated array.

Rating: 4
Very interesting August 07, 2002
Reviewer: A reader from USA

Rating: 4
2-column collection INTO 2-level associative array using BULK June 12, 2003
Reviewer: Jan from Europe
I have collection:
----------------------
CREATE TYPE my_rec_type AS OBJECT(
       val_name   VARCHAR2(10),
       val    VARCHAR2(30));

CREATE TYPE my_list_type AS TABLE OF my_rec_type;
-----------------------
my_list have data like:

my_list(1) = ('AGE','10');
my_list(2) = ('AGE','15');
my_list(3) = ('GENDER','M');
...

And I want to assign these data into 2-level associative array in PL/SQL using a
 single statement (no LOOP - this I know)

-------------------------
TYPE ty_my_val IS TABLE OF VARCHAR2(30) INDEX BY BINARY_INTEGER;
TYPE ty_my_indexed_list IS TABLE OF ty_my_val INDEX BY VARCHAR2(10);
-------------------------

So the result will be:

my_indexed_list('AGE')(1) = '10'
my_indexed_list('AGE')(2) = '15'
my_indexed_list('GENDER')(1) = 'M'
...

Is it possible to do it in the way like (pseudocode) :

SELECT my_list(name,val) BULK COLLECT
  INTO my_indexed_list(index,val)
    from TABLE(CAST(my_list...

Thanks.

August 07, 2002 Yogesh -- Thanks for the question regarding "Standby Database ", version 8.1.7
You Asked
I have Implemented the standby database in the following manner ... I'm facing
one problem... Please explain

a. copied datafiles (cold backup) from source to standby database.
b. Activated archive log mode for primary database
c. created controlfile for standby database
d. Copied control file to standby database
e. made changes in tnsnames.ora (primary) and listener.ora (standby)
f. copied the init.ora file from primary database to standby database, made
changes.
g. made changes in init.ora of primary DB
h. mounted standby database
i. applied gap sequences by copying the needed files, prompted by oracle after
saying recover database.
j. to put standby db in managed mode I issued RECOVER MANAGED STANDBY DATABASE
... but I never got my SVRMGRL prompt back .. it seems to be a infinite process
... but when I checked the logs ... internally oracle was applying the logs i.e.
process of applying archive log from primary db to standby db was going on.

What is the problem ? Is it a demon kind of a process, which keeps on executing
? Please explain.
and we said...
j is correct, it does not go into the background, the prompt will not return
until you cancel the recovery.

In 9i, there is the option to "background it", it'll run sqlplus in the
background instead of the foreground.  Same effect -- sqlplus is still running
-- just in the background.

You can set up a script that backgrounds this yourself in 8i.
Review & Followup
Rating: 5
Thanx May 28, 2002
Reviewer: Yogesh Bhandarkar from Pune, India
Thanx tom, cleared my doubt.

Rating: 2
STANDBY DATABASES IN 8.1.7 August 06, 2002
Reviewer: Rich Stucke from Saint Louis, MO USA
Is there a document somewhere that takes you step by step through the
configuration and enabling of a standby database ??  Some of the steps listed in
this query is confusing me, and I want to insure I do it right.

Thanks

Followup:
YES  -- in fact, there is documentation for pretty near EVERYTHING in Oracle.
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76995/toc.htm
Oracle8i Standby Database Concepts and Administration

If you goto otn.oracle.com -- you'll find ALL Of the docs, most of which
unfortunately remain unread by the majority of the people.  Check it out --
there is alot of good stuff in there!

August 07, 2002 -- Thanks for the question regarding "Saving rollup Info through MV", version 8.1.7
You Asked
Hello Tom,

We have a situation of rolling up the data as follows:

The table definition is:

Process table:
---------------

P_Id
Date Created,
Amt,
Processed Date,
Status,
Modified Date
Include Date
Account number

The Status can be Ready(RD), Hold(HL), Cancel(CL) and others. On a particular
day for a given P_Id the Status can change from 'RD' to 'HL' or from 'HL' to
'RD' and all the other combinations.

Once the Status is 'RD', the record is picked for processing.
Say for Aug-5-2002, for a given account number the sum of all 'RD' is $5000. But
on Aug-6-2002, for a given P_Id($200) the status is changed to 'HL' from 'RD'.
Then when the sum is done for a given account number it should omit the status
changed record and consider the rest of them. So on Aug-6-2002 the report should
show 4800$ as the balance.

My problem here is to incorporate a logic which shows that at the end of each
day what the sum is without losing the updated records.

The materialized view should not be refreshed each night, or the info
in the materialized view should be saved before refreshing.

That is on Aug-5-2002 the sum is 5000$ (for a given account number and date
created)
           Aug-6-2002 the sum should be 4800$.

We are currently planning to implement this logic by inserting the
changed(updated) records in to another table and then comparing them
with the latest record in the master table and all sort's of things
omitting the MV and query rewrite capability. The whole logic gets extremly
complicated and its tough to maintain the rollup info.

Q)  Is there a way to use materialized view for this kind of rollups?

There are about 4 million records which get inserted daily and about 5% of them
get their status changed every day.

Please advise?

Thanking you.
and we said...
why not just run something like:

insert /*+ APPEND */ into your_report_table
select trunc(sysdate), p_id, sum(amt)
  from t
 where status = 'RD'
   and exists ( select null
                  from t t2
                 where t2.p_id = t.p_id
                   and modified between trunc(sysdate) and trunc(sysdate)+1
                   and modified <> trunc(sysdate)+1
              )
group by p_id;

every night?  (or some variation of that like:

insert /*+ APPEND */ into your_report_table
select trunc(sysdate), p_id, sum(amt)
  from t
 where status = 'RD'
   and p_id in (select p_id
                  from t
                 where modified between trunc(sysdate) and trunc(sysdate)+1
                   and modified <> trunc(sysdate)+1
              )
group by p_id;

depending on what runs best for you given indexes and all)

That would get the sum(amts) for all RD status for any account modified that
day.  You could schedule this insert to run every night using DBMS_JOBS (that is
what an MV would use).  It would achieve your goal.
Review & Followup
Rating: 3
Can we do like this?? August 07, 2002
Reviewer: Basharat from Dubai
Tom, I am a little bit confused here... If the sub-query is from the same table
then isn't it better to write the same query like this....

insert /*+ APPEND */ into your_report_table
select trunc(sysdate), p_id, sum(amt)
  from t
 where status = 'RD'
 and   modified between trunc(sysdate) and trunc(sysdate)+1
 and   modified <> trunc(sysdate)+1
group by p_id;

I know its too trivial but just for my clarification please...
Thanks & Regards

Followup:
that'll only get records when they were modified TO BE and 'RD' status.

What about a record modified from RD to something else?  What I mean is, suppose
there is a group P_ID = 1 with two records. Both records right now are 'RD'.
Tomorrow you update ONE of them to something else.  Your query would NOT pick up
the fact that P_ID = 1 has a new sum(amt) by RD status -- mine will (cause it
looks for all P_ID's that have had SOME change)

You tell me -- is that what you want?  My point is -- just find the query that
gets you the sum(amt) when you want it and schedule that query to run daily with
dbms_job.

August 07, 2002 Katuscia -- Thanks for the question regarding "Dynamic SQL", version oracle 8.0.5
You Asked
Hi Tom

     We were trying to write a database procedure using dynamic
sql to return a value .But we are getting problem to return the
value.

Please find below the above code :
--p_tname = table name
--p_colcode = code in table
--p_colvalue1 = :code (form)
--p_coldesc =  description in table
--p_value2 = :description  (form)

PROCEDURE FETCH_DESC(p_tname in varchar2,
                     p_colcode in varchar2,
                     p_colvalue1 in varchar2,
                     p_coldesc in varchar2,
                     p_colvalue2 out varchar2) As

/* Store Procedure to return value */

source_cursor integer;
x_validate integer;
x_value varchar2(240);
Begin
source_cursor := dbms_sql.open_cursor;
 Begin
   dbms_sql.parse(source_cursor,'select ' || p_coldesc || ' from
' || p_tname || ' where ' || p_colcode || ' = :x_colvalue1'
,dbms_sql.v7);

dbms_sql.bind_variable(source_cursor,':x_colvalue1',p_colvalue1);

   x_validate := dbms_sql.execute_and_fetch(source_cursor);
   DBMS_SQL.COLUMN_VALUE(SOURCE_CURSOR,1,p_colvalue2);
--
dbms_sql.bind_variable(source_cursor,'x_value',p_colvalue2);
 End;

Thanking you in advance
Kat
and we said...
You are missing a call to dbms_sql.define_column and
dbms_sql.close_cursor:

ops$tkyte@ORA8I.WORLD> create or replace
  2  PROCEDURE FETCH_DESC(p_tname in varchar2,
  3                       p_colcode in varchar2,
  4                       p_colvalue1 in varchar2,
  5                       p_coldesc in varchar2,
  6                       p_colvalue2 out varchar2)
  7  As
  8     source_cursor integer;
  9     x_validate    integer;
 10     x_value       varchar2(240);
 11  Begin
 12       source_cursor := dbms_sql.open_cursor;
 13       dbms_sql.parse(source_cursor,
 14                      'select ' || p_coldesc ||
 15                      ' from ' || p_tname ||
 16            ' where ' || p_colcode || ' = :x_colvalue1',
 17                      dbms_sql.v7);
 18
 19       dbms_sql.bind_variable
 20       (source_cursor,':x_colvalue1',p_colvalue1);
 21       dbms_sql.define_column
 22       ( source_cursor, 1, p_colValue2, 255 );
 23
 24       x_validate := dbms_sql.execute_and_fetch
                                 (source_cursor);
 25       DBMS_SQL.COLUMN_VALUE(SOURCE_CURSOR,1,p_colvalue2);
 26           dbms_sql.close_cursor( source_cursor );
 27  End;
 28  /

Procedure created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> set autoprint on
ops$tkyte@ORA8I.WORLD> variable x varchar2(5)
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> exec fetch_desc( 'dual', 'dummy', 'X',
'dummy', :x )

PL/SQL procedure successfully completed.

X
-----------
X
Review & Followup
Rating: 5
Dynamic SQL August 07, 2002
Reviewer: Orabandit from Ireland
I don't understand why these sorts of questions are published on this site- it
is clearly a question on how to code correctly which can be ascertained from any
pl/sql manual.  Either that or the person asking this question could have
searched asktom.oracle.com and found the answer.
I would simply ignore these "lazy" questions and answer those that seem to be
from people who seem to have researched the topic prior to asking tom.
I have been using this site for nearly 6 months now and have never had the need
to even ask a question mainly due to the question having already been asked and
answered.

If you are considering writing a "lazy" question- PLEASE DON'T- spend an extra
30 minutes searching this site and your oracle manuals for the answer.  I for
one, and many others find it frustrating to find the same question when
searching asktom.

You should have a randomly generated questionnaire prior to submitting questions
which tests peoples knowledge of oracle- and if ya don't get em all right- ya
don't get to ask your question!  That'll make people read an oracle manual!

Followup:
Well, here is my perspective on it.

When I see the same or similar question asked over and over and over -- I
publish it.  For every question you see, there are AT LEAST 2 that you don't
(currently, I have recieved over 16,600 questions -- about 5500 of which are
published)

For whatever resason -- DBMS_SQL is a common stumbling block.  In looking at the
documentation and examples (none really), I would say that dbms_sql examples
make the cut every now and then for "publishing".

What people seem to appreciate most about this site is the examples -- short,
sweet to the point and working.  That is what this is about.

As for having a litmus test "are you smart enough to even be asking a question"
-- that I find totally out of the question.

As Henry Kissinger once said "do you have any questions for my answers" -- ask
away.  That is what this is all about.

Rating: 5
Thanks for being a precise and patient teacher! August 07, 2002
Reviewer: Robert from USA

August 07, 2002 -- Thanks for the question regarding "cbo", version 817
You Asked
Hi Tom
Just wanted to get a better understanding of  the cost based optimizer. What
does the "cost" and card signify. Does reducing the cost mean that performance
is increased.

Thanks
and we said...
the cost is a number we associate with a step in a query plan.  card is the
cardinality (number of rows) we expect from a step in a query plan.  Many times,
cost*card is the cost of the current/subsequent operations (the total cost for
the step is a function of the number of rows).

The optimizer develops dozens or even hundreds of plans for a query.  Each query
has a cost associated with it.  The query plan with the lowest cost wins.

The cost has NO RELEVANCE WITH RESPECT TO OVERALL PERFORMANCE.  It just means
that using the rules in place when the query plan was picked -- that query was
the BEST choice.

A query with a cost of 10,000 might run in 1/1000 of a second.  Another query
with the cost of 1 might take an hour.  The cost has no relevance with respect
to performance.

The only thing it means is that the query with that cost was choosen above the
other queries that time (use a hint and the cost of queries will change --
nothging else changed -- just that hint.  now a different query with a different
 cost won.  will it be "faster" or "slower"?  don't know.  Hints are just an
artificial means to influence the cost of various steps making them more
appealing and helping the optimizer pick them as the preferred plan.
Review & Followup
Rating: 5
Excellent explanation! October 20, 2001
Reviewer: A reader

Rating: 5
A question August 06, 2002
Reviewer: A reader from Bangalore, India
Tom,
    My question is on the cardinality figure. Eventhough my explain plan
mentions the usage of Index scan why is card equal to the total rows in the
table?

Here the total rows in the table is 63428 and the card after using index is also
63428.

TABLE ACCESS (BY INDEX ROWID) OF 'FI_SECURITY_FACT_A' (Cost=3 Card=63428
Bytes=951420)
INDEX (RANGE SCAN) OF 'FI_SECURITY_FACT_A_PK' (UNIQUE) (Cost=2 Card=63428)

Can you please throw more light on what card is.

regards
Ik

Followup:
crystal ball is a little hazy this morning, sooooo without actually seeing the
entire plan, or the query -- I'll have to hazzard a guess.

You accessed all of the rows of the table via the index.  For example:

  1* select /*+ FIRST_ROWS */ empno, ename from emp where ename > chr(0) order
by ename
scott@ORA817DEV.US.ORACLE.COM> /

     EMPNO ENAME
---------- ----------
      7876 ADAMS
      7499 ALLEN
      7698 BLAKE
      7782 CLARK
      7902 FORD
      7900 JAMES
      7566 JONES
      7839 KING
      7654 MARTIN
      7934 MILLER
      7788 SCOTT
      7369 SMITH
      7844 TURNER
      7521 WARD

14 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=HINT: FIRST_ROWS (Cost=826 Card=14
Bytes=252)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'EMP' (Cost=826 Card=14 Bytes=252)
   2    1     INDEX (RANGE SCAN) OF 'EMP_IDX' (UNIQUE) (Cost=26 Card=14)

Just like that.  That would do it.

CARD = cardinality.  The expected number of rows that will result from execting
a step in the plan one time.  In my case -- the optimizer says "I expect to get
14 rows from this unique index EMP_IDX".  (and it does -- since it is doing a
range scan on it and expects to hit EVERY row since ename > chr(0) will get
every name)

Rating: 3
How can we use the cost? August 07, 2002
Reviewer: Robert from Memphis, TN USA
Tom,

Can we use the cost information to help us tune our queries?
If so, how?

Thanks,

Robert

Followup:
You cannot influence the cost -- only see the net cost of a query.  In *theory*
a query with a lower cost should execute faster then a query with a higher cost.
 However, there are many factors that influence this and it is just software
after all so it is not always true.

The optimizer uses the cost to find the "cheapest" plan out of all possible
plans for a given sql statement.  You can look at the cost of individual steps
and say "Oh, Ok -- I see why the cost is X now".  You cannot really use the cost
to "tune" per-say.

August 07, 2002 Boris -- Thanks for the question regarding "Display a counter in forms while processing", version
You Asked
While using 'TEXT_IO' in Developer to load a csv file
into a table, I wish to have a counter dynamically displaying.

Using an item in the Block, It only displays on completion of
the
loop although it is 'refreshed' on every
text_io.get_line.

any idea ?.. Thank you
and we said...
Are you calling synchronize?

begin
      for i in 1 .. 10 loop
          :dual.dummy := i;
          synchronize;
          sys.dbms_lock.sleep(1);

      end loop;
end;

for example shows a counter in a field on the screen counting
from 1 to 10
Review & Followup
Rating: 5
Display the counter while the form is writing to the csv file August 07, 2002
Reviewer: Vasaku
Tom

I want to display the counter on the screen or on the status bar as a message
after the user clicks on the button which writes to the flat file.

How is this possible.

Followup:
Great, that is exactly what the answer above shows you how to do......

replace the call to sys.dbms_lock.sleep with text_io.put_line and away you go.

August 07, 2002 Robert -- Thanks for the question regarding "Question about (asktom) Archive Questions", version 8.1.7.2
You Asked
Hi Tom, hope you enjoyed your vacation.

I started to read the very earliest AskTom questions in the archive.
And so far the ones I have gone thru, it *seems* they are all like
one-answer-to-one-question kind, no subsequent feedbacks from other readers and
additional comments from you...
So I am wondering if it's a practice with AskTom to "trim" past questions based
on certain criteria.

Thanks
and we said...
No, I don't trim them -- It is just that:

o asktom was done via email from jan 2000 to may 2000, no followups at all.

o it was done on the web without any followup capability from may 2000 to 11
march 2001.  People could followup -- my followup was embedded in the answer
itself (see:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:752030266230
for an example of an "early" followup from me -- there is a "followup to
comment" in the answer and then later more reviews with my followup right there
with their comment.

o starting on 17 august 2001, I had the ability to followup to a specific review
August 07, 2002 Ron -- Thanks for the question regarding "alternative approach to passing a java resultset to plsql as a ref cursor", version 8.1.6
You Asked
I read in chapter 19 of your book about the alternative of accessing other
databases from the Oracle server using Java JDBC drivers.

This would be a significantly better way for us to access our AS400 data. I
loaded our type 4 drivers into an Oracle db and, after a little learning curve,
got it to return one row of data.

However, handling more than 1 row is problematic.  Apparently you can pass a ref
cursor to java, but NOT pass a java result set to PLSQL  (refer to the 'expected
behavior' mentioned in bug 987753).

My objective was to provide a PLSQL procedure that would take a query as a
string and return a REF CURSOR.

Can you help with a alternative that will be almost as flexible without adding
to much overhead?

Thank You
Ron Plank
rwp24560@gsk.com
and we said...
Here is an approach you can use -- use a temporary table like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create global temporary table result_set
  2  ( c1 varchar2(4000),
  3    c2 varchar2(4000),
  4    c3 varchar2(4000),
  5    c4 varchar2(4000)
  6  )
  7  on commit delete rows
  8  /

although you'll have LOTS of columns there (as many as you think you need)

Then, create a package like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package get_java_result_set
  2  as
  3      type rc is ref cursor;
  4
  5      procedure doit( in_query in varchar2, ref_cursor out rc );
  6  end;
  7  /

Package created.

the doit procedure will call a java stored procedure that will connect to the
AS400, query the data out and stuff it into the temp table.  At the same time,
that java stored procedure will build a query that we can select with -- to get
the data out of the temp table.

Now the package body is very simple:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package body
get_java_result_set
  2  as
  3
  4  procedure jrc( in_query in varchar2, out_query out varchar2 )
  5  is language java
  6  name 'jrc.run_query( java.lang.String, java.lang.String[] )';
  7
  8  procedure doit( in_query in varchar2, ref_cursor out rc )
  9  is
 10      l_query long;
 11  begin
 12      jrc( in_query, l_query );
 13      open ref_cursor for l_query;
 14  end;
 15
 16  end;
 17  /

Package body created.

Here is the template java.  In this case, you would have to augment the code
to actually open the connection to AS400 and create the statment on that
connection.  Other then that -- it is "good to go" pretty much (enhancment for
you -- use SUBSTR() on the character fields to "trim" them to the right length
-- eg: instead of just selecting C1, select substr( c1, 1, 1 ) in the example
below since DUMMY in DUAL is a varchar2(1) -- use the result set metadata to
figure that out)

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace
  2     and compile java source named "jrc"
  3  as
  4  import java.sql.*;
  5  import java.io.*;
  6  import oracle.jdbc.driver.*;
  7
  8  public class jrc
  9  {
 10
 11  public static void run_query( String in_query, String[] out_query ) throws
Exception
 12  {
 13      Connection conn = new OracleDriver().defaultConnection();
 14
 15      Statement stmt = conn.createStatement();
 16
 17      ResultSet rset = stmt.executeQuery(in_query);
 18      ResultSetMetaData rsmd = rset.getMetaData();
 19
 20      int counter = rsmd.getColumnCount();
 21
 22      String insert_stmt ="insert into result_set ( c1";
 23      for(int i=2;i<=counter;i++) insert_stmt = insert_stmt + ", C"+i;
 24      insert_stmt = insert_stmt + ") values (";
 25
 26      String result_set_query = "select ";
 27
 28      for(int i=1;i<=counter;i++)
 29      {
 30          if ( i > 1 )
 31          {
 32              insert_stmt  = insert_stmt  + ",";
 33              result_set_query = result_set_query + ",";
 34          }
 35          insert_stmt = insert_stmt + "?";
 36          switch(rsmd.getColumnType(i))
 37          {
 38              case java.sql.Types.VARCHAR:
 39              case java.sql.Types.CHAR:
 40                  result_set_query = result_set_query + "C" + i;
 41              break;
 42              case java.sql.Types.NUMERIC:
 43              case java.sql.Types.DECIMAL:
 44              case java.sql.Types.DOUBLE:
 45              case java.sql.Types.SMALLINT:
 46              case java.sql.Types.TINYINT:
 47              case java.sql.Types.FLOAT:
 48              case java.sql.Types.REAL:
 49              case java.sql.Types.INTEGER:
 50                    result_set_query = result_set_query + "to_char( C" + i +
")";
 51              break;
 52              case java.sql.Types.DATE:
 53              case java.sql.Types.TIMESTAMP:
 54                  result_set_query = result_set_query + "to_date( C" + i +
")";
 55              break;
 56              default:
 57                  result_set_query = result_set_query + "C" + i;
 58              break;
 59          }
 60                  result_set_query = result_set_query + " \"" +
rsmd.getColumnName(i) + "\"";
 61      }
 62      insert_stmt = insert_stmt+")";
 63      out_query[0] = result_set_query + " from result_set";
 64
 65      System.out.println( result_set_query );
 66      System.out.println( insert_stmt );
 67
 68      PreparedStatement pstmt = conn.prepareStatement(insert_stmt );
 69
 70      while(rset.next())
 71      {
 72          for( int i=1;i<=counter;i++)
 73          {
 74            switch(rsmd.getColumnType(i))
 75            {
 76              case java.sql.Types.VARCHAR:
 77              case java.sql.Types.CHAR:
 78                pstmt.setString(i,rset.getString(i));
 79                break;
 80              case java.sql.Types.NUMERIC:
 81              case java.sql.Types.DECIMAL:
 82                pstmt.setBigDecimal(i,rset.getBigDecimal(i));
 83                break;
 84              case java.sql.Types.DOUBLE:
 85                pstmt.setDouble(i,rset.getDouble(i));
 86                break;
 87              case java.sql.Types.SMALLINT:
 88              case java.sql.Types.TINYINT:
 89                pstmt.setShort(i,rset.getShort(i));
 90                break;
 91              case java.sql.Types.FLOAT:
 92              case java.sql.Types.REAL:
 93                pstmt.setFloat(i,rset.getFloat(i));
 94                break;
 95              case java.sql.Types.INTEGER:
 96                pstmt.setInt(i,rset.getInt(i));
 97                break;
 98              case java.sql.Types.DATE:
 99              case java.sql.Types.TIMESTAMP:
100                pstmt.setDate(i,rset.getDate(i));
101                break;
102              default:
103                pstmt.setString(i,rset.getString(i));
104                break;
105            }
106            if(rset.wasNull())
107              pstmt.setNull(i,rsmd.getColumnType(i));
108          }
109          pstmt.executeUpdate();
110        }
111        rset.close();
112        pstmt.close();
113        stmt.close();
114  }
115  }
116  /

Java created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set serveroutput on
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_java.set_output( 1000000 );
PL/SQL procedure successfully completed.

and now to run it -- we'll see the dynamically generated INSERT and SELECT:

ops$tkyte@ORA817DEV.US.ORACLE.COM> variable x refcursor
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec get_java_result_set.doit( 'select dummy,
sysdate dt, rownum some_number from dual', :x )
select C1 "DUMMY",to_date( C2) "DT",to_char( C3) "SOME_NUMBER"
insert into result_set ( c1, C2, C3) values (?,?,?)

PL/SQL procedure successfully completed.

and now the data:

ops$tkyte@ORA817DEV.US.ORACLE.COM> print :x

DUMMY
---------------------------------------------------------------------------------
--------------------------------------------------
DT
---------
SOME_NUMBER
---------------------------------------------------------------------------------
--------------------------------------------------
X
07-AUG-02
1

ops$tkyte@ORA817DEV.US.ORACLE.COM>

 as you can see -- adding substr to the char fields would be a nice touch
Review & Followup
Rating: 5
Thanks! September 03, 2002
Reviewer: Ron Plank from Raleigh NC USA
That'll work great!

August 08, 2002 David -- Thanks for the question regarding "Recipe to backup single 8.1.7 database NOARCHIVELOG", version 8.1.7
You Asked
Tom,
What is the simplest to follow recipe for backing up a single Oracle 8.1.7.3
database in NOARCHIVELOG mode, and capturing everything needed to be able to
restore it after a total hardware failure ?
Criteria include no second system or database around to use for RMAN catalogue.
The database is in NOARCHIVELOG mode because the system owners dont want to keep
having to deal with accumulations of redo logs (all data is on mirrorer or RAID
disks anyway). Recovery only needs to be to last full backup.
If it is possible for this to be a hot backup, that's better but not vital.
One possibility is shut down the database and backup all the disks before
restarting, but that's excessively slow, and catches too much stuff, but I'm
afraid to specify backing up less in case something is missed.  I've tried RMAN
a bit, but it seems to really want a second database (the doco is not easy to
follow for use without a catalogue database), and you still seem to need to
separately backup the control file (or "Contact Oracle Support" to restore)
Thanks.
and we said...
You cannot HOT backup a noarchivelog mode database.

You know -- just 2 weeks ago we had BOTH sides of a mirror fail within 3 minutes
of eachother, they are delusional if they believe RAID protects them.  Only good
data managment will protect their data.

They must be prepared to lose data at some point, it is inevitable.

In order to backup (without RMAN) you will follow the steps in:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76993/toc.htm
which is the backup and recovery guide.  It covers what structures you must
backup in chapter 1.  Basically you will need to

o shutdown normal
o copy the datafiles, control files, init.ora files
o startup

You do not want or need the online redo log files, when you restore, you'll be
doing an "open resetlogs" which will recreate them.

Make SURE you test these backups by restoring them and seeing that everything
you need is there.  Make sure that as you add datafiles to the database, your
backup routine will pick them up.

Don't do un-attended shutdowns either -- see:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2876092892845
for my reasoning on that one.  The solution to that would be to run in archive
log mode and do HOT backups (never ever shutdown)
August 08, 2002 Randy -- Thanks for the question regarding "how to format sql output", version 8.1.7
You Asked
is there just ONE space between words or is it totally free form?  Actually,
it's a totally free form.  Thanks for your response.

Hi Tom,
 I have a sql statement:
    Select Document_title ||'or'|| Document_title
    from atas_query_v

  I understand this just repeats the column twice using 'OR' in between.  What
I'm looking for is a way to have the 'OR' placed between each word for that
column.   Is that possible to accomplish in a SQL output?   I know this sounds
like a strange request, but I need to use the 'OR' between each keyword in my
Oracle Forms application.  Users have one text item which queries the DB using
the Intermedia text function.  If they enter two keywords it searches on the
exact keyword. I want to use the OR function between keywords so more results
are returned.  I hope this makes sense...

Thanks in advance,
Randy
and we said...
We'll have to write a stored procedure to do that.  Here is the code I use on
asktom to process the search string you guys type in:

create or replace package delimited
as
    function word( p_str             in varchar2,
                   p_n               in varchar2,
                   p_enclosed_by     in varchar2 default '''',
                   p_separated_by   in varchar2 default ',' )
    return varchar2;

    function context_query( p_search_str in varchar2,
                            p_sep in varchar2 default 'OR' )
    return varchar2;

    pragma restrict_references( word, WNDS, RNDS );
end;
/

create or replace package body delimited
as
    type vcArray is table of varchar2(2000) index by binary_integer;

    g_words            vcArray;
    g_empty            vcArray;
    g_last_string    varchar2(4096);

    function de_quote( p_str in varchar2,
                       p_enc_by in varchar2 )
    return varchar2
    is
    begin
        if ( p_enc_by is null )
        then
            return p_str;
        else
            return replace( ltrim( rtrim( p_str, p_enc_by ), p_enc_by ),
                                p_enc_by||p_enc_by, p_enc_by );
        end if;
    end de_quote;

    procedure parse( p_str      in varchar2,
                     p_delim in varchar2,
                     p_sep   in varchar2 )
    is
        l_n            number  default 1;
        l_in_quote    boolean default FALSE;
        l_ch        char(1);
        l_len        number default nvl(length( p_str ),0);
    begin
        if ( l_len = 0 ) then
            return;
        end if;

        g_words := g_empty;
        g_words(1) := NULL;

        for i in 1 .. l_len loop
            l_ch := substr( p_str, i, 1 );
            if ( l_ch = p_delim ) then
                l_in_quote := NOT l_in_quote;
            end if;
            if ( l_ch = p_sep AND NOT l_in_quote ) then
                l_n := l_n + 1;
                g_words(l_n) := NULL;
            else
                g_words(l_n) := g_words(l_n)||l_ch;
            end if;
        end loop;

        for i in 1 .. l_n loop
            g_words(i) := de_quote( g_words(i), p_delim );
        end loop;
    end parse;

    function word( p_str             in varchar2,
                   p_n               in varchar2,
                   p_enclosed_by     in varchar2 default '''',
                   p_separated_by   in varchar2 default ',' )
    return varchar2
    is
    begin
        if ( g_last_string is NULL or p_str <> g_last_string ) then
            g_last_string := p_str;
            parse( p_str, p_enclosed_by, p_separated_by );
        end if;
        return g_words( p_n );
    exception
        when no_data_found then return NULL;
    end;

    function context_query( p_search_str in varchar2,
                            p_sep in varchar2 default 'OR' )
    return varchar2
    is
        l_temp_value   varchar2(1000);
        l_return_value varchar2(1000) := NULL;
        l_start_token  number := 1;
        l_in_token     boolean := FALSE;
        l_sep          varchar2(20);
    begin
        if nvl(length(p_search_str),0) = 0 then
            return NULL;
        end if;
        --
        l_temp_value := trim(replace( lower(p_search_str), ':', ' '));
        l_temp_value := trim(replace( l_temp_value, ';', ' '));
        l_temp_value := trim(replace( l_temp_value, '"', ' '));
        l_temp_value := trim(replace( l_temp_value, ':'',', ' '));
        l_temp_value := trim(replace( l_temp_value, '(', ' '));
        l_temp_value := trim(replace( l_temp_value, ')', ' '));
        l_temp_value := trim(replace( l_temp_value, '!', ' '));
        l_temp_value := trim(replace( l_temp_value, '&', ' '));
        l_temp_value := trim(replace( l_temp_value, '+', ' '));
        l_temp_value := trim(replace( l_temp_value, '-', ' '));
        l_temp_value := trim(replace( l_temp_value, ',', ' '));
        l_temp_value := trim(replace( l_temp_value, ' and ', ' ' ));
        l_temp_value := trim(replace( l_temp_value, ' or ', ' ' ));
        --
        if length(l_temp_value) > 0 then
            l_in_token := TRUE;
        end if;
        --
        for i in 1..length(l_temp_value) loop
            if substr(l_temp_value,i,1) = ' ' then
                if l_in_token = TRUE then
                    l_return_value := l_return_value || l_sep || ' {' ||
                          substr(l_temp_value,l_start_token, i-l_start_token) ||

                            '}' || ' ';
                    l_sep := p_sep;
                    l_in_token := FALSE;
                end if;
            elsif l_in_token = FALSE then
                l_in_token := TRUE;
                l_start_token := i;
            end if;
        end loop;
        if l_in_token = TRUE then
            l_return_value := l_return_value || l_sep || ' {' ||
                             substr(l_temp_value,l_start_token) || '} ';
        end if;
        return trim(l_return_value);
    end;

end delimited;
/

You use it like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select delimited.context_query( 'how now
brown cow' ) from dual;

DELIMITED.CONTEXT_QUERY('HOWNOWBROWNCOW')
---------------------------------------------------------------------------------
--------------------------------------------------
{how} OR {now} OR {brown} OR {cow}

ops$tkyte@ORA817DEV.US.ORACLE.COM> select delimited.context_query( 'how now
brown cow', 'AND' ) from dual;

DELIMITED.CONTEXT_QUERY('HOWNOWBROWNCOW','AND')
---------------------------------------------------------------------------------
--------------------------------------------------
{how} AND {now} AND {brown} AND {cow}
Review & Followup
Rating: 5
July 09, 2003
Reviewer: A reader

August 08, 2002 Michel -- Thanks for the question regarding "Active X", version 8.1.6
You Asked
Good morning Tom,
I hope you are doing good!

I would like to try and include Active X in the generation of Forms(6i) from
Designer(6i). Would you know of any other available text(s) besides OnLine help,
where I could get a consise method and/or relevent examples to get me started?

Thank you and have a good day!

Michel
and we said...
http://otn.oracle.com/
under discussion forums -> developer suite

There you will find many other Oracle forms developers and even product
developers from Oracle participating in discussions about forms.  That would be
an excellent place to ask this sort of question.
August 08, 2002 --
      Thanks for the question regarding "driving tables - reg", version 8.1.7.0

      You Asked
Yr example given below really  stunned  me.

http://asktom.oracle.com/pls/ask/f?p=4950:8:107868::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:688839853415,
Going by yr example we absolutely dont have a control over which
has to be executed first.

is concept of driving tables still holds good.

I thought driving table is the table that has highest probability
of processing for obtaining the result set.

I thought driving table should be placed first in the where clause.
if we go by choose optimizer and vice versa

Is it still holds good?

Regards

      and we said...
the order of predicates in the where clause didn't affect the selection of a
driving table -- rather the order of the tables in the FROM clause does.
Consider this example which shows that the order of the tables in the from
clause will HELP decide the driving table:

ops$tkyte@DEV816> create table emp as select * from scott.emp;
Table created.

ops$tkyte@DEV816> create index emp_idx on emp(deptno);
Index created.

ops$tkyte@DEV816> create table dept as select * from scott.dept;
Table created.

ops$tkyte@DEV816> create unique index dept_idx on dept(deptno);
Index created.

ops$tkyte@DEV816> set autotrace traceonly explain
ops$tkyte@DEV816> select * from emp, dept where emp.deptno = dept.deptno;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   NESTED LOOPS
   2    1     TABLE ACCESS (FULL) OF 'DEPT'
   3    1     TABLE ACCESS (BY INDEX ROWID) OF 'EMP'
   4    3       INDEX (RANGE SCAN) OF 'EMP_IDX' (NON-UNIQUE)

ops$tkyte@DEV816> select * from dept, emp where emp.deptno = dept.deptno;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   NESTED LOOPS
   2    1     TABLE ACCESS (FULL) OF 'EMP'
   3    1     TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'
   4    3       INDEX (UNIQUE SCAN) OF 'DEPT_IDX' (UNIQUE)

so, by flip/flopping the order of the tables -- we either full scan DEPT and
then index read into EMP or vice versa.  Notice I said "helps" decide.  It
doesn't have to be that way:

ops$tkyte@DEV816> create index ename_idx on emp(ename);
Index created.

ops$tkyte@DEV816> select * from emp, dept where emp.ename like 'X%' and
emp.deptno = dept.deptno;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   NESTED LOOPS
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'EMP'
   3    2       INDEX (RANGE SCAN) OF 'ENAME_IDX' (NON-UNIQUE)
   4    1     TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'
   5    4       INDEX (UNIQUE SCAN) OF 'DEPT_IDX' (UNIQUE)

ops$tkyte@DEV816> select * from dept, emp where emp.ename like 'X%' and
emp.deptno = dept.deptno;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   NESTED LOOPS
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'EMP'
   3    2       INDEX (RANGE SCAN) OF 'ENAME_IDX' (NON-UNIQUE)
   4    1     TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'
   5    4       INDEX (UNIQUE SCAN) OF 'DEPT_IDX' (UNIQUE)

Here the RBO used EMP as the driving table in both queries -- because of the
existence of a predicate that opened up a new access path possibility.  So, when
all things are "equal", the RBO uses tables from RIGHT to LEFT (right most table
in from clause drives) but that is not an absolute.

The CBO on the other hand, doesn't care about the order of tables:

ops$tkyte@DEV816> alter session set optimizer_goal = first_rows;
Session altered.

ops$tkyte@DEV816> select * from emp, dept where emp.deptno = dept.deptno;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=83 Card=67 Bytes=7839)
   1    0   NESTED LOOPS (Cost=83 Card=67 Bytes=7839)
   2    1     TABLE ACCESS (FULL) OF 'EMP' (Cost=1 Card=82 Bytes=7134)
   3    1     TABLE ACCESS (BY INDEX ROWID) OF 'DEPT' (Cost=1 Card=82
   4    3       INDEX (UNIQUE SCAN) OF 'DEPT_IDX' (UNIQUE)

ops$tkyte@DEV816> select * from dept, emp where emp.deptno = dept.deptno;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=83 Card=67 Bytes=7839)
   1    0   NESTED LOOPS (Cost=83 Card=67 Bytes=7839)
   2    1     TABLE ACCESS (FULL) OF 'EMP' (Cost=1 Card=82 Bytes=7134)
   3    1     TABLE ACCESS (BY INDEX ROWID) OF 'DEPT' (Cost=1 Card=82
   4    3       INDEX (UNIQUE SCAN) OF 'DEPT_IDX' (UNIQUE)

It came up with the same plan for both queries -- it does not CARE about the
placement of things in the query itself -- the RBO is influenced by HOW the
query is written.  The CBO is not.

Review &
      Followup

      Rating: 4
Oracle
      documentation vs Your answer  October 12, 2001

Reviewer:  Kamaljeet Singh  from NewCastle Upon Tyne
      , UK
Tom,
Your response though helpful contradicts what oracle documnetation says. Or
probably I am missing something somewhere.
The following page of oracle documentation

http://technet.oracle.com/doc/server.815/a67775/ch4_dbop.htm#7311
says that the join order does matter. One of the point stressed is 'Choose the
join order so as to join fewer rows to tables later in the join order.'

This obviously does not fit in with your answer.
Will you pleasee clarify this ?

Regards.

          Followup:

It tells you to choose the join order but it doesn't say how to.

There are three ways:

1) use the RBO, order the tables explicitly as demonstrated above.  In other
words -- DIY (do it yourself)

2) use the CBO.  Analyze the tables, give the optimizer the info it needs.  IT
will pick the join order based on your query and the statistics (LTDDI -- let
the database do it)

3) use the ORDERED hint, sort of like 1 but uses the CBO.  The order of the
tables matters again.

No conflicts from my point of view here at all.

I never said the join order did not matter, quite the contrary -- it is vital to
the performance of your query.  My statements were "you can DIY or LTDDI".  Me,
I'm lazy, I'll LTDDI.

      Rating: 4
CBO choosing
      bad join order  August 07, 2002
Reviewer:
      William  from Australia
Hi Tom,

We are running a third party application and one of the poor
performing queries I found is:
SELECT    DISTINCT
        ORG.CODE SCODE,
        ORG.TITLE,
        ORG_TYP.DESCRIPTION ORG_TYPE,
        ORG.RELEASE_LEVEL,
        alert_utils.COUNT_active_ALERTS(org.code,'ORG') Active_Alerts,
        alert_utils.COUNT_inactive_ALERTS(org.code,'ORG') InActive_Alerts,
        (CRIMINAL_HISTORY.ORG_ID) CRIM_EXIST,
        -1 ALIAS_ID
FROM        CRIMINAL_HISTORY,
        ORGANISATIONS ORG,
        ORGANISATION_TYPES ORG_TYP
WHERE        ORG_TYP.ORG_TYPE = ORG.ORG_TYPE
AND        ORG.DATE_LOGICALLY_DELETED IS NULL
AND        ORG_TYP.DESCRIPTION = 'TRANSPORT AND STORAGE (AIR, ROAD, RAIL, SEA)'
AND        (RELEASE_LEVEL = 'G'
OR         RELEASE_LEVEL IS NULL
OR         RELEASE_LEVEL = 'T'
AND         CHECK_ACCESS.ACCESS_TO_ENTITY('NJB',ORG.CODE,'ORG') <> 'N')
AND        ORG.CODE = CRIMINAL_HISTORY.ORG_ID(+)
AND        CRIMINAL_HISTORY.DATE_LOGICALLY_DELETED(+) IS NULL
/

Setting autotrace and timing I get:
Operation                                          Options          Object Name
-------------------------------------------------- ----------------
--------------------------------------------------
SELECT STATEMENT Optimizer=CHOOSE Cost=971
  SORT                                             UNIQUE
(Bytes=2289, Cost=971)
    NESTED LOOPS                                   OUTER
(Bytes=33027, Cost=965)
      NESTED LOOPS                                                   (Bytes=87,
Cost=11)
        TABLE ACCESS                               BY INDEX ROWID
ORGANISATION_TYPES (Bytes=40, Cost=1)
          INDEX                                    UNIQUE SCAN
ORG_TYP_DESC_UK
        TABLE ACCESS                               FULL
ORGANISATIONS (Bytes=1927, Cost=10)
      TABLE ACCESS                                 FULL
CRIMINAL_HISTORY (Bytes=667238, Cost=954)

Elapsed: 00:14:876.81

Statistics
----------------------------------------------------------
       2065  recursive calls
       1618  db block gets
    3537681  consistent gets
    3535384  physical reads
         72  redo size
      26528  bytes sent via SQL*Net to client
       1573  bytes received via SQL*Net from client
         17  SQL*Net roundtrips to/from client
         41  sorts (memory)
          0  sorts (disk)
        226  rows processed

If I were to rewrite this query to:
SELECT    /*+ ORDERED */ DISTINCT
        ORG.CODE SCODE,
        ORG.TITLE,
        ORG_TYP.DESCRIPTION ORG_TYPE,
        ORG.RELEASE_LEVEL,
        alert_utils.COUNT_active_ALERTS(org.code,'ORG') Active_Alerts,
        alert_utils.COUNT_inactive_ALERTS(org.code,'ORG') InActive_Alerts,
        (CRIMINAL_HISTORY.ORG_ID) CRIM_EXIST,
        -1 ALIAS_ID
FROM        CRIMINAL_HISTORY,
        ORGANISATIONS ORG,
        ORGANISATION_TYPES ORG_TYP
        WHERE        ORG_TYP.ORG_TYPE = ORG.ORG_TYPE
AND        ORG.DATE_LOGICALLY_DELETED IS NULL
AND        ORG_TYP.DESCRIPTION = 'TRANSPORT AND STORAGE (AIR, ROAD, RAIL, SEA)'
AND        (RELEASE_LEVEL = 'G'
OR         RELEASE_LEVEL IS NULL
OR         RELEASE_LEVEL = 'T'
AND         CHECK_ACCESS.ACCESS_TO_ENTITY('NJB',ORG.CODE,'ORG') <> 'N')
AND        ORG.CODE = CRIMINAL_HISTORY.ORG_ID(+)
AND        CRIMINAL_HISTORY.DATE_LOGICALLY_DELETED(+) IS NULL
/

I get:
Elapsed: 00:00:12.49

Statistics
----------------------------------------------------------
       1456  recursive calls
        107  db block gets
      18317  consistent gets
      15725  physical reads
          0  redo size
      26528  bytes sent via SQL*Net to client
       1573  bytes received via SQL*Net from client
         17  SQL*Net roundtrips to/from client
         70  sorts (memory)
          0  sorts (disk)
        226  rows processed

Operation                                          Options          Object Name
-------------------------------------------------- ----------------
-----------------------------------------
SELECT STATEMENT Optimizer=CHOOSE Cost=976
  SORT                                             UNIQUE
(Bytes=2247, Cost=976)
    HASH JOIN
(Bytes=47508, Cost=968)
      TABLE ACCESS                                 BY INDEX ROWID
ORGANISATION_TYPES (Bytes=38, Cost=1)
        INDEX                                      UNIQUE SCAN
ORG_TYP_DESC_UK
      HASH JOIN                                    OUTER
(Bytes=858015, Cost=965)
        TABLE ACCESS                               FULL
ORGANISATIONS (Bytes=1927, Cost=10)
        TABLE ACCESS                               FULL
CRIMINAL_HISTORY (Bytes=667238, Cost=954)

I have stats on the database by:
exec
sys.dbms_stats.gather_table_stats('PROMIS','ORGANISATION_TYPES',NULL,NULL,FALSE,'
FOR ALL INDEXED COLUMNS',4,'DEFAULT',TRUE);

exec
sys.dbms_stats.gather_table_stats('PROMIS','ORGANISATIONS',NULL,60,FALSE,'FOR
ALL INDEXED COLUMNS',4,'DEFAULT',TRUE);

exec
sys.dbms_stats.gather_table_stats('PROMIS','CRIMINAL_HISTORY_JN',NULL,30,FALSE,'F
OR ALL INDEXED COLUMNS SIZE 1',4,'DEFAULT',TRUE);

The table sizes are:
Table Name                    Size(K)   Blocks     Num Rows
------------------------ ------------ -------- ------------
ORGANISATIONS                   1,280      160       13,567
CRIMINAL_HISTORY              126,976   15,872      606,580
ORGANISATION_TYPES                128       16           51

Why is the optimizer choosing such a bad execution plan?
What can I do/change to get the CBO to choose the better
execution plan without changing the query?

Thanks in advance

          Followup:

what are the optimizer related init.ora parameters set to (optimizer*, *_size
for example)

      Rating: 4
init.ora
      parameters  August 07, 2002
Reviewer:
      William  from Australia
Tom,

We are running Oracle Standard Edition 8.1.7.2.0.
The init.ora parameters you asked for are:

NAME                                 TYPE    VALUE
------------------------------------ ------- ------------------------------
optimizer_features_enable            string  8.1.7
optimizer_index_caching              integer 0
optimizer_index_cost_adj             integer 100
optimizer_max_permutations           integer 80000
optimizer_mode                       string  CHOOSE
optimizer_percent_parallel           integer 0

bitmap_merge_area_size               integer 1048576
create_bitmap_area_size              integer 8388608
db_block_size                        integer 8192
hash_area_size                       integer 1500000
java_max_sessionspace_size           integer 0
java_pool_size                       string  32768
large_pool_size                      string  0
max_dump_file_size                   string  10240
object_cache_max_size_percent        integer 10
object_cache_optimal_size            integer 102400
oracle_trace_collection_size         integer 5242880
parallel_execution_message_size      integer 2176
shared_pool_reserved_size            string  12000000
shared_pool_size                     string  64000000
sort_area_retained_size              integer 491520
sort_area_size                       integer 491520

Cheers

          Followup:

lets see the full plans with costs and all (remove some whitespace to make it
"narrow")

      Rating: 4
Execution
      Plans  August 07, 2002
Reviewer:  William
       from Australia
Tom,

Here is the execution plan without the hint:

Operation            Options        Object Name
-------------------- -------------- ----------------------------------------
SELECT STATEMENT Optimizer=CHOOSE Cost=971
  SORT               UNIQUE          (Bytes=2289, Cost=971)
    NESTED LOOPS     OUTER           (Bytes=33027, Cost=965)
      NESTED LOOPS                   (Bytes=87, Cost=11)
        TABLE ACCESS BY INDEX ROWID ORGANISATION_TYPES (Bytes=40, Cost=1)
          INDEX      UNIQUE SCAN    ORG_TYP_DESC_UK
        TABLE ACCESS FULL           ORGANISATIONS (Bytes=1927, Cost=10)
      TABLE ACCESS   FULL           CRIMINAL_HISTORY (Bytes=667238, Cost=954)

Statistics
----------------------------------------------------------
       2065  recursive calls
       1618  db block gets
    3537681  consistent gets
    3535384  physical reads
         72  redo size
      26528  bytes sent via SQL*Net to client
       1573  bytes received via SQL*Net from client
         17  SQL*Net roundtrips to/from client
         41  sorts (memory)
          0  sorts (disk)
        226  rows processed

This is the execution plan with the ORDERED hint:

Operation            Options        Object Name
-------------------- -------------- ---------------------------------------
SELECT STATEMENT Optimizer=CHOOSE Cost=976
  SORT               UNIQUE          (Bytes=2247, Cost=976)
    HASH JOIN                        (Bytes=47508, Cost=968)
      TABLE ACCESS   BY INDEX ROWID ORGANISATION_TYPES (Bytes=38, Cost=1)
        INDEX        UNIQUE SCAN    ORG_TYP_DESC_UK
      HASH JOIN      OUTER           (Bytes=858015, Cost=965)
        TABLE ACCESS FULL           ORGANISATIONS (Bytes=1927, Cost=10)
        TABLE ACCESS FULL           CRIMINAL_HISTORY (Bytes=667238, Cost=954)

Statistics
----------------------------------------------------------
       1456  recursive calls
        107  db block gets
      18317  consistent gets
      15725  physical reads
          0  redo size
      26528  bytes sent via SQL*Net to client
       1573  bytes received via SQL*Net from client
         17  SQL*Net roundtrips to/from client
         70  sorts (memory)
          0  sorts (disk)
        226  rows processed

          Followup:

Where is the CARD?

Given the query:

SELECT    DISTINCT
        ORG.CODE SCODE,
        ORG.TITLE,
        ORG_TYP.DESCRIPTION ORG_TYPE,
        ORG.RELEASE_LEVEL,
        alert_utils.COUNT_active_ALERTS(org.code,'ORG') Active_Alerts,
        alert_utils.COUNT_inactive_ALERTS(org.code,'ORG') InActive_Alerts,
        (CRIMINAL_HISTORY.ORG_ID) CRIM_EXIST,
        -1 ALIAS_ID
FROM        CRIMINAL_HISTORY,
        ORGANISATIONS ORG,
        ORGANISATION_TYPES ORG_TYP
WHERE        ORG_TYP.ORG_TYPE = ORG.ORG_TYPE
AND        ORG.DATE_LOGICALLY_DELETED IS NULL
AND        ORG_TYP.DESCRIPTION = 'TRANSPORT AND STORAGE (AIR, ROAD, RAIL, SEA)'
AND        (RELEASE_LEVEL = 'G'
OR         RELEASE_LEVEL IS NULL
OR         RELEASE_LEVEL = 'T'
AND         CHECK_ACCESS.ACCESS_TO_ENTITY('NJB',ORG.CODE,'ORG') <> 'N')
AND        ORG.CODE = CRIMINAL_HISTORY.ORG_ID(+)
AND        CRIMINAL_HISTORY.DATE_LOGICALLY_DELETED(+) IS NULL
/

And the fact:

Table Name                    Size(K)   Blocks     Num Rows
------------------------ ------------ -------- ------------
ORGANISATIONS                   1,280      160       13,567
CRIMINAL_HISTORY              126,976   15,872      606,580
ORGANISATION_TYPES                128       16           51

do you expect 20 rows from ORGANISATIONS to be returned?  Or is the optimizer
making a bad assumption here?

Here is what I suggest:

o you run both queries in SQLPlus with
   alter session set timed_statistics=true;
   alter session set sql_trace=true;

o you run tkprof on the resulting trace file

o you send me the resulting tkprof via email (tkyte@oracle.com)

o i'll take this thread offline into a new question.

August 08, 2002 balasubramanian -- Thanks for the question
      regarding "SQL PLUS HELP COMMAND AND HELP TABLE", version 8.1.6/8.0.4

      You Asked
Dear Tom,

 I have three questions regarding sqlplus & help facility.

1.Is 'HELP' is a command of sqlplus alone ? Because when I am
executing the HELP command in sqlplus it is working. When I am
executing at anyother client (e-g) powerbuilder sql painter, it
is not working.

2.I have installed oracle8i (8.1.5) personal edition in my
windows-98. In sqlplus, when I am executing 'help' command it
says 'SP2-0171 HELP NOT ACCESSIBLE'. How to correct this error ?

3.I am very much interested to know, when the table 'HELP' in
'System' schema is created ? And when all the records of help
table is inserted and updated ? Is there any sql file available
to do this by ourself (like utlxplan.sql or catalog.sql) ?

      and we said...
1) help is always a function of the client.  help is not a sql
command.  SQLPlus has a builtin help facility -- as does
Powerbuilder, MS Word and so on.  SQLPlus help is in sqlplus.

2) to load help, goto $ORACLE_HOME/sqlplus/admin/help and then
using sqlplus run helptbl:

system@ORA8I.WORLD> @helptbl

then, run sqlldr to load plushelp.ctl

$ sqlldr system/password plushelp.ctl

and then run sqlplus again to run helpindx:

system@ORA8I.WORLD> @helpindx

Do this as SYSTEM only.

3) CREATED= when you run helptbl.sql.  LOADED=plushelp.ctl.
SQL=contents of $ORACLE_HOME/sqlplus/admin/help
Review &
      Followup

      Rating: 4
HOW TO USE HELP
      COMMAND.  August 08, 2002
Reviewer:  Deepak
      Gupta  from INDIA
I had followed all your instruction but still I am not able to get help through
HELP COMMAND.

SQL> HELP
SQL*Plus Help Files are included in Oracle Documentation.
You have to read SQL*Plus Help Files from Oracle Documentation.

MY ORACLE DATBASE VERSION
--------
Oracle8 Release 8.0.4.0.0 - Production
PL/SQL Release 8.0.4.0.0 - Production

          Followup:

I do not believe you followed the directions then.

I just did this

o using the SYSTEM account ran helptbl
o using the SYSTEM account ran sqlldr
o using the SYSTEM account ran helpindx

and away i go....

      Rating:
      3
  November 04, 2002
Reviewer:  Sagi
       from India
Hi Tom,

I am on 8.1.7 (Win2K).

I dont have the following files:
1) plushelp.ctl
2) helpindx

Is this version specifc? What would these files do?

However i executed the helpbld.sql with helpus.sql as a parameter. Now when I
issue help command at SQL Prompt I get:

SQL> help index

 @             COPY         PAUSE                    SHUTDOWN
 @@            DEFINE       PRINT                    SPOOL
 /             DEL          PROMPT                   SQLPLUS
 ACCEPT        DESCRIBE     QUIT                     START
 APPEND        DISCONNECT   RECOVER                  STARTUP
 ARCHIVE LOG   EDIT         REMARK                   STORE
 ATTRIBUTE     EXECUTE      REPFOOTER                TIMING
 BREAK         EXIT         REPHEADER                TTITLE
 BTITLE        GET          RESERVED WORDS (SQL)     UNDEFINE
 CHANGE        HELP         RESERVED WORDS (PL/SQL)  VARIABLE
 CLEAR         HOST         RUN                      WHENEVER OSERROR
 COLUMN        INPUT        SAVE                     WHENEVER SQLERROR
 COMPUTE       LIST         SET
 CONNECT       PASSWORD     SHOW

So does the HELP gives information on only the above or we can get similar help
on CREATE TABLE etc....

Regards,
Sagi

August 08, 2002 Ahmad -- Thanks for the question regarding
      "Maintain Tables - how to rename a column", version 8.1.5

      You Asked
How I can rename a table column's?

      and we said...
There are various ways.  The way I prefer to do it is as such
(it requires the least effort on my part and on the part of the
database.  it is the least error prone.  it is easily undone):

o you have a table T with a column C. You would like C to be
called NewName from now on:

 1) rename t to t_table
 2) create view t as select ..., C newName, ... from T
 3) select 'grant ' || privelege || ' on T to ' || grantee||';'
      from user_tab__privs
     where table_name = 'T_TABLE';

    spool and save the results of that query, run those grants
 4) select 'revoke ' || privelege || ' on T_TABLE from ' ||
            grantee||';'
      from user_tab__privs
     where table_name = 'T_TABLE';

    spool and save the results of that query, run those revokes

As far as anyone is concerned, the column has been renamed with
minimal IO and no impact on future performance.

o you have the same situation as above.  You have Oracle8i or
up. you could:

   1) alter table T add newName <datatype>;
   2) update t set NewName = C, C = Null;
   3) alter table T set unused column C; -- flag delete, since
      C is null everywhere, no real need to rewrite the entire
      table by dropping C, flag deletion is more then sufficient

o you have the same situation.  You have Oracle8.0 and before.
Your options (other then the view) are somewhat limited.  You
can create a copy of the table as select (renaming the column on
the way into the new table), dropping the original table and
then renaming the copy back -- but you must restore the
grants, triggers, constraints, etc.  If you take an export of
the table with rows=N before you do the copy and then import the
table AFTER you rename the copy, it'll get most of that stuff
back for you.  Here is an full annotated example using that
method:

Here is our table.  Our goal will be ultimately to rename Y to
"newName"

ops$tkyte@8i> create table t
  2  ( x int primary key,
  3    y int check ( y > 0 ) );
Table created.

ops$tkyte@8i>
ops$tkyte@8i> create or replace trigger t_trigger
  2  before insert on t for each row
  3  begin
  4          :new.y := :new.y+1;
  5  end;
  6  /

Trigger created.

ops$tkyte@8i> grant select on t to connect;
Grant succeeded.

ops$tkyte@8i> grant insert on t to resource;
Grant succeeded.

ops$tkyte@8i> create index y_idx on t(y);
Index created.

ops$tkyte@8i> select object_name, object_type
  2    from user_objects
  3  union all
  4  select constraint_name, 'CONSTRAINT'
  5    from user_constraints
  6  union all
  7  select privilege, grantee
  8    from user_tab_privs
  9  /

OBJECT_NAME                    OBJECT_TYPE
------------------------------ ------------------------------
SYS_C0025444                   INDEX
T                              TABLE
T_TRIGGER                      TRIGGER
Y_IDX                          INDEX
SYS_C0025443                   CONSTRAINT
SYS_C0025444                   CONSTRAINT
SELECT                         CONNECT
INSERT                         RESOURCE

8 rows selected.

So, there are the things about T we need.  We have some
indexes, triggers, constraints, grants...

ops$tkyte@8i> host exp userid=/ tables=t rows=n

Export: Release 8.1.5.0.0 - Production on Thu Jun 15 09:13:11
2000

(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.5.0.0 -
Production
With the Partitioning and Java options
PL/SQL Release 8.1.5.0.0 - Production
Export done in US7ASCII character set and US7ASCII NCHAR
character set
Note: table data (rows) will not be exported

About to export specified tables via Conventional Path ...
. . exporting table                              T
Export terminated successfully without warnings.

That export got what we needed structurally about T.  It
extracted only DDL, no data

ops$tkyte@8i> create table t2 as select x x, y newName from t;
Table created.

That "renamed" the column, we can now:

ops$tkyte@8i> drop table t;
Table dropped.

ops$tkyte@8i> rename t2 to t;
Table renamed.

So, effectively the column has been renamed, now we need to
get back all of the "stuff" about T from above (constraints,
grants, etc)

ops$tkyte@8i> host imp userid=/ full=y ignore=y

Import: Release 8.1.5.0.0 - Production on Thu Jun 15 09:13:15
2000

(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.5.0.0 -
Production
With the Partitioning and Java options
PL/SQL Release 8.1.5.0.0 - Production

Export file created by EXPORT:V08.01.05 via conventional path
import done in US7ASCII character set and US7ASCII NCHAR
character set
. importing OPS$TKYTE's objects into OPS$TKYTE
IMP-00017: following statement failed with ORACLE error 904:
 "CREATE INDEX "Y_IDX" ON "T" ("Y" )  PCTFREE 10 INITRANS 2
MAXTRANS 255 STOR"
 "AGE(INITIAL 524288) TABLESPACE "UTILS" LOGGING"
IMP-00003: ORACLE error 904 encountered
ORA-00904: invalid column name
IMP-00017: following statement failed with ORACLE error 904:
 "ALTER TABLE "T" ADD  CHECK ( y > 0 ) ENABLE"
IMP-00003: ORACLE error 904 encountered
ORA-00904: invalid column name
IMP-00041: Warning: object created with compilation warnings
 "CREATE TRIGGER "OPS$TKYTE".t_trigger"
 "before insert on t for each row"
 ""
 "begin"
 "      :new.y := :new.y+1;"
 "end;"
Import terminated successfully with warnings.

So, according to the import,

o the INDEX Y_idx failed (we need to create that, convienently,
the create index we need to modify is right there...).

o the check constraint failed, again we have the statement we
need to fix right there

o the create trigger failed as well -- we'll have to fix that
one up

ops$tkyte@8i> select object_name, object_type
  2    from user_objects
  3  union all
  4  select constraint_name, 'CONSTRAINT'
  5    from user_constraints
  6  union all
  7  select privilege, grantee
  8    from user_tab_privs
  9  /

OBJECT_NAME                    OBJECT_TYPE
------------------------------ ------------------------------
SYS_C0025445                   INDEX
T                              TABLE
T_TRIGGER                      TRIGGER
SYS_C0025445                   CONSTRAINT
SELECT                         CONNECT
INSERT                         RESOURCE

6 rows selected.

ops$tkyte@8i>

but as you can see -- all of the other things (grants and
such) are in place for us.
Review &
      Followup

      Rating: 3
Lilian An from
      Wilmington DE  June 13, 2001
Reviewer:  Lilian
      An  from usa
very clear and detail explanation and example.

      Rating:
      4
Waiting  June 12, 2002
Reviewer:  Jack
      Mulhollan  from Little Rock, AR USA
Tom, did you ever create a table and later realize that one of the column names
you chose was not ideal?  So did I.  So did everyone else who ever created a
table.

Your answer demonstrates that changing a column name in Oracle is truely a pain.

The view solution is simple enough.  Here's the problem with it:  It adds one
more layer of complexity, one more source of confusion, one more thing to
document, one more thing to remember.  In my experience, these little things add
up over time.  Eventually you end up with a database that only the designer
understands.  Unfortunately, she got a better job at Initec two years ago.

Here's the solution I prefer:

  alter table T rename column C to NewName;

I try this from time to time just to see if God has come down and implemented it
in Oracle.  He hasn't yet.  I'm still waiting.  I think he'll fix it eventually,
or someone at Oracle will get a spare 5 minutes.

          Followup:

Well, here is my take on this:

If you didn't name the column correctly and you are going to correct it, you
must not have any code that accessess it -- hence this is a BRAND SPANKING NEW
TABLE and is most likely EMPTY.  drop it, recreate it.

Also, some people use views as their only access path -- no table access.  This
gives them the flexibility to

o hide columns as new ones are added to the table, protecting existing
applications (they'll create a new view that exposes these new columns for new
applications)

o lets you "rename" a column easily (as it is just a view)

among other nice abstractions.

(and to think that it takes a spare "5 minutes....." hmmm)

However...

In Oracle9iR2:

ops$tkyte@ORA920> desc t
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 OWNER                               NOT NULL VARCHAR2(30)
 OBJECT_NAME                         NOT NULL VARCHAR2(30)
 SUBOBJECT_NAME                               VARCHAR2(30)
 OBJECT_ID                           NOT NULL NUMBER
 DATA_OBJECT_ID                               NUMBER
 OBJECT_TYPE                                  VARCHAR2(18)
 CREATED                             NOT NULL DATE
 LAST_DDL_TIME                       NOT NULL DATE
 TIMESTAMP                                    VARCHAR2(19)
 STATUS                                       VARCHAR2(7)
 TEMPORARY                                    VARCHAR2(1)
 GENERATED                                    VARCHAR2(1)
 SECONDARY                                    VARCHAR2(1)

ops$tkyte@ORA920> alter table t rename column owner to ownerX;

Table altered.

ops$tkyte@ORA920> desc t;
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 OWNERX                              NOT NULL VARCHAR2(30)
 OBJECT_NAME                         NOT NULL VARCHAR2(30)
 SUBOBJECT_NAME                               VARCHAR2(30)
 OBJECT_ID                           NOT NULL NUMBER
 DATA_OBJECT_ID                               NUMBER
 OBJECT_TYPE                                  VARCHAR2(18)
 CREATED                             NOT NULL DATE
 LAST_DDL_TIME                       NOT NULL DATE
 TIMESTAMP                                    VARCHAR2(19)
 STATUS                                       VARCHAR2(7)
 TEMPORARY                                    VARCHAR2(1)
 GENERATED                                    VARCHAR2(1)
 SECONDARY                                    VARCHAR2(1)

      Rating: 2
Difference
        August 08, 2002
Reviewer:  Fiza  from
      Canada
Hi i read docs related to index organized table and regular table but still not
clear is any of can help me...Thanks

          Followup:

Hmm, and this has to do with renaming a column how?

If you have my expert one on one book -- i cover it in detail there in the
chapter on tables.

If you want to read what I've written about them in other questions, just search
for

index organized tables

you'll get some meaningful hits there.

August 08, 2002 -- Thanks for the question regarding "Sequence
      no's", version 8.1.7

      You Asked
Hello sir,

I have developed a inventory application(devloped using d2k).  Users from 4
branches will be connecting to head office server using telephone line.

"Our management wants all the invoice no's SHOULDBE in sequence ..ie order.  Not
a single no should be missed."

My problem is at present no's are JUMPING in the application.  They warned me
that if I didnot find any alternate logic/method to get all numbers in
sequence,I WILL LOSE MY JOB.  Please help me.( If possible with example pl).

Thankyou very much....
best regs
mubeen

      and we said...
Quit and find a new job with reasonable people who understand how things should
work in the real world.  That is what I would do.

Short of that explain to them -- "ok, we can do that however you will make it so
that each and ever invoice will be a serial operation.  Bob will not be able to
make an invoice for the entire duration of time that Mary is creating one
because we have to wait for Mary to finish and commit her invoice number before
Bob can generate one.  If we don't wait, Mary might encounter an error and
rollback, causing a GAP."

So, you'll basically be saying, you'll be doing ONE thing at a time.  Period.

You can do this:

create table silly_way_to_sequence( x int );
inset into silly_way_to_sequence values ( 0 );

And a function like:

create or replace funtion bad_way_to_do_this return number
is
   l_new_number;
begin
   update silly_way_to_sequence set x = x+1 returning x into l_new_number;
   return l_new_number;
end;
/

there you go.  gap free, but slow and serial.

Review &
      Followup

      Rating: 1
out of touch
      with non pros  August 23, 2001
Reviewer:
      A reader  from ma
Sorry Tom, way out of line, there. I think you've been hanging around with too
many professionals.

Every, non-technical end user I have ever come across has had a requirement
(demand really) to have all invoice numbers in exact sequence order, (mostly old
time accounting issues) and won't/can't be swayed. I've dealt with it the
serially "silly" way for years. Insert the sequence, commit, get the number back
then open the invoice app. Whether rollback or not, increment is secure. whether
invounce is populated or not, order is stable.

          Followup:

but isn't that a gap?  You have a non-populated invoice that might never, ever
be used (after you commit, the app crashes, power goes out).  There goes the gap
free-ness.

Other then the tongue in cheek comment about quitting (which was driven
primarily by their comment " I didnot find any alternate logic/method to get all
numbers in
sequence,I WILL LOSE MY JOB." I don't really see anything "out of line".  That
comment alone would be enough for me to start packing my bags and looking
elsewhere.

Search for

gap free

on my search.  I've been beating this drum for a while...

      Rating: 2
Really it is
      silly Tom  August 23, 2001
Reviewer:  nana

Mubeen,

Can't you explain it to your manager,about this type of scenarios.

Tom i'm really sorry for you.

      Rating: 3
Allocate the
      number only on invoice creation  August 23, 2001

Reviewer:  Andrew  from CA, USA
I've used the "silly" method but overcome the serialization issue by only
allocating the number when the invoice is actually created in the database. The
downside is that you can't display it until the invoice has been created. One
reason for gaps when using sequence number generators is when CACHED values are
lost at shutdown etc. Alter the sequence to NOCACHE to help with that - at the
expense of speed.

          Followup:

Other reasons for gaps are rollbacks (errors in entry), power failures, software
failures and so on.

If there cannot be gaps, you cannot use a sequence.  They will definitely
produce a gap.

      Rating: 5
re: out of
      touch with non pros  August 23, 2001
Reviewer:
      Lester Burnham  from Orlando, FL USA
I just wonder what would happen if these "managers" (or whatever non-technical
people are involved) had to void a check in their personal checkbook.  Would
they open a new account so their statement doesn't have a gap in it when it
arrives?

      Rating: 5
Two parts to
      this  August 23, 2001
Reviewer:  Shrek

1) There is no guarantee that gaps won't be created using sequences, alternative
approach, although one that sucks, designed for people that suck was
bad_way_to_do_this
2) As for packing your bags and quitting: If you are really really sharp (like
Tom) and also widely recognized as sharp then you have plenty of cool options
and you can literally decide what you want to say before you quit. If your
capabilities are limited, then you would have limited options and quitting in
style would be RECKLESS!

But in any case Mubeen, I urge you to look for another position, because even if
you solve this situation there will be other such situations sooner or later.

BTW, Tom I don't believe you were out of line!

      Rating: 5
if you're
      really stuck with this....  August 23, 2001

Reviewer:  Connor  from UK
A recent suggestion I read somewhere (apologies to who it was 'cos I can't
remember), was to have an autonomous transaction get the max+1, insert this into
an "audit" table, and have a trigger on the target table remove the  audit row.
In this way, after rollback/etc, the audit + target table should contain every
sequence number....

... Still an awful non-scalable way of doing things.

      Rating: 4
My apologies,
      not out of touch  August 24, 2001
Reviewer:
      J  from MA
Sorry, meant no disrespect here. Out of line was a little much. I was just
trying to point out that regardless to what is logical, there are some who don't
care about it, they want order. Yes, there would be a blank invoice, but no gap.
I didn't mean to imply a sequence Ie somenum_nextval, meant more the max+1
thought.
No, I wouldn't get a new checking account, Lester, but I would have the voided
check in my hand, no?
At the end of the day, when theyre tracking the money, it would be 1,2,3,4......

No?

      Rating: 4
Find out the
      real problem  August 24, 2001
Reviewer:  Jim
      Kennedy  from Oregon, USA
I think we need to find out the real problem.  By that I mean the boss or
customer has some business need (we hope) or requirement.  Granted it really
isn't a very useful method to meet some requirement to threaten the guy with his
job.

I would ask what the business need or requirement is.  Perhaps Accounting when
they do audits is looking for "missing" invoices - think paper here.  Perhaps
Accounting is concerned that a paper invoice will be kited or show up
unexpectedly.  I think with a better understanding of the business need you can
better come up with a solution that meets the need.  Often bosses specify the
solution and not what it is they are trying to accomplish.  Hopefully, you have
a boss that will take the time to explain the business need so you can help him
or her accomplish the goal without undue solution constraints.

      Rating:
      4
  August 25, 2001
Reviewer:
      A reader

      Rating: 4
thanks
      !!!  August 27, 2001
Reviewer:
      A reader
Thank you all guys for participating in this discussion and thanks a lot to Tom
!  As for as changing of job is concerned that I will do at an appropriate time
!!!

cheers
mubeen

      Rating: 3
pls help with
      this similar question  May 13, 2002
Reviewer:
      Godwin  from Ghana
Please help me with this problem given to me by my boss.
''I need to create sequence/primary key which will pick the first 4 characters
of a column called dept and count the number of records in that same dept to
generate the p.k. the sequence must restart at every new dept. eg.
  select dept,grade from staffrecord
  insert into mytable
  values (dept,grade,seq)
Thus the records in mytable should look like
 Zoology    Senior Clerk  ZOOL001
 Zoology    Junior Clerk  ZOOL002
 Mathematics Lecturer     MATH001
 Mathematics T.A          MATH002
............  .....       ......
in that order.
Will plsql rather be okay?
Thanks

          Followup:

see the other question you posted this same question to...

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3823006469599

      Rating: 4
use an
      autonomus transaction  May 13, 2002
Reviewer:
      Jim Kennedy  from Wilsonville, OR
Conner from UK probably read about using an autonomous transaction in Jonathan
Lewis's book "Practical Oracle 8i Building Efficient Databases" - a nice
compliment to Tom's book. (Chapter 20 pages 450 to 453)  Buy the books (Tom's
and Jonathan's).  Basically, an audit table is constructed and so you can track
all the sequence numbers and "not lose" any.  So you could tell if someone
"discarded an invoice".(who did it when and to what invoice.)  That should
satisfy most auditors.

      Rating: 2
OK, am I
      missing something here?  May 13, 2002
Reviewer:
      Colin Davies  from Los Angeles, California
What about the ORDER and NOCACHE parameters? I thought that the purpose of ORDER
was to maintain sequence.

          Followup:

It takes ONE rollback after a select seq.nextval to create a gap.  That is all.

the sequence (by default) is nicely ordered but it will still have gaps -- 100%
sure of that.

      Rating: 5
How can we move
      sequences from one schema to another dynamically?  August
      08, 2002
Reviewer:  A Reader  from USA
Hi, Tom:
We are moving a lot of objects from one schema to another, including sequences.
Is there a way to move them dynamically without human intervine?

When I do
create sequence schema2.seq1 start with (select last_value schema1.seq1from
schema1.seq1) increment by one nocache;

I get the following error:
ERROR at line 1:
ORA-01722: invalid number

I tried some other ways to do it, they all fail.  What I can do now is query
first and then create them using the last_value as the starting value of new
creation.  Is there a better way to do it dynamically?

Thank you in advance.

          Followup:

begin
   for x in ( select schema1.seq1.nextval nv from dual )
   loop
      execute immediate 'create sequence schema2.seq1 start with ' || x.nv ||
                        ' increment by one nocache';
   end loop;
end;
/

will do that -- use that instead of just the CREATE.

August 08, 2002 -- Thanks for the question regarding "session
      info/redo-archive", version 8.1.7

      You Asked
Hi Tom,
How can I find out all the session related setting from SQLPLus?
ex. I set the nls_date_format for my session. set the pagesize for my session.

When do the contents of the redo log file go to archive log?(After commit/before
commit/...)

      and we said...
Well, nls_date_format is a database session setting

Pagesize is a sqlplus setting -- not database.

SQL> show all
SQL> show pagesize

will show you SQLPLUS settings.  As for database settings, most of them are not
"retrievable".  NLS ones are however using:

select * from nls_session_parameters;

August 08, 2002 -- Thanks for the question regarding "Best way to
      Ref a Cursor", version 816

      You Asked
Hi,
   I have to create a new application - Its 3 tier with IIS , ASP and database
Oracle 816. Its been decided that the entire Business Logic will be in the
Backend thru packages.Fine Now i have to return result
sets which I will with Ref cursors. What is the best approach to code these
refcursors
1) Are refcursors, as they are dynamic ,  will it stay in shared pool for future
similar query execution.
2) For every sql query result set required ,create packaged functions returning
ref cursor type.
  -- This will be a lot of functions But will it be using bind varables.
3) Use dynamic sql open c1 for 'input_parameter'
   This way I will have only one generic function and the front end can just
pass the entire select statement as a parameter.
  Will this be a very expensive thing to do from a performance stand point.
though it will save me a lot of code.

which is better 3 or 4

4) Is it better to return result sets thru ref cursor or let the asp page write
select statements to query data ?

which is better 2  or 3 or 4

      and we said...
1) yes it will -- ALL sql in Oracle is dynamic.  Even "static" sql is
dynamically executed in the end.

2) #2 is a statement of fact.  It will use bind variables - absolutely.  You'll
pass inputs to the procedures, they will be bound into the cursor you open.

3) that would be a TRUELY bad idea and would subvert the entire intention of
putting the business logic in the package.  It would not be able to make
effective use of bind variables -- would kill the shared pool.  Most importantly
it would not achieve your objective (separation of logic).

4) I hate having SQL insert/update/delete/selects in the application.  I love
having it in a stored procedure.

IMO -- #2 wins 100%.  Its more secure, easier to fix and will promote sharing
(code reuse) of packaged functions (something ASP isn't very good at)

Review &
      Followup

      Rating: 3
Is it open
      ?  December 20, 2001
Reviewer:  Reader
       from NY
If  one of my application opens a refcusrsor and returns a
result set and another application needs the same output
do I need to open it again or the same resultset can be passed with out opening
refcursor.

 ( because somehow if the result set edges out of the
shared pool, the whole refcursor query will be parced,executed again i think (is
it true ?))

          Followup:

Once fetched from -- a ref cursor is "emptied".  If you need to fetch the rows
two times -- you need two cursors (or to open a cursor twice)

      Rating: 4
explain more
      please  December 21, 2001
Reviewer:
      A reader
Hi,

I agree with you.
But if you need two cursors, the problem is the
result sets of these two ref cursors may be
diff(in transaction table). OR please give me
an example of how to open cursor twice.

Thanks

          Followup:

Well, the most efficient method would be to open a SINGLE cursor and do all of
the necessary work against the SINGLE cursor -- not to process it twice.

If you need two read consistent (with respect to each other) cursors, you can:

o use isolation level serializable in a single transaction.  All queries will
return data as it looked at the beginning of your transaction.  read the server
concepts manual before you do this in order to understand the ramifications of
this isolation level.

o use an autonomous transaction that does a set transaction read only and then
opens the two cursors.  consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package types
  2  as
  3      type rc is ref cursor;
  4  end;
  5  /

Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure open_cursor(
p_cursor1 in out types.rc, p_cursor2 in out types.rc )
  2  as
  3      pragma autonomous_transaction;
  4  begin
  5      set transaction read only;
  6
  7      open p_cursor1 for select ename from emp;
  8
  9      dbms_lock.sleep( 60 ); -- in another session delete from emp and commit
 10
 11      open p_cursor2 for select ename from emp;
 12      commit;
 13  end;
 14  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> variable x refcursor
ops$tkyte@ORA817DEV.US.ORACLE.COM> variable y refcursor
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec open_cursor(:x,:y)

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> print x

ENAME
----------
SMITH
ALLEN
WARD
JONES
MARTIN
BLAKE
CLARK
SCOTT
KING
TURNER
ADAMS
JAMES
FORD
MILLER

14 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> print y

ENAME
----------
SMITH
ALLEN
WARD
JONES
MARTIN
BLAKE
CLARK
SCOTT
KING
TURNER
ADAMS
JAMES
FORD
MILLER

14 rows selected.

but here you have to realize that neither cursor1 or cursor2 will see the
uncommitted changes of YOUR transactions either (read about autonomous
transactions and understand them before using them is the moral to that story)

http://osi.oracle.com/~tkyte/autonomous/index.html
...

      Rating: 5
Do we have to
      close the reference cursors?  August 08, 2002

Reviewer:  A Reader  from USA
Tom,
In your example, you have

open p_cursor1 for select ename from emp;
open p_cursor2 for select ename from emp;

There are no close statements there.  Do we need or have to close these cursors.
Oracle document says we need to close cursors. We have a long going argument
here, similar to your procedure here, about whether we need to close the opened
ref cursors. How can the cursor be shared. Our concept is not clear. Your answer
may help settle this down.
Thank you.

          Followup:

The client closes them.

In this case, SQLPlus is the client who recieved the ref cursors (:x and :y).
When sqlplus exhausted the result set -- it closed them.

If java was the client, java would have to issue the rset.close()

Pro*c -- likewise

and so on.  It is the clients responsibility.  If plsql had OPENED and FETCHED
the results -- PLSQL would have closed them.

Yes, they need to be closed.

August 09, 2002 Sanjai -- Thanks for the question regarding "SQL
      Plus", version 8.1.7

      You Asked
Hi Tom,

I was wondering if there is a way to supress the output in sqlplus.

For example this is what I get now.

scott@ORCL> select * from emp;

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM
DEPTNO
---------- ---------- --------- ---------- --------- ---------- ----------
----------
      7369 SMITH      CLERK           7902 17-DEC-80        800
  20
      7499 ALLEN      SALESMAN        7698 20-FEB-81       1600        300
  30
      7521 WARD       SALESMAN        7698 22-FEB-81       1250        500
  30
      7566 JONES      MANAGER         7839 02-APR-81       2975
  20
      7654 MARTIN     SALESMAN        7698 28-SEP-81       1250       1400
  30
      7698 BLAKE      MANAGER         7839 01-MAY-81       2850
  30
      7782 CLARK      MANAGER         7839 09-JUN-81       2450
  10
      7788 SCOTT      ANALYST         7566 19-APR-87       3000
  20
      7839 KING       PRESIDENT            17-NOV-81       5000
  10
      7844 TURNER     SALESMAN        7698 08-SEP-81       1500          0
  30
      7876 ADAMS      CLERK           7788 23-MAY-87       1100
  20
      7900 JAMES      CLERK           7698 03-DEC-81        950
  30
      7902 FORD       ANALYST         7566 03-DEC-81       3000
  20
      7934 MILLER     CLERK           7782 23-JAN-82       1300
  10

14 rows selected.

Elapsed: 00:00:00.80

I want this output.

scott@ORCL> select * from emp;

14 rows selected.

Elapsed: 00:00:00.80

Thanks for your help.

      and we said...
Well, I would:

alter session set timed_statistics=true;
alter session set sql_trace=true;
set autotrace traceonly
select * from emp;
set autotrace off

and then use tkprof against the resulting trace files.  Otherwise the autotrace
times will be including in the sqlplus "timing".  tkprof will have much much
better timing information.

Alternatively, you can:

variable n number
set serveroutout on
exec :n := dbms_utility.get_time;
set termout off
select * from emp;
set termout on
exec dbms_output.put_line( (dbms_utility.get_time-:n)/100 || ' seconds' );

and run that in a script, termout off will surpress the display on screen.
Review &
      Followup

      Rating:
      2
count(*)  August 08, 2002
Reviewer:
      Sam  from US
How about just running count(*) ?

SQL > SELECT count(*) from emp;

14 rows selected

          Followup:

Well, that would be like comparing an apple to a toaster oven.

The plan for "count(*)" can and will be radically different then the plan for
the original query.

You cannot comparing the results (performance wise) of "select count(*)
<rest of query>" to "select * <rest of query>".  They are totally incomparable.

      Rating: 5
Follow up to
      Sam from US  August 09, 2002
Reviewer:  Jim
       from Oz
Sam,
    The output required was execution timings
not just the answer set count.

August 09, 2002 YL. -- Thanks for the question regarding "about
      dbms_output.put_line and UTL_FILE.PUT_LINE", version 7.3.4

      You Asked
Dear Tom,

  Your website is very useful for those developers that work on
oracle. Please Keep it on.

  Now, I have below questions to ask you:

  1. can i use utl_file.put_line without modifying the init.ora file?
     since I have no rights to access the production server.

  2. I want to use spo command in below codes, but it does not
     work, can u give me some suggestion on that?

     **************************************************
     spo c:\abc.txt

     DECLARE
     l_start integer;
     BEGIN
     l_start := dbms_utility.get_time;
     <<mk_loop>>
       while (dbms_utility.get_time-l_start < 1000)
       loop
         dbms_output.put_line('test');
       end loop mk_loop;

     END;
     spo off
     ***************************************************

     My expect result for abc.txt is that, it contains many
     "test" in the file.

  3. Your showsql.sql is very useful. Now I have modified the script
     which use utl_file to suit my case (I will loop the
     My_showsql.sql for about 5 minutes to monitor the server
     activities.) But, our company policies do not allow me to modify
     the init.ora file. Instead, i believe i can use
     dbms_output.put_line to replace the utl_file, but it seems it
     does not work and the buffer size is a consideration. Do u have
     any suggestion on my coding?

     Below is the code of My_showsql.sql:
     **************************************************************

     declare

     file_handle            UTL_FILE.FILE_TYPE;
     file_directory         varchar2(30) := '&1';
     file_name              varchar2(30) := '&2';
     v_mode                 varchar2(5)  := '&3';
     error_message          varchar2(60);
     v_now           long;
     v_username v$session.username%type;
     v_sid      v$session.sid%type;
     v_serial   v$session.serial#%type;
     v_process  v$session.process%type;
     v_status   v$session.status%type;
     l_str      long;
     v_user          long;
     v_program       long;
     v_last_call_et  long;
     v_logon_time    long;
     v_current_time  long;
     v_sql_text      long;
     l_str2          long;

     CURSOR p_cur IS
      select username, sid, serial#, process, status
        from v$session
       where username is not null;

     cursor p_cur2 IS
      select username||'('||sid||','||serial#||')' user_name,
             s.program,
             to_char(round(s.LAST_CALL_ET/60,2)) LAST_CALL_ET,
             to_char(s.LOGON_TIME,' Day HH24:MI') logon_time,
         to_char(sysdate,' Day HH24:MI') current_time,
             max(decode(v.piece,0,sql_text,' ')) ||
             max(decode(v.piece,1,sql_text,' ')) ||
             max(decode(v.piece,2,sql_text,' ')) ||
             max(decode(v.piece,3,sql_text,' ')) ||
             max(decode(v.piece,4,sql_text,' ')) ||
             max(decode(v.piece,5,sql_text,' ')) ||
             max(decode(v.piece,6,sql_text,' ')) ||
             max(decode(v.piece,7,sql_text,' ')) ||
             max(decode(v.piece,8,sql_text,' ')) ||
             max(decode(v.piece,9,sql_text,' '))
             sql_text
        from v$session s, v$sqltext_with_newlines v
       where s.status in ('ACTIVE', 'INACTIVE')
         and rawtohex(s.sql_address) <> '00'
         and s.username is not null
         and s.sql_address=v.address
       group by username||'('||sid||','||serial#||')',
                s.program, s.LAST_CALL_ET,
                to_char(s.LOGON_TIME,' Day HH24:MI'),
                to_char(sysdate,' Day HH24:MI')
       order by s.last_call_et;

     BEGIN
     file_handle := UTL_FILE.FOPEN(file_directory,file_name, v_mode);

     SELECT '* '||rpad(TO_CHAR(SYSDATE, 'MM-DD-YYYY 4:MI:SS'),47,' ')
            ||'*' into v_now FROM DUAL ;

    UTL_FILE.PUT_LINE(file_handle, v_now );
    UTL_FILE.PUT_LINE(file_handle, '*****');

    UTL_FILE.PUT_LINE(file_handle, '                      ');
    UTL_FILE.PUT_LINE(file_handle, 'Current Connections:  ');
    UTL_FILE.PUT_LINE(file_handle, '                      ');

    l_str := null;

    UTL_FILE.PUT_LINE(file_handle, 'User                 SID_and_Serial
Process         Status  ');
    UTL_FILE.PUT_LINE(file_handle, '-------------------- --------------------
--------------- --------');
    open p_cur;
    LOOP
    fetch p_cur into v_username, v_sid, v_serial, v_process, v_status;
        l_str:= rpad(v_username,20,' ')||' '||rpad(v_sid||','||v_serial,20,'
')||' '||rpad(v_process,15,' ')||' '||rpad(v_status,8,' ');
        UTL_FILE.PUT_LINE(file_handle, rtrim(l_str,chr(10)) );
        exit when (p_cur%notfound);
    END LOOP;
    close p_cur;

    UTL_FILE.PUT_LINE(file_handle, '                      ');
    UTL_FILE.PUT_LINE(file_handle, 'User(s) Details:      ');
    UTL_FILE.PUT_LINE(file_handle, '                      ');

    l_str2 := null;

    UTL_FILE.PUT_LINE(file_handle, 'User                 Program
Idle Time     Logon Time           Current Time        SQL Statement
                                      ');
    UTL_FILE.PUT_LINE(file_handle, '-------------------- --------------------
------------ -------------------- --------------------
----------------------------------------------------------------');
    open p_cur2;
    LOOP
    fetch p_cur2 into v_user, v_program, v_last_call_et, v_logon_time,
v_current_time, v_sql_text;
        l_str2:= rpad(v_user,20,' ')||' '||rpad(v_program,20,' ')||'
'||rpad(v_last_call_et,12,' ')||' '||rpad(rtrim(v_logon_time,chr(10)),20,' ')||'
'||rpad(v_current_time,20,' ')||' '||v_sql_text;
        UTL_FILE.PUT_LINE(file_handle, rtrim(l_str2,chr(10)) );
        exit when (p_cur2%notfound);
    END LOOP;
    close p_cur2;

    UTL_FILE.PUT_LINE(file_handle, '                      ');

    UTL_FILE.FCLOSE(file_handle);

  exception
    when utl_file.invalid_mode then ......barbarbar...
******************************************************************

   I hope you can understand what i am asking and Thank you Very much for your
help.

Regards,
Lok

      and we said...
1) only if you are HAPPY with the current utl_file_dir init.ora setting.
Otherwise, if you need to add a path and you are using a version of oracle
BEFORE 9i Release 2 -- you will need to not only modify the init.ora (or spfile
in 9i) you will need to restart the database.

In 9i Release 2, UTL_FILE uses a "directory" object instead of the utl_file_dir
init.ora parameter and these directory objects are created when needed, without
touching the parameter files, without restarting.

2) you seem to be missing "set serveroutput on size 1000000".  without that,
dbms_output goes into the great bit bucket in the sky.

3) umm, that'll collect quite a bit of data don't you think?  What you could do
would be to set up some database tables and using dbms_job, schedule a stored
procedure that inserts into these tables relevant information that you want to
collect (don't run a report every 5 minutes, gather some data every 5 minutes).
Then, report on the data whenever you want, however you want.

My solution -- don't utl_file every 5 minutes, stuff the data into a table
somewhere and report on that.
Review &
      Followup

      Rating: 5
Good solution,
      Tom.  August 06, 2002
Reviewer:  Lok
       from HK
Thank you very much, Tom. You have given me another possible solution. And your
idea is very nice.

But, my boss would like to have a script being run only, without affecting the
production setting and creating objects in the database. He said running a
script is the
least affective solution.

          Followup:

I agree that running a script is the LEAST effective solution.  (believe you
made a typo there but it is a true freudian slip!)

Fine, don't use dbms_job, run a script -- use dbms_output and spool the results.

      Rating: 5
Just follow the
      TOM and Try to convince ur Boss  August 06, 2002

Reviewer:  Sikandar Hayat Awan  from Pindi Bhattian -
      Pakistan
I am also inserting performance data into tables for history and the scripts are
scheduled. In ur case if u don't have access to init.ora but u must have
priviliges of creating tables. Just create a tables and insert data into it and
after some time show the results to your boss if he is still not happy with it
then read the followings,

1- Boss is always right.
2- If Boss is not right then see rule no 1.

Dear TOM please update this site as on some places u r still on vacation as at
the moment on my right side vertical banner.

      Rating:
      4
  August 07, 2002
Reviewer:  Robert
       from PA
>>please update this site as on some places u r
>>still on vacation as at
>>the moment on my right side vertical banner.

Check your keyboard, looks like some keys are taking a vacation

      Rating: 1
Pl.
      Read  August 07, 2002
Reviewer:  Sikandar Hayat
      Awan  from Pindi Bhattian - Pakistan
Dear Robert my keyboard is working fine but really the text is there and pasting
of text is as below,

"I'm on vacation, I won't be responding to anything until after August 4th, 2002

This forum is not intended for additional questions. If you would like to ask
additional questions, please go to Ask Tom. "

          Followup:

What he meant by the keyboard being "not working" is your use of "u" and "r" for
real words.  (it is a pet peeve of mine, makes it very very hard to read text
like that.  maybe it works fine on a cell phone where you have 20
characters/line and one or two lines but... in the real world the words are YOU
and ARE and YOUR and so on...)

I've taken the "on vacation thing" down from the review section.

      Rating: 4
another
      problem  August 08, 2002
Reviewer:  yl.chan
       from HK
Dear Tom,

I have added "set serveroutput on" in the script, but
error still occur.

Here is my script (my_script.sql):

set serveroutput on size 999999
spo c:\abcabc.txt
DECLARE
  l_start integer;
  l_no    number;
BEGIN
 l_start := dbms_utility.get_time;
 l_no    := 1000;
  <<mk_loop>>
  while (dbms_utility.get_time-l_start < l_no)
  loop
    dbms_output.put_line('test : ' );
  end loop mk_loop;
END;
spo off

And below is the error message:
SQL>  @c:\my_script.sql
spo off
*
ERROR at line 27:
ORA-06550: line 27, column 1:
PLS-00103: Encountered the symbol "SPO"

Thank you!

Regards,
Lok

          Followup:

Oh, simple:

    dbms_output.put_line('test : ' );
  end loop mk_loop;
END;
/
spo off

you were missing the "/" used to submit the plsql block to the database.

August 09, 2002 Zen -- Thanks for the question regarding "Label
      format", version 9.0.1.1

      You Asked
Hi Tom,
Thanks for previous helps.
I am trying to design Lable template for printing  with a specific format as
below.
We have two filed in table T:-
1.category number(2)
2.daf number(4)

Runt time Input = category =01 ,daf=:from_daf  (eg.1011) , :to_daf (1324)
and oupt put should required  like:-

01/1011    01/1012   01/1013   01/1014

01/1021    01/1022   01/1023   01/1024

01/1031    01/1032   01/1033   01/1034

01/1041    01/1042   01/1043   01/1044
....
last 4 record
01/1321    01/1322   01/1333   01/1324

each line have 4 record and incrementing by 1 with respect to line no. also.
Please try to solve or help to accomplish it.
Rgds
Zen

      and we said...
Something like this:

ops$tkyte@ORA9I.WORLD> create table t ( category number, daf number );

Table created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> insert into t select 1, 1011+rownum-5 from all_objects
where rownum < 500;

499 rows created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> select trunc((rownum-1)/4),
  2         max( decode( mod(rownum,4), 1, data, null )) c1,
  3         max( decode( mod(rownum,4), 2, data, null )) c2,
  4         max( decode( mod(rownum,4), 3, data, null )) c3,
  5         max( decode( mod(rownum,4), 0, data, null )) c4
  6    from (select to_char(category,'fm00')||'/'||to_char(daf,'fm0000') data
  7            from t
  8           where category = 1
  9             and daf between 1011 and 1324
 10           order by category, daf )
 11   group by trunc((rownum-1)/4)
 12  /

TRUNC((ROWNUM-1)/4) C1        C2        C3        C4
------------------- --------- --------- --------- ---------
                  0 01/1011   01/1012   01/1013   01/1014
                  1 01/1015   01/1016   01/1017   01/1018
                  2 01/1019   01/1020   01/1021   01/1022
                  3 01/1023   01/1024   01/1025   01/1026
                  4 01/1027   01/1028   01/1029   01/1030
                  5 01/1031   01/1032   01/1033   01/1034
                  6 01/1035   01/1036   01/1037   01/1038
                  7 01/1039   01/1040   01/1041   01/1042
                  8 01/1043   01/1044   01/1045   01/1046
                  9 01/1047   01/1048   01/1049   01/1050
                 10 01/1051   01/1052   01/1053   01/1054
                 11 01/1055   01/1056   01/1057   01/1058
.....

will do that.
Review & Followup

      Rating:
      1
  August 07, 2002
Reviewer:
      A reader

      Rating: 4
More
      Specific  August 07, 2002
Reviewer:
      A reader
Hi Tom
Thanks,
But sorry the out put is not as required.
We reqd. like
01/1011    01/1012   01/1013   01/1014

01/1021    01/1022   01/1023   01/1024

01/1031    01/1032   01/1033   01/1034

01/1041    01/1042   01/1043   01/1044
....

i.e each line is differnet from previous line.and second last digit is
incremented by 1 to its previous line.

if first record of first line is= 01/1011
then first record of second line is= 01/1021
third line start with 01/1031
...
Sorry to unable to expalin previously.

Thanks

          Followup:

Well, thanks for the 1 star above with no comment -- that wasn't very useful to
me either.

Anyway -- now I'm thoroughly confused.  What the heck is in the table -- try
giving a COMPLETE example.  It sounds like you want us to "make up" data -- that
the data on the labels are not the data in the tables.

You need to give a slightly better question definition (1 star for that ;)

      Rating: 4
I think he
      means this  August 08, 2002
Reviewer:  Jim
      Kennedy  from Portland, OR USA
The pattern I noticed was incriment each lline by one with in a line.  Each row
is 10 more than the prior row

01/1011
01/1021

So I would just multiply the rownum by 10 and add 1,2,3,or 4 for each column.
The person who posed the question should be able to make a stab at that given
all the information

      Rating: 4
More
      detail  August 08, 2002
Reviewer:  Zen

Hi Tom.
Sorry again not able to explain u properly.
Please find the detail of our existing Dbase 3 + prog. that we are already used
developed by our previous prog. Now this we want to convert in to Oracle.

"set talk off
set status off
set confirm off
close all

clea
use bobspec.dbf

stor space(2) to D_MO,D_DAY,D_DOF,B_LOT,TNUM,POSITION,B_YAM
* STOR SPACE(2) TO STW,CLW
stor space(1) to T_LINE, ANS,ANS1
stor space(3) to B_DENIER,B_FMENT
stor space(16) to B_SPEC,T_DOFF1,T_DOFF2,T_DOFF3,T_DOFF4
stor space(6) to b_typ
STW=0
CLW=0
Y=6
L=1
N=0
ANS='Y'

do while upper(ANS)='Y'
    @22,1 say 'Do you want to set PRINTER (Y/N) ? ' get ANS
    read
    @22,1 say '                                           '
    if upper(ANS) = 'N'
       loop
    endif
    ANS1='N'

    do while upper(ANS1)='N'
        set device to printer
            @l,1  say '.              .'
            @1,55 say '.              .'
            @l,1  say ' '
              wait
            @l,1  say '.              .'
            @1,55 say '.              .'
            @l,1  say '  '
            wait
        set device to screen
        clear
        ANS1=' '
        @23,28 say 'Confirm PRINTER (Y/N) = ' get ANS1
        read
        @23,28 say '                           '
        if upper(ANS1) = 'Y'
           ANS = 'N'
        endif
    enddo

enddo

clear

@1,30 say 'LABEL PRINTING'
@3,1  say '*-DOFF NO.-*'
@3,27 say '*------- BOBBIN SPEC. ------*   '
@4,1  say 'MM-DD-DOFFNO    LINE'
@4,27 say 'LOT      DENIER FILEMENT YARN   '
@5,1  say '-- -- ------    ----'
@5,27 say '-------- ------ -------- ----   '
@5,60 say '-----------------'
@23,5 say 'Month(MM) = 99 (Exit)'
END_PROC='N'

do while END_PROC='N'
   if y > 20
      @1,30  say   'LABEL PRINTING'
      @3,1   say   '*-DOFF NO.-*'
      @3,27  say   '*------- BOBBIN SPEC. ------*'
      @4,1   say   'MM-DD-DOFFNO    LINE'
      @4,27  say   'LOT      DENIER FILEMENT YARN '
      @5,1   say   '-- -- ------    ----'
      @5,27  say   '-------- ------ -------- ---- '
      @5,60  say   '-----------------'
      @23,5 say 'Month(MM) = 99 (Exit)'
      y = 6
   endif

   set confirm on
   @Y,1 get D_MO
   read
   set confirm off

   if D_MO='99'
      END_PROC='Y'
      loop
   endif

   if D_MO < '01' .or. D_MO > '12'
      ans = ' '
      @23,55 say 'Invalid MONTH ' get ans
      read
      @23,55 say '                     '
      loop
   endif

   @Y,4  get D_DAY
   @Y,8  get D_DOF
   @Y,18 get T_LINE
   @Y,27 get B_TYP
   * @Y,31 get B_LOT
   @Y,37 get B_DENIER
   @Y,45 get B_FMENT
   @Y,52 get B_YAM
   @Y+3,1 SAY 'FROM'
   @Y+3,15 SAY 'TO'
   @Y+4,1 GET STW
   @Y+4,15 GET CLW
   read

   if t_line = '9' .or. t_line < '5'
    else
      ans = ' '
      @23,55 say 'Invalid LINE No. ' get ans
      read
      @23,55 say '                     '
      loop
   endif

   ANS='Y'
   set confirm on
   @23,55 say 'CONFIRM DATA (Y/N) ' get ANS
   set confirm off
   read
   @23,55 say '                        '
   if upper(ANS) = 'Y'
      NUM1=0
      NUM1=STW-1
      B_SPEC = 'L'+B_TYP+' '+B_DENIER+'/'+B_FMENT+B_YAM

      LOOP_PRINT = 'Y'

      set device to print
      N=1

      do while LOOP_PRINT = 'Y'
         NUM1=NUM1+1
         TNUM=ltrim(str(NUM1))
         POSITION=STUFF(TNUM,1,0,REPL('0',2-LEN(TNUM)))
         T_DOFF1='DOF '+D_MO+D_DAY+' '+D_DOF+'/'+T_LINE+POSITION+'1'
         T_DOFF2='DOF '+D_MO+D_DAY+' '+D_DOF+'/'+T_LINE+POSITION+'2'
         T_DOFF3='DOF '+D_MO+D_DAY+' '+D_DOF+'/'+T_LINE+POSITION+'3'
         T_DOFF4='DOF '+D_MO+D_DAY+' '+D_DOF+'/'+T_LINE+POSITION+'4'
         if N>4
            N=1
            L=L+3
         endif
          @L,1  say B_SPEC
          @L,19 say B_SPEC
          @L,37 say B_SPEC
          @L,55 say B_SPEC
         L=L+1
          @L,1  say T_DOFF1
          @L,19 say T_DOFF2
          @L,37 say T_DOFF3
          @L,55 say T_DOFF4
         L=L+2
         N=N+1

         if T_LINE = '9' .AND. N > 4
            L=L+2
            @L,1 say '  '
            clear
            set device to screen
                @1,30 say 'LABEL PRINTING'
            @3,1  say '*-DOFF NO.-*'
            @3,27 say '*------- BOBBIN SPEC. ------*'
            @4,1  say 'MM-DD-DOFFNO    LINE'
            @4,27 say 'LOT      DENIER FILEMENT YARN'
            @5,1  say '-- -- ------    ----'
            @5,27 say '-------- ------ -------- ---- '
            @5,60 say '-----------------'
            @23,5 say 'Month(MM) = 99 (Exit)'
             y = 6
             LOOP_PRINT='N'
              else

            if T_LINE = '4' .and. NUM1 >= CLW
               L=L+2
               @L,1 say ' '
               set device to screen
               LOOP_PRINT='N'
             else
               if NUM1 >= CLW
                  L=L+2
                  @L,1 say '   '
                  set device to screen
                  LOOP_PRINT='N'
               endif

            endif

         endif

      enddo

      if T_LINE < '5'
         append blank
         replace BB_MM       with  D_MO
         replace BB_DD       with  D_DAY
         replace BB_DOF      with  D_DOF

I the above example after giving the input it will post to database and print in
the format I explain u prevously.Posted data is not important to us only is the
label in reqd. format.

In original example there is additional field alos that per printing on
label,but main in put is same .
Hope this time I make it clear.
Thanks once again for your valuable time and space too.
Zen

          Followup:

Sorry, I'm not a dbase 3 compiler ;)

August 09, 2002 Albert -- Thanks for the question regarding
      "patchset 8.1.7.4", version 8.1.7

      You Asked
Hello Tom, I hope you can answer my following question.
I have 8.1.7.0.0 SE on Win2K Server. Because of application requirements, we
must go to the latest patch 8.1.7.4. (Patchset::2376472 ).
Now, in the install instructions, there are multiple steps listed. First, you
use an OIU like tool to install, and just follow the instructions.
Then there are several other steps listed in the instructions like invoking
sqlplus, run some statements etc..
But what if no database is present yet. Then only the OUI can be done.
So, in this case, just running the OUI is ok?
We have no database yet, because the application vendor will create one, but
expects Oracle to be on level 8.1.7.4
Thanks in advance !

      and we said...
all newly created databases will be created with 8174 scripts so no upgrade will
be necessary for them.

The sqlplus steps are used to upgrade existing instances.  all newly created
instances will be 8174 ready out of the box.
Review &
      Followup

      Rating: 5
Installing New
      database 8.1.7.4  August 09, 2002
Reviewer:
      Yogeeraj  from Mauritius
Hello,

Thank you in advance for your guidance.

If i need to install Oracle Database 8.1.7.4 on a new server, which one of A or
B (see below) is the right way?
=======
Case A:
=======
STEPS:
1. Install base release 8.1.7.0 - do not create database, save installation
scripts
2. Install Patchset 8.1.7.4
3. Create Database (using installation scripts saved in 1. above)

OR
=======
Case B:
=======
1. Install base release 8.1.7.0 - create database
2. Install Patchset 8.1.7.4

Best Regards
Yogeeraj

          Followup:

if you do case b, you'll have to upgrade the database.

if you do case a, you'll have "less steps"

      Rating: 5
Thanks, because
      its clear now  August 09, 2002
Reviewer:  Albert
      van der Sel  from The Netherlands
Thank you very much.

August 09, 2002 Lee -- Thanks for the question regarding
      "Multiple Instances of PL/SQL Procedures", version 8.1.7

      You Asked
I have been searching everywhere to see if it is possible to run a pl/sql
procedure in parallel with the same pl/sql procedure.

For example,  you have a procedure called GET_DATA and each specific user would
need to call this procedure to view specific personal data.  I want to be able
to have the procedure GET_DATA being ran by Fred Jones run in parallel with
procedure GET_DATA being ran by Martha Jebonski.

In my particular case, I would need 10 instances of the same procedure executing
simultaneously.  Is the only way to accomplish this by using Objects?

Thanks in advance!

Lee

      and we said...
Each user can run the procedure without doing anything special at all -- if you
log ten people in and each starts running the procedure -- there will be ten
copies of the procedure running (we'll share the text, the code -- but each
session will have its own data segment)

You need do nothing other then connect, run procedure.  It just happens.  The
database is like an operating system -- you just connect and run.
Review &
      Followup

      Rating: 3
A little extra
      info  August 09, 2002
Reviewer:
      A reader
Don't forget that prior to 8.1.5 and the default from then
on is the following behaviour

User A calls Stored Proc owned by SPOWNER, the stored proc
will access tables owned by SPOWNER not User A!

From 8.1.5 on you can have invoker privs so
User A calling SPOWNER's stored proc will result in User
A tables being accessed

      Rating: 3
what if i want
      only one user ?  August 09, 2002
Reviewer:
      Joachim"Hannibal"W.R.Mayer  from Austria/Europe
the answer was clear but there is something open
( for me ).
what if sombody has a job hs wants
to start 10 times at once from sql-plus f.e. like
unix
# for i 1..10 ; run_my_job & ; next i;

in the way of multiple instances of a backgroundjob.
is there also a way to do this
or will i always need 10 user to log in ?

thx

hannibal

          Followup:

read about dbms_job.

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76936/dbms_job.htm#999107
...

August 09, 2002 -- Thanks for the question regarding "SQL Query
      Tuning and identifying Indexes", version 8.1.7

      You Asked
Hi Tom,

I have a query like this on one of my databases and needs tuning:

Select * from (
Select LH_LETTER_NO,
LH_LETTER_ID,  LH_ATH_FST_NAME,
 LH_ATH_FAM_NAME,
 LH_SDR_FST_NAME,
 LH_SDR_FAM_NAME,
 LH_SEND_DT,
 LH_SUBJECT,
 LH_DESCRIPTION,
 LH_CREATE_DT,
 LS_STATUS_DESC,
 LS_STATUS_ORDER,
 rownum ROWCOUNT
 from (
Select LH_LETTER_NO,
LH_LETTER_ID,  LH_ATH_FST_NAME,
 LH_ATH_FAM_NAME,
 LH_SDR_FST_NAME,
 LH_SDR_FAM_NAME,
 LH_SEND_DT,
 LH_SUBJECT,
 LH_DESCRIPTION,
 LH_CREATE_DT,
 LS_STATUS_DESC,
 LS_STATUS_ORDER,
 rownum ROWCOUNT
 from (
Select LH_LETTER_NO,
LH_LETTER_ID, initcap(LH_ATH_FST_NAME)  LH_ATH_FST_NAME,
initcap(LH_ATH_FAM_NAME)  LH_ATH_FAM_NAME,
initcap(LH_SDR_FST_NAME)  LH_SDR_FST_NAME,
initcap(LH_SDR_FAM_NAME)  LH_SDR_FAM_NAME,
LH_SEND_DT,
LH_SUBJECT,
LH_DESCRIPTION,
LH_CREATE_DT,
LS_STATUS_DESC,
LS_STATUS_ORDER
from PRM_LETTER_HISTORY, PRM_LETTER_STATUS where LS_STATUS = LH_STATUS AND
LH_STATUS != 'DT' AND
LH_ATH_CCO_ID = 'asundare' AND
LH_CREATE_DT between to_date( '2002-07-06','YYYY-MM-DD' ) AND to_date(
'2002-08-06 23:59','YYYY-MM-DD HH24:MI' )
UNION
Select LH_LETTER_NO,
LH_LETTER_ID, initcap(LH_ATH_FST_NAME)  LH_ATH_FST_NAME,
initcap(LH_ATH_FAM_NAME)  LH_ATH_FAM_NAME,
initcap(LH_SDR_FST_NAME)  LH_SDR_FST_NAME,
initcap(LH_SDR_FAM_NAME)  LH_SDR_FAM_NAME,
LH_SEND_DT,
LH_SUBJECT,
LH_DESCRIPTION,
LH_CREATE_DT,
LS_STATUS_DESC,
LS_STATUS_ORDER
from PRM_LETTER_HISTORY, PRM_LETTER_STATUS where LS_STATUS = LH_STATUS AND
LH_STATUS != 'DT' AND
 LH_LETTER_NO in (select LV_LETTER_NO from PRM_LETTER_REVIEWER
where LV_CCO_ID = 'asundare') AND
 LH_CREATE_DT between to_date( '2002-07-06','YYYY-MM-DD' ) AND to_date(
'2002-08-06 23:59','YYYY-MM-DD HH24:MI' )
) ORDER BY LH_SEND_DT desc, LH_LETTER_ID desc ) ) where ROWCOUNT BETWEEN 1 AND
10

and the tables are

CREATE TABLE PRM_LETTER_STATUS
(
 LS_STATUS        CHAR(2)     NOT NULL,
 LS_STATUS_DESC        VARCHAR2(100)    NOT NULL,
 LS_STATUS_ORDER    CHAR(1)        NOT NULL
);

CREATE TABLE PRM_LETTER_HISTORY (
  LH_LETTER_NO      NUMBER( 38 )  NOT NULL,
  LH_SUBJECT        VARCHAR2(300)  NOT NULL,
  LH_DESCRIPTION    VARCHAR2(1200),
  LH_ATH_FST_NAME   CHAR(75)      NOT NULL,
  LH_ATH_FAM_NAME   CHAR(75)      NOT NULL,
  LH_ATH_CCO_ID     CHAR(50)      NOT NULL,
  LH_CREATE_DT      DATE          NOT NULL,
  LH_SENDER_FLAG    CHAR(1),
  LH_SEND_DT        DATE,
  LH_LETTER_CTGRY   CHAR(150)      NOT NULL,
  LH_PRODUCT_CTGRY  CHAR(300)     NOT NULL,
  LH_STATUS         CHAR(2)       NOT NULL,
  LH_HAND_DEL_FLAG  CHAR(1)       NOT NULL,
  LH_CREATE_DT_SVR  DATE          NOT NULL,
  LH_SEND_DT_SVR    DATE,
  LH_GREETING       VARCHAR2(300),
  LH_FOOTER         VARCHAR2(600),
  LH_SENDER_CCO_ID  CHAR(50),
  LH_SDR_FST_NAME   CHAR(50),
  LH_SDR_FAM_NAME   CHAR(50),
  LH_MULTIPLE_FLAG  NUMBER(38),
  LH_LETTER_ID NOT  NULL CHAR(15),
  LH_SUB_PROD_CTGRY VARCHAR2(362),
  PRIMARY KEY ( LH_LETTER_NO )
    USING INDEX  PCTFREE 10
      STORAGE(INITIAL 10240 NEXT 10240 PCTINCREASE 50 )
      TABLESPACE PRM_DATA)
 TABLESPACE PRM_DATA PCTUSED 40 PCTFREE 10
 STORAGE(INITIAL 10240 NEXT 24576 PCTINCREASE 50 )
 PARALLEL (DEGREE 1 INSTANCES 1) NOCACHE;

 CREATE TABLE PRM_LETTER_REVIEWER (
   LV_LETTER_NO    NUMBER(38)    NOT NULL,
   LV_CCO_ID       CHAR(50)      NOT NULL,
   LV_REVIEW_DATE  DATE          NOT NULL,
   LV_FIRST_NAME   CHAR(75),
   LV_FAMILY_NAME  CHAR(75),
   LV_COMMENTS     VARCHAR2(1200),
   PRIMARY KEY ( LV_LETTER_NO, LV_CCO_ID, LV_REVIEW_DATE )
     USING INDEX  PCTFREE 10
       STORAGE(INITIAL 10240 NEXT 10240 PCTINCREASE 50 )
       TABLESPACE PRM_DATA)
  TABLESPACE PRM_DATA PCTUSED 40 PCTFREE 10
  STORAGE(INITIAL 10240 NEXT 10240 PCTINCREASE 50 )
 PARALLEL (DEGREE 1 INSTANCES 1) NOCACHE;

table row count:
---------------
PRM_LETTER_STATUS    :5 records
PRM_LETTER_HISTORY    :9 million records
PRM_LETTER_REVIEWER    :6 million records

There are no other indexes on any of the table columns.

can you please suggest like how to go through the query tuning and indexes on
the tables.

regards,
Vijaya Chander V.S

      and we said...
There are BOOKS on this topic.  can i do that here?  no, I can recommend a book:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76992/toc.htm
Guy Harrison has a good one as well (search on amazon for it)

I will say this:

o can you use UNION ALL instead of UNION?  UNION ALL is faster.  a union must do
a SORT UNIQUE after running both queries.  A UNION ALL does not.

o can you use OR's in a single statement instead of UNION at all?  eg:

where LS_STATUS = LH_STATUS
  AND LH_STATUS != 'DT'
  AND ( LH_ATH_CCO_ID = 'asundare'
        OR
        LH_LETTER_NO in (select LV_LETTER_NO from PRM_LETTER_REVIEWER where
LV_CCO_ID = 'asundare')
      )
  AND LH_CREATE_DT between to_date( '2002-07-06','YYYY-MM-DD' )
                       AND to_date( '2002-08-06 23:59','YYYY-MM-DD HH24:MI' )

o push ROWNUM in as far as possible.  don't use:

       where ROWCOUNT BETWEEN 1 AND 10
use:

   ... ) ORDER BY LH_SEND_DT desc, LH_LETTER_ID desc )
        where ROWNUM <= 10 ) where ROWCOUNT >= 1

instead.

instead

Review &
      Followup

      Rating: 4
SQL Query
      Tuning and Identifying Indexes  August 09, 2002

Reviewer:  Vijaya Chander V.S  from Bangalore, India

Thank you for ur response and we did the same what you have recommended.This
query was in a search page and its taking lots of time to come up with results.
Now its really good in response time.

I went through the link that you provided.Is the Book that you recommended is
Oracle SQL High-Performance Tuning (2nd Edition)by Guy Harrison? Please me know.

          Followup:

yes, that is the book.

      Rating: 5
How about using
      the Anayltical Function   August 09, 2002

Reviewer:  A reader
Hi Tom,

How about using the analytical Function row_count for selcting the rownum.

When we tried using that we got a very skewed tkprof output.

Can you explain this behaviour.

The TkProf Outputs.

Without Analytical Function

call count cpu elapsed disk query current
rows
------- ------ -------- ---------- ---------- ---------- ----------
----------
Parse 1 0.02 0.02 0 0 0
0
Execute 1 0.00 0.00 0 0 0
0
Fetch 2 2.30 2.30 1270 3182 64
10
------- ------ -------- ---------- ---------- ---------- ----------
----------
total 4 2.32 2.32 1270 3182 64
10

With Analytical Function

call count cpu elapsed disk query current
rows
------- ------ -------- ---------- ---------- ---------- ----------
----------
Parse 1 0.01 0.01 0 0 0
0
Execute 1 0.00 0.00 0 0 0
0
Fetch 1 51.33 116.57 0 1011993 1469
0
------- ------ -------- ---------- ---------- ---------- ----------
----------
total 3 51.34 116.58 0 1011993 1469
0

Regards,
Ganesh R

          Followup:

that output is unreadable and the queries are missing.

ROWNUM is more appropriate here, ROWNUM will be more preformant.  TOP-N queries
are best done using rownum -- not ROW_NUMBER().  ROW_NUMBER is great for getting
TOP-N from different partitions but when the partition is the entire set --
ROWNUM is best.

August 09, 2002 Jay -- Thanks for the question regarding "Bind
      Variables with In Lists ", version 8.1.6

      You Asked
Hi Tom,

I have been pushing our developers to use bind variables and quoting from you
and others.  Many of your suggestions have been implemented however I have been
unable to find a way to use bind variables with dynamically generated in lists.

EX.

select rid_id, ri_data from registration_info where rid_id in
(6982,6983,6984,6985,6986,6987,6988, .......) and mem_id = :V00001

The developer in question says that there can be up to 1000 variables in the "IN
LIST" depending on the organization.

How should they code it?   I personally think 1000 variables in an "in list" is
too high but I can't find supporting documentation to prove it.  What is your
opinion?

thanks,

Jay

      and we said...
three solutions:

a) have them issue "alter session set cursor_sharing=force" before parsing that
query and "alter session set cursor_sharing=exact" after parsing it.  This will
REDUCE the number of statements in the shared pool to the number of statments
that have the same cardinality of in list values.  That is, you'll have queries
like:

where rid in ( :bv1 ) and mem_id = :v00001
where rid in ( :bv1, :bv2 ) and mem_id = :v00001
....
where rid in ( :bv1, :bv2, .... :bv1000 ) and mem_id = :v00001

so you'll have upto 1,000 (instead of upto infinity) copies.

b) use global temporary tables -- array insert the in list values and then:

where rid in ( select * from global_temp_table ) and mem_id = :v0001

c) use the technique found here:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:110612348061

Review &
      Followup

      Rating: 4
Use of global
      temp Tables may be a problem  August 08, 2002

Reviewer:  SKV  from NJ
Hi Tom,

I had a function which was returning the result set from global temp table to
the application. My function was something like this.

CREATE OR REPLACE PACKAGE Test As
  PROCEDURE P1(inpId NUMBER, inpArray VAR_ARRAY, T_TBL VARCHAR2);
  FUNCTION F1(inpId1 NUMBER, inpArray1 VAR_ARRAY, inpId2 NUMBER, inpArray2
VAR_ARRAY) RETURN Test.cursorType;
  TYPE cursorType IS REF CURSOR;
END Test;
/
CREATE OR REPLACE PACKAGE BODY Test AS
PROCEDURE P1(inpId NUMBER, inpArray VAR_ARRAY, T_TBL VARCHAR2) AS
        sqlString  VARCHAR2(256);
        sqlString1 VARCHAR2(200);
        riTable    VARCHAR2(30);
        riCol       VARCHAR2(15);
    BEGIN
        SELECT rev_idx_tbl_nm,rev_idx_colmn_nm
        INTO riTable,riCol
    FROM key_tbl_mpng
    WHERE key_id = inpId;
    -- Delete any record in the Temp_array Table if any.
    EXECUTE IMMEDIATE 'DELETE from TEMP_ARRAY';
    -- Load all the values in the Array INTO the Temp Table.
    FOR i in 1..inpArray.COUNT
    LOOP
        INSERT INTO Temp_Array VALUES (inpArray(i));
    END LOOP;

    -- Construct the Sql Statement for execute.
    sqlString  := 'INSERT INTO '||T_TBL||' SELECT /*+INDEX('||riTable||'
'||riTable||'_NK)*/';
        sqlString  := sqlString||' UNIQUE Colmn_Val FROM '||riTable||' WHERE
'||riCol;
    sqlString1 := ' in (SELECT t_array FROM TEMP_ARRAY)';

    -- Execute the SQL statement.
    EXECUTE IMMEDIATE sqlString||sqlString1;
END;

FUNCTION  F1(inpId1 NUMBER, inpArray1 VAR_ARRAY, inpId2 NUMBER, inpArray2
VAR_ARRAY) RETURN Test.cursorType AS
    riCur Test.cursorType;
    sqlString  VARCHAR2(256);
    riNum      integer;
    riTable       VARCHAR2(15);
BEGIN
    -- Clear all temp Tables.
    EXECUTE IMMEDIATE 'COMMIT';

        riTable := 'TEMP_TBL1';
        Test.P1(inpId1, inpArray1, riTable);
        riTable := 'TEMP_TBL2';
        Test.P1(inpId2, inpArray2, riTable);

    -- Get the Union of all the resultset from Temp Table.
        sqlString := 'SELECT UNIQUE COL1 from TEMP_TBL1 WHERE t_duns in ';
        sqlString := sqlString||'(SELECT UNIQUE COL1 from TEMP_TBL2)';

    -- Execute the Cursor.
        Open riCur for sqlString;

    RETURN(riCur);

    EXCEPTION WHEN OTHERS THEN
        dbms_output.put_line(sqlerrm);
        RETURN (riCur);
END;
END Test;
/

When I run this function from sqlplus, it works fine and returns the right
resultset. But when this process is called from Java, it gives the roor that
object does not exists. When this function is called from java, then it executes
this perfectly ok. But when recordset is fetched at that point it says that
object does not exists. Does Java treats the each statement a new connection to
database? I presume that it is making a new connection to database at the fetch
level and it will not find any records as global temp table maintains records
with respect to individual user session.

Is my diagonosis correct? How to solve this problem, if possible?

          Followup:

It is because JAVA/JDBC is brain dead and the creators of Jdbc decided in their
infinite wisdom (sic) that -- TRANSACTIONS ARE BAD and shouldn't be supported by
default.

JDBC, likes its weaker brother ODBC, commit by default after each statement.

Since "create global temporary table t ( x int )" is really "on commit delete
rows" -- by the time your statment finished -- JDBC commited in the database --
trashing your result set!  The temporary table you used was GONE.

It is surprising how many Java/JDBC developers do not realize this and they
happily code things like:

try
    update t1 ..
    update t2 ....
    commit
catch
     rollback

thinking -- HEY, if the T2 update fails, it is OK, we'll rollback -- not
understanding that the update to t1 was committed long before the update to t2
took place.

It is my opinion that immediate after connecting to the database, the very next
line of code MUST BE:

         conn.setAutoCommit(false);

in all well written Java programs.  If not, you have a bug waiting to happen.

This will correct your issue 100%

      Rating: 4
Bind Variable
      and In Lists Review  August 09, 2002
Reviewer:
      Jay Earle  from Fredericton, New Brunswick, Canada
The answer was  both interesting and useful.  I guess the fact that we tried a
couple of the things you suggested make me feel a bit better.  Unfortunately I
am pretty sure that we will have to tackle this in the Fall when we go to 9i.

1.  Cursor_sharing=FORCE.    might work but we have had white space issues with
Broad Vision (3rd Party login App) before and will be unable to re-implement.
This may be an option in 9i.

2.  Global TT.  We have tried these as well and got into  problems with sessions
using huge Global temp tables (all 1000 or more variables generating 2GB+ GTT)
and really hurting the database server.

3. use an object type.  We have thrown this idea around a bit but the Design
Team decided that development should have some additional training before we
introduce types.

          Followup:

1) I'm suggesting only using the cursor sharing force right BEFORE and disabling
right AFTER the statement.  I would only use the cursor sharing for that QUERY
and no others.

2) for little piddling 1000 row entries?  Even if you had 10,000 users doing
this, I cannot imagine 2gig?

Besides, how does 2gig (which is small) "hurt" a database server?

3) huh?  it is totally transparent to them -- have one "smart person" implement
the code and then it is just like a builtin utility.  No training needed here at
all.

August 09, 2002 PJ -- Thanks for the question regarding "Stored
      Procedures and Space Fragmentation in System Tablespace", version 8.1.7

      You Asked
Our application code (combination of oltp,batch,and reporting) is moving towards
100% PLSQL stored in the database. Suppose we have hundreds of packages making
up one Gig of parsed code when complete.  What concerns should we have as to the
fragmentation of the system tablespace as this Gig of code is moved in and out
of the system during its lifecycle?  What database optimization suggestions
should we consider?

      and we said...
I wouldn't do a thing.

In fact I don't do anything special at all here

I think it is very very safe to say that on my machines we use nothing other
then PLSQL (little C, little Java but 99.99% plsql).

It is also safe to say that we compile and recompile ALOT, I mean -- A ton.

Tablespace fragmentation will occur only when you drop an object.  You never
drop SYS owned things, hence the tablespace will not fragement.

In the table itself, we do cluster (index) or convention index access to data --
no full scans of the PLSQL code -- so that is OK as well.

Just use it.
Review & Followup

      Rating: 5
default extents
      of system tables/indexes  August 09, 2002

Reviewer:  PJ  from Kansas City, Missouri
Thanks for your response Tom, good points about the SYS tables/indexes. I had
been wondering about the 'small' default storage settings which system
tables/indexes are initially created with Oracle create scripts...but after
looking at our 8.1.7 database the system initial and next extents are set at
65K, and the average size of packages/procedures we have created so far all
would fit easily into one extent.  I guess previous to 8i the default
initial/next extent of SYS tables/indexes was only 10K.

August 09, 2002 Mark -- Thanks for the question regarding "Is
      group by equivalent to distinct ", version 9.0.1

      You Asked
Tom,
I have a developer that uses the group by clause without any summary functions.
It looks like it is equivalent to distinct in functionality...I was just
wondering if you could confirm this or not. I have done some preliminary
investigation and have not found much...
Thanks for offering such a great site.

ops$oracle@vanp2> set autotr traceonly explain
ops$oracle@vanp2> select SWERRORMESSAGE , FNALTLASTNAME
 from swbapps.sw_case
 group by SWERRORMESSAGE , FNALTLASTNAME
ops$oracle@vanp2> /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE
   1    0   SORT (GROUP BY)
   2    1     TABLE ACCESS (FULL) OF 'SW_CASE'

ops$oracle@vanp2> select distinct SWERRORMESSAGE , FNALTLASTNAME
 from swbapps.sw_case
ops$oracle@vanp2> /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE
   1    0   SORT (UNIQUE)
   2    1     TABLE ACCESS (FULL) OF 'SW_CASE'

      and we said...
it is equivalent but distinct would be preferred (it is more understandable what
you are doing!)
August 09, 2002
      -- Thanks for the question regarding
      "Modifying lob segments", version 8.1.6

      You Asked
How do I change the maxextents value for a lobsegment from 99 to 121?

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int, y clob ) tablespace
system;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t
  2  modify (y)
  3  storage ( maxextents 121 );

Table altered.
Review & Followup

      Rating: 4
Modifying
      maxextents for lob segments.  August 09, 2002

Reviewer:  Aaron MacLennan  from Gunnison, CO USA

Thank you for your response.  That was exactly what I needed.

August 09, 2002 Louise -- Thanks for the question regarding "How
      to get all user ids that are in the Portal sessions similar as
      V$session.", version 9.0.1.3

      You Asked
Hi Tom,

I am looking for a WEB page API (portal) that can return all the users in the
Portal sessionm (similar as database v$sesson).  The wwctx_api.get_session only
returns one user.  The wwcts_api.get_user only returns one user id.  I believe
must be an view or API some places that Portal holds this information.  Please
help me find out. Thank you very much.

Louise

      and we said...
I asked Kim Better, our resident portal guru to look at this and she says:

.....
Well, currently, there are no methods in the PDK API that will give you all
users with a current portal session. But, alternatively from your code, to find
all of the users with a current active portal session, you can execute the
following query:

In Portal version 1:
select * from portal30.wwctx_sso_session$
where trunc(session_start_time) = trunc(sysdate)
and active = 1 and IS_LOGGED_ON = 1

Portal version 2:
select * from portal.wwctx_sso_session$
where trunc(session_start_time) = trunc(sysdate)
and active = 1 and IS_LOGGED_ON = 1

You can choose to modify the query and pick columns that are specific for your
needs.

Hope this helps!

--kim
August 09, 2002 Bob -- Thanks for the question regarding "Capture
      the explain plan", version 9.0.1.3

      You Asked
Tom, is there a way to capture a series of queries, and their explain plans that
were chose after the query was run? I would like to catch any of the explain
plans, and then run querys based on cost, the DESCRIPTION (i.e. TABLE ACCESS
FULL) and the query that built it. We have a system that generates thousands of
specific querys based on the screens and privs and I would like to grab and sort
them and try and pick out bad ones that maybe could use some adjustment. This
would not have to be run all the time, but I would like to be able to capture
all the queries and plans for a timeframe.

      and we said...
v$sql_plan

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/server.901/a90190/ch3177.htm#993931
it  is already there just waiting for you to look at it.

New in 9i.

August 09, 2002 Sampath -- Thanks for the question regarding "How
      to change case of whole array ", version 8.1.6

      You Asked
Hi Tom!
         Thanks for taking my question and your endless help to Oracle
community . I have some reports  developed in webdb . For each report
I have parameter form . The problem is the para meters are case sensitive . Our
client wants them to be case insensitive . I did check the code of the Package
Body for the reports , and it is passing the parameters as an arry .  Is there
any way I can edit the Package body and change the case of all values in the
arry before binding the query ?
Thanks in advance,
Sampath

      and we said...
I asked Chris Beck, our resident webdb guru, to look at this and he says:

Tom,

There are two things that I can think of off the top of my head.

1.  Make the reports based on a query.  That way you can write them like
this:

select blah
  from T
 where col1 = upper( :parameter )

as long as col1 is stored as upper then you are good to go.  Or if you need
to upper(col1), consider the use of a function based index to avoid a full
table scan on T.

2.  Use JavaScript.

Create a JavaScript validation like this

function toUpper(field)
{
    field.value = field.value.toUpperCase();
    return true;
}

Then attached it to any field that you want uppered and make it FORM based
validation.

Pros and Cons...

Option 1 is good if you don't mind rewriting your reports from Query Wizard.
Option 2 is good but if javascript support is disabled in browser, you are
out of luck.  There is a third option which involves changing the generated
code but then you lose the ability to use WebDB to make any more
modification to the report without then again modifying the generated code.
Plus that is not a supported option so I would stick with options 1 or 2.

chris.

Review &
      Followup

      Rating: 3
Thanks for your
      update   August 09, 2002
Reviewer:  Sampath

Tom,Chris !
          Thanks for taking your time .I have already concidered the two
solutions you have geiven .
 I have many reports and most of them have links to the FORMs from the fields .
All the coding for links in sql reports is harder .So I can't use solution no#1
 I do not know how to use javaScript in reports ParameterForm .It works for
Forms, so could you please
let me know how to use javaScript in reports ParameterForm ?
 If it's not posible please explain me how to exit package manually to change
case of whole arry . I don't mind redoing it every time I edit those reports
becase that's the only option I have .
Thank you so mush for you help .
Sampath

          Followup:

Sampath, this is Chris again.  You are right.  You can not add a validation to a
parameter form :-(  I assumed that you could do it from any input field of webdb
but I was wrong.  Sorry.

I would still go with option 1.  It may be harder, but it is the better and
supported option.  If you really want to go and edit the generated code
everytime then you could do this...

<b>** Warning - Unsupported Solution Below **</b>

Edit the procedure SHOW() in the report package body and add the following lines
after the comment Bind query:

    --
    -- Bind query
    --
    for i in 1 .. l_arg_values.count loop
      l_arg_values(i) := upper( l_arg_values(i) );
    end loop;

Recompile the package body and you're good to go.

** End of Unsupported Solution **

      Rating: 5
Still have a
      problem  August 09, 2002
Reviewer:  Sampath

Chris,
        Thanks for your update . I copied the code you posted in the bind query
section and package compiles without fail . But when I run the report I'm
getting the following error ......
WWV-10201 Unable to execute query WWV_RENDER_REPORT.SHOW
ORA-01001 ORA-01001: invalid cursor WWV_RENDER_REPORT.SHOW
 If I comment out the code just pasted in the  bind query section the report
runs fine . Any ideas ?
I apologize for extending this thread .

          Followup:

Sampath,

Without knowing exactly the query being processed and the parameters being
passed in, it is hard to debug.  You can try and enhance the code to only upper
the parameters you want to.  To do this first you need to know what they are
called.  To see what parameters are passed in use this code in the bind query
section...

for i in 1 .. l_arg_names.count loop
  htp.p( l_arg_names(i) || ' = ' || l_arg_values(i) || '<br>' );
end loop;

This will pring a list of all the parameters passed in.  This should make it
much clearer as to what is going on in this procedure.  Using this information
you can then modify the loop like this...

for i in 1 .. l_arg_names.count loop
  if l_arg_names(i) in ( 'PARAMETER_NAME', 'PARAMETER_NAME' ) then
    l_arg_values(i) := upper( l_arg_values(i) );
  end if;
end loop;

This method should work or at least give you enough information to accomplish
what you want.  Supported option 1 is starting to look better and better, isn't
it? ;-)

      Rating: 4
Thanks
        August 14, 2002
Reviewer:  Sampath

Chris,Tom !
          Thank you so much for your help . It works now .
Sampath

August 10, 2002 Venkatasubramanian -- Thanks for the question
      regarding "sql loader", version 8.1.7

      You Asked
Tom,

In our testing evnironment, we will have to insert 20 million records in a
table. Like this, we will have to upload data into 30 tables.We will have to
generate data.

A few in the application team suggest procedural method towards data generation
which consumes much time. Another team suggests that we can insert 1 million
records first and duplicate for further addition and doing updates wherever
necessary.

I feel that we can generate a flat file by writing shell script and use SQL
loader to upload data.

Kindly suggest the best method of achieving this and if possible with an
example.

Thanks and Regards,
Venkat

      and we said...
What I do is use a small C program "dg" (data generator). It works to generate
test data using smaller sample datafiles.

Suppose I have a table like this:

create table t ( name varchar2(80), state varchar2(2), zip number );

and wanted to populate it, well, I would have a file "states.dat" with the 50
state codes in it.  Another file with a bunch of numbers (zip codes) and would
probably use /usr/dict/words for the names.  Or I might use my HR table to
generate these files (thats what I did here, I ran

set heading off
set feedback off
set embedded on
set termout off
spool first.dat
select distinct first_name from wwc_people_people;
spool off

to get names, and likewise for states/zips)

then, to generate data, I:

$ dg -t, first.dat states.dat zip.dat | head
Muthuvel,NH,07054
Zhong,VA,54650
Amato,VA,28217
Padmanabhan,HI,78701
Mutasem,AL,48864
Hiroshige,IL,80921
Marjan,NC,32822
Lajos,NC,46268
Jorge Ivan,KS,38125
Maria Pilar,OK,17011

Now, you could use

$ dg -t, first.dat states.dat zip.dat | head -20000000 > data.dat

to generate 20,000,000 records.  It would be a tad more "random" then generating
1,000,000 records and duping them 20 times.

True, the zips and states don't "go together" here -- the solution for that
would be:

set heading off
set feedback off
set embedded on
set termout off
spool state_zip.dat
select distinct  work_state||','||work_zip from wwc_people_people;
spool off

to generate a single file with states+zip and then:

$ dg -t, first.dat state_zip.dat | head
Vijish,TX,75024
Wassiliki,ID,83701
Migliore Antonino,IL,60601
Tammy,KS,66210
Theng Theng,FL,33607
Karunakaran,UT,84101
Ramazzotti,CA,92626
Fenner,VA,20190
Sukhjit,MO,63131
Gariboldi Andrea,WI,54650

and now they do.

Here is dg:

/*
 * Random data generator.
 * Generate an infinite number of lines, by printing side-by-side
 * random lines from the files specified in arguments.
 */
#include <stdio.h>

#ifndef random
long rand();
#endif
extern char *malloc(), *realloc();

#define USAGE   "Usage: %s [-tx] files\n"

struct fl {
    FILE *fp;
    long size;
    char *name;
    char *sep;
} *files;

char *sep = "\t";

ranl(flp)
struct fl *flp;
{
    register FILE *fp = flp->fp;
    register c;

    if (fseek(fp, rand() % flp->size, 0))
        return(-1);
    while ((c = getc(fp)) != '\n' && c != EOF)
        ;
    if ((c = getc(fp)) == EOF)
        rewind(fp);
    else
        (void)ungetc(c, fp);

    while ((c = getc(fp)) != '\n' && c != EOF)
        (void)putchar(c);

    fputs(flp->sep, stdout);
    return(-ferror(fp) - ferror(stdout));
}

main(argc, argv)
char **argv;
{
    int i;
    register struct fl *fend, *flp;

    if (!(fend = files = (struct fl *)malloc(sizeof(*files)))) {
        perror(*argv);
        exit(-1);
    }

    for (i = 1; i < argc; i++) {
        if (*argv[i] == '-') {
            switch (argv[i][1]) {
                case 't':
                    sep = argv[i] + 2;
                    if (fend > files)
                        fend[-1].sep = sep;
                    continue;
            }
            (void)fprintf(stderr, USAGE, *argv);
            exit(i);
        }

        flp = (struct fl *)realloc((char *)files,
            (unsigned)(fend - files + 1) * sizeof(*files));
        fend = flp + (fend - files);
        if (!(files = flp)) {
            (void)fprintf(stderr, "%s: ", *argv);
            perror(argv[i]);
            exit(i);
        }

        for (flp = files; flp < fend; flp++) {
            if (strcmp(flp->name, argv[i]))
                continue;
            *fend = *flp;
            goto done;
        }

        fend->name = argv[i];
        if (!(fend->fp = fopen(argv[i], "r"))
         || fseek(fend->fp, 0L, 2)
         || (fend->size = ftell(fend->fp)) < 0) {
            (void)fprintf(stderr, "%s: ", *argv);
            perror(argv[i]);
            exit(i);
        }
        if (!fend->size) {
            (void)fprintf(stderr, "%s: %s: empty file\n",
                *argv, argv[i]);
            exit(i);
        }
    done:
        fend++->sep = i == argc - 1 ? "\n" : sep;
    }

    if (fend == files) {
        (void)fprintf(stderr, USAGE, *argv);
        exit(-1);
    }

    srand(getpid());

    for (;;)
        for (flp = files; flp < fend; flp++) {
            if (ranl(flp) >= 0)
                continue;
            (void)fprintf(stderr, "%s: ", *argv);
            perror(flp->name);
            exit(-2);
        }
    /* NOTREACHED */
}

Review &
      Followup

      Rating: 4
sql
      loader  August 10, 2002
Reviewer:  venkat
       from Chennai, India
Tom,

Thank you very much. Can you suggest/explain a method using shell script?

Thanks,
Venkat

          Followup:

call dg from a shell script.

I know of no way in a shell script to generate data.  shell script is fairly
basic, i provided you a tool (dg) to do this easily.  Thats the best I can do.

August 10, 2002 Daniel -- Thanks for the question regarding
      "Hybrid OLTP-DSS application -- shared pool problems", version 8.1.6

      You Asked
Hi Tom,

My company is developing an application that will be mostly OLTP (high traffic,
a 100-1000 distinct SQL statements), but a subset of users will regular execute
DSS-style queries. Those queries will populate tables which will then be
accessed in "OLTP mode."

In other words, there will be high concurrency on the OLTP parts of the app,
with medium-to-low concurrency on the OLAP part. Furthermore, we want to execute
the DSS queries using native dynamic SQL.

(To be specific regarding the no. of DSS queries we're going to be running:
ideally we'd like to scale up to around 1.0 queries/min. at peak. Here are some
ballpark *worst-case* estimates: 5 per minute, 10-20 per hour, 100-200 per day.
Also, while these resemble data warehouse/star schema type queries, they
complete comparatively quickly (1-5 seconds each).)

Our concern is that the DSS queries will cause contention on the library cache,
because they are essentially uncacheable; even without literal values
(everything will use bind variables), the structure of SQL is driven by run-time
user input and has millions of permutations. (We're also concerned about the CPU
overhead of hard-parsing the DSS queries every time they execute, but that goes
hand in hand with the nature of the application.)

Looking for answers in your archives, I noticed that you recommend against
running two instances on a single machine:

http://asktom.oracle.com/pls/ask/f?p=4950:8:1147408::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:4347545437371,%7BOLTP%7D%20and%20%7BDSS%7D
http://asktom.oracle.com/pls/ask/f?p=4950:8:1147408::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:897828464193,%7BOLTP%7D%20and%20%7BDSS%7D
But that's our most likely option now: one instance will be optimized for OLTP,
the other for an OLAP application. We'd populate tables using a DB link or
something like that.

Another possibility would be to run separate instances on separate physical
machines (each with 4 CPU & 4 GB RAM)

What's your opinion?

Thanks.

      and we said...
With that load -- it'll be fine to have

o OLTP apps with fine tuned queries using bind variables
o DSS apps with unique statements

Not a problem at all, no worries on that one.  It is imperative to use reusable
sql on OLTP apps where you are executing hundreds/thousands of statements per
second.  A couple of dozen unique queries per minute won't be an issue for you
at all.
Review
      & Followup

      Rating: 5
Useful (and
      reassuring)  August 10, 2002
Reviewer:
      Daniel  from NY
As usual, very helpful. Thanks!

August 10, 2002 NAG -- Thanks for the question regarding "Before
      starting to TUNE a query", version 8.1.7

      You Asked
Tom

In my opinion before we start to tune a query we should do the following.

1.Get the sizes and the number of rows in all the tables (in the from clause)

2.Get all the constraints between the related tables.

3.Get info on all the indexes on all the tables.

I'm I right.

Can you please suggest the names of the scripts on your sites for the scripts,
which can do the above. So that I can search them..

      and we said...
You forget step ZERO.

Understand what exactly the solution you are trying to tune is supposed to do.
If you do not understand the question, you'll never be able to come up with the
answer.

You see -- the optimizer and and will do all of the "rewrites" you can do
mechanically.  If you don't understand the question being asked, you'll never be
able to come up with semantically equivalent but "better" query.

I don't have scripts to do any of the above -- It is all in the data dictionary
(i just query that) but MOST SYSTEMS have this thing called "documentation" that
lists all of that as well (eg: your developers should have this in a book
somewhere for your ease of use -- most of the times, they even have an ERD)

Actually -- I don't care about existing indexes in as much as indexes that would
be necessary to make the query perform well.  Then, after deciding what SHOULD
be there, I see whats there and try to reconcile the two.
Review &
      Followup

      Rating: 5
This piece of
      advice makes a lot of sense in this thread  August 11, 2002

Reviewer:  Nag
"
Do this (this is how I write
queries myself) -- phrase the QUESTION in english.  Forget about any existing
queries and develop a query that answers the question.
"

August 10, 2002 maher -- Thanks for the question regarding
      "differnt questions", version 8.1.7 EE

      You Asked
hi tom,
1)when i need to rebuild index,is it necessary to rebuild index,how it effect
the DB performance.
2)i can not make select to v$session from my user,although i make puplic
synonyms for it from user sys,but i can for example select from v$parameter ,i
do not why this happend?

      and we said...
1) sort of a circular question don't you thing:

   a) I need to rebuild an index
   b) is it necessary to rebuild index?  (see a)

so, supposing you believe you need to rebuild an index -- how will it effect the
db performance?  I can only say that one of three things will happen:

   1 - the performance will improve
   2 - the performance will be unaffected
   3 - the performance will be negatively impacted

here is where YOU come in.  YOU will collect metrics before doing these things
(like how many TPS you do now, stats pack reports, etc).  YOU will do this
rebuild.  and then you will compare the before to the after to see if you should
consider rebuilding this object again in the future (see:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2522324565957
2) the view you want to grant on is SYS.V_$SESSION.

Log in as SYS or AS SYSDBA or internal and grant select on SYS.V_$SESSION to
user <with grant option>

then the synonym v$session will be accesible to that user.
Review &
      Followup

      Rating: 4
different
      questions  August 11, 2002
Reviewer:  maher
       from palestine

      Rating: 4
differnt
      questions  August 11, 2002
Reviewer:  maher
       from palestine
sorry tom for this question but it is very important:
i make this selection:
 SELECT * FROM V_STD_ADMISSION_DEPT
then i face this problem :
ERROR at line 1:
ORA-03232: unable to allocate an extent of 36 blocks from tablespace 6
how can i resolve it.

August 10, 2002 Senthil -- Thanks for the question regarding
      "direct load not using temp tablespace", version 8.1.7

      You Asked
Tom-

one of our client loading a huge data into database using sqlldr with direct
load option. After sometime the load process get started, it throws an error
message saying,

ORA-1652: unable to extend temp segment by 37171 in tablespace MY_TS

as we had stored data and index into 2 different tablespace, it fails on data
tablespace(MY_TS). i understand at the end of load process, it rebuilds index on
the same tablespace where index is stored. but my question is why its failing on
data tablespace. is there anyway to make use of temporary tablespace, as we have
2GB of free space in it. if i had missed any option to be included, please
enlighten.

thanks,
senthil

      and we said...
When we load, we load into TEMP extents which upon successful completion we turn
into "REAL" extents.

We do this so that if the load fails for whatever reason (eg: kill -9 sqlldr),
SMON will clean up the orphaned temp extents for us automagically.

Hence, this message is telling you that you have insufficient space in MY_TS to
hold the data being loaded.  The temp segment is in fact your loaded data (it'll
be TEMP until we are done loading)
Review &
      Followup

      Rating:
      5
  August 10, 2002
Reviewer:
      A reader
its very useful, thanks.

      Rating:
      5
  August 10, 2002
Reviewer:
      A reader
Sorry Tom, I don't follow this. Could you clarify a little more?

You are saying, when sqlldr Direct option is used, the data is loaded to the
temp segments. And in this case the temp segment ran out of space, right? So
error message should have been on the TEMP tablespace and not on MY_TS (unless
MY_TS is his temp tablespace too.).

Or, are you saying that sqlldr direct would load data onto temp segments
situated in MY_TS? (is it possible?). In anycase, when he has 2GB freespace on
MY_TS, why is he getting an ORA-1652 on that tablespace?

Please explain. Thank you very much.

          Followup:

I am saying when we load the segment we load into is marked and treated as a
TEMPORARY segment.

It is NOT in your temp tablespace, it is a temp segment.  This is so that if we
crash (system fails), when we restart SMON will see this temp segment in this
tablespace MY_TS and clean it up (get rid of it).  If it were a permanent
segment, SMON would not (and we would just have "junk" there).

So, when we load, the segment is marked "temporary".  When we are done with the
load into MY_TS, the "temporary" segment is marked "permanent".

They quite simply didn't have enough space in MY_TS to load their "huge" data
set.

August 10, 2002 Bala -- Thanks for the question regarding
      "freelists on hash partitioned table", version 8.1.7

      You Asked
Tom,

I need your help on this subject. I changed the freelist parameter
from default 1 to 20 on all heavy insert tables just to make the
batch load data migration faster. This batch load does only insertion into those
identified tables. But the the keys column values are
generated through the sequence in all the 5 hash partitioned tables.

Even after i changed the freelist parameter, i don't see any
performance improvement. I set the freelist to 20 for all the
indexes of the tables too. But still no change in performance.

We ran 20 concurrent processes of batch load. This batch load is developed
internally and it does validating the input data and generates the records on
the fly and insert into the database.

Each input from the batch files loades data into 5 hash partitioned tables and
after inserted then we commit for each input feed.

As per Oracle recommendation, i tried to increase the freelist groups to 2 on
all 5 partitoned tables. but still no change. I tried changing initrans equal to
20. No change.

Could it be because each concurrent process is waiting for its turn to get its
unique sequence no. ? I do see some waits on seq$.

Please help me with some suggestions.

Thanks

      and we said...
Umm, did you stop to see if freelist contention was an issue in the first
place??

You can consider to issue:

alter sequence S cache 10000;

before doing the load, that'll reduce contention on the sequence for sure.  You
can alter the sequence after the load back down if you like.

You might also want to use statspack to see WHAT you are waiting on.  You need
to determine WHAT you are waiting on before you even begin to try and change
things.
Review
      & Followup

      Rating:
      5
Excellent  August 10, 2002
Reviewer:
      Bala Murugan
Thanks Tom for your quick response. I will try your suggestions and post the
result.

Thanks again.

August 11, 2002 Nag -- Thanks for the question regarding "Date
      variable in sqlplus", version 8.1.7

      You Asked
SQL> var my_var date
Usage: VAR[IABLE] [ <variable> [ NUMBER | CHAR | CHAR (n) | VARCHAR2 (n) |
                               NCHAR | NCHAR (n) | NVARCHAR2 (n) |
                               CLOB | NCLOB | REFCURSOR ] ]

Tom , we cannot declare a date variable in SQLPLUS.. why is it so?
If I want to test  a query or a program by using date variables declared in
sqlplus.. how should I do it , what is the work around.

Thank you

      and we said...
because sqlplus doesn't support it.

variable bind_variable varchar2(20)
exec :bind_variable := '01-jan-2002'
select to_date( :bind_variable, 'dd-mon-yyyy' ) from dual;

just to_date it.

August 12, 2002 belal -- Thanks for the question regarding "about
      Import", version 817 EE

      You Asked
Hello Mr tom
is it important to do drop the user's objects (or user) when i do import
user?....If no how can i do import with out droping the user's objects
Regards

      and we said...
You tell me.

It is not important at all.

Or, maybe it is vitally important.

Do this.  create a user "a".  in A, copy the scott/tiger table EMP.  Now, exp
owner=a and then

a) imp owner=a full=y   this fails, the table exists
b) imp full=y ignore=y this succeeds, but the table emp now has 28 (2x14 rows)
in it.  Is that what you want?
c) drop table a.emp, imp full=y.  this succeeds and you get what you started
with

So, you tell me, which of a, b, c do you want to achive???

Review &
      Followup

      Rating: 3
about
      import  August 12, 2002
Reviewer:  muneer el
      massry  from Palestine
Hi,

procedure b is work but we are looking to appened the new rows to existing rows
without dupplicated the rows
b) imp full=y ignore=y this succeeds, but the table emp now has 28 (2x14 rows)
in it.
Thanks

          Followup:

You can use import/export like that.  Just be prepared to have lots of error
messages if you have lots of dups:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table emp as select * from scott.emp;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table emp add constraint emp_pk primary
key(empno);

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> host exp userid=/ tables=emp

Export: Release 8.1.7.3.0 - Production on Mon Aug 12 08:02:34 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.7.3.0 - Production
With the Partitioning option
JServer Release 8.1.7.3.0 - Production
Export done in WE8ISO8859P1 character set and WE8ISO8859P1 NCHAR character set

About to export specified tables via Conventional Path ...
. . exporting table                            EMP         14 rows exported
Export terminated successfully without warnings.

ops$tkyte@ORA817DEV.US.ORACLE.COM> delete from emp where mod(empno,2) = 1;

4 rows deleted.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select empno, ename from emp;

     EMPNO ENAME
---------- ----------
      7566 JONES
      7654 MARTIN
      7698 BLAKE
      7782 CLARK
      7788 SCOTT
      7844 TURNER
      7876 ADAMS
      7900 JAMES
      7902 FORD
      7934 MILLER

10 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> host imp userid=/ tables=emp ignore=y

Import: Release 8.1.7.3.0 - Production on Mon Aug 12 08:02:34 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.7.3.0 - Production
With the Partitioning option
JServer Release 8.1.7.3.0 - Production

Export file created by EXPORT:V08.01.07 via conventional path
import done in WE8ISO8859P1 character set and WE8ISO8859P1 NCHAR character set
. importing OPS$TKYTE's objects into OPS$TKYTE
. . importing table                          "EMP"
IMP-00019: row rejected due to ORACLE error 1
IMP-00003: ORACLE error 1 encountered
ORA-00001: unique constraint (OPS$TKYTE.EMP_PK) violated
Column 1 7566
Column 2 JONES
Column 3 MANAGER
Column 4 7839
Column 5 02-APR-1981:00:00:00
Column 6 2975
Column 7
Column 8 20
IMP-00019: row rejected due to ORACLE error 1
IMP-00003: ORACLE error 1 encountered
ORA-00001: unique constraint (OPS$TKYTE.EMP_PK) violated
Column 1 7654
Column 2 MARTIN
Column 3 SALESMAN
Column 4 7698
Column 5 28-SEP-1981:00:00:00
Column 6 1250
Column 7 1400
Column 8 30
IMP-00019: row rejected due to ORACLE error 1
IMP-00003: ORACLE error 1 encountered
ORA-00001: unique constraint (OPS$TKYTE.EMP_PK) violated
Column 1 7698
Column 2 BLAKE
Column 3 MANAGER
Column 4 7839
Column 5 01-MAY-1981:00:00:00
Column 6 2850
Column 7
Column 8 30
IMP-00019: row rejected due to ORACLE error 1
IMP-00003: ORACLE error 1 encountered
ORA-00001: unique constraint (OPS$TKYTE.EMP_PK) violated
Column 1 7782
Column 2 CLARK
Column 3 MANAGER
Column 4 7839
Column 5 09-JUN-1981:00:00:00
Column 6 2450
Column 7
Column 8 10
IMP-00019: row rejected due to ORACLE error 1
IMP-00003: ORACLE error 1 encountered
ORA-00001: unique constraint (OPS$TKYTE.EMP_PK) violated
Column 1 7788
Column 2 SCOTT
Column 3 ANALYST
Column 4 7566
Column 5 09-DEC-1982:00:00:00
Column 6 3000
Column 7
Column 8 20
IMP-00019: row rejected due to ORACLE error 1
IMP-00003: ORACLE error 1 encountered
ORA-00001: unique constraint (OPS$TKYTE.EMP_PK) violated
Column 1 7844
Column 2 TURNER
Column 3 SALESMAN
Column 4 7698
Column 5 08-SEP-1981:00:00:00
Column 6 1500
Column 7 0
Column 8 30
IMP-00019: row rejected due to ORACLE error 1
IMP-00003: ORACLE error 1 encountered
ORA-00001: unique constraint (OPS$TKYTE.EMP_PK) violated
Column 1 7876
Column 2 ADAMS
Column 3 CLERK
Column 4 7788
Column 5 12-JAN-1983:00:00:00
Column 6 1100
Column 7
Column 8 20
IMP-00019: row rejected due to ORACLE error 1
IMP-00003: ORACLE error 1 encountered
ORA-00001: unique constraint (OPS$TKYTE.EMP_PK) violated
Column 1 7900
Column 2 JAMES
Column 3 CLERK
Column 4 7698
Column 5 03-DEC-1981:00:00:00
Column 6 950
Column 7
Column 8 30
IMP-00019: row rejected due to ORACLE error 1
IMP-00003: ORACLE error 1 encountered
ORA-00001: unique constraint (OPS$TKYTE.EMP_PK) violated
Column 1 7902
Column 2 FORD
Column 3 ANALYST
Column 4 7566
Column 5 03-DEC-1981:00:00:00
Column 6 3000
Column 7
Column 8 20
IMP-00019: row rejected due to ORACLE error 1
IMP-00003: ORACLE error 1 encountered
ORA-00001: unique constraint (OPS$TKYTE.EMP_PK) violated
Column 1 7934
Column 2 MILLER
Column 3 CLERK
Column 4 7782
Column 5 23-JAN-1982:00:00:00
Column 6 1300
Column 7
Column 8 10          4 rows imported
About to enable constraints...
Import terminated successfully with warnings.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select empno, ename from emp;

     EMPNO ENAME
---------- ----------
      7369 SMITH
      7499 ALLEN
      7521 WARD
      7566 JONES
      7654 MARTIN
      7698 BLAKE
      7782 CLARK
      7788 SCOTT
      7839 KING
      7844 TURNER
      7876 ADAMS
      7900 JAMES
      7902 FORD
      7934 MILLER

14 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

You might need to redirect the output of IMP away from the screen for this to be
useful.

August 12, 2002 -- Thanks for the question regarding "To do dml
      on a cursor records?", version 8.1.7

      You Asked
To filter records from a cursor:

  For example I have a cursor which will have the following results (after the
complicated
select statement):

    CatA  Prod1  1000
    CatA  Prod1  2000
    CatB  Prod2   500
    CatB  Prod2  1000

 I need to process the cursor to filter out only the record with the
highest sales group by Category and Product. Is there a way to do
DML statemnet like select statement from a cursor ? Eg.
  select * from (cursor) where ....?

 Thks.

      and we said...
You will take the "complicated query" and then:

select * from
(
select a.*,
       row_number() over ( partition by category, product
                           order by sales_group DESC nulls last ) rn
  from ( complicated_query ) a
)
where rn = 1;

that'll get the row with the highest sales_group by category /product.

Review &
      Followup

      Rating: 5
Best Solution
        August 12, 2002
Reviewer:  Jesse
       from Malaysia
Hi Tom,
  This is the best solution I'm looking for. I didn't know that there is a
function called row_number() and partition can be used in dml. Tom, appreciate
if you can briefly explain what is the logic lie benearh and when these
functions/sql can be most usefully used? Thks.

          Followup:

If you have my book "Expert one on one" -- read the chapter on analytic
functions.

If you don't -- I can honestly say I think I explain them rather well in that
chapter (and it takes a couple of pages to get the concepts across)

Also documented in:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76994/analysis.htm#11237
and the sql language ref.

      Rating: 5
Can this be
      used in REF CURSOR?  August 12, 2002
Reviewer:
      Jesse
I tried to use as the following
in a package:

  OPEN curs FOR
    select * from
(
select a.*,
       row_number() over ( partition by category, product
                           order by sales_group DESC nulls last ) rn
  from ( complicated_query ) a
)
where rn = 1;

It gives error when try to compile. I'm using 8.1.7. Can
row_number() and partition used in ref cursor? Thks.

          Followup:

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3027089372477
.

August 12, 2002 Peter -- Thanks for the question regarding "Top 5
      items ", version 8.1.7

      You Asked
Hello Tom
I sent in this question a few weeks ago and maybe it didnt
arrive, so I will send it again. Apologies for if its a duplication.

This is another top 5 question. I cant see how to apply
your previous example with this situation so would appreciate some help.
I am using Release 8.1.7.2.0

For the example I will use a shopping list table

Shop    Fruit   Price
COOP    Apple   20
COOP    Banana  30
COOP    Orange  40
COOP    Pear    25
COOP    Grape   34
COOP    Apricot 35
ADSA    Apple   20
ASDA    Banana  50
ASDA    Orange  40
ASDA    Pear    25
ASDA    Grape   24
ASDA    Apricot 25
SPAR    Apple   10
SPAR    Banana  20
SPAR    Orange  30
SPAR    Pear    55
SPAR    Grape   14
SPAR    Apricot 15

So here there are three shops and they all have the same groceries
but with different prices. I want to know which is the most expensive shop
to visit by purchasing the 5 most expensive products from each one.
The result would be something like

Rank  Shop  TotalBill
1     COOP  130
2     ASDA  120
3     SPAR   90

If there is an SQL statement that could answer this or PL/SQL function
then it would be much appreciated.

      and we said...
This is the answer I gave you last time:

select shop, sum(price)
  from ( select show, price,
                row_number() over( partition by show order by price DESC ) rn
           form T )
  where rn <= 5
 group by shop

should do it....

Review &
      Followup

      Rating:
      4
Autocorrect  January 28, 2002
Reviewer:
      AB  from UK
Tom

Seems like you have the same problem as me with typing "form" instead of
"from"... ;) (See your in-line view).

          Followup:

I do that ALL of the time... ;)

      Rating: 4
BE
      CAREFULL  January 28, 2002
Reviewer:  Alexandre
      Matthes  from Rio de Janeiro - Brasil
Be VERY carefull... there are two situations

The first one, let´s consider that the "COOP" shop bellow with it´s fruit´s
price...

Shop    Fruit   Price
----    ------- -----
COOP    Apple   20
COOP    Banana  30
COOP    Orange  40
COOP    Pear    25
COOP    Grape   34
COOP    Apricot 35

QUESTION: What are the three highest prices of shop "COOP"

ANSWER: The right answer is:

Price
-----
40
35
34

If you would query the fruits that have the three most expensive products on
shop "COOP",
you should have these three lines bellow:

Shop    Fruit   Price
------- ------- -----
COOP    Orange  40
COOP    Apricot 35
COOP    Grape   34

BUT NOW....

Let´s consider the other situation...

Shop    Fruit   Price
----    ------- -----
COOP    Apple   20
COOP    Banana  30
COOP    Orange  40
COOP    Pear    40
COOP    Grape   34
COOP    Apricot 35

Obs: The fruit "PEAR" has a different price...

QUESTION: What are the three highest prices of shop "COOP"

ANSWER: The right answer STILL the same:

Price
-----
40
35
34

If you would query the fruits that have the three most expensive products on
shop "COOP", you should have these three lines bellow:

Shop    Fruit   Price
------- ------- -----
COOP    Orange  40
COOP    Pear    40
COOP    Apricot 35
COOP    Grape   34

And here is the point that you must pay attention...
Considering that you want just the three most expensive fruits on each store,
the query that Tom suggested you will sum() the prices of the top 3 lines of the
above list, or

it´ll sum()..
Shop    Fruit   Price
------- ------- -----
COOP    Orange  40
COOP    Pear    40
COOP    Apricot 35
               ---
               115  <------ this will be the answer of Tom´s query for each
shop...
This query will disconsider the follow product that have one of the most
expensive price EITHER:
Shop    Fruit   Price
------- ------- -----
COOP    Grape   34

So, I´d suggest you to run the query bellow if you want ALL fruits that have the
three highest prices for each shop...

select Shop, sum(price)
from (select Shop, price
    from shops s1
    where 3 > (select count(distinct price)
                from shops s2
                where s2.price > s.price
                and s2.Shop = s1.Shop)
    order by Shop)
group by Shop;

if you want the five highest prices, just change the number "3" per number "5"
on the above query.

          Followup:

If you pick up my book -- I go through this in detail in the section on analytic
functions as well.

Its one of my favorite questions -- as it is SO ambigous... thanks for the
followup...

      Rating: 4
I thank
      you...  January 28, 2002
Reviewer:  Alexandre
      Matthes  from Rio de Janeiro - Brasil
Ok Tom,

I've already bought your book and I think that this is the greatest Oracle book
on my bookshelf...

I have to thank you for this very helpfull site...

Best wishes and sorry for my poor english...

      Rating: 4
Can this work
      in procedures  August 12, 2002
Reviewer:  Peter
      Jones  from Switzerland
Hello
I found that the 1st option using row_number() over( partition gave me the
correct answer. I could not get the second option to return the correct results.

Anyway as the 1st one worked I will stick with that.
When I put this statement inside a procedure using either
explicit or implicit cursors then the compilation fails
with the error
1176/34  PLS-00103: Encountered the symbol "(" when expecting one of the
         following:
         , from

I got around this by creating a VIEW and referencing the VIEW in the procedure.
I am quite happy with this but wondered if this is normal behaviour

          Followup:

read

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3027089372477
...

August 12, 2002 Munzer -- Thanks for the question regarding
      "Maximum number of cursors", version 8i

      You Asked
Tom:

You told me earlier to resolve oracle error 01000 for the maximum number of
cursors open is to set it for a web application to 1000.  I asked the dba to do
that and he set it to 300 since he said it takes a lot of resources.  He claims
that another similar application is using a maximum of 50 and working fine.

I checked the other application code and he is using Explicit cursors for
drop-down lists, while I am using owa_util.listprint to create the drop-down
lists.

1. DO you know if using explicit cursors will control memory better or not.

2.  I do not understand why the web server keeps those open in memory even
though the maximum cursors I may have in a program is 30.  I keep going from one
program to another.  Does oracle keep those open in memory until you close the
browser.

3.  DO you think setting the maximun number of cursors open to 1000 will eat a
lot of resources?

4. DO you know which system table show u the status of open cursors and how to
release those from memory.

Thank you

      and we said...
0) your dba is mistaken.  we allocate 64 cursors at a time so setting to 300 or
1000 -- no big deal.  If you need them, you need them.  anyway 300 is probably
more then sufficient.  I just hate to have to bounce an instance to change open
cursors if I don't have to and with 1000 i don't have to.

1) owa_util.listprint closes the cursors.

Just a side note:
explicit cursors run slower
they are harder to code
you can accidently leave them open

implicit cursors run faster
they are easier to code
you can never accidently leave them open.

2) cursors live for the life of a database session or until you close them.  Not
knowing your environment -- i would hazzard a guess that you might have a cursor
leak in your middle tier and you are using some form of connection pool where
the sessions last for many pages.  Use v$open_cursor to see the open cursors --
search this site for open_cursors or ora-01000 to see some queries you can use.

3) no, its what I use.

4) see #3, the only way to "release" them is to end the session or code it so
that they are closed by your middle tier whatever that be.
Review &
      Followup

      Rating: 4
more
      explanation  December 22, 2001
Reviewer:
      Kulguru
tom

kindly expand on how the below can happen

"i would hazzard a guess that you might have a cursor
leak in your middle tier and you are using some form of connection pool where
the sessions last for many pages"

          Followup:

You open a cursor and THINK you are closing it but you are not -- it is
"leaking".  It can happen due to bad programming, forgetting to close things in
exception blocks, any number of reasons.

      Rating: 3
ora-01000 from
      Web Application.  December 24, 2001
Reviewer:
      Nilesh D. Talele.  from India
I think person is using prepare statement(STMT) in his Web application.so he
should close the result set,STMT and if possible connection also, after
executing the STMT.Here prepare STMT in application open Explicit Cursor in
ORACLE.

      Rating: 5
Still getting
      Ora-01000  August 12, 2002
Reviewer:  Momen
       from Riyadh
Hi Tom,

  We have set max. open_cursors to be 1000. We are running a script which
consists of less than 1000 Insert statements and few updates. The script is run
from Forms 6.0. We are getting Max. open cursors exceeded error. If we introduce
frequent Commits the error vanishes and even if we raise error messages between
the Insert statement the error does not occur. I ran the script in a debug mode
and have investigated that the error occurs at different statements.

  Could you please tell us the possible reason ???

Thanking You

          Followup:

and how do you run a "script" in forms.

August 12, 2002 Om -- Thanks for the question regarding "Extents
      Question", version 8.1.7

      You Asked
Hi Tom,

How are you doing.

I have an extent question for you today.

With the Query written below

select  segment_name, extents, max_extents,
extents/( decode( max_extents, 0, 1, max_extents ) )*100 "ExtPerc"
from dba_segments
where nvl( max_extents, 0 ) <> 0
and extents/( decode( max_extents, 0, 1, max_extents ) ) > .25
order by extents/( decode( max_extents, 0, 1, max_extents ) ) desc;

We can find out what is the number of extents allocated to a segment and the
percentage of extents filled for an object.

Is there any way to find out that "WHEN EXCATLY A PARTICULAR EXTENT GOT
ALLOCATED
TO THE OBJECT"

FOR Example

Let's say a table T1 got created on 1st of July 2002 with maxextents as 300
and at present it occupies 100 extents out of 300.

Is there any way to find out the dates i.e. when exactly these 100 extensts
were allocated to the table date wise.

Is it possible to write a trigger on fet$ or uet$ tables to get the info.

Best regards,
Om

      and we said...
Nope, we do not track that.

Also, I see you are using 817, hence you should be using locally managed
tablespaces, hence maxextents would be a constant -- unlimited!  Your query will
go the way of the dinosaurs very soon.

No triggers on fet$/uet$ -- no triggers on sys owned objects (and again, use
locally managed tablespaces and no ROWS in uet$/fet$!!!! either.  They are on
the way out)

August 12, 2002 Basharat -- Thanks for the question regarding
      "SPF in 9i", version 8.1.7

      You Asked
Tom
What is major difference between SPF(Stored Procedure File) in 9i and Init.ora
file in earlier versions of Oracle?
Can we set parameter in 9i without restarting database?

Thanks
Basharat

      and we said...
It is a stored PARAMETER file, not procedure

search this site for

spfile

for articles.  Many, but not all, parameters are changeable while the database
is up and running in 8i and 9i -- many more in 9i then 8i
August 12, 2002 Bin -- Thanks for the question regarding "A
      question about OO4O", version 8.1.7

      You Asked
Now, I am interested in OO4O. But I find I am at a loss to resolve the following
basic problems:
1. How to determine whether the connection to database is success?

For Example:
Dim OraSession As OraSession
Dim OraDatabase As OraDatabase
Set OraSession = CreateObject("OracleInProcServer.XOraSession")
Set OraDatabase = OraSession.OpenDatabase("ExampleDb","scott/tiger",0&)

Then: How to write codes to determine whether the connection to database is
success or failure?

2. So far, I haven't found anything to deal with unexpected errors in OO4O to
prevent application from breaking down.
   Can you give me some advices?

Thank you in advance!

      and we said...
See the OO4O documentation - there is a section on error handling:

oo4o doc:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/win.817/a85257/toc.htm
error handling:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/win.817/a85257/o4o00073.htm
and specifically:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/win.817/a85257/o4o00084.htm
which tells you the properties of the OraSession object you can inspect.
August 12, 2002 chirayu -- Thanks for the question regarding "No
      Data Found ", version 8.1.6

      You Asked
Dear Tom,
  When I run ,
    select count(*) from empmast where empno = 11111 ;
   It does not give me the no data found error.

 Employee number 11111 does not exist in the empmast table.

 However when I run,
   select count(*) from empmast where empno = 11111
   group by empno ;
  It throws the exception 'When No Data Found'

  What could be the reason for that.

Thanks In advance

Chirayu sutaria

      and we said...
Think about it

query1:

select count(*) from empmast where empno = 11111 ;

says "how many employees with empno = 11111 exist".  Answer is a quite
reasonable "0 employees".

Now, query2:

select count(*) from empmast where empno = 11111
   group by empno ;

by empno -- (thats the group by)  how many employees exist with empno = 11111?
The answer here is a very reasonable "there is no answer by empno, since the
result set we are grouping by is empty"

Any aggregate query (using any aggregate, avg, min, max, sum, count, etc) that
does not have a group by will ALWAYS

o return at least one row
o return at most one row

Any aggregate query that includes a group by will return as many rows as you
have distinct group by elements -- in your second query -- 0 rows are returned
since you have ZERO distinct group by elements.

Review &
      Followup

      Rating: 4
Nice
      Explanation  August 12, 2002
Reviewer:
      chirayu  from India
The explanation was nice as always,
 keep the great work going

August 12, 2002 sammy -- Thanks for the question regarding "To
      optimize a program unit", version 8.1.7

      You Asked
1.Do you have some feature in oracle 8i by means of which you can determine the
access path & the cost of a program unit. could you please give a small example?

thanks in advance...

      and we said...
Answer is definitely YES.

If you have my book "expert one on one" you want to read chapters 10 and the
section on dbms_profiler in the appendix.

Else, you want to learn about:

SQL_TRACE, TIMED_STATSITICS + TKPROF

http://asktom.oracle.com/~tkyte/tkprof.html
and dbms_profiler (search for that term on this site, read the supplied packages
guide)

August 12, 2002 tgt -- Thanks for the question regarding
      "Parallel execution", version 8.1.7

      You Asked
what is parallel execution of a query.could you please give an example?

thanks.

      and we said...
suggest strongly you read the entire document:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/toc.htm
You will never be sorry you did in the future!

the answer to your question is:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c22paral.htm#365
which is just but one chapter in that book.
August 12, 2002 Jerry -- Thanks for the question regarding "The
      Dreaded Table is Mutating Error", version 8.1.7

      You Asked
We are running PeopleSoft HR here, and I have written a PL/SQL function to
general userids and default passwords.  It looks for an existing last name,
first and middle initial.  If it finds it, it increments the index.

It works ok when called from PeopleSoft, but I am wondering if there is a design
problem, because when I do

INSERT INTO PSOPRDEFN_SHADOW
SELECT CORE_UTILS.CORE_OPRID(E.EMPLID),CORE_UTILS.CORE_PASSWD(E.EMPLID)
FROM PS_EMPLOYEES E
WHERE..

This is where I get the mutating trigger error.

Your comments and advice would be greatly appreciated.

CREATE OR REPLACE PACKAGE CORE_UTILS AS
   FUNCTION CORE_Oprid(p_emplid IN PS_PERSONAL_DATA.emplid%TYPE) RETURN
VARCHAR2;
   FUNCTION CORE_Passwd(p_emplid IN PS_PERSONAL_DATA.emplid%TYPE) RETURN
VARCHAR2;
END;
/

CREATE OR REPLACE PACKAGE BODY CORE_utils AS
   FUNCTION CORE_Oprid(p_emplid IN PS_PERSONAL_DATA.emplid%TYPE)
      RETURN VARCHAR2
   IS
     dummy VARCHAR2(1);
     l_reccount VARCHAR2(2);
     l_lnfnmi VARCHAR2(30);  -- lastname,firstname, middle initial
   BEGIN
       SELECT
       REPLACE(TRANSLATE(UPPER(n.last_name_srch || SUBSTR(n.first_name,1,1) ||
SUBSTR(RTRIM(n.middle_name,' '),1,1)),'''.- ','~'),'~','')
       INTO l_lnfnmi
       FROM PS_PERSONAL_DATA n
       WHERE n.emplid = p_emplid;

        BEGIN
           SELECT 'x'
           INTO dummy
           FROM PSOPRDEFN a
           WHERE a.oprid = l_lnfnmi;
        EXCEPTION
           WHEN NO_DATA_FOUND THEN
           RETURN l_lnfnmi;
        END;

        BEGIN
           SELECT TO_CHAR(COUNT(*) + 1)
           INTO l_reccount
           FROM PSOPRDEFN c
           WHERE c.oprid LIKE  l_lnfnmi || '%'
           AND
TRANSLATE(SUBSTR(oprid,LENGTH(l_lnfnmi)+1,LENGTH(oprid)),'1234567890','1111111111
') LIKE '1%';
           RETURN l_lnfnmi || l_reccount;
        END;
   EXCEPTION
      WHEN NO_DATA_FOUND THEN
        RETURN NULL;
   END;

   FUNCTION CORE_Passwd(p_emplid IN PS_PERSONAL_DATA.emplid%TYPE)
      RETURN VARCHAR2
   IS
     l_passwd VARCHAR2(10);
   BEGIN
       SELECT SUBSTR(a.last_name_srch,1,1) || SUBSTR(a.first_name_srch,1,1) ||
SUBSTR(b.national_id,6,4)
       INTO l_passwd
       FROM PS_PERSONAL_DATA a
           ,PS_PERS_NID b
       WHERE a.emplid =  b.emplid
       AND b.country = 'USA'
       AND b.national_id_type = 'PR'
       AND b.emplid = p_emplid;
       RETURN l_passwd;
   EXCEPTION
      WHEN NO_DATA_FOUND THEN
        RETURN NULL;
   END;
END;
/

      and we said...
A before insert for each row trigger can read the triggering table IF and ONLY
IF the insert is a single row insert:

   insert into t values

A trigger on T (before INSERT for each row) can read T.

If you do:

   insert into t select

which can insert 0, 1 or many more rows -- cannot, the table is "mutating" and
reading the table during the insert is not permitted.

So, you can either:

   for x in ( select
CORE_UTILS.CORE_OPRID(E.EMPLID),CORE_UTILS.CORE_PASSWD(E.EMPLID)
FROM PS_EMPLOYEES E
WHERE.. )
    loop
       insert into  PSOPRDEFN_SHADOW values ( x...... )
    end loop

or recode the trigger to be an AFTER INSERT trigger (with no for each row)

http://asktom.oracle.com/~tkyte/Mutate/index.html

August 12, 2002 Randy -- Thanks for the question regarding "HTML
      Form that Updates Table", version 8.1.7

      You Asked
Hi Tom,
  I have an HTML survey that stores the response in a database table.  I'm
having one small problem with my stored procdure.  When a user does not answer
every question the update errors.  I've tried to set a default value of NULL,
but when I do I get the "ORA-01401: inserted value too large for column" error.
My code is:

Create or Replace Package PEO_Teleworking
Is
Procedure entrance_survey;
Procedure add_survey
(P_QUESTION1 in varchar2,
P_QUESTION2 in varchar2,
P_QUESTION3 in varchar2,
P_QUESTION4 in varchar2,
P_QUESTION5 in varchar2,
P_QUESTION6 in varchar2,
P_QUESTION7 in varchar2,
P_QUESTION8 in varchar2,
P_QUESTION9 in varchar2,
P_QUESTION10 in varchar2,
P_QUESTION11 in varchar2,
P_QUESTION12 in varchar2,
P_QUESTION13 in varchar2,
P_QUESTION14 in varchar2,
P_QUESTION15 in varchar2,
P_QUESTION16 in varchar2,
P_QUESTION17 in varchar2,
P_QUESTION18 in varchar2,
P_QUESTION19 in varchar2,
P_QUESTION20 in varchar2,
P_QUESTION21 in varchar2,
P_QUESTION22 in varchar2,
P_QUESTION23 in varchar2,
P_QUESTION24 in varchar2,
P_COMMENTS in varchar2);
End PEO_Teleworking;
/

create or replace package body PEO_Teleworking
is
Procedure add_survey
(P_QUESTION1 in varchar2 default NULL,
P_QUESTION2 in varchar2,
P_QUESTION3 in varchar2,
P_QUESTION4 in varchar2,
P_QUESTION5 in varchar2,
P_QUESTION6 in varchar2,
P_QUESTION7 in varchar2,
P_QUESTION8 in varchar2,
P_QUESTION9 in varchar2,
P_QUESTION10 in varchar2,
P_QUESTION11 in varchar2,
P_QUESTION12 in varchar2,
P_QUESTION13 in varchar2,
P_QUESTION14 in varchar2,
P_QUESTION15 in varchar2,
P_QUESTION16 in varchar2,
P_QUESTION17 in varchar2,
P_QUESTION18 in varchar2,
P_QUESTION19 in varchar2,
P_QUESTION20 in varchar2,
P_QUESTION21 in varchar2,
P_QUESTION22 in varchar2,
P_QUESTION23 in varchar2,
P_QUESTION24 in varchar2,
P_COMMENTS in varchar2)
is
BEGIN
Insert into Entrance_Survey (surveyno, question1, question2, question3,
question4, question5, question6, question7, question8, question9, question10,
question11, question12, question13, question14, question15, question16,
question17, question18, question19, question20, question21, question22,
question23, question24, comments)
Values (surveyno_id_seq.NEXTVAL, p_question1, p_question2, p_question3,
p_question4, p_question5, p_question6, p_question7, p_question8, p_question9,
p_question10, p_question11, p_question12, p_question13, p_question14,
p_question15, p_question16, p_question17, p_question18, p_question19,
p_question20, p_question21, p_question22, p_question23, p_question24,
p_comments);
htp.htmlOpen;
htp.headOpen;
htp.title('Updating Entrance Survey');
htp.headclose;
htp.bodyOpen(cattributes => 'bgcolor="#FFFFCC"');
htp.header(1, 'Your Entries Have Been Updated Successfully!');
htp.para;
htp.bodyclose;
htp.htmlClose;
End add_survey;
PROCEDURE Entrance_Survey
     IS
     BEGIN
        htp.htmlOpen;
        htp.headOpen;
        htp.title('Teleworking Pilot Program');
        htp.headClose;
        htp.bodyOpen(cattributes => 'bgcolor="#FFFFCC"');
        htp.centerOpen;
      htp.header(1, 'PEO-GCS');
        htp.header(1, 'Teleworking Pilot Program Entrance Survey');
        htp.para;
        htp.img(curl=> '/ows-img/peo.gif');
       htp.br;
       htp.br;
        htp.centerClose;
        htp.para;
 htp.p('The following questions will be used to help determine the climate for
the teleworking pilot project in the PEO/GCS.  This survey is anonymous; please
consider the questions carefully and answer them as you truly feel.  If you have
any further comments, please note them in the spaces provided at the end of the
survey.');
     htp.para;
     htp.br;
        htp.bold('Please fill in every field');
        htp.para;
    htp.formOpen(OWA_UTIL.GET_OWA_SERVICE_PATH||'PEO_teleworking.add_survey');
        htp.para;
        htp.para;
      htp.bold('Demographics');
        htp.para;
htp.p('Are you in a management/supervisory position? ');
        htp.formRadio(cname=> 'p_question1',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question1',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
 htp.p('What is your gender? (OPTIONAL) ');
        htp.formRadio(cname=> 'p_question2',
                      cvalue=> 'MALE');
        htp.p('Male');
        htp.formRadio(cname=> 'p_question2',
                      cvalue=> 'FEMALE');
        htp.p('Female');
      htp.para;
        htp.para;
    htp.br;
     htp.bold('General Expectations');
        htp.para;
htp.p('Do you support teleworking? ');
        htp.formRadio(cname=> 'p_question3',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question3',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('Do you feel that teleworking in the PEO will work? ');
        htp.formRadio(cname=> 'p_question4',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question4',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('Do you feel the selection process will be equitable? ');
        htp.formRadio(cname=> 'p_question5',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question5',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('Do you feel organizational morale will be enhanced by telework? ');
        htp.formRadio(cname=> 'p_question6',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question6',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('Does your position lend itself to teleworking? ');
        htp.formRadio(cname=> 'p_question7',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question7',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('Do you wish to participate in the telework pilot program? ');
        htp.formRadio(cname=> 'p_question8',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question8',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
    htp.br;
 htp.bold('Specific Expectations (if you are selected)');
        htp.para;
htp.p('Are you disciplined enough to work unsupervised? ');
        htp.formRadio(cname=> 'p_question9',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question9',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('Will you maintain your present level of performance?');
        htp.formRadio(cname=> 'p_question10',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question10',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('Will teleworking have a positive impact on your personal morale?');
        htp.formRadio(cname=> 'p_question11',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question11',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('Do you feel you will receive a fair appraisal rating?');
        htp.formRadio(cname=> 'p_question12',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question12',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('Do you feel your career will be affected?');
        htp.formRadio(cname=> 'p_question13',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question13',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('If so, in a positive way? ');
        htp.formRadio(cname=> 'p_question14',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question14',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
    htp.br;
htp.bold('Specific Expectations (if you are not selected)');
        htp.para;
htp.p('Do you think your workload will increase due to others teleworking?');
        htp.formRadio(cname=> 'p_question15',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question15',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('Will teleworking by others have a negative impact on your personal
morale?');
        htp.formRadio(cname=> 'p_question16',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question16',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
    htp.br;
htp.bold('Expectations for Supervisors/Managers Only (these questions refer to
your view of your workforce)');
        htp.para;
htp.p('Do you support teleworking in your area of supervision?');
        htp.formRadio(cname=> 'p_question17',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question17',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('Are you employees disciplined enough to work unsupervised?');
        htp.formRadio(cname=> 'p_question18',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question18',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('Do you feel organizational morale will be enhanced by telework?');
        htp.formRadio(cname=> 'p_question19',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question19',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('Do you feel your workforce will be as readily available as they are
now?');
        htp.formRadio(cname=> 'p_question20',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question20',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
    htp.br;
htp.bold('Preparation for Teleworking');
        htp.para;
htp.p('Have you received any information about the pilot program?');
        htp.formRadio(cname=> 'p_question21',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question21',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('Have you visited the Teleworking Website (PEO Home Page)?');
        htp.formRadio(cname=> 'p_question22',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question22',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('Was the information there benefical to you?');
        htp.formRadio(cname=> 'p_question23',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question23',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
htp.p('Do you feel you need telework training before the program begins?');
        htp.formRadio(cname=> 'p_question24',
                      cvalue=> 'YES');
        htp.p('Yes');
        htp.formRadio(cname=> 'p_question24',
                      cvalue=> 'NO');
        htp.p('No');
      htp.para;
        htp.para;
        htp.p('Comments');
        htp.nl;
        htp.formTextareaopen(cname=> 'p_comments', nrows=>'10', ncolumns=>'60');
        htp.formTextareaClose;
        htp.para;
        htp.formSubmit;
        htp.formReset;
        htp.formClose;
        htp.bodyClose;
        htp.htmlClose;
    END Entrance_Survey;
    END PEO_Teleworking;

thanks for taking a look,
Randy

      and we said...
see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1205650687947
for a code generator that'll help you find what column is giving you the issue.

Suggest you use that, generate the "helper procedure" to do the insert and go
from there.

without your table and all -- I cannot debug this.  One of your columns in the
database isn't big enough to hold what you are inserting.  The generated routine
will help you there.
August 12,
      2002 Sanjai -- Thanks for the question
      regarding "UTL_SMTP", version 8.1.7

      You Asked
Hi Tom,

In one of my projects I have a requirement of sending the email from the
database. I am using UTL_SMTP package. But this package has this limitation.

text line
The maximum total length of a text line including the <CRLF> is 1000 characters
(but not counting the leading dot duplicated for transparency).

If the body of my text exceeds 1000 char then is there a way to get around this
limitation.

Thanks a lot.

      and we said...
LINE != body

Also, those limits listed are not UTL_SMTP limits- but limits in the underlying
SMTP protocol itself (eg: we do not impose them SMTP does)

the body can be very very very long.

The max line length is 1000 character.  Just keep the LINES of text (text
without a CRLF in it) to 1000 characters or less (eg: 72 characters is a good
length).

You can email thousands of 72 character lines and they fit on the recipients
screen nicly.
Review & Followup

      Rating:
      5
UTL_SMTP  August 12, 2002
Reviewer:
      sanjai
Thanks for your prompt response.

August 12, 2002 Richer -- Thanks for the question regarding
      "Oracle Standards", version 8.1.6

      You Asked
Hi Tom,

Here's my question.  We are in a 4-step implementation process i.e. developer
must go through 4 different databases before the implementation goes into
production. The DBA currently in place has establised different standards and I
think those standards should not be there.

1- Each application owner has a different name for each database (for example
application XYZ has 4 different users in 4 different databases (dev_xyz,
tst_xyz, acc_xyz, prd_xyz).

2- The DBA wants to use differents username for data extraction and data loading
on the owner account? Is there any benefits?  Is this a good way of doing thing?

3- Should we keep our databases up and running most of the time even we access
it only during the day?  Do you think that shutting down and starting up a
database every day is a bad thing?

4- Can you give us an idea of what standard we should implement for our ORACLE
file?

5- Should the developper includes the username of the owner of the objects that
they are creating in their scripts?  For example
CREATE TABLE XYZ.EMPLOYEES instead of
CREATE TABLE EMPLOYEES?

Can you comment each one of these thoughts?

Thank you!

Rich

      and we said...
1) that is a bad idea if you ask me.  There should be one schema name "xyz" and
4 databases.  What a nightmare having different schema names depending on what
stage of the process you are in.

2) seems like a very strange request.  What is their justification.  Hopefully
they would be able to list at least one percieved benefit.

3) When do lightbulbs burn out most often -- when you turn them on.  I would
never shutdown a database unless I had to (scheduled downtime).  You'll spend
all morning warming up that buffer cache, getting sql parsed -- just to lose it
all and start all over again.

Ask the dba -- do you turn off your car at red lights?  If not, don't do that to
my database.

4) don't know what you mean by that.

5) I would never put the schema name there unless I had to.  And If I did I
would use &OWNER..EMPLOYEES and use:

define OWNER=&1

in the sqlplus 'install' script so I could pass in the schema name (in case I
want to change it sometime)
Review &
      Followup

      Rating:
      5
  August 12, 2002
Reviewer:  Rich
       from Montreal, QC CANADA
Hi Tom,

Many thanks for your answer.

I would like to clarify the idea beneath the first point.  The DBA seems to
think that this way the programmer will always know in which stage he/she is
executing their scripts.  Seems to me that differents password for the 4
differents databases would do the trick.  What do you think?

As well, sorry for the 4th point the question should be:  Do you use any naming
convention for your databases files?

I read your book.  I really like the way you demonstrate things in such a
rigourous way.

          Followup:

different password would do it, putting the global name in the sqlplus prompt
like I do should do it.  Using a different schema name wouldn't protect you
anymore then that would (eg: using different passwords).  It is not like the
schema names is left in blinking red lights at the top of your screen

Since the developer should never be touching production -- and the others are
just "testing" the chance of errors causing any serious damage should be --
zero.

I name my files after my tablespaces.

      Rating: 5
Different
      usernames...  August 14, 2002
Reviewer:  Jan van
      Mourik  from Houston, TX, USA
>> The DBA wants to use differents username for data
>> extraction and data loading on the owner account?

You could do some parameter changing depending on the user's type of work, like
changing sort_area_size and such upon login...

August 12, 2002 Saradha -- Thanks for the question regarding
      "error line# from exception", version 8.1.7.3

      You Asked
Tom,

Can you please tell me a way to print the error line number from the
exception handlers.

For example,

DECLARE
   var1 char(2);
BEGIN
   var1 := '123';
END;
/

DECLARE
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too
small
ORA-06512: at line 4

As I get the line 4 as the error line reported, how do I get the same if I add
an exception handler to this code.

DECLARE
   var1 char(2);
BEGIN
   var1 := '123';
EXCEPTION
WHEN OTHERS THEN
    -- I would like to trap and print the error line # here.
    DBMS_OUTPUT.PUT_LINE(SQLERRM ||' at line # ..');
END;

Thanks
Saradha

      and we said...
Only the client can get that -- the line number is "lost" as soon as you catch
the exception -- the act of catching the exception means "there is no error
anymore"

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1932043078402
for a possible solution for testing/debugging purposes.
Review &
      Followup

      Rating: 4
error line#
      from exception  August 12, 2002
Reviewer:
      saradha bavanandam  from NJ USA
Tom,

I have created the tables and the sequence under my schema
using the /rdbms/admin/tracetab.sql script by replacing the
alias sys with my schema name.
SQL> EXEC dbms_trace.set_plsql_trace(dbms_trace.trace_all_exceptions);
SQL> /** then i ran your example script that raises the exception and traps the
error */

PL/SQL procedure successfully completed

But it did not trap the error nor anything written into the plsql_trace_events
table.

My question is, does these objects in tracetab.sql script need to be created
only under SYS ownership ?

Thanks
Pushparaj

          Followup:

YES, it must be sys.

August 12, 2002 Rajesh -- Thanks for the question regarding
      "Unused OBJECTS", version 8.1.7.2

      You Asked
Hi Tom,

We are in a process of removing unused
objects(tables/functions/procedures/packages) from the database. Is there any
script(suggestions) or short-cut method to find  these unused
objects(tables/functions/procedures/packages not used in ddl/dml/select
statements for more than 3 months).

There are more than 500 objects(tables/functions/procedures/packages) in our
database.

At least PLEASE help me in finding unused TABLES.For other objects I'm thinking
to check manually in the application code(using find and grep commands)

Please Help me.

Thanks in advance for your reply

      and we said...
You'll have to enable auditing and then come back in 3 months to see.

We don't track this information by default -- also, even with auditing, it may
be very possible to have an object that is INDIRECTLY accessed (eg: via a
foreign key for example) that won't show up.

You can try USER_DEPENDENCIES but that won't tell you about objects referenced
by code in client apps or via dynamic sql

I'm always perplexed by this.  How does one get into a production environment
where  by they don't know what the objects are used by/for?  No documentation or
anything?

Review &
      Followup

      Rating: 4
I'll tell you
      why it happens...  August 12, 2002
Reviewer:
      Randy  from Texas
Project managers,directors,etc. that don't understand the little problems that
arise many months later (after go-live) from having ambiguous crap in the
production database won't allow us to control things the way we'd like.  They
just want the developers to be able to "do their job", which means the privs. to
create objects w/o going through the DBA.

You are a rare specimen, Tom.  I've realized that not many developers have
respect for the database and will trash it on a whim.

          Followup:

but production?  test, ok -- development -- ok, but production?  that costs
money (and time).

I'm against withholding things from people (not just developer vs dba) -- like
"don't use feature X, we are afraid of it" but some amount of source code
control is just necessary.

      Rating: 4
Thanks for your
      suggestions!!  August 12, 2002
Reviewer:
      Rajesh
Hi Tom,

Thank you very much for your responce. We thought the same thing to use
USER_DEPENDENCIES.

You are right, we dont have access to the production machine.

It is a very big project, so many modules/layers/releases and we'll always
maintain last 3 releases in production(end users can access any release).People
created some new objects for new designs and some how they forgot to drop old
and unused objects.

Thanks again!!

Rajesh

      Rating: 4
Oops! I was
      late!! I didn't see the other review and follow up.   August
      12, 2002
Reviewer:  Rajesh

      Rating: 4
Responding to
      Randy's Post  August 12, 2002
Reviewer:
      Shrek  from MD
IMHO:
No access on Production -- no problem
   (no SELECT only also, some queries can really
    affect performance)
Test (QA, Demo, or similar)
    Limited access through roles,
    or execute privileges on packages OR as needed basis
Development:
    I think the developers need Connect, Resource, definitely PLUSTRACE, and
some other privileges/roles on an as needed basis. Because they Can't do squat
without it, and they need a destructive test env. Therefore, run in ARCHIVELOG
mode, and give developers full freedom

On a separate note:
    Tom: I hope you reconsider the Yahoo/HotMail questions. Many people (myself
included) are nomads (contractors) who want to use a Yahoo or a HotMail account.

Thanks,

Shrek

          Followup:

I haven't disabled them -- It is really teed me off this morning when over HALF
of the questions came bouncing right back at me, and they are always from
yahoo.com, hotmail.com, msn.com -- never from <real company here>.com

      Rating: 3
Third Party
      tools  August 13, 2002
Reviewer:  Gary Myers
       from Australia
I've used SQL*Impact for this sort of thing, as it can pick references to SQL
from batch code, forms etc.

You'd probably need some idea of where to start looking. While SQL*Impact does
have reports for 'unused' columns and other anomalies, nothing will help if
you've got a form using a table when no-one ever uses the form.

>I'm always perplexed by this.
>How does one get into a production environment whereby
>they don't know what the objects are used by/for?

Easy. I've experienced the following
Make your documentation 'development' focused (ie 'create a procedure to mail
clients latest offer' as opposed to 'Procedure MAIL_CLIENT is used during the
annual STOCKTAKE reconciliation process...'). All too common. In one case, the
documentation was discarded after the development as it had served its purpose.

Make it impossible for developers to keep documentation up to date ("You can't
change it. It's already been signed off.") Especially applies to updates by
support staff, often with the excuse that 'they only fix the code so the design
won't be changed'

Don't have any procedures for dropping objects and programs, etc out of the
application environment. Or just make it so hard that no-one bothers.

Don't differentiate between code written for a one-off task (eg data conversion)
and regular, repeated, processing.

August 12, 2002 Gustavo -- Thanks for the question regarding
      "Execute executable", version 8.1.7

      You Asked
Hi Tom,
I have a little problem.I'm trying to call a Java function from Oracle using
this command:
    "Runtime.getRuntime( ).exec ("D:\\1JOB\\AB.exe");"
AB.exe its a simple Delphi program that shows a message.

But the program "AB.exe" doesn't run completed,just appear in the process
list.When i try to kill it show a message "No acesss.The operation can't be
finish".
Do you know what can I do to "AB.exe" run and not continue in the process list?
The syntaxe of command is(Java):

CREATE OR REPLACE import java.io.*;
        import java.lang.*;
public class Runit {
public static void Runthis()
 {
 try
  {
  Runtime.getRuntime( ).exec ("D:\\1JOB\\AB.exe");
  }
 catch (java.io.IOException ioe)
  {
  System.out.println("Error in AB");
  }
 }
}

And the syntaxe of Oracle procedure is:
CREATE OR REPLACE function RUN_CMD
        as
        language java
       name 'Runit.RunThis()';

Thanks,
Gustavo.

      and we said...
Well, the external procedure doesn't have a display to display to.  You must be
showing a message like "press this button to make the window go away" -- only no
one can EVER push the button!!!!

External procedures run in the background totally, there is no display.  Do
NOTHING in a host command that requires user interaction as that program will
just sit there forever waiting for a user -- only the user can never ever get to
it.

Only do things that require no human intervention, no display.


Subject | Article | Last Updated (East coast time)
NDS - Associating placeholders with bind values ? | article only | Aug 13, 2002 08:08 am
Full text search in Oracle | article only | Aug 13, 2002 08:18 am
SQL-Loader | article only | Aug 13, 2002 08:35 am
FGAC - sys_context returned value | article only | Aug 13, 2002 04:59 pm
memory usage error, explanation? | article only | Aug 13, 2002 05:26 pm
Sequence & Ref Cursor | article only | Aug 14, 2002 09:24 am
exception handling | article only | Aug 14, 2002 10:50 am
number of cursors -- initialization parameter(init.ora) | article only | Aug 14, 2002 10:51 am
Connectivity Probel to Management Server | article only | Aug 14, 2002 01:23 pm
Performance of Loops | article only | Aug 14, 2002 01:43 pm
pctfree and pctused for snapshot logs tables | article only | Aug 14, 2002 02:05 pm
About object storage | article only | Aug 14, 2002 02:55 pm
Data types | article only | Aug 15, 2002 08:00 am
Query & application access through views vs tables - costs/benefits? | article only | Aug 15, 2002 08:14 am
Performance tuning scripts | article only | Aug 15, 2002 01:00 pm
Oracle limit for recordsize | article only | Aug 15, 2002 01:03 pm
Trying to eliminate excess SQL calls in java .. use IN? PL/SQL BULK? | article only | Aug 15, 2002 07:08 pm
DBMS_AQ.LISTEN | article only | Aug 15, 2002 07:22 pm
Create Function based index with multiple columns | article only | Aug 15, 2002 07:27 pm
Truncate table by non table non-owner | article only | Aug 15, 2002 07:34 pm
how to get next record from a "resultset"? | article only | Aug 15, 2002 09:52 pm
running report's priviewer maximized.! | article only | Aug 16, 2002 07:26 am
Static linking in pro*c | article only | Aug 16, 2002 08:07 am
Controlling Grant of Create Table | article only | Aug 16, 2002 08:17 am
Monitor the Program Execution. | article only | Aug 16, 2002 11:15 am
Regarding Pro *C | article only | Aug 16, 2002 11:18 am
How to find, how many open_cursors active at a particular time | article only | Aug 16, 2002 06:47 pm
Session | article only | Aug 16, 2002 06:48 pm
Parameter p1,p2,p3 | article only | Aug 17, 2002 09:19 am
Size of SQL*AREA in shared_pool | article only | Aug 17, 2002 09:20 am
ANALYZE PL/SQL CODE | article only | Aug 17, 2002 09:22 am
Sample Pro*C/C++ Compile Error | article only | Aug 17, 2002 09:25 am
displaying only columns with values in them | article only | Aug 17, 2002 09:33 am
best way to update a blob | article only | Aug 17, 2002 09:44 am
What is the limit on Sending bulk emails from Oracle DB using SMTP. | article only | Aug 17, 2002 09:47 am
searching files in a directory in OS using intermdia with AND OR NOT NEAR operators | article only | Aug 17, 2002 12:14 pm
decimal separator when using to_char | article only | Aug 17, 2002 01:43 pm
How to find Indexes dropped | article only | Aug 18, 2002 09:28 am
UTF8 | article only | Aug 19, 2002 07:23 am
Crating standby database on different OS platform | article only | Aug 19, 2002 07:30 am
Storing queries in database | article only | Aug 19, 2002 07:38 am
Forms entry in Product_User_Profie table | article only | Aug 19, 2002 09:45 am
Storing And Retrieving Blobs interfaced with VB | article only | Aug 19, 2002 09:49 am
crash recovery and media recovery. | article only | Aug 19, 2002 11:21 am
update stmt | article only | Aug 19, 2002 12:58 pm
DBMS_UTILITY example | article only | Aug 19, 2002 01:03 pm
First comma in a string | article only | Aug 19, 2002 01:21 pm
Question about where results are kept in memory | article only | Aug 19, 2002 01:52 pm
I want to export the oracle table data to a plain text format with out using spool. | article only | Aug 19, 2002 02:15 pm
Nested Tables - accessing objects returned from a subquery | article only | Aug 19, 2002 04:00 pm
Stress test | article only | Aug 19, 2002 04:12 pm
add log_archive_dest_2 on another server | article only | Aug 19, 2002 04:17 pm
row(s) 1 - 52 of 52
August 13, 2002 Robert -- Thanks for the question regarding "NDS - Associating placeholders with bind values ?", version 8.1.7.2
You Asked
Wow! this is a first I have seen....4 PM ET and still open to questions. You're
getting better at this huh Tom :)

This question is about NDS -

What I find a need to do is build a UNION of multiple SELECTs...

my_sql :=
'select...from TEXAS where x = :one and y = :two
 UNION
 select...from UTAH where x = :one and y = :two
 UNION
 select...from PENN where x = :one and y = :two';

Then:

OPEN myRefCur FOR my_sql USING bv1, bv2;

...# of select is *only known at run-time*,

Is this possible, i.e associating the place-holders with
the bind values so that the above "OPEN FOR" line works ?

Thanks
Robert
and we said...
I would use an application context for this, it would look like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> variable x refcursor
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace context my_ctx using
my_proc
  2  /

Context created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure my_proc( p_name
in varchar2, p_val in varchar2 )
  2  as
  3  begin
  4      dbms_session.set_context( 'my_ctx', p_name, p_val );
  5  end;
  6  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_one number default 1;
  3      l_two varchar2(1) default 'X';
  4  begin
  5      my_proc( 'one', l_one );
  6      my_proc( 'two', l_two );
  7
  8      open :x for
  9      'select * from dual
 10        where rownum = to_number(sys_context(''my_ctx'',''one''))
 11          and dummy = sys_context( ''my_ctx'', ''two'' )
 12       UNION ALL
 13       select * from dual
 14        where rownum = to_number(sys_context(''my_ctx'',''one''))
 15          and dummy = sys_context( ''my_ctx'', ''two'' )';
 16  end;
 17  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> print x

D
-
X
X

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_one number default 1;
  3      l_two varchar2(1) default 'X';
  4  begin
  5      my_proc( 'one', l_one );
  6      my_proc( 'two', l_two );
  7
  8      open :x for
  9      'select * from dual
 10        where rownum = to_number(sys_context(''my_ctx'',''one''))
 11          and dummy = sys_context( ''my_ctx'', ''two'' )
 12       UNION ALL
 13       select * from dual
 14        where rownum = to_number(sys_context(''my_ctx'',''one''))
 15          and dummy = sys_context( ''my_ctx'', ''two'' )
 16       UNION ALL
 17       select * from dual
 18        where rownum = to_number(sys_context(''my_ctx'',''one''))
 19          and dummy = sys_context( ''my_ctx'', ''two'' )';
 20  end;
 21  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> print x

D
-
X
X
X

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_one number default 1;
  3      l_two varchar2(1) default 'X';
  4  begin
  5      my_proc( 'one', l_one );
  6      my_proc( 'two', l_two );
  7
  8      open :x for
  9      'select * from dual
 10        where rownum = to_number(sys_context(''my_ctx'',''one''))
 11          and dummy = sys_context( ''my_ctx'', ''two'' )
 12       UNION
 13       select * from dual
 14        where rownum = to_number(sys_context(''my_ctx'',''one''))
 15          and dummy = sys_context( ''my_ctx'', ''two'' )
 16       UNION
 17       select * from dual
 18        where rownum = to_number(sys_context(''my_ctx'',''one''))
 19          and dummy = sys_context( ''my_ctx'', ''two'' )';
 20  end;
 21  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> print x

D
-
X

ops$tkyte@ORA817DEV.US.ORACLE.COM>

Also, I put the last block in to show the difference between UNION ALL and
UNION.  If at all possible use UNION ALL (faster).  A union forces a SORT
DISTINCT on there which can take a bit if you have tons of data.
Review & Followup
Rating: 5
What If ? August 12, 2002
Reviewer: A reader
my_sql :=
'select...from TEXAS where x = :one and y = :two
 UNION
 select...from UTAH where x = :three and y = :four
 UNION
 select...from PENN where x = :five and y = :six';
and also if I am constructing the sql itself at runtime ?
 Here the bind variables itself are changing dynamically.
So how can we use Sys_context method here.
  I may be wrong then I will have to even call my_proc in a
loop or something like that or even assign these values dynamically?
Any clues ?

Followup:
So, now it is a wholly different question.

So, you must have variables "l_one", "l_two", "l_three" right?  Just call

my_proc( 'one', l_one );
my_proc( 'two', l_two );
my_proc( 'three', l_three );
....
and so on.

then when you build the query, instead of putting :one in there, put in

  sys_context( 'my_ctx', 'one' )

and you are done.  See also:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1288401763279
for a complete example where the nature of the predicate is wholly unknown until
runtime and we use sys_context to cater for that.

August 13, 2002 Harish -- Thanks for the question regarding "Full text search in Oracle", version 8i and above
You Asked
Hi Tom,
  The Sql server has Full text search which indexes the data.I
want know whether oracle provides that kind of facility,if yes,
how to configure that?

Thanks
Harish
and we said...
Yes, it is called interMedia text.  See
http://technet.oracle.com/doc/oracle8i_816/inter.816/index.htm
for details.
Review & Followup
Rating: 5
intermedia August 07, 2002
Reviewer: munz from Reston, USA
Tom:

1.  Is this product InterMedia a separate thing that you have to buy or it comes
with 8i.

2.  What would it do that a user with "CREATE INDEX" and "SELECT * FROM TABLE"
does in SQL*PLUS.

3.  Is this a user-ready-to-use GUI for an application, or you use it inside of
your application code.

4.  Is there a unix or windows GUI for oracle like ISQL for informix. Bascially,
view and query data by selecting tables and entering a filter.

Followup:
1) it is an option of 8i EE (enterprise)

2) i don't understand?

3) it is an extension to SQL -- you just use "create index" on some text data
and then you can search on it using SQL.  Any app that can run sql can use
intermedia.

4) OEM has one, yes -- sql worksheet.

Rating: 5
intermedia August 08, 2002
Reviewer: munz from Reston, USA
Tom:

1.  Just a little confused on what can you do with internedia that you usually
can not in SQL*PLUS.  you can use select * from tab where name like '$xxx' to
search for records containing strings.  When do you really need to use it.

2.  Does SQL worksheet come in with any product.  Is it a free or you have to
purchase it.  Is it also unix/windows tool.

Thank you,

Followup:
first -- stop saying "sqlplus" -- sqlplus is a tool, and environment.  It is
like Toad or something.

You mean to say "sql", period.

1) consider that name is a CLOB or a LONG.  Now what?  Consider that name is on
average 512k in size and you have 1,000,000 rows.  Now what (do you want to wait
for an unindexed full scan of 1,000,000 rows with .5 meg of data each to search
through?  I don't -- I want an index)

Consider that name is actually a blob and contains a MS-Word document..

you want to read the intermedia documention, it justifies itself in there.

2) it is part of OEM.  Enterprise Manager.

Rating: 5
internedia August 11, 2002
Reviewer: munz from reston, USA
Tom:

1.  Even if you have a blob or long . Can;t you index it with SQL CREATE INDEX
command?

2.  DO I understand that if you want to search a long or blob column the n
internedia is a must and the only solution.

3.  Is intermedia a product that comes in with 8i.  Is there a way for me to
check if it is installed im my 8i database?

4.  SQLWORKSHEET is a product for developers.  What I realy need is forms free
gui for users to view tables. something like access.  Does oracle has anything
like that?

Thank you,

Followup:
1) try it. what would the create index by to facilitate keyword searching?  to
search for all rows that have a document that contains the word "cool" somewhere
in it.  What would that be?  (interMedia)

2) it is not the only solution.  It is the easiest and most performant that I
know of

3) yes, yes.  look for ctxsrv in $ORACLE_HOME/bin, look for the ctxsys schema in
your database

4) that is called Discoverer -- Oracle Discoverer.
http://technet.oracle.com/docs/products/discoverer/content.html

Rating: 5
internedia August 12, 2002
Reviewer: Munz from Reston, USA
Tom:

1. can any user use this Discoverer product. It seems you have to read a whole
manual to learn how to create queries and reports.

2. is it only web supported through application server?

3. Any idea about pricing for it?

Followup:
1) it is designed and actually used by end users.  And surprise surprise, yes,
you should actually read the documentation in order to be able to exploit it but
it is not mandatory (many sites just do a one day training session on it)

Tell me, did all of your workers just use Excel right out of the box -- or did
they get training (if you tell me they did it out of the box, I'll have to say
one of two things:

o you are mistaken
o they don't use excel for real

I've used excel, in order to do anything beyond adding a column of numbers you
need training or you need to read the book.

2) there is a client server edition.  for a while anyway, client server is dead.

3) Oraclestore.com, your sales rep, those would be the channels for getting that
stuff.

Rating: 5
Discoverer in 9iAs July 30, 2003
Reviewer: Abdul Wahab from Jeddah..Bride Of Red Sea
Dear Tom,
  I am trying to configure discoverer and portal. I installed 9i AS (1.0.2)
.After installation i am able to view discoverer 4i from the browser.Its not
connecting. But I can't understand one thing. Do i have to install discoverer
from a seperate c.d and create EUL tables? Doesn't 9i As installation do that?.
If i have to installa it seperately , then how do i link it. in which database
whould i create the EUL tables? (the origin database of 9iAS or any other
database wull also be ok?)
Thankyou

August 13, 2002 Gabriel -- Thanks for the question regarding "SQL-Loader", version 8.1.5
You Asked
Tom,

I really apreciate to response my question, because I worked all
the day with this problem

 I used sqlload several time and never saw this error, my
workmate can not help me about this
------------------
Copy/Paste Error:
------------------
thor >sqlload dssadmin/dssagent   act_particionado_sincoma.ctl

SQL*Loader: Release 8.0.5.0.0 - Production on Fri Sep 29
11:47:41 2000

(c) Copyright 1998 Oracle Corporation.  All rights reserved.

SQL*Loader-524: partial record found at end of datafile
(act_particionado_sincom
a.ctl)
-------------------------------------------
control file: act_particionado_sincoma.ctl
-------------------------------------------
LOAD DATA
INFILE 'btgdm_trucho_corto.txt'
BADFILE 'btgdm_trucho_corto.bad'
DISCARDFILE 'btgdm_trucho_corto.dis'
DISCARDMAX 9999
APPEND
INTO TABLE aux_rot_stock
FIELDS TERMINATED BY ';'
   (    id_producto                INTEGER EXTERNAL,
        id_suc                     INTEGER EXTERNAL,
    id_dia                     DATE(10) "dd/mm/yyyy",
        ST_STOCKINICIAL          DECIMAL EXTERNAL
                            "to_number(:ST_STOCKINICIAL,
'099D999S')",
        ST_STOCKFINAL           DECIMAL EXTERNAL
                                "to_number(:ST_STOCKFINAL,
'099D999S'')",
        ST_STOCKPROMEDIO         DECIMAL EXTERNAL
                                "to_number(:ST_STOCKPROMEDIO,
'099D999S'')",
    ST_CPP_IVA              DECIMAL EXTERNAL
                              "to_number(:ST_CPP_IVA,
'099D999S')",
    ST_CPP_SIN_IVA           DECIMAL EXTERNAL
                               "to_number(:ST_CPP_SIN_IVA,
'099D999S')",
        ST_CR_IVA                DECIMAL EXTERNAL
                                "to_number(:ST_CR_IVA,
'099D999S')",
    ST_CR_SIN_IVA            DECIMAL EXTERNAL
                                "to_number(:ST_CR_SIN_IVA,
'099D999S')")
---------------------------------
btgdm_trucho_corto.txt
---------------------------------
000000000000889;0041;"13/09/2000";034.000+;036.000+;036.000+;001.
624+;001.342+;001.790+;001.479+;002.190+;001.810+;055.216+;045.62
8+;060.860+;050.286+;074.460+;061.540+;058.464+;048.312+;064.440+
;053.244+;078.840+;065.160+;058.464+;048.312+;064.440+;053.244+;0
78.840+;065.160+
000000000001004;0041;"13/09/2000";045.000+;043.000+;043.000+;000.
881+;000.728+;000.910+;000.752+;000.950+;000.785+;039.645+;032.76
0+;040.950+;033.840+;042.750+;035.325+;037.883+;031.304+;039.130+
;032.336+;040.850+;033.755+;037.883+;031.304+;039.130+;032.336+;0
40.850+;033.755+
000000000001007;0041;"13/09/2000";016.000+;013.000+;013.000+;002.
823+;002.333+;002.859+;002.363+;003.990+;003.298+;045.168+;037.32
8+;045.744+;037.808+;063.840+;052.768+;036.699+;030.329+;037.167+
;030.719+;051.870+;042.874+;036.699+;030.329+;037.167+;030.719+;0
51.870+;042.874+
000000000001008;0041;"13/09/2000";145.000+;282.000+;282.000+;000.
893+;000.738+;000.927+;000.766+;000.950+;000.785+;129.485+;107.01
0+;134.415+;111.070+;137.750+;113.825+;251.826+;208.116+;261.414+
;216.012+;267.900+;221.370+;251.826+;208.116+;261.414+;216.012+;2
67.900+;221.370+
----------------------------------
END
----------------------------------
I did several test, for example I changed .txt file or changed
.ctl file and all the time I had the same error. I think that I
have the mistake with the txt file but I do not sure.
Wainting for your reply...

Regarts
Gabriel Bovetti
(I'm really sorry, because I know that I have to improve my
english)
and we said...
The last line is missing a newline -- the record terminator is
not there.

I took a simple table t ( x varchar2(25) ) and using a control
file like this:

$ cat demo7.ctl
LOAD DATA
INFILE *
INTO TABLE T
REPLACE
( x )
BEGINDATA
data1
data2

I wrote a small C program to remove the last character so that:

$ od -c t.ctl
0000000   L   O   A   D       D   A   T   A  \n   I   N   F   I
 L   E
0000020       *      \n   I   N   T   O       T   A   B   L   E
     T
0000040  \t  \n   R   E   P   L   A   C   E  \n   (       x
 )  \n
0000060   B   E   G   I   N   D   A   T   A  \n   d   a   t   a
 1  \n
0000100   d   a   t   a   2
0000105

$ od -c demo7.ctl
0000000   L   O   A   D       D   A   T   A  \n   I   N   F   I
 L   E
0000020       *      \n   I   N   T   O       T   A   B   L   E
     T
0000040  \t  \n   R   E   P   L   A   C   E  \n   (       x
 )  \n
0000060   B   E   G   I   N   D   A   T   A  \n   d   a   t   a
 1  \n
0000100   d   a   t   a   2  \n
0000106

So, t.ctl has no ending newline bug demo7.ctl does.  Now:

$ sqlldr / demo7.ctl

SQL*Loader: Release 8.1.5.0.0 - Production on Sat Sep 30
08:49:00 2000

(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Commit point reached - logical record count 2

$ sqlldr / t.ctl

SQL*Loader: Release 8.1.5.0.0 - Production on Sat Sep 30
08:49:04 2000

(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Commit point reached - logical record count 1
SQL*Loader-524: partial record found at end of datafile (t.ctl)

So, that is the error -- missing record terminator on the last
record.  If you just pull the file up in vi and write it - it'll
be fixed.
Review & Followup
Rating: 2
What if file is not delimited? August 13, 2002
Reviewer: anshula from india
input file is neither delimited not properly fixed width. its like, on the
record line ... one character,column1,one character,column2.  that 'one
character' decides whether column is NULL or not? if one character is  '%' then
column is null and if it is one space then column is not null. how to extract
data in this case using sql loader?

example:
record line is
%column1 column2%column3
so, column1 and column3 are null.

Followup:
read
http://asktom.oracle.com/~tkyte/SkipCols/index.html
and use a technique similar (not exactly the same as -- similar too, requires
you extend the concept on your own) to the one I use in the delimited package.

In short, you have complex logic to implement.  sqlldr doesn't do "complex
logic" -- plsql does.  You'll parse the line in plsql and sqlldr will be able to
use plsql.

August 13, 2002 kiril -- Thanks for the question regarding "FGAC - sys_context returned value", version 9.0.1
You Asked
Hi Tom
I am reading your book "expert-one-on-one". At page 931 source code is
.....
function select_function( p_schema in varchar2,
                          p_object in varchar2) return varchar2
is
begin

     if ( g_sel_pred is NULL )
     then
         if ( sys_context( g_app_ctx, 'RoleName' ) = 'EMP' )
         then
           g_sel_pred:=
               'empno=sys_context('''||g_app_ctx||''',''EmpNo'')';
         elsif ( sys_context( g_app_ctx, 'RoleName' ) = 'MGR' )
         then
            g_sel_pred :=
             'empno in ( select empno
                   from emp_base_table
                  start with empno =
                         sys_context('''||g_app_ctx||''',''EmpNo'')
                connect by prior empno = mgr)';

         elsif ( sys_context( g_app_ctx, 'RoleName' ) = 'HR_REP' )
         then
            g_sel_pred := 'deptno in
                     ( select deptno
                         from hr_reps
                        where username =
                          sys_context('''||g_app_ctx||''',''UserName'') )';

         else
            raise_application_error( -20005, 'No Role Set' );
         end if;
     end if;

     return g_sel_pred;
end;
......

Q1) The datatype of the return value of "sys_context" is VARCHAR2.
If I have an index on column empno of EMP_BASE_TABLE as you have(PK) with this
VARCHAR2 returned predicate index will be out of use, because of NUMBER type of
field EMPNO?
Am I right or with "sys_context" function it is not true?
Please make a short comment.
Thanks
kiro
and we said...
Actually, in this case, it is OK:

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_dix on t(x);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where x= '5';

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   INDEX (RANGE SCAN) OF 'T_DIX' (NON-UNIQUE)

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

The string is turned into a number and then compared -- so the index can be
used.  The other way:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x varchar2(5) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_dix on t(x);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where x= 5;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'T'

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

is bad (string in DB, number in query).

I did however violate my rule of

o compare strings to strings
o numbers to numbers
o dates to dates.

I should have coded:

..
            g_sel_pred :=
             'empno in ( select empno
                   from emp_base_table
                  start with empno =
                   to_number( sys_context('''||g_app_ctx||''',''EmpNo'') )
                connect by prior empno = mgr)';
.....

to avoid the implicit (but in this case "safe") conversion.
Review & Followup
Rating: 5
August 13, 2002
Reviewer: kiro from Bulgaria
Thanks Tom,
Excellent as usual!

Rating: 4
Signature of a FGAC predicate August 13, 2002
Reviewer: A P Clarke from London, England
Tom

Forgive me for tagging to this question, but on this side of the pond it's the
only way of beating your backlog!

Your examples of FGAC predicates always have this signature:

function select_function( p_schema in varchar2,
                          p_object in varchar2);

but these parameters are never used in the actual function.  FGAC is somewhat
under-documented for 8.1.7 so I'm asking you: what are these parameters for?

I know I need them, because I get ORA-28112 if I omit them from my predicate's
function.  Why I need to know is because I'm thinking of sharing predicates
between several different policies, i.e. where different tables have a common
column like customer_ref, and those parameters make me wonder if there are any
implications.

Thanks in advance, APC

Followup:
Nay way -- I get in at 7am'ish and answer a batch.  This morning for example, I
opened up the floodgates at 7:30am east coast time.  I believe that is lunchtime
for you (11:30am UTC)!   Between 11am and 2pm UTC is prime time.

These parameters are the name of the schema owner and object that the policy is
being invoked for.  Allows you to use one function for many tables (and the
function can look at what table is being queried to figure out what to do in
this case)

August 13, 2002 George -- Thanks for the question regarding "memory usage error, explanation?", version 9.0.1.3.0
You Asked
Tom,

Our application currently relies heavily on plsql tables to pass data (its still
in development...)  But we are concerned about the memory it will use,
especially after the following error occurred:

======================================================================
desc string_list_t
 string_list_t TABLE OF VARCHAR2(32767)

declare
   n1 number;
   n2 number;
   v_nbrs   spi_varutils.string_list_t;
BEGIN
   n1 := dbms_utility.get_time;
   select doc_nbr_id
   BULK COLLECT
   into v_nbrs
   from doc;
   -- where doc_nbr_id like '0%';
   -- where PRPNT_ORG_OBJ_ID = '000E00A600000003';

   n2 := dbms_utility.get_time;

   dbms_output.put_line( 'doc_nbrs returned ' || v_nbrs.count || ' rows' );
   dbms_output.put_line( 'doc_nbrs execution time: ' || to_char( n2 - n1 ));

EXCEPTION
WHEN no_data_found THEN
   dbms_output.put_line( 'error' );
END;
/

declare
*
ERROR at line 1:
ORA-04031: unable to allocate 16408 bytes of shared memory ("large
pool","unknown object","koh-kghu
sessi","pl/sql vc2")
ORA-06512: at line 7
======================================================================

The query should have returned >175000 rows of a 20 character string.

I have spoken with our dba and read a bit on configuring the shared pool, but I
am still confused.

- where is memory allocated for table variables within our scripts?
- much of what we're doing is dynamic.. what memory segment should we be
concerned with when running those scripts?
- is there a better way you'd recommend passing large amounts of data (across
remote databases in some instances) - to allow the database to manage the memory
better?

Thanks, George
and we said...
175k rows into a plsql table type -- probably NOT a good idea.  You want to bulk
fetch a couple hundred rows at a time and process them

declare
   c cursor for select doc_nbr_id from doc;

    ....

begin
   open c;
   loop
      fetch c bulk collect into v_nbrs LIMIT 500;
      process( v_nbrs );
      exit when c%notfound;
   end loop;
   close c;
end;

You are using MTS (shared server) i presume since the error came from the large
pool.  It is wise to be memory concious here.

You DO NOT want arrays (in any language, this is not just plsql) that have 175k
entries in them.

I don't know exactly what you are doing, but you might consider using tables a
little differently -- and instead of passing huge arrays back and forth -- pass
back and forth chunks of arrays (500 rows at a time for example) or just access
the table using a cursor over the dblink.
Review & Followup
Rating: 5
August 13, 2002
Reviewer: kiro from Bulgaria
excellent!

Rating: 3
yes, but where? August 13, 2002
Reviewer: George Liblick from Chester Springs, PA USA
thanks, your message has openned some eyes to the need for chunking our return
values.  But it is not possible for us to use this approach in every situation,
so we will still need to manage a limit.. and specify how a user/dba might
configure his system to increase that limit if there is no other choice.

so my question is, which memory segment / initialization parameter do we need to
be concerned with for large arrays? and is it the same when we do this
dynamically?

- My suspicion is that arrays get allocated from the UGA which, since our
instance is configured for MTS, gets allocated from the large pool which is part
of the SGA.  But I haven't found that in so many words - even in your book!
That would also mean that if we configured for dedicated server, this same
memory would be allocated from the PGA.  And, if all this is true, then in
dedicated server mode our processes are limited by the operating system, and in
MTS mode, by the parametere LARGE_POOL_SIZE.

Please confirm my understanding, or set me straight!

Thanks, George

Followup:
depends on whether you use shared server (mts) or dedicated server.

PLSQL allocates from the UGA.  In shared server MTS -- that is in the SGA.  In
dedicated server, that is in the PGA (process memory)

Your message indicates MTS most likely.  That is the large pool in the SGA.

Under dedicated server, it'll be in the process memory and there are no
init.ora's for you to set (unless you are using the pga_aggregate_target new
feature)

August 14, 2002 kam -- Thanks for the question regarding "Sequence & Ref Cursor", version 8.1.7
You Asked
1. Is it possible to geneerate a sequence with +1,
                                               -1,
                                               +2,
                                               -2 and so on?

2.Could u please give me an example of a Dynamic Ref cursor?
  (i.e Dynamically passing the columns)
and we said...
1) nope.  A sequence very simply put is a way to generate, in a highly
concurrent fashion, a unique number.

You cannot make any assumptions about it such as

o it is gap free (it is not gap free, in fact, I can assure you 100% that at
some point, no matter what you do, there will be a gap)

o it is increasing always (eg: the higher the seq number, the "newer" the
record).  This is not true either

The only thing you can say about a sequence is that it generates unique numbers,
they might as well be random numbers, and it does this very very concurrently
(scalable)

2)
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3027089372477
shows an example of opening a query from a string.  the query of course could be
any query.
Review & Followup
Rating: 3
sequence workaround... August 12, 2002
Reviewer: andrew from ca, usa
create sequence t_seq;

select decode(mod(t_seq.nextval, 2), 1, 1, -1)*(round(t_seq.nextval/2))
from all_objects where rownum < 6;
1
-1
2
-2
3

I'm confused about your statement about "newer" records not always having the
higher sequence number. How does this relate to the ORDER property in the
documentation?

Followup:
big deal?

now what - use that in an application to have session 1 get 1, session 2 get -1,
session 3 get 2 and so on.

Show us how to use it in a trigger:

begin
   select my_seq.nextval into :new.primary_key from dual;
end;

to auto-sequence???  (you can't)

basically, you cannot get a sequence to return 1, -1, 2, -2, 3, -3, etc.

                 Session 1 does this                  Seesion 2 does this
Time
t1               insert a record
                 with seq.nextval=1
t2                                                    insert a record
                                                      with seq.nextval=2

t3                                                    commit;

t4               commit;

Now, to an outside observer -- in session 3 -- who was continously polling this
table, which record appeared first (was the oldest) and which appeared second?

The newer record (according to session 3) is the one with seq.nextval = 1, not
2...

Rating: 2
Help me find the difference... August 13, 2002
Reviewer: Subhro from Mumbai, India
Hi Tom,

1)Select t_seq.nextval from dual
and

2)Select decode(mod(t_seq.nextval, 2), 1, 1, -1)*(round(t_seq.nextval/2)) from
dual

differ only in that the second uses a function of t_seq.nextval.

Why should it affect scalability and concurrent access as such. I am not missing
anything big here, am I?

In fact, I could generate a sequence like 1, -1, 2, -2 using that.

Followup:

Rating: 2
Need to extend your comments about "newer" August 13, 2002
Reviewer: andrew from ca, usa
Tom
If you say that higher sequence numbers don't always represent newer records,
then you need to extend that argument to timestamping records too. It's no fault
of the sequence generator as such.

Followup:
Yup, agreed.

Rating: 2
Two stars August 13, 2002
Reviewer: andrew from ca, usa
Tom
Before anyone gets all excited about two stars. All it means to me is "Of some
use". I always appreciate your comments and insight.

Followup:
Doh -- never mind, that's cool -- i just read it too fast.

I thought it was dependent on being selected in a single statement:

select decode(mod(t_seq.nextval, 2), 1, 1, -1)*(round(t_seq.nextval/2))
from all_objects where rownum < 6;

and didn't look at it close enough.  Cool -- that does in fact work.

Rating: 2
I must be missing your point about the trigger August 13, 2002
Reviewer: andrew from ca, usa
create table T ( x number(10) primary key, y number );

create sequence t_seq;

create trigger t_trigger before insert on T for each row
begin
   if ( :new.x is null ) then
      select decode(mod(t_seq.nextval, 2), 1, 1, -1)*(round(t_seq.nextval/2))
into :new.x from dual;
   end if;
end;
/

insert into t(y) values (11);
insert into t(y) values (22);
insert into t(y) values (33);
insert into t(y) values (44);

select * from t;
1    11
-1    22
2    33
-2    44

Rating: 2
are two nextvals in a single select statement atomic? August 13, 2002
Reviewer: Vivek Sahani from australia
Tom,
are the two nextvals in the select statement executed atomically? meaning, can
after the first nextval being executed another session takes up the nextval from
the same before the second nextval is executed? if so, then this sequence may
run into some issues. could there be a locking issue too?

Followup:
Locking issue -- no, concurrency issue, well, that IS an interesting question,
but I don't think it matters really.

the first grab of nextval just assigns a sign (+/-)

the next grab decides the number

you'll still get all numbers in the sequence and they will still all be unique.

Interesting thought though, but i don't think it would affect the overall (neat)
behaviour.

Rating: 2
concurrency issue example August 14, 2002
Reviewer: Vishal Shah from Atlanta, GA USA
Well, lets take this example:
select decode(mod(t_seq.nextval, 2), 1, 1, -1)*(round(t_seq.nextval/2)) from
dual;
Lets say session 1 executes the first nextval of the above statement and gets
the value 1, hence the decode part results in a 1.
Session 2 executes now executes the first nextval of the above statement and
gets the value 2, hence the decode part results in a -1.
Session 1 now executes the second nextval of the above statement and gets the
value 3, hence the result of the select would be 1.
Session 2 now executes the second nextval of the above statement and gets the
value 4, hence the result of the select would be -2.
So the values resulting from the two executions of the select statement would be
1,-2.
This would break away from the expected result of 1 and -1 followed by 2 and -2
if concurrency were not an issue.
So in a highly active system, you may not get the desired results.

Followup:
But regardless -- sequences are NEVER NEVER NEVER gap free so in this case I
would say "no problem".  True the 1, -1, 2, -2 in order is "broken" but then
again, getting 1, 2, 3, 4, 5, ... in order is always "broken" so it is to be
expected.

You end up with half even and half odd sequences over time regardless and that
is the best you could ever hope to achieve.

Rating: 4
Example of the concurrency issue... I guess u got it wrong August 14, 2002
Reviewer: Subhro from Mumbai, India
It does return the sequence 1,-1,2,-2...

when the nextval returns 3, the round(t_seq.nextval/2) returns 2. So, the result
is 2.

That stuff was really cool from Andrew.

Rating: 2
A parlour trick August 14, 2002
Reviewer: Andrew Clarke from London, England
The alternating positive and negative sequence generator is a clever trick but
it subverts some of the usefulness of the Oracle sequence object.

For starters, the sequence's actual value and the populated column's value are
going to drift apart:

SQL> insert into t(y) values (55);

1 row created.

SQL> select max(x) from t;

    MAX(x)
----------
         3

SQL> select t_seq.currval from dual;

   CURRVAL
----------
         5

Still, it has taught me something: the sequence gets incremented once per SELECT
statement.

SQL> insert into  t1 values (t_seq.nextval, t_seq.nextval, t_seq.nextval,
'Huh?');

1 row created.

SQL> select * from t1 where col4 = 'Huh?';

      COL1       COL2       COL3 COL4
---------- ---------- ---------- --------------------
         6          6          6 Huh?

SQL>

I'm sure _that_ will score me a point the next time I play 'Trivial Pursuit'
with the DBAs :)

Cheers, APC

Rating: 5
Not A Concurrency Issue August 14, 2002
Reviewer: Lefteris Vakalis from Athens, CREECE
Well, if anyone is afraid of concurrency issues here, could
replace the 2nd reference to 'NEXTVAL' with 'CURRVAL',
as the desired result is obtained only by the fact that we
get the same number in both 1st and 2nd sequence
reference ;)
but the statement still works fine as is, because in the
documentation we can read that

<... if NEXTVAL is referenced more than once
in a single statement, the first reference generates
the next number and all subsequent
references in the statement return the same number.>
e.g.
SQL> select t_seq.currval, t_seq.nextval,
  2  t_seq.nextval, t_seq.nextval from dual;

   CURRVAL    NEXTVAL    NEXTVAL    NEXTVAL
---------- ---------- ---------- ----------
        35         35         35         35

Best Regards

Followup:
correct.

August 14, 2002 Charlie -- Thanks for the question regarding "exception handling", version 8.1.7
You Asked
Hi Tom,

Like your site so much!!  Please keep up good work!

Here is my question:

I want to insert some data from table t1, t2 into table t3;

create table t3(id number(5), name varchar2(40), primary key(id, name));

So the combination of id and name in t3 is unique.  I tried two approaches.

(1)
insert into t3(id, name)
select t1.id, t2.name
from t1, t2
where t1.id = t2.id;

FAILED 'cause "select t1.id, t2.name" cannot guarantee uniqueness.  I don't want
to use "select distinct t1.id, t2.name" since the execution plan I got will do
FULL SCAN on both tables, and each of them contains 2 million records.  So I
tried the second approach:

(2)
begin
    for x in
    (
       select t1.id, t2.name
       from t1, t2
       where t1.id = t2.id
    )
    loop
       insert into t3 values(x.id, x.name);
    exception
       when dup_val_on_index then
           null;
    end loop;
end;
/

This works but super slow.

Is there any way to handle exception caused by "insert into.. select from",
which is executed much faster?

Thanks,
and we said...
UMMM -- you will be full scanning t1 and t2 regardless -- you are processing ALL
ROWS IN BOTH TABLES.

If we use an index to read either of t1 or t2, it'll be painfully slow.  This is
a clear cut "full scan + hash join" to me.

#2 is super slow because doing things procedurally is pretty much ALWAYS going
to be super slow as compared to doing things

The correct and fastest approach will be:

create table t3 ( id number(5), name varchar2(40) );

insert /*+ APPEND */ select DISTINCT t1.id, t2.name from t1, t2 where t1.id =
t2.id;

commit;

create unique index t3_pk on t3(id,number) parallel N unrecoverable;
alter table t3 add constraint t3_pk primary key(id,number);

The full scans -- totally 100% unavoidable if you think about it.  You are going
to read each and every row regardless!!!
August 14, 2002 Shalini -- Thanks for the question regarding "number of cursors -- initialization parameter(init.ora)", version 8.1.7
You Asked
Hi,
    We have developed a project using Oracle 8.1.7 as database. The interaction
of the application with the database is only through calling stored procedures.
I have used ref cursors in the stored procedures. The application design is such
that it interacts a lot with the database. Yesterday I was going through the
init.ora file of the databaseand found that the number of cursors parameter is
300. I have got a doubt. Our client is a company with large number of employees
and they will be using this application quite frequently. So if many users
access the application at the same time will it not give some error like
"maximum number of cursors exceeded". If so, should I increase the number of
cursors and could you please tell me the optimum value I should increase it to
so that we will never get such error in future.
Thanks in advance

P.S: Please don't neglect my Question because my email ID is Yahoo's.
     Mine is genuine.
and we said...
That is 300 per session.

Each session gets 300 cursors.

If you don't hit max cursors with one user, you won't hit it with 10000 users.
August 14, 2002 Faisal -- Thanks for the question regarding "Connectivity Probel to Management Server", version 8.1.6
You Asked
I HAVE INSTALL ORACLE 8i DATABASE WITH ADVANCE SERVER 2000 OPERATING SYSTEM.WHEN
I TRY TO CONNECT THE MANAGEMENT SERVER THAN I RECEIVE THE FOLLOWING ERRROR
ERROR CODE VTK-1000
UNABLE TO CONN TO THE MANAGEMENT SERVER NULL.PLEASE VERIFY THAT YOU HAVE ENTERED
THE CURRENT HOST NAME AND THE STATUS OF THE ORACLE MANAGEMENT SERVER.
and we said...
Oracle8i, release 2 (version 816) is most definitely not supported or
supportable on this operating system.

This operating system was released long after Oracle8i release 2 was.

You need to use Windows NT with this release or upgrade to at least Oracle8i
release 3 (version 817)

This is a metalink support note on this topic HOWEVER -- your software
configuration is just asking for huge and continuing problems

Article-ID:         <Note:114682.1>
Circulation:        PUBLISHED (EXTERNAL)
Folder:             em.Console.V2
Topic:              Troubleshooting Guides and Tracing
Title:              TROUBLESHOOTING GUIDE: How to Troubleshoot A VTK-1000 Error
                    Logging into EM 2
Document-Type:      BULLETIN
Impact:             MEDIUM
Skill-Level:        NOVICE
Attachments:        NONE
Content-Type:       TEXT/PLAIN
Keywords:           EMCONNECT; EMCONSOLE; EMOMS;
Errors:             VTK-1000;
Products:           1/EM_CONSOLE (2);
Platforms:          GENERIC;

TROUBLESHOOTING GUIDE: VTK-1000 Error
=====================================

Goal: How to troubleshoot a VTK-1000 error.

Fact: Oracle Enterprise Manager

Change: NOTE ROLE:
The Enterprise Manager (EM) Console connects to a middle-tier service called the

Management Server (OMS).  If this connection attempt fails for any reason, the
EM Client will receive the error:

   VTK-1000: Unable to connect to Management Server <Name>
   Please verify that you have entered the correct name and
       status of the Oracle Management Server
                           OK

Fix: The VTK-1000 error can occur due to problems with the EM Client setup
or because the Management Server is not started.  To troubleshoot the error,
begin at the EM Client:

EM Client:
1.  Verify the Management Server hostname listed in the EM Console logon window
    is the correct hostname.
    For additional information, see
    <Note:1067911.6> Logging into Enterprise Manager Console Fails with VTK-1000

                     If OMS Name is Wrong

2.  Verify the Management Server hostname is reachable through TCP/IP from the
    EM Client system.
    For additional information, see
    <Note:1077548.6> Logging into Enterprise Manager Console Fails with VTK-1000

                     if OMS Name Changes

3.  Verify the EM Console and Management Server are at the same patch level.

Next, go to the Management Server:
4.  Verify the Management Server is started.
    For additional information, see:
    <Note:107235.1> How To Start the EM 2 Management Server from the MS-DOS
                    Command Prompt
    <Note:117193.1> How to Start the EM 2 Management Server on a Node using DHCP
    <Note:109494.1> How to Start the EM 2 Components on Sun Solaris

    If the Management Server will not start, turn on OMS logging and look in
    the oms log file for possible failure messages.

    NOTE: For instructions on how to log the OMS startup process, see
          Additional Troubleshooting hints listed below.

If the EM client is setup correctly, check the Management Server:
See <Note:133799.1> TROUBLESHOOTING GUIDE: How To Troubleshoot Management
                    Server Startup Errors

Additional Troubleshooting
--------------------------

If the solutions listed above do not resolve the VTK-1000, additional debugging
information must be captured.

1.  To Trace the EM Console, see:
    <Note:69522.1> How to Activate Logging and Tracing for EM 2 Components

2.  To Trace the OMS, see:
    <Note:69522.1> How to Activate Logging and Tracing for EM 2 Components

to retrieve the referenced notes, goto metalink.oracle.com
Review & Followup
Rating: 3
Same error with 8.1.7 November 15, 2002
Reviewer: Riaz Shahid from PRAL, Islamabad, Pakistan
I am using 8.1.7.2.1 and recieving the same error. Will i have to make any
configuration/steps for this ?

Regards

Rating: 5
How to start OEM in Linux? September 24, 2003
Reviewer: Sikandar Hayat from Pindi Bhattian - Pakistan
I am trying to connect to OMS (oracle 9.2) but getting the message "VTK-1000". I
think I have to start the management server first and it was just starting a
service in win but unable to find a way in Linux to start the OMS. In the
services there is no Oracle services so how can I start OMS on RH9?

Thanks in advance for your co.

Rating: 5
Done September 24, 2003
Reviewer: Sikandar Hayat from Pindi Bhattian - Pakistan
The following worked,

$oemctl start oms

August 14, 2002 Jean -- Thanks for the question regarding "Performance of Loops", version 8.12
You Asked
Hi Tom,

Like yr answers.

I would like to know which is better if I have many rows to be processed

1. Using a Cursor FOR LOOP as follows

   Cursor c1 is
          Select ....

   For r1 in c1 LOOP
       insert into table abc ( flds)
        for each row in r1

   End Loop

2.  Using an insert/Select for multiple rows

    Insert into abc
    ( fld1, fld2, ...)
    Select fld1,fld2
    where .....

3.  Use a FOR ALL
    But for the FOR ALL, there is always to use bulk collect into
    and array then use the FOR ALL.

    In which cases do you advise using FOR ALL when we can use simple
    cursor for Loops

Thks for the reply
Jean
and we said...
In order of speed:

2, 3, 1

In order of ease of programming:

2, 1, 3

In order of the right way to do things:

2, 2, 2

My mantra -- upon which I wrote a book "expert one on one"

o if you can do it in a single sql statment, do it.
o if not, use a little (as little as you can) plsql
o if you cannot do it in plsql, consider a small, very small, java stored
procedure
o if you cannot do it in java, a C external routine is ok.
August 14, 2002 Ruben -- Thanks for the question regarding "pctfree and pctused for snapshot logs tables", version 8.1.7.3.0
You Asked
the prod. db has a block size of 8192.

Hi Tom,
thanks to share your huge knowlegde. You great.

We have our production database(Unix) in basic replication mode, only a few
tables are
being replicated every five minutes by replication server(Windows NT).
Following are the tables and the corresponding statistics in three queries:

analyze table mlog$_bok_guia_head compute statistics;
analyze table mlog$_bok_guia_ADDR compute statistics;
analyze table mlog$_bok_guia_STUS compute statistics;
analyze table mlog$_bok_guia_SRVC_ITEM compute statistics;

Query #1
---------
select table_name, num_rows, blocks, EMPTY_BLOCKS, avg_space,
AVG_ROW_LEN,NUM_FREELIST_BLOCKS,pct_free,pct_used
from user_tables where table_name in
('MLOG$_BOK_GUIA_HEAD','MLOG$_BOK_GUIA_ADDR','MLOG$_BOK_GUIA_STUS','MLOG$_BOK_GUI
A_SRVC_ITEM');

TABLE_NAME                       NUM_ROWS     BLOCKS EMPTY_BLOCKS  AVG_SPACE
AVG_ROW_LEN NUM_FREELIST_BLOCKS   PCT_FREE   PCT_USED
------------------------------ ---------- ---------- ------------ ----------
----------- ------------------- ---------- ----------
MLOG$_BOK_GUIA_ADDR                    93         10         1269       7487
     39                   9         60         30
MLOG$_BOK_GUIA_HEAD                   202        210         1069       7836
     46                 207         60         30
MLOG$_BOK_GUIA_SRVC_ITEM              350         50         1229       7598
     39                  46         60         30
MLOG$_BOK_GUIA_STUS                   162        145         1134       7827
     38                 143         60         30

Query #2
---------
select a.vsize as mlog$_bok_guia_head,
       b.vsize as mlog$_bok_guia_ADDR,
       c.vsize as mlog$_bok_guia_STUS,
       d.vsize as mlog$_bok_guia_SRVC_ITEM from
(select
avg(nvl(vsize(m_row$$),0))+avg(nvl(vsize(SNAPTIME$$),0))+avg(nvl(vsize(DMLTYPE$$)
,0))+
       avg(nvl(vsize(OLD_NEW$$),0))+avg(nvl(vsize(CHANGE_VECTOR$$),0)) vsize
from mlog$_bok_guia_head) a,
(select
avg(nvl(vsize(m_row$$),0))+avg(nvl(vsize(SNAPTIME$$),0))+avg(nvl(vsize(DMLTYPE$$)
,0))+
       avg(nvl(vsize(OLD_NEW$$),0))+avg(nvl(vsize(CHANGE_VECTOR$$),0)) vsize
from mlog$_bok_guia_ADDR) b,
(select
avg(nvl(vsize(m_row$$),0))+avg(nvl(vsize(SNAPTIME$$),0))+avg(nvl(vsize(DMLTYPE$$)
,0))+
       avg(nvl(vsize(OLD_NEW$$),0))+avg(nvl(vsize(CHANGE_VECTOR$$),0)) vsize
from mlog$_bok_guia_STUS) c,
(select
avg(nvl(vsize(m_row$$),0))+avg(nvl(vsize(SNAPTIME$$),0))+avg(nvl(vsize(DMLTYPE$$)
,0))+
       avg(nvl(vsize(OLD_NEW$$),0))+avg(nvl(vsize(CHANGE_VECTOR$$),0)) vsize
from mlog$_bok_guia_SRVC_ITEM) d;

MLOG$_BOK_GUIA_HEAD MLOG$_BOK_GUIA_ADDR MLOG$_BOK_GUIA_STUS
MLOG$_BOK_GUIA_SRVC_ITEM
------------------- ------------------- -------------------
------------------------
                 38                  31                  30
 31

Query #3 (this code I get it from this site)
---------
select 'mlog$_bok_guia_head' tname  , min(cnt), max(cnt), trunc(avg(cnt)),
count(*)  from
(select dbms_rowid.rowid_block_number(rowid), count(*) cnt from
mlog$_bok_guia_head
 group by dbms_rowid.rowid_block_number(rowid) )
 union
 select 'mlog$_bok_guia_ADDR' tname, min(cnt), max(cnt), trunc(avg(cnt)),
count(*)  from
(select dbms_rowid.rowid_block_number(rowid), count(*) cnt from
mlog$_bok_guia_ADDR
 group by dbms_rowid.rowid_block_number(rowid) )
 union
 select 'mlog$_bok_guia_STUS' tname, min(cnt), max(cnt), trunc(avg(cnt)),
count(*) as from
(select dbms_rowid.rowid_block_number(rowid), count(*) cnt from
mlog$_bok_guia_STUS
 group by dbms_rowid.rowid_block_number(rowid) )
union
  select 'mlog$_bok_guia_SRVC_ITEM' tname, min(cnt), max(cnt), trunc(avg(cnt)),
count(*) as  from
(select dbms_rowid.rowid_block_number(rowid), count(*) cnt from
mlog$_bok_guia_SRVC_ITEM
 group by dbms_rowid.rowid_block_number(rowid) );

TNAME                      MIN(CNT)   MAX(CNT) TRUNC(AVG(CNT))   COUNT(*)
------------------------ ---------- ---------- --------------- ----------
mlog$_bok_guia_ADDR               1         76              31          3
mlog$_bok_guia_SRVC_ITEM         44         77              70          5
mlog$_bok_guia_STUS               5         79              54          3
mlog$_bok_guia_head               2         65              40          5

As can you see in query #1, the average free space per block is too much, I
think this is a waste of space
for each block, the only way to make use of most space per block (let me know
whether i'm wrong or right, please),
is to change de PCT_USED.
Let's subtract the block overhead to the block (Assuming is 100 bytes -- in avg.
is 89 to 107 --), so  our free
space for our data is 8092 bytes.

Calculation with current PCT_USED(30%) = 8092*0.30 = 2428 bytes.
I'll use avg. space per record according query #1 results.

mlog$_bok_guia_addr        2428/31 = 78
mlog$_bok_guia_srvc_item    2428/31 = 78
mlog$_bok_guia_stus        2428/30 = 81
mlog$_bok_guia_head        2428/38 = 64

this tell us the number of records per block which are very similar to those in
query #2, column number 2.

but, what about to change PCT_USED to 90% ?

8092*0.90 = 7283 bytes

mlog$_bok_guia_addr        2428/31 = 191
mlog$_bok_guia_srvc_item    2428/31 = 235
mlog$_bok_guia_stus        2428/30 = 243
mlog$_bok_guia_head        2428/38 = 235

we see that we have more records per blocks !!

I need to do this since we have to start to delete a thousands of records from
master tables
( bok_guia_addr,bok_guia_srvc_item,bok_guia_stus, bok_guia_head) and this will
generate the
same amount of records into the snapshot logs tables.

I made a test by inserting same number of records to be deleted from those
underlying tables:

mlog$_bok_guia_head
--------------------
begin
for j in 1..64435
loop
    insert into mlog$_bok_guia_head
(M_ROW$$,SNAPTIME$$,DMLTYPE$$,OLD_NEW$$,CHANGE_VECTOR$$)

values('AAAA2UAAFAAAOg/AAQ',to_date('4000-01-01:00:00:00','YYYY-MM-DD:HH24:MI:SS'
),'I','N', 'FEFFFFFFFFFFFFFFFFFFFF');
end loop;
end;
/

mlog$_bok_guia_addr
-------------------
begin
for j in 1..193305
loop
   insert into mlog$_bok_guia_addr
(M_ROW$$,SNAPTIME$$,DMLTYPE$$,OLD_NEW$$,CHANGE_VECTOR$$)

values('AAAA2RAAeAAAE47AAL',to_date('4000-01-01:00:00:00','YYYY-MM-DD:HH24:MI:SS'
),'I','N', 'FEFFFFFF');
end loop;
end;
/

mlog$_bok_guia_stus
-------------------
begin
for j in 1..64435
loop
    insert into mlog$_bok_guia_stus
(M_ROW$$,SNAPTIME$$,DMLTYPE$$,OLD_NEW$$,CHANGE_VECTOR$$)

values('AAAA2mAAJAAANY3AA5',to_date('4000-01-01:00:00:00','YYYY-MM-DD:HH24:MI:SS'
),'I','N', 'FEFFFF');
end loop;
end;
/

mlog$_bok_guia_srvc_item
------------------------
begin
for j in 1..259713
loop
    insert into mlog$_bok_guia_srvc_item
(M_ROW$$,SNAPTIME$$,DMLTYPE$$,OLD_NEW$$,CHANGE_VECTOR$$)

values('AAAA2gAAHAAAN2TAAQ',to_date('4000-01-01:00:00:00','YYYY-MM-DD:HH24:MI:SS'
),'I','N', 'FEFFFFFF');
end loop;
end;
/

create a tablespace to see its growing :

create tablespace ts_mlog datafile
'h:\oracle\oradata\sipweb\ts_mlog.dbf' size 1088k autoextend on
extent management local uniform size 250k;

( I recreate this tbs before run all inserts at once )

=================================================================================
================================
with
PCTFREE = 60
PCTUSED = 30

FILE_NAME                                                              BYTES
----------------------------------------------------------------- ----------
H:\ORACLE\ORADATA\SIPWEB\TS_MLOG.DBF                                 1114112

We execute all insertions from above ....

TABLE_NAME                       NUM_ROWS     BLOCKS EMPTY_BLOCKS  AVG_SPACE
AVG_ROW_LEN NUM_FREELIST_BLOCKS   PCT_FREE   PCT_USED
------------------------------ ---------- ---------- ------------ ----------
----------- ------------------- ---------- ----------
MLOG$_BOK_GUIA_ADDR                193305       2451           12       4868
     39                   5         60         30
MLOG$_BOK_GUIA_HEAD                 64435        964           27       4894
     46                   3         60         30
MLOG$_BOK_GUIA_SRVC_ITEM           259713       3288            7       4863
     39                   1         60         30
MLOG$_BOK_GUIA_STUS                 64435        809           22       4916
     38                   4         60         30
Elapsed: 00:00:00.04

MLOG$_BOK_GUIA_HEAD MLOG$_BOK_GUIA_ADDR MLOG$_BOK_GUIA_STUS
MLOG$_BOK_GUIA_SRVC_ITEM
------------------- ------------------- -------------------
------------------------
                 38                  31                  30
 31

TNAME                      MIN(CNT)   MAX(CNT) TRUNC(AVG(CNT))   COUNT(*)
------------------------ ---------- ---------- --------------- ----------
mlog$_bok_guia_ADDR              71         79              78       2447
mlog$_bok_guia_SRVC_ITEM         40         79              78       3288
mlog$_bok_guia_STUS              35         80              79        806
mlog$_bok_guia_head              48         67              66        962

FILE_NAME                                                              BYTES
----------------------------------------------------------------- ----------
H:\ORACLE\ORADATA\SIPWEB\TS_MLOG.DBF                                62193664

=================================================================================
=================================
with
PCTFREE = 10
PCTUSED = 90

FILE_NAME                                                              BYTES
----------------------------------------------------------------- ----------
H:\ORACLE\ORADATA\SIPWEB\TS_MLOG.DBF                                 1114112

We execute all insertions from above ....

TABLE_NAME                       NUM_ROWS     BLOCKS EMPTY_BLOCKS  AVG_SPACE
AVG_ROW_LEN NUM_FREELIST_BLOCKS   PCT_FREE   PCT_USED
------------------------------ ---------- ---------- ------------ ----------
----------- ------------------- ---------- ----------
MLOG$_BOK_GUIA_ADDR                193305       1097           22        877
     39                   5         10         90
MLOG$_BOK_GUIA_HEAD                 64435        430           17        909
     46                   4         10         90
MLOG$_BOK_GUIA_SRVC_ITEM           259713       1469            2        853
     39                   2         10         90
MLOG$_BOK_GUIA_STUS                 64435        356           27        862
     38                   2         10         90
Elapsed: 00:00:00.01

MLOG$_BOK_GUIA_HEAD MLOG$_BOK_GUIA_ADDR MLOG$_BOK_GUIA_STUS
MLOG$_BOK_GUIA_SRVC_ITEM
------------------- ------------------- -------------------
------------------------
                 38                  31                  30
 31

TNAME                      MIN(CNT)   MAX(CNT) TRUNC(AVG(CNT))   COUNT(*)
------------------------ ---------- ---------- --------------- ----------
mlog$_bok_guia_ADDR              21        177             176       1093
mlog$_bok_guia_SRVC_ITEM         54        177             176       1468
mlog$_bok_guia_STUS               7        182             181        355
mlog$_bok_guia_head             109        151             150        427

FILE_NAME                                                              BYTES
----------------------------------------------------------------- ----------
H:\ORACLE\ORADATA\SIPWEB\TS_MLOG.DBF                                28114944

by changing pctused to 90 and pctfree to 10 we obtain the following :

1.- less used blocks per table
2.- more space to be added for each block
3.- more records for block
4.- less bytes to be used in our datafile

is there any inconvenience to change the oracle default settings for pctfree and
pctused
in snapshot logs ??
is there any problem with my production database if I change this physical
attributes clauses ?

Tom, do we can get any good benefits by changing the pct_used like above,
having more records for each block in tables with intensive insertion ?
after all, to my knowledge, oracle makes only inserts in mlog$_* tables , is it
right ?

any advice on this issue will be very appreciated.

Thanks a lot beforehand
and we said...
so, your biggest log table is using 1.7meg.  This is much ado about nothing.

Lets look at the "biggest" table:

TABLE_NAME                       NUM_ROWS     BLOCKS EMPTY_BLOCKS  AVG_SPACE
AVG_ROW_LEN NUM_FREELIST_BLOCKS   PCT_FREE   PCT_USED
------------------------------ ---------- ---------- ------------ ----------
----------- ------------------- ---------- ----------
MLOG$_BOK_GUIA_HEAD                   202        210         1069       7836

     46                 207         60         30

It is using 210 8k blocks.  That is 1.64meg.

Of that, 207 block *are on the free list* waiting to be reused.  1069 blocks
have never been used.

The avg free space is so high because most of the 207 blocks on the freelist are
in fact *empty*

TNAME                      MIN(CNT)   MAX(CNT) TRUNC(AVG(CNT))   COUNT(*)
------------------------ ---------- ---------- --------------- ----------
mlog$_bok_guia_head               2         65              40          5

shows us that you have 5 blocks (count(*)) with rows on them (out of 210).  You
are getting about 40 rows/block on these 5 blocks so you have 200 rows -- but
one of the blocks in question only has 2 rows so you really have 4 blocks with
data meaning you are getting more like 50 rows/block.

All these numbers mean one thing:

a) at some point you had 10,500 rows (210 * 50 rows/block on average) in this
log table

b) that caused the table to grow to 210 (1.64 meg) blocks of used space.

c) you know have 200 rows in this table and lots of empty blocks just waiting
for data.

I see no need to take any action on anything.  In fact, changing pctfree and
pctused here would have ZERO effect (the blocks are on the free list, there is
no wastage here).

All in all -- this is getting close to CTD -- compulsive tuning disorder.  There
is no problem here at all.  The sizes are are talking about are way small -- way
way small.
August 14, 2002 Maximiliano -- Thanks for the question regarding "About object storage", version 8.1.7
You Asked
Hi Tom,

   I would like to give for you a big congratulations for this service. It's
very valuable for us, DBAs.

   I read a very good bulletin (Note:186826.1), in a which one part say:

   "....
   2.2. All segments should inherit the storage values from the storage clause
of the tablespace. The MINEXTENTS clause guarantees the amount of space for one
segment at creation time. ..."

  What do you think about the object's storage clause is the same of the
tablespace ?
and we said...
in 817 -- better advice:

Use locally managed tablespaces, period.

Then all you need supply would be pctfree and pctused (if anything at all).
Nothing more or less.

Don't use storage clauses at all! Just use LTM's with uniform extents (or system
managed extents if you don't know how big the things will get to be)
August 15, 2002 -- Thanks for the question regarding "Data types", version 9201
You Asked
Hi Tom
Tom
I am sure you have answered my question several times as " floats are synonyms
for numbers. But I have  a situation at work where my colleague wont agree with
me that there is no difference between oracle float and number datatype with no
precision. This is affecting my implemention in which I want to use Float as the
datatype vs Number. THis is for conveneince in conversion of my scripts to Sql
server where we want such columns to be converted to Float (Number column are
converted to numeric in sql).

He did some test through JDBC and got the metadata for the float & number
columns as
FLOAT         Number(0)
Number         Number(38)

IF you have a table t with (id Number, Code Float)

We are able to insert exactly 126 digits in both as follows:

insert into t
values(12345678901234567890123456789012345678901234567890123456789012345678901234
5678901234567890123456789012345678901234567890123456,
123456789012345678901234567890123456789012345678901234567890123456789012345678901
234567890123456789012345678901234567890123456);

       ID         CODE
---------- ----------
1.235E+125 1.235E+125

set numwidth 50
Select * from t;

       ID         CODE
---------- ----------
1.2345678901234567890123456789012345678900000E+125
1.2345678901234567890123456789012345679000000E+125

So the float column has more precision.

Also if you execute the following

SQL> select * from xtest where num=fl;
no rows selected

Does this prove that in Oracle FLoat columns are different from Number columns.

Please help in solving this confusion.

Thanks
and we said...
Floats are numbers.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( id number, code float );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t
  2  values(
  3
123456789012345678901234567890123456789012345678901234567890123456789012345678901
234567890123456789012345678901234567890123456,
  4
123456789012345678901234567890123456789012345678901234567890123456789012345678901
234567890123456789012345678901234567890123456)
  5  /

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set linesize 50
ops$tkyte@ORA817DEV.US.ORACLE.COM> set numwidth 50
ops$tkyte@ORA817DEV.US.ORACLE.COM> select id, code, id-code from t;

                                                ID
--------------------------------------------------
                                              CODE
--------------------------------------------------
                                           ID-CODE
--------------------------------------------------
1.2345678901234567890123456789012345678900000E+125
1.2345678901234567890123456789012345679000000E+125
-1.0000000000000000000000000000000000000000000E+87

Doesn't show that floats aren't numbers, they are just a number with a different
precision then number in itself.

(it also shows that numbers in this particular case are capable of storing 39
digits -- this may vary a bit, 38 is all we assure you and that is what the
float is actually storing.  The float is less precise in this case and in your
case, not the other way around).

In anycase, give me a REAL WORLD example where this difference makes a
difference  - for example, are you storing numbers with 39 digits of precision
(if you are BEWARE, 38 is what we promise, not 39).  If you are storing with 38,
we can do.
Review & Followup
Rating: 4
One conversion item to beaware of August 14, 2002
Reviewer: Jim Kennedy from Portland, OR USA
One problem is that certain operations in that the other database vendor might
give different results when done in Oracle due to the differences in percision
of the two vendors.

So if one vendor stores numbers up to 15 or 16 digits of percision and the other
38 or 39 then in some instances mathmatical operations / conversions will be
different.

Followup:
Oh definitely -- 100%

All you get here is syntatic compatibility -- eg: the scripts will run.

But, you don't get application level "sameness".

The other vendors will only give you 7 to 13 digits typically as they use native
4 and 8 byte floats.

That is just the very teeny tiny tip of the iceberg as well.  We won't even get
into the differences in locking, concurrency, consistency, data integrity, etc.

August 15, 2002 Richard -- Thanks for the question regarding "Query & application access through views vs tables - costs/benefits?", version 8.1.7.2
You Asked
Tom,
I inherited a local standard for a home-grown data warehouse environment where
"master" views are created for their underlying tables.  They are exact replicas
of the table definitions (i.e. create or replace view view_name as select * from
table_name).  The table name is identical to the corresponding view name except
it has "_T" appended to the name to distinguish the table vs the view.

All query access for users is granted to the "master" views and not the tables
if the user is referencing data within single tables.  In the case of "modeled"
views or "non-master" views (i.e. a view that is not an exact replica of a
particular table; it may reference only certain columns within a single table or
join several tables), users are granted access to the "modeled" views (and those
"modeled" views reference the "master" views and not the tables directly).

The benefit is that when maintenance is performed on tables that require a drop,
the grants are not affected (since the grants are on the views & not the
tables).

Does this "master" view approach generally make sense?  Maybe if there's only a
lot of potential maintenance of the definitions?  Are there implications in
carrying this approach over to transactional environments as well whereby
applications have grants on "master" views rather than tables directly?

Is a possible drawback performance degradation?  Or is accessing the "master"
view rather than its underlying table result in only a negligible performance
hit?  One cost is that it's another layer of definitions that needs to be
maintained/compiled.

Thanks in advance for any feedback,
-Rick
and we said...
I actually quite like it.  Think about things people seem to (for whatever
reason) constantly want to do

o rename a column (trivial with a CREATE OR REPLACE VIEW)
o reorder the columns (trivial with a CREATE OR REPLACE VIEW)
o limit access to certain columns (trivial....)
o change the data you can access - filter it (trivial....)

the added one you have -- a persistent method to maintain grants is another
great example.

Except for a very small hit on the parse to retrieve the view text (and remember
for a common view, its a cache hit), there isn't that much of a difference
between using the view and a table beyond all of the advantages you get from the
view.  Especially in a data warehouse where the parse time to execute time ratio
is tiny (parse time is soooo small compared to the overall execution time).

I would leave the views as they are -- you'll be glad some day you did.
Review & Followup
Rating: 5
Why January 03, 2002
Reviewer: Kulguru
".  Especially in a data warehouse where the parse time to execute time ratio is
tiny (parse time is soooo small compared to the overall execution time)."

Why is it so , can you expand on your statement. And is the converse true for
oltp applications.

Followup:
In a data warehouse, queries run in seconds.

In OLTP, queries run in hundredths or thousandths of seconds.

Parse time can take MORE time in OLTP then actual execution.  Parse time could
be a very large percentage of your time in OLTP if you

a) don't use bind variables
b) parse a statement more then once per session

They could in fact be as much as 90% of your execution time -- since in OLTP
your actual SQL statements are fast (once compiled).

In a data warehouse you generally

a) run significantly LESS queries (you don't have 10,000 users running 1,000 TPS
in a DSS system)

b) run queries that take significantly more then 1/100 of a second.

Rating: 5
Thank-you for the validation January 03, 2002
Reviewer: Bruce from Edmonton, Alberta, Canada
Thank-you!  I have trouble getting support from other DBAs when I explain to
them this is why I want to create the extra views and do things this way.

I also make more differentiation in privileges in that role privileges are
granted to the views and specific object privileges are granted to users
(usually only object owners) at the base table level.  This also helps me
identify who has privileges granted directly and who has them granted via roles.

Rating: 4
Pros of Views Outweight Cons...except maybe hiniting with views January 04, 2002
Reviewer: MGP from PA USA
I think the benefits for view use are quite promising..  However I have
experienced problems with views and optimizing complex SQL statements..

As we know Oracle does not always choose what we might consider the best way to
get at the data..(Possibly due to incomplete statistics, disk/ram hardware
effectiveness, or maybe they way the moon aligns with Uranus ;))  Anyway, I have
found it difficult at times prodding the optimizer to use the right path to the
data when views are used....  Generally I have not been able to force the use of
an index on a views underlying tables and/or force a Merge Join or Nested Loop
join when I feel that they will produce better runtime results. In those times I
have resorted to redefining the underlying view with hints and/or at worst case
resorting to using the base tables so that the hints are interpreted by the
optimizer correctly..

This is at least been my experience with views in connection with complex SQL
statements...

Followup:
Apples and oranges I think here.

There are views against a single table -- such as the question is about.

There are complex views that ANSWER A SPECIFIC question (what you are referring
to).

there can be issues when querying complex views that were not designed to answer
the ultimate question.  99.999% of the time -- this is 100% unavoidable due to
the semantics of the question.  Sometimes, the optimizer could do better.

There is no difference between:

create view v as < complex query >;

select * from V, T1, T2, ... where ...;

and

select * from ( <complex query> ), T1, T2, ... where ... ;

all views are inlined -- if the inlined view can be MERGED, it will be as if you
wrote the query straight against the base tables.  If it cannot be (because it
would change the semantics of the query itself), it won't be.  This is where
people start to say "hey, views are slow".  No, the view isn't slow, its the
question you asked that was slow.  Many times I find that the "rewritten query
against the base table" is semantically different then the query against the
view (they return DIFFERENT results).

Joining to a group by is different then grouping by a join, outer joining to a
join is differnt then joining to an outer join and so on.  Many times, these
rewritten queries are really different (meaning, if the rewritten query is
CORRECT, the view was totally inappropriate to use in the first place)

Given the constraints above -- single table views -- there is nothing to be
concerned about.

Rating: 3
A pathological case? August 13, 2002
Reviewer: Dany from Sydney, Australia
I have a specific instance where my view is most definitely slower

create view project_roles_view as
select projectid,
       manager,
       leader,
       startdate,
      lead (startdate)
           over (partition by projectid, manager, leader
                 order by startdate) enddate
  from (select distinct
               projectid,
               manager,
               leader,
               startdate
          from project_status)
as read only;

Now if I issue

select * from project_roles_view where projectid = '1234';

it takes many times longer than selecting using the raw query.

Why?

Followup:
and what is the raw query.

I'll betcha your "raw" query is not:

select *
  from  (select projectid,
       manager,
       leader,
       startdate,
      lead (startdate)
           over (partition by projectid, manager, leader
                 order by startdate) enddate
  from (select distinct
               projectid,
               manager,
               leader,
               startdate
          from project_status) )
where projectid = '1234'

if it isn't, we are comparing apples to toasters.

Rating: 4
Re: A pathological case? August 13, 2002
Reviewer: Dany from Sydney, Australia
You are right ... my fault, I was incorrectly comparing
with

select projectid,
       manager,
       leader,
       startdate,
      lead (startdate)
           over (partition by projectid, manager, leader
                 order by startdate) enddate
  from (select distinct
               projectid,
               manager,
               leader,
               startdate
          from project_status)
where projectid = '1234';

which is missing the parentheses and is therefore a toaster and not an apple.

So, is it possible to convice Oracle (8.1.7) to do the merge?

The MERGE hint? UNNEST_SUBQUERY or COMPLEX_VIEW_MERGING parameters?

Followup:
It can be hard to push into aggregates like that.  In many cases, it can alter
the meanging of the query -- here I don't think it does, but the CBO seems to be
doing it for me:

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into project_status select * from
project_status;

102400 rows created.

(i generated 100 random rows and kept inserting them)

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into project_status values ( '1234',
11, 11, sysdate );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create index project_status_idx on
project_status( projectid );

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table project_status
  2  compute statistics
  3  for table
  4  for all indexes;

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace on
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select projectid,
  2         manager,
  3         leader,
  4         startdate,
  5        lead (startdate)
  6             over (partition by projectid, manager, leader
  7                   order by startdate) enddate
  8    from (select distinct
  9                 projectid,
 10                 manager,
 11                 leader,
 12                 startdate
 13            from project_status)
 14  where projectid = '1234';

PROJE    MANAGER     LEADER STARTDATE ENDDATE
----- ---------- ---------- --------- ---------
1234          11         11 13-AUG-02

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=55 Card=2049 Bytes=79911)
   1    0   WINDOW (BUFFER)
   2    1     VIEW (Cost=40 Card=2049 Bytes=79911)
   3    2       SORT (UNIQUE) (Cost=40 Card=2049 Bytes=63519)
   4    3         TABLE ACCESS (BY INDEX ROWID) OF 'PROJECT_STATUS' (Cost=27
Card=2049 Bytes=63519)
   5    4           INDEX (RANGE SCAN) OF 'PROJECT_STATUS_IDX' (NON-UNIQUE)
(Cost=6 Card=2049)

Statistics
----------------------------------------------------------
         82  recursive calls
          0  db block gets
          8  consistent gets
          0  physical reads
          0  redo size
        606  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          4  sorts (memory)
          0  sorts (disk)
          1  rows processed

and if you had an index on (projectid, manager, leader, startdate) -- it works
really well.

do you have an example?

Rating: 4
Re: A pathological case? August 13, 2002
Reviewer: Dany from Sydney, Australia
Thank you for your help, I do appreciate it.

If I understand correctly, then yes using the toaster the CBO does push the
predicate into the view, but if you now use the apple and query with

select * from
(select projectid,
       manager,
       leader,
       startdate,
      lead (startdate)
           over (partition by projectid, manager, leader
                 order by startdate) enddate
  from (select distinct
               projectid,
               manager,
               leader,
               startdate
          from project_status))
where projectid = '1234';

The you (well, I do) get a full table scan and sort.

My specific example is different to what I stated to protect the innocent, and
harbour the ugly, but the essence of it remains the same.

    1. There is a table that you want to extract the
       distinct set of certain columns

    2. There is an analytical function that you apply to
       the resulting set from 1.  In my case to determine
       the enddate for timestamped rows by a particular
       partitioning of the set.

       At the very least there is some transformation of
       the columns in the result set from 1 to produce
       additional columns.  This is an important reason
       for the view.

    3. You want the results of 2 to be available as a view
       that you can issue arbitrary queries on, preferably
       without loss of performance.

I am not forced to use a view, since I am also writing the application that
issues the queries, but I agree with your sentiments about the advantages of
views, in particular modularity, hence why I would like to use one here.

Thanks again for your help.

Followup:
I do not get the full scan:

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace on
ops$tkyte@ORA817DEV.US.ORACLE.COM> set echo on
ops$tkyte@ORA817DEV.US.ORACLE.COM> @test
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from
  2  (select projectid,
  3         manager,
  4         leader,
  5         startdate,
  6        lead (startdate)
  7             over (partition by projectid, manager, leader
  8                   order by startdate) enddate
  9    from (select distinct
 10                 projectid,
 11                 manager,
 12                 leader,
 13                 startdate
 14            from project_status))
 15  where projectid = '1234';

PROJE    MANAGER     LEADER STARTDATE ENDDATE
----- ---------- ---------- --------- ---------
1234          11         11 13-AUG-02

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=55 Card=2049 Bytes=98352)
   1    0   VIEW (Cost=55 Card=2049 Bytes=98352)
   2    1     WINDOW (BUFFER)
   3    2       VIEW (Cost=40 Card=2049 Bytes=79911)
   4    3         SORT (UNIQUE) (Cost=40 Card=2049 Bytes=63519)
   5    4           TABLE ACCESS (BY INDEX ROWID) OF 'PROJECT_STATUS' (Cost=27
Card=2049 Bytes=63519)
   6    5             INDEX (RANGE SCAN) OF 'PROJECT_STATUS_IDX' (NON-UNIQUE)
(Cost=6 Card=2049)

It uses the index.

Lets see your autotrace.  Also, tell us more about the table -- what indexes are
there, what is the cardinality of the various columns, what is the size of the
table.

Rating: 4
Example August 14, 2002
Reviewer: Dany from Sydney, Australia
I dropped all the indexes and issued the following query

08:47:38 NPIVS@MDM.WORLD> edit
Wrote file afiedt.sql

  1  SELECT v.PROJECTID,
  2         v.MANAGER,
  3         v.LEADER,
  4         v.STARTDATE,
  5         v.ENDDATE
  6    FROM (SELECT PROJECTID,
  7                 MANAGER,
  8                 LEADER,
  9                 STARTDATE,
 10             LEAD (STARTDATE - 1)
 11                 OVER (PARTITION BY PROJECTID, MANAGER, LEADER ORDER BY
STARTDATE) ENDDATE
 12       FROM PROJECT_STATUS) v
 13*  WHERE v.PROJECTID = '6203547821'
08:48:01 NPIVS@MDM.WORLD> /

PROJECTID  MANAGER    LEADER     STARTDATE            ENDDATE
---------- ---------- ---------- -------------------- --------------------
6203547821 POWCP      POWERCOR   01-JAN-2001 00:00:00

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   VIEW
   2    1     WINDOW (SORT)
   3    2       TABLE ACCESS (FULL) OF 'PROJECT_STATUS'

Statistics
----------------------------------------------------------
       1671  recursive calls
        298  db block gets
       7471  consistent gets
      20967  physical reads
      31468  redo size
        621  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          1  sorts (disk)
          1  rows processed

Next I created a primary key index with projectid as the first element ...

08:48:48 NPIVS@MDM.WORLD> ALTER TABLE PROJECT_STATUS ADD CONSTRAINT
PROJECT_TABLE_PK PRIMARY KEY (PROJECTID, MANAGER, STARTDATE) ENABLE VALIDATE;

Table altered.

... then I reissued the query ...

08:51:11 NPIVS@MDM.WORLD> edit
Wrote file afiedt.sql

  1  SELECT v.PROJECTID,
  2         v.MANAGER,
  3         v.LEADER,
  4         v.STARTDATE,
  5         v.ENDDATE
  6    FROM (SELECT PROJECTID,
  7                 MANAGER,
  8                 LEADER,
  9                 STARTDATE,
 10            LEAD (STARTDATE - 1)
 11             OVER (PARTITION BY PROJECTID, MANAGER, LEADER ORDER BY
STARTDATE) ENDDATE
 12       FROM PROJECT_STATUS) v
 13*  WHERE v.PROJECTID = '6203547821'
08:51:27 NPIVS@MDM.WORLD> /

PROJECTID  MANAGER    LEADER     STARTDATE            ENDDATE
---------- ---------- ---------- -------------------- --------------------
6203547821 POWCP      POWERCOR   01-JAN-2001 00:00:00

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   VIEW
   2    1     WINDOW (SORT)
   3    2       TABLE ACCESS (FULL) OF 'PROJECT_STATUS'

Statistics
----------------------------------------------------------
       1788  recursive calls
        299  db block gets
       7492  consistent gets
      20967  physical reads
      31488  redo size
        621  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          4  sorts (memory)
          1  sorts (disk)
          1  rows processed

... with little difference ...

Here's the query without the "view" ...

08:52:13 NPIVS@MDM.WORLD> edit
Wrote file afiedt.sql

  1  SELECT PROJECTID,
  2                 MANAGER,
  3                 LEADER,
  4                 STARTDATE,
  5            LEAD (STARTDATE - 1)
  6             OVER (PARTITION BY PROJECTID, MANAGER, LEADER ORDER BY
STARTDATE) ENDDATE
  7       FROM PROJECT_STATUS
  8*  WHERE PROJECTID = '6203547821'
08:54:39 NPIVS@MDM.WORLD> /

PROJECTID  MANAGER    LEADER     STARTDATE            ENDDATE
---------- ---------- ---------- -------------------- --------------------
6203547821 POWCP      POWERCOR   01-JAN-2001 00:00:00

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   WINDOW (SORT)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'PROJECT_STATUS'
   3    2       INDEX (RANGE SCAN) OF 'PROJECT_TABLE_PK' (UNIQUE)

Statistics
----------------------------------------------------------
         65  recursive calls
          0  db block gets
         32  consistent gets
          3  physical reads
          0  redo size
        621  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          1  rows processed

... it's significantly faster

The table is this size ...

08:54:42 NPIVS@MDM.WORLD> select count(*) from project_status;

  COUNT(*)
----------
    638148

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'PROJECT_STATUS'

Statistics
----------------------------------------------------------
          0  recursive calls
         15  db block gets
       6943  consistent gets
       6937  physical reads
          0  redo size
        369  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

Okay, I have forgotten to analyze the table and indexes.

08:55:34 NPIVS@MDM.WORLD> analyze table project_status estimate statistics for
table for all indexes;

Table analyzed.

... Now reissue the query on the "view"

08:57:00 NPIVS@MDM.WORLD> edit
Wrote file afiedt.sql

  1  SELECT v.PROJECTID,
  2         v.MANAGER,
  3         v.LEADER,
  4         v.STARTDATE,
  5         v.ENDDATE
  6    FROM (SELECT PROJECTID,
  7                 MANAGER,
  8                 LEADER,
  9                 STARTDATE,
 10            LEAD (STARTDATE - 1)
 11             OVER (PARTITION BY PROJECTID, MANAGER, LEADER ORDER BY
STARTDATE) ENDDATE
 12       FROM PROJECT_STATUS) v
 13*  WHERE v.PROJECTID = '6203547821'
08:57:43 NPIVS@MDM.WORLD> /

PROJECTID  MANAGER    LEADER     STARTDATE            ENDDATE
---------- ---------- ---------- -------------------- --------------------
6203547821 POWCP      POWERCOR   01-JAN-2001 00:00:00

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=9322 Card=638263 Byt
          es=24892257)

   1    0   VIEW (Cost=9322 Card=638263 Bytes=24892257)
   2    1     WINDOW (SORT)
   3    2       TABLE ACCESS (FULL) OF 'PROJECT_STATUS' (Cost=1054 Car
          d=638263 Bytes=22339205)

Statistics
----------------------------------------------------------
       1753  recursive calls
        296  db block gets
       7480  consistent gets
      16766  physical reads
      31528  redo size
        621  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          2  sorts (memory)
          1  sorts (disk)
          1  rows processed

... nope, no difference from pre-analyze ...

Here's the query without the "view" again

08:58:24 NPIVS@MDM.WORLD> edit
Wrote file afiedt.sql

  1  SELECT PROJECTID,
  2                 MANAGER,
  3                 LEADER,
  4                 STARTDATE,
  5            LEAD (STARTDATE - 1)
  6             OVER (PARTITION BY PROJECTID, MANAGER, LEADER ORDER BY
STARTDATE) ENDDATE
  7       FROM PROJECT_STATUS
  8*  WHERE PROJECTID = '6203547821'
08:58:57 NPIVS@MDM.WORLD> /

PROJECTID  MANAGER    LEADER     STARTDATE            ENDDATE
---------- ---------- ---------- -------------------- --------------------
6203547821 POWCP      POWERCOR   01-JAN-2001 00:00:00

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1157 Card=6383 Bytes
          =223405)

   1    0   WINDOW (SORT)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'PROJECT_STATUS' (Cost=
          1054 Card=6383 Bytes=223405)

   3    2       INDEX (RANGE SCAN) OF 'PROJECT_TABLE_PK' (UNIQUE) (Cos
          t=28 Card=6383)

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

... also no difference from pre-analyze

Thank you for your help.

Followup:
The cardinalities look a bit off don't they --

3    2       INDEX (RANGE SCAN) OF 'PROJECT_TABLE_PK' (UNIQUE) (Cost=28
Card=6383)

it thinks there are 6300+ rows with that projectid, but there are obviously much
less.

Try the analyze COMPUTE (its just 700k rows)

Also, play with the optimizer_index_cost_adj, try setting that to 35/25 (alter
session)

Rating: 4
Is it the analytical function? August 14, 2002
Reviewer: Dany from Sydney, Australia
Having "ANALYZE ... COMPUTE STATISTICS" and set OPTIMIZER_INDEX_COST_ADJ to 25
did not change the cardinality of either view.

It appears to be the analytical function though.

The second instance has the analytical function commented out and matches the
statistics of the "non view" query in the previous review (see above).

Note that the cardinality for the query without the analytical function is still
very high.

12:37:08 NPIVS@MDM.WORLD> edit
Wrote file afiedt.sql

  1  SELECT v.PROJECTID,
  2          v.MANAGER,
  3          v.LEADER,
  4          v.STARTDATE,
  5     v.ENDDATE
  6     FROM (SELECT PROJECTID,
  7                  MANAGER,
  8                  LEADER,
  9                  STARTDATE,
 10             LEAD (STARTDATE) OVER (PARTITION BY PROJECTID, MANAGER ORDER BY
STARTDATE) ENDDATE
 11       FROM PROJECT_STATUS) v
 12*  WHERE v.PROJECTID = '6203547821'
12:38:06 NPIVS@MDM.WORLD> /

PROJECTID  MANAGER    LEADER     STARTDATE            ENDDATE
---------- ---------- ---------- -------------------- --------------------
6203547821 POWCP      POWERCOR   01-JAN-2001 00:00:00

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=9321 Card=638148 Byt
          es=24887772)

   1    0   VIEW (Cost=9321 Card=638148 Bytes=24887772)
   2    1     WINDOW (SORT)
   3    2       TABLE ACCESS (FULL) OF 'PROJECT_STATUS' (Cost=1054 Car
          d=638148 Bytes=22335180)

Statistics
----------------------------------------------------------
       1671  recursive calls
        296  db block gets
       7472  consistent gets
      14030  physical reads
      31508  redo size
        621  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          2  sorts (memory)
          1  sorts (disk)
          1  rows processed

12:38:38 NPIVS@MDM.WORLD> edit
Wrote file afiedt.sql

  1  SELECT v.PROJECTID,
  2          v.MANAGER,
  3          v.LEADER,
  4          v.STARTDATE /*,
  5     v.ENDDATE */
  6     FROM (SELECT PROJECTID,
  7                  MANAGER,
  8                  LEADER,
  9                  STARTDATE /*,
 10             LEAD (STARTDATE) OVER (PARTITION BY PROJECTID, MANAGER ORDER BY
STARTDATE) ENDDATE */
 11       FROM PROJECT_STATUS) v
 12*  WHERE v.PROJECTID = '6203547821'
12:40:58 NPIVS@MDM.WORLD> /

PROJECTID  MANAGER    LEADER     STARTDATE
---------- ---------- ---------- --------------------
6203547821 POWCP      POWERCOR   01-JAN-2001 00:00:00

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=548 Card=6382 Bytes=
          223370)

   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'PROJECT_STATUS' (Cost=54
          8 Card=6382 Bytes=223370)

   2    1     INDEX (RANGE SCAN) OF 'PROJECT_TABLE_PK' (UNIQUE) (Cost=
          28 Card=6382)

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

12:41:01 NPIVS@MDM.WORLD>

Thank you for your help.

Followup:
No, not entirely (mine has the analytics)

go a step further and analyze compute for table, for all indexes, for all
indexed columns.  compute histograms.

Rating: 5
Just to clarify... August 15, 2002
Reviewer: Connor McDonald from London
"Except for a very small hit on the parse to retrieve the view text (and
remember for a common view, its a cache hit)"

I thought that the view text is not stored in the dictionary or library cache
(of course the blocks from view$ would hopefully be in the buffer cache) -
tkprof seems to show that every parse on a view results in a fresh query to
view$.

(Not a problem in the case of the original poster)

Followup:
I meant buffer cache yes, it is not in the dc cache.

August 15, 2002 Daniel -- Thanks for the question regarding "Performance tuning scripts", version 8.1.7
You Asked
hi, Tom

     Can you give some performance tuning scripts sample or URL ,I need them.
      Thank you very much!
and we said...
There are only two that I use.

tkprof
statspack

and you already have both!

if you have my book "Expert one on one Oracle" I walk you through these tools in
depth in chapter 10.

If not, read
http://asktom.oracle.com/~tkyte/tkprof.html
http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96533/statspac.htm#34837

August 15, 2002 Doug -- Thanks for the question regarding "Oracle
      limit for recordsize", version 8.1.7

      You Asked
Tom - a more theoretical question inspired by a comment I heard today that MS
Access'97 does not support record size beyond 2000 characters.  What that seems
to mean is the TOTAL of all fields in the records cannot exceed 2000 characters
in MS Access...

I was wondering what the limit for Oracle might be.  How many columns are
allowed in one Oracle table?  (You've answered this before but I couldn't find
it).. 1000?  How many of those columns can be LOB's at 4GB each?  999? for a
total recordsize of over 3 terabytes?  Let's say you leave out unstructured data
(LOB's).. then what is the maximum? 999 varchar2(2000's)?   Any differences
between 8i and 9i?
How about using complex datatypes?

Side question regarding my search problems - Is there any way to search the
archives for an EXACT string match?  I try using quotes but I seem to always get
the same hits and # of hits.

For example -
1 search - maximum number of columns
is the same as "maximum number of columns" and doesn't seem to look for an exact
match.

Thanks..

      and we said...
1000 columns at 4000 bytes per column, 4,000,000 bytes.  If you count LOBS, 1000
* 4gig!

SQL Server for a long time had a limit of 1,962 bytes/row.  Not sure if the
later releases surpassed that one (but I'm pretty sure someone will say below if
they did ;)

These limits are as of Oracle8.0

It was 255 columns * 2000 bytes or 254*2000 bytes + 2gig in 7.x and before (only
one long/table back then)

Right now, I only have a super simple search  that AND's all of the meaningful
terms together.  That string will be:

{maximum} and {number} and {of} and {columns}

and sent to intermedia that way....
Review &
      Followup

      Rating:
      5
Great!  August 21, 2002
Reviewer:
      Doug  from CT, USA
Just what I was looking for.. Thanks Tom.

August 15, 2002 Doug -- Thanks for the question regarding "Trying
      to eliminate excess SQL calls in java .. use IN? PL/SQL BULK?", version
      8.1.7

      You Asked
Tom - the following code snippet is passing in an array in thingVTList
and executing the query for each element in the array.  There must be a better
way.  2 ideas I thought you could comment on.

A) One big IN statement?  section 2 of that question, how long can you make an
IN statement
B) Would there be a way to pass it to a stored procedure and do a bulk  return
of some kind?

(sytax altered slightly for readability)

private Long[] getSomeordersBythings ThingVT[] thingVTList) throws SQLException
{
    HashSet woSet = new HashSet(); //we need a set since multiple iterations can
bring same someorder ids
    String query =     "SELECT UNIQUE pw1.fk_someorder_id
            FROM thing_someorder pw1, thing_someorder pw2,
   thing_someorder pw3
            WHERE pw1.fk_thing_id = pw2.fk_thing_id
            AND pw2.fk_someorder_id = pw3.fk_someorder_id
            AND pw3.fk_thing_id = ?";
    PreparedStatement stm = _connection.prepareStatement(query);

    for(int i=0; i<thingVTList.length; i++)
    {
        stm.setLong(1, thingVTList[i].getId());
            ResultSet rs = stm.executeQuery();
        while(rs.next())
        {
            woSet.add(new Long(rs.getLong(1)));
        }
        rs.close();
    }
    stm.close();

    Long[] togo = new Long[woSet.size()];
    woSet.toArray(togo);

    woSet.clear();
    woSet = null;
    return togo;
}

      and we said...
If you put it in an object type - we can do this.

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:712625135727
for "howto" bind an array

and then you can:

select unique pw1.fk...
  from ....
 where ....
   and pws.fk_thing_id in ( select * from TABLE( cast (? as yourTableType) ) );

Review &
      Followup

      Rating: 5
Excellent -
      precisely what I was looking for  October 17, 2001

Reviewer:  Doug  from CT, USA
Thanks again Tom.  Quick follow up - how long can I make an IN statement anyhow?
256 bytes?

          Followup:

well, if you make it an IN statement with constants, you'll be violating my
prime directive of "bind variables are VERY important".

You can have 1,000 items in an in list.

      Rating: 5
follow up to IN
      statement  October 17, 2001
Reviewer:  Doug
       from CT, USA
Have no fear, I have no intention of actually using an IN statement with or
without bind variables - just curious as to the limitation :)

      Rating: 5
this right? and
      will parsing work well?  October 25, 2001

Reviewer:  A reader
Had a little stumble with the cast syntax -
I ended up with this:
We've got the java sending the array to pl/sql
TYPE num_array IS TABLE OF thing.id%TYPE;
is in package spec -
thing is a table, id is a column - number(38)

Then
we have a procedure that takes
p_thing_array IN num_array as an argument -

Then we have the query :
something like - (as above)
OPEN p_anotherthing_ref FOR
select
where ....
      and pws.fk_thing_id in IN (select * from TABLE(cast(p_thing_array as
num_array)))

Is that right?

So this is the follow up - when we do an IN like this with an object type - the
array can be of varying size so I assume the "type" acts like a bind variable?
I'm so certain you never ever would have suggested a solution that would cause
hard parses - but hey.. I have to ask :)
Let's say this array is HUGE - couldn't it change the execution plan?   I mean
if I did like a
IN (1,2)
verus a
IN (1,32,434,4,,5,6,7,87,8,,7,2,23, on and on and on)
in SQL
I would imagine I could see some different explain plans, so this looks like a
Catch-22 to me.  If the answer is - yes, it acts like a bind variable - then I
would wonder if it is always efficient.  If the answer is no, it isn't like a
bind variable, than don't I have a parsing problem?

Thanks -
Doug

          Followup:

It acts like (and is) a bind variable.  The plan will be static -- regardless of
whether there is 0, 1, or thousands of elements.

Only if you used literals would you arrive at a different plan for different
inputs.  Then you pay the price for hard parses.

      Rating: 5
little
      problem  October 26, 2001
Reviewer:  Doug
       from CT, USA
While proceeding with java, it gave an oracle error on:
ArrayDescriptor descriptor =
ArrayDescriptor.createDescriptor("pkg_thing.num_array", conn);

java.sql.SQLException: invalid name pattern: schemaname.num_array

          Followup:

Sorry -- didn't see that.

Don't define the type in a spec -- it must be done at the SQL layer so you can
use it in SQL.

create or replace type num_array .....;

NOT in a plsql spec

      Rating: 5
SQLJ example of
      "IN" binding  August 15, 2002
Reviewer:  John
      DiMiceli  from Washington, DC
I need to do this using SQLJ:

SELECT * FROM tablename WHERE col IN
(SELECT * FROM TABLE (CAST (? AS MySQLTableType)));

I've read most of your excellent explanations of binding for "IN" predicates but
I have several outstanding questions.

Some of your examples have "the", some don't.  For the life of me I can't find
reference to the "THE" operator.  When is the "THE" operator used in a cast? I'm
using 9i release 2.

I can bind an array using java.sql preparedStatement.setArray() in JDBC, can you
give me an example of a bind using dynamic SQLJ?

I assume I can bind String[] to type TABLE OF VARCHAR2.

If I have several IN predicates in one WHERE section, do I need to create
individually-named SQL TABLE types of VARCHAR2 to cover each one or can I re-use
the same type in the WHERE statement?

          Followup:

THE is 8.0 syntax, depreciated in 8i and above.

Dynamic sqlj can be done via JDBC, hence if you know how to do it with java.sql,
you already know how to do it with sqlj.

As for string[] to table of varchar2, no, you would be using PLSQL table types
and the special api calls for that in 817 and up with the THICK OCI8 drivers or
you would be using an object type and the api's for doing that. (search for
"java array" on this site for examples)

You can use the same type over and over.

August 15, 2002 Muraleedaran -- Thanks for the question regarding
      "DBMS_AQ.LISTEN ", version 8.1.7

      You Asked
Hi Tom

I need your expert opinion / thoughts on this.

We have a process runs multiple threads to read and process ids ( that are
already enqueued ). We are using Oracle Advanced Queueing in Version 8.1.7.

At any point in time, would like to know count of pending ids ( say N,  that are
still waiting in Q to be picked up ). Depending on that value (N), the program
will decide whether to span more threads to finish the job.

When I query the Q table as

select count(*) from QTable
where q_name = 'MYQNAME';

This count(*) also includes the ids, currently being locked & processed by other
threads ( expected behaviour ).

Is there any other way to find out count(*) of only pending Ids.

Thanks much in advance.

Muralee

      and we said...
I asked Julie Wells, our resident AQ guru and she says:

Hi Tom,

There is a MSG_STATE column in each queue table.  The different states of the
message are:  WAITING, READY, PROCESSED, EXPIRED

You would want to run the query with the additional qualifier:
select count(*) from QTable
where q_name = 'MYQNAME'
  and MSG_STATE = 'READY' If you have any more questions, please let me know.

Thanks!

Julie
Review
      & Followup

      Rating: 5
Simply the
      best  August 15, 2002
Reviewer:  Sajid Anwar
       from London
Hi Tom,
I never saw such a to the point answer before. You guys are simply the best.

Keep the good work going.
All the best.

Regards,
Sajid Anwar

      Rating: 2
But still this
      query doesn't exclude ids that are already dequeued but not
      committed.  August 15, 2002
Reviewer:
      Muraleedaran  from Boston
Tom & Julie, Thank you very much for the quick response. But  could you clarify
the below on the same subject.?

I have 400 entries in the Q table and I dequeued 2 entries from Q and left the
session open ( Neither committed nor rolled back ).

When I fired the query given by you,

select * from MyQTable
where q_name = 'MYQ' and msg_state = 'READY'

it sill says 400 entries are in 'READY' state. But what i expected is 398 ( as 2
entries are already dequeued but not commited ).

In my set up, the Ids are cleared from Q only on commit.

Please throw some light on this.

Thanks much in advance.. Muralee

          Followup:

While Julie looks into this I can offer this up.

Your "dequeuers" should use dbms_application_info.set_client_info( 'MYQ Process'
) to set a column in v$session to indicate they are a MYQ de-queuer.  You can
then:

select count(*)-cnt
  from myQtable, (select count(*) cnt
                    from v$session
                   where client_info = 'MYQ Process' )
 where q_name = 'MYQ'
   and msg_state='READY'
/

for it should be safe to assume that if there are ready messages and de-queuers
logged in, they are processing a message.

You would want to use dbms_application_info to register these guys anyway....

      Rating: 2
Dont forget the
      queue-table type  August 07, 2003
Reviewer:
      Gautam Sharma  from UK
Its not just the fact that queues-messages which are being currently processed
that show up in the count;
...Persistence of records in the queue-table also depends on whether the queue
is a MCQ or SCQ.
(multiple_consumers=TRUE or FALSE)

August 15, 2002 -- Thanks for the question regarding "Create
      Function based index with multiple columns", version 8.1.7

      You Asked
Does Oracle 8.1.7 support creating a function based index on a user defined
function that accepts _more_than_one_ parameter(all parameters being column
names of the same table)? if so, could you please give an example ddl?

      and we said...
Yes.

ops$tkyte@ORA817DEV.US.ORACLE.COM> ops$tkyte@ORA817DEV.US.ORACLE.COM> create
table t ( x int, y int, z int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx on t(x+y+z);
Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function f( px in number,
py in number , pz in number ) return number
  2  DETERMINISTIC
  3  as
  4  begin
  5     return px+py*pz;
  6  end;
  7  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx2 on t(f(x,y,z));
Index created.

Review &
      Followup

      Rating: 4
ddl for
      function based index with multiple columns  August 15, 2002

Reviewer:  Vijay  from Bridgeport, CT USA
Thanks for the input Tom. Using the syntax you provided, I tried to create the
index, but I got a ERROR --
ORA-00904: invalid column name. So I removed the parentheses that encloses the
function name. And it then gave me a message saying that I cannot create index
on those columns as there is already an existing index on them. (The parameters
of this function are composite primary keys, so the table already has a index on
those columns by virtue of the composite primary key). Since this entry is
supposed to be details of the review let me not pose more questions here. I
found the answers very useful. Crisp and clear. Just what developers need,
without having to go through too much to get the right information, real quick!

          Followup:

Hey, how about an EXAMPLE????

and versions....

      Rating: 5
Great Example!!
        August 16, 2002
Reviewer:  Yogeeraj
       from Mauritius
hello,

works fine for me ;)

============================================================
yd@CMTDB.CMT.MU> drop table t;

Table dropped.

Elapsed: 00:00:02.62
yd@CMTDB.CMT.MU> create table t (x int, y int, z int);

Table created.

Elapsed: 00:00:00.13
yd@CMTDB.CMT.MU> create index t_idx on t(x+y+z);

Index created.

Elapsed: 00:00:00.11
yd@CMTDB.CMT.MU> create or replace function f( px in number,
  2  py in number , pz in number ) return number
  3  DETERMINISTIC
  4  as
  5  begin
  6     return px+py*pz;
  7  end;
  8  /

Function created.

Elapsed: 00:00:02.28
yd@CMTDB.CMT.MU> create index t_idx2 on t(f(x,y,z));

Index created.

Elapsed: 00:00:00.05
yd@CMTDB.CMT.MU> exit
Disconnected from Oracle8i Enterprise Edition Release 8.1.7.4.0 - 64bit
Production
With the Partitioning option
JServer Release 8.1.7.4.0 - 64bit Production
L-oracle8i: home/deg>
============================================================

Best Regards
Yogeeraj

August 15, 2002 Azman -- Thanks for the question regarding
      "Truncate table by non table non-owner", version 8.1.7

      You Asked
Hi,
I would like to fing whats the best way to allow user to truncate table that is
own by some one else thru forms on-line basis. I know that one way this can be
achived is by granting drop any table system previledge. However this previledge
is to distructive as its has other serious security implications. Is thsre any
other way to just give the user truncate table previledge.

      and we said...
Sure, this is what stored procedures are all about.

To selectively give someone the abilitly to truncate a specific table, or all of
the tables owned by some schema, you would code:

create or replace procedure do_the_truncate
as
begin
   execute immediate 'truncate table T';
end;

or (any of the tables owned by some schema, or if that schema has the drop any
table priv ANY table)

create or replace procedure do_the_truncate( p_tname in varchar2 )
as
begin
   execute immediate 'truncate table ' || p_tname;
end;

and then just grant execute on that procedure to any user that needs to run that
command.  Since stored procedures run with the base privs of the OWNER of the
procedure, you do not need any powerful privs like "drop any table" to truncate
that table.

Review &
      Followup

      Rating: 5
base
      privs  September 23, 2001
Reviewer:
      A reader
Tom

"Since stored procedures run with the base privs of the OWNER of the procedure"

Can you elaborate on your above statement

          Followup:

It is known as "definers rights" (there is another mode procedures may run in
"invokers rights" as well but definers is the default).

This is one of the most powerful features of a stored procedure -- it is what
makes them more then useful.  A stored procedure by default runs with the
privilege set (minus roles) of the DEFINER of the procedure.  This means I can
write a procedure like:

create procedure insert_into_t( p_data in varchar2 )
as
begin
    -- whatever logic I want to code to ensure the current USER is
    -- allowed to insert

    -- whatever complex validation of data I want to do
    -- whatever auditing I want to do

    insert into t values ( p_data );
end;

And then grant execute on insert_into_t to SCOTT.  Now SCOTT can insert into
table T via this procedure -- but SCOTT cannot insert into T directly (and hence
bypass my authorization logic, my validation, my auditing and so on).

You can take this a step further with REF CURSORS -- allowing the user to get an
OPEN cursor to a result set you want them to see -- without having to give them
TOTAL select.  For example you might code:

create procedure get_emp_data( p_cursor in out types.rc_type )
as
begin
    if ( user in ( 'ADMIN', 'TKYTE', 'SYSTEM' ) )
    then
         open p_cursor for select ename, sal, job from emp;
    else
         open p_cursor for select ename, to_number(null) sal, job from emp;
    end if;
end;
/

Now, if the user is one of the "special" users - they can see the sal column,
else they just see NULL.

      Rating: 3
Reply for
      Review 1  September 24, 2001
Reviewer:
      Senthil  from OH USA
The stored procedure runs with the privilege of the table owner it will trucante
the table. Since the other user
is granted with the  execute privilege on the procedure, when the procedure
executed the table will get truncated.

      Rating: 3
What if a
      stored procedures is to be created with their by someone other then
      owner  August 15, 2002
Reviewer:
      A reader
Say i have a user a who is the owner of all objects
I use only user b to create objects(stored procedure, functions, seqeunces,
table) etc for the user a
This case ussually happens when u have external consultants doing development
and you can't give them the object owner id and password , but still allow them
to create objects in user a and audit all their statements

          Followup:

same answer.

create or repalce procedure create_a_procedure_for_a( p_stmt in varchar2 )
as
begin
  execute immediate 'create or replace procedure ' || p_stmt;
end;
/

create execute on create_a_procedure_for_a to b;

as b:

begin
   create_a_procedure_for_a( '
p
as
begin
   dbms_output.put_line( ''Hello there'' );
end;
');

end;

Or, have them create/test the code in their own database (as A) and then pass
the code along to you for installation -- so you know you can

a) install it
b) have the code for when they walk away and still be able to install it

I like the second option personally.

(and if I was a smart consultant, I would be able to gain access to A's account
in about 30 seconds if you grant me execute on create_a_procedure_for_a but that
is an exercise left to the reader)

August 15, 2002 yuefu -- Thanks for the question regarding "how
      to get next record from a "resultset"?", version 8.1.7

      You Asked
I want to coding a function (e.g called fun_query)which can visit database and
return a "resultset", such as a pointer to it, and other functions can call it
to get the record from the "resultset" without visiting the database.

it's something like the functions in MySQL:
mysql_real_query
mysql_store_result
mysql_fetch_row

thanks :)

      and we said...
Not being a MYSQL programmer -- and not getting a very clear description of the
needs in the question - I can only point you to:

http://asktom.oracle.com/~tkyte/ResultSets/index.html
which shows how to use plsql to return a result set.  Getting a result set
MANDATES a visit to the database (i'd love to hear how mysql can get a result
without doing so).
Review & Followup

      Rating: 5
you did help
      me  August 15, 2002
Reviewer:  yuefu
       from China
you did help me.
thank u very much.:)

August 16, 2002 -- Thanks for the question regarding "running
      report's priviewer maximized.!", version 8.0

      You Asked
Hello sir,

I would like to know 1) whenever I run reports from forms I
would like to run it in maximize state. what I have to do ?

2) I have a table with values
range1  range2  ratio
-----  -----   ----
1000    2000   1.5
2000    3000   1.6
.
.
'ALL'   'ALL'  2.5

This values willbe displayed in a form at form startup.  That
user can delete any record and insert a new record.  But I would
like restrict the user from deleting the record 'ALL'. Is it
possible ?

thanks

Best regs
Abdullah

      and we said...
#1, you should be able to create a parameter block to be passed
in with run_Product that includes "maximize=yes".  maximize is a
parmeter the reports runtime understands (see the reports
documentation for all valid parameters you can use to control
reports behaviour)

#2, use a WHEN-NEW-RECORD-INSTANCE on that block.  Lets say you
have a block on DEPT and you don't want them to delete deptno =
10, you would code:

if ( :dept.deptno = 10 )
then
    set_block_property( 'dept', DELETE_ALLOWED,
                             PROPERTY_FALSE );
else
    set_block_property( 'dept', DELETE_ALLOWED,
                             PROPERTY_TRUE );
end if;

Review &
      Followup

      Rating: 1
"running
      report's priviewer maximized.!"   August 12, 2002

Reviewer:  AAMIR HUSSAIN  from PAKISTAN
Question NO 1 .PLEASE explain in detail.

          Followup:

umm, how more detailed can I get?

      Rating:
      2
  August 15, 2002
Reviewer:  AAMIR
      HUSSAIN  from PAKISTAN
where can i create this parameter block. ?

          Followup:

It is a forms builtin, look up "parameter" in the docs.

      Rating: 5
Where is this
      parameter?  August 15, 2002
Reviewer:  Sikandar
      Hayat Awan  from Pindi Bhattian - Pakistan
PROCEDURE RUN_PRODUCT
(product   NUMBER,
module  VARCHAR2,
commmode  NUMBER,
execmode  NUMBER,
location  NUMBER,
paramlist_id      VARCHAR2,
display   VARCHAR2);

I con't find maximize parameter.

          Followup:

You create a named parameter block (see the CREATE_PARAMETER_LIST builtin)

You use ADD_PARAMETER to add the maximize = yes

You pass the name of the parameter block you just created to run_product.

      Rating: 5
I think this is
      the code  August 15, 2002
Reviewer:  Claudio
      Biale  from Colonia Liebig, Corrientes, Argentina
DECLARE
     PL_ID PARAMLIST;
BEGIN
     PL_ID := GET_PARAMETER_LIST('TMPDATA');
     IF NOT ID_NULL(PL_ID) THEN
        DESTROY_PARAMETER_LIST(PL_ID);
     END IF;
     PL_ID := CREATE_PARAMETER_LIST('TMPDATA');

     -- add the parameter

     ADD_PARAMETER(pl_id,'MAXIMIZE',TEXT_PARAMETER,'YES');

     -- run report (product = REPORT,
     --             module = Name of report,
     --             commode = SYNCHRONOUS or ASYNCHRONOUS,
     --             execmode = BATCH or RUNTIME,
     --             location = FILESYSTEM or DB,
     --             Paramlist_name or paramlist_ID,
     --             dysplay = ¿?)

     RUN_PRODUCT(REPORTS,'EXAMPLE.REP',ASYNCHRONOUS,
                 RUNTIME,FILESYSTEM,PL_ID,NULL);
END;

          Followup:

thanks! that looks good.

      Rating: 5
Thanks but
      still problem is there...  August 15, 2002

Reviewer:  Sikandar Hayat Awan  from Pindi Bhattian -
      Pakistan
I have tried your code in Dev2000 but no success as this is not documented and
if documented I am still unable to reach those lines.

In the online help I found the following,

Description  MAXIMIZE specifies .whether to maximize the Runtime Previewer.
Syntax

[MAXIMIZE=]{YES|NO}
Values
YES or NO
Default
NO
Usage Notes   This keyword is not valid on Motif platforms, due to limitations
in Motif.

          Followup:

Umm, what problem?

The API's listed above, create parameter list, add parameter, have been around
for a while in FORMS.

      Rating: 5
? ? ? ?
      ?  August 16, 2002
Reviewer:  Riaz Shahid
       from PRAL, Islamabad, Pakistan
AOA Mr. Sikander !

I couldn' understand what r u talking abt ? What additional info you need in
this regard ? The solution is
*right there* and is correct (100%). If it is not, then please tell me the error
you are receiving.

Thanks

Riaz

      Rating: 5
Sorry for
      again  August 16, 2002
Reviewer:  Sikandar Hayat
      Awan  from Pindi Bhattian - Pakistan
I have used the following code and received no error but report is not
maximized.

DECLARE
     PL_ID PARAMLIST;
BEGIN
     PL_ID := GET_PARAMETER_LIST('TMPDATA');
     IF NOT ID_NULL(PL_ID) THEN
        DESTROY_PARAMETER_LIST(PL_ID);
     END IF;
     PL_ID := CREATE_PARAMETER_LIST('TMPDATA');

     ADD_PARAMETER(pl_id,'MAXIMIZE',TEXT_PARAMETER,'YES');

     RUN_PRODUCT(REPORTS,'O:\Softwares\AMS\REPORTS\REM_CODE.REP',ASYNCHRONOUS,
                 RUNTIME,FILESYSTEM,PL_ID,NULL);
END;

I am using Dev 2000 and Reports 3.0 forms 5. I don't know what thing I am
missing?

      Rating: 5
Sorry but I use
      Forms 6.0 and Report 6.0  August 16, 2002

Reviewer:  Claudio Biale  from Cnia. Liebig -
      Argentina
I dont know if the code work whit forms 5 and Report 3

      Rating:
      5
  August 16, 2002
Reviewer:  AAMIR
      HUSSAIN  from PAKISTAN
Thanks Mr. Claudio Biale . thats what i want.........

      Rating: 2
For Mr.
      Sikander Hayat  March 03, 2003
Reviewer:  Riaz
      Shahid  from PRAL, Islamabad, Pakistan
AOA Mr. Sikander Hayat !

First there is a suggestions:
(1) NEVER use path while calling forms/reports...NEVER. what if u'll have to
copy this report on another drive? And what if u copy this S/W (including thjis
report) to client side and ur client has no O drive ???

(2) Don't use .rep extension while calling reports.

Any how, here's routine i use to call report from forms:

declare
 pl_id ParamList;
 FileExt varchar2(10);
 od varchar2(17);
 a varchar2(10);
BEGIN

  pl_id := Get_Parameter_List('tmpdata');
  IF NOT Id_Null(pl_id) THEN
    Destroy_Parameter_List( pl_id );
  END IF;

    pl_id := Create_Parameter_List('tmpdata');
   Add_Parameter(pl_id, 'type', TEXT_PARAMETER, :type);
   Add_Parameter(pl_id, 'ph_no', TEXT_PARAMETER,:ph_no);
   Add_Parameter(pl_id, 'maximize',TEXT_PARAMETER, 'YES');

  Run_Product(REPORTS, :parameter.report_name, SYNCHRONOUS, RUNTIME,FILESYSTEM,
pl_id, NULL);
END;

It works in all versions of forms/reports.

Regards

August 16, 2002 Rob -- Thanks for the question regarding "Static
      linking in pro*c", version 9.2

      You Asked
Hi Tom:

I have a pro*c program that was compiled under 9i. I want to be able to run it
from an account that has it's environment pointing to an 8i home. I think it was
linked shared so that when run under the 8i home it complains that it can't find
a shared object. Will static linking allow it to run in the 8i home. What option
should the make file have.

Thanks as always.

Rob

      and we said...
You cannot do that.  It is more then libraries.  It is message files and other
support files.

You will either have a pro*c program compiled in 8i that'll use net8 to talk to
9i and an oracle home pointing to 8i

or

You will have the oracle home pointing to 9i.

Review &
      Followup

      Rating:
      5
  August 16, 2002
Reviewer:
      A reader

August 16, 2002 Sudhanshu -- Thanks for the question regarding
      "Controlling Grant of Create Table", version 8.1.7

      You Asked
Hi

In my Database having 3 user A,B,C. I want to restrict that table can be created
in schema A by user B but not C.

Is there something in between "Create table" in my own schema and "Create any
Table"?

In my production environment, there are some repairs(DML from sql) required how
can i make an audit/trace as to what is getting executed.

Thanks

Sudhanshu

      and we said...
I would create a procedure in schema A called "create_table".  It would look
like:

create procedure create_table( p_stmt in varchar2 )
as
begin
   execute immediate 'create table ' || p_stmt;
end;

You would grant CREATE TABLE directly to A -- not via a role, read:

http://asktom.oracle.com/~tkyte/Misc/RolesAndProcedures.html
then grant execute on create_table to B but not C.

Now B can:

exec a.create_table( 'foo ( x int )' );

Review &
      Followup

      Rating: 2
Yes a smart
      workable solution  August 16, 2002
Reviewer:
      Sudhanshu  from India
Hi Tom

Yes this is infact a solution. but why Oracle does not allow object creation
privileges from 1 user to another.

          Followup:

I don't know, I've never sat around and talked about this with anyone.  Part of
the answer is "we do, that is what stored procedures are all about".

      Rating: 5
Not just
      workable, it is creative.  August 16, 2002

Reviewer:  Mark Manning  from Atlanta GA
How boring would it be if Oracle just gave you a "one size fits all/must do it
this way" answer like some other "databases"?  Personally, I like the fact that
Oracle provides you with the raw materials to exercise one's creativity and
imaginaiton in developing solutions whether it is for redundant operations
(management of tables/indexes, etc) to complex custom solutions (data
warehouses, ERP, CRM, etc).  After 8 years of working on Oracle, I thought I hit
the "creativity ceiling", then Oracle 8i came out.  The analytic funtions alone
will probably be exercising my creative juices for another 12 years.

August 16, 2002 farhan -- Thanks for the question regarding
      "Monitor the Program Execution.", version 8.1.7

      You Asked
Hi Tom,

 How can you monitor and analyze a program execution (procedure)?
 Please give a small example?

      and we said...
See the DBMS_APPLICATION_INFO package -- it is very useful for doing this.

If you have my book, "Expert one on one", I have a good section on that in there
with tons of examples.

You can search for dbms_application_info on this site for examples.

You can read:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76936/dbms_app.htm#999107

August 16, 2002 Kandasamy -- Thanks for the question regarding
      "Regarding Pro *C", version 9.2.0

      You Asked
Q1. Who is supplying the Pro*C tool?
Q2. What are the steps involved in Pro*C from Source code (.pc) to get the data
from database? I need the conversion of .pc to executable.
Q3. Will OOPS concept improve the performance of the application?

      and we said...
q1) that would be Oracle
q2) write the .pc file
    precompile the .pc file with proc
    compile the .c file into object code and link it into an executable

whoops -- forgot step zero, read the documentation

q3) doubtful.  OOPS is for the ease and convienence of the programmer, not the
compiler, not the machine.  OOPS generally (remember folks, tom said GENERALLY)
implies "slower".
Review & Followup

      Rating: 5
Step
      Zero  August 16, 2002
Reviewer:  Mark A.
      Williams  from Indianapolis, IN USA
Kandasamy:

FYI...

Step Zero is all right here:

http://otn.oracle.com/docs/products/oracle9i/doc_library/release2/appdev.920/a97269/toc.htm
- Mark

August 16, 2002 Ashish -- Thanks for the question regarding "How
      to find, how many open_cursors active at a particular time", version 8.1.6

      You Asked
Hello Tom,

I was wondering if there is any query to find out the
number of OPEN_CURSORS active at a particular time,
in the database

When I say active I mean , if you look at some of the
data dictionary views $open_cursors it will show all
the cursors ,including ones which are not active anymore.

The reason I ask is because I used to get the
running out of OPEN_CURSORS errors will running
the application against the database (8.1.6.0).

The db OPEN_CURSORS parameter was set to 500 (which is
quite high,considerig it is per process).

The application was written in Java and was using JDBC.

Thanks

      and we said...
Well, I have my open_cursors set to 1000.  Cursors are allocated
64 at a time upto OPEN_CURSORS so having it set high it OK.  You
use what you need and if you don't need them all -- they aren't
there.

select * from v$sysstat where statistic# = 3;

will get you the total number of current open cursors in the
system.

select * from v$sesstat where statistic#= 3;

will show you by session...

select * from v$mystat where statistic# = 3;

will show you YOUR open cursor count.

Review &
      Followup

      Rating: 4
<>
      between mystat and sesstat  August 16, 2002

Reviewer:  Alex Daher  from Brazil
Tom, is there any difference between V$MYSTAT and V$SESSTATS where SID = "my
SID"?

I don't understand this:

system@ORACLE8> select * from v$sesstat where sid = 59 minus select * from
v$mystat;

       SID STATISTIC#      VALUE
---------- ---------- ----------
        59         15      72168
        59         16      85040
        59        188         20
        59        190         39

Elapsed: 00:00:00.01
system@ORACLE8> select * from v$mystat minus select * from v$sesstat where sid =
59;

       SID STATISTIC#      VALUE
---------- ---------- ----------
        59         15      72168
        59        188         22
        59        190        493

Elapsed: 00:00:00.01

          Followup:

They are the same -- when you understand whats going on under the covers,
this'll be an "ah hah" sort of question.

See

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76961/ch3.htm#5014
which says in part:

<quote>
About Dynamic Performance Views

Oracle contains a set of underlying views that are maintained by the server and
accessible to the database administrator user SYS. These views are called
dynamic performance views because they are continuously updated while a
database is open and in use, and their contents relate primarily to performance.

Although these views appear to be regular database tables, they are not.
These views provide data on internal disk structures and memory structures. You
can select from these views, but you can never update or alter them.

Notes:

    * You can query the dynamic performance views to extract information from
them. However, only simple queries are supported. If sorts, joins, GROUP BY
clauses and the like are needed, you should copy the information from each V$
view into a table (for example, using a CREATE TABLE ... AS SELECT statement),
and then query from those tables.

* Because the information in the V$ views is dynamic, read consistency is not
guaranteed for SELECT operations on these views.
....
</quote>

That last part -- about read consistency -- is the kicker.  I've modified your
queries a tad so we can see the stats that were different:

ops$tkyte@ORA817DEV.US.ORACLE.COM> column sid new_val s
ops$tkyte@ORA817DEV.US.ORACLE.COM> select sid from v$mystat where rownum=1;

       SID
----------
        18

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value
  2    from v$statname a, v$sesstat b
  3   where a.statistic# = b.statistic#
  4     and b.sid = &S
  5  MINUS
  6  select a.name, b.value
  7    from v$statname a, v$mystat b
  8   where a.statistic# = b.statistic#
  9  /
old   4:    and b.sid = &S
new   4:    and b.sid =         18

NAME                                VALUE
------------------------------ ----------
session pga memory                 119152
session pga memory max             119152
session uga memory                  53456
session uga memory max              57900
sorts (memory)                          0
sorts (rows)                            0

6 rows selected.

what that is saying is the when we processed the v$sesstat table, we were
using 119,152 bytes of PGA memory.  When we processed v$mystat -- we had some
DIFFERENT amount of PGA memory -- by the time we got there, it was different,
hence the minus shows us the row from v$sesstat.

Now, the sorts are just the same -- you KNOW a minus needs a sort right (no
indexes here).  Well, when we scanned v$sesstat, we hadn't sorted yet.  By the
time we got to v$mystat -- WE HAD

ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value
  2    from v$statname a, v$mystat b
  3   where a.statistic# = b.statistic#
  4  MINUS
  5  select a.name, b.value
  6    from v$statname a, v$sesstat b
  7   where a.statistic# = b.statistic#
  8     and b.sid = &S
  9  /
old   8:    and b.sid = &S
new   8:    and b.sid =         18

NAME                                VALUE
------------------------------ ----------
session uga memory                  53456
sorts (memory)                          6
sorts (rows)                         1416

ops$tkyte@ORA817DEV.US.ORACLE.COM>

what that shows us is the memory usage sort of settled down, little different
UGA numbers but that is it.  Again the sort numbers come out but that is
expected -- the first query must have done 4 sorts of 1,416 rows and by the time
we got to v$sesstat in this query -- we had done more!

Sort of 'neat', unexpected maybe -- but 'neat'.

Here is the topper:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.statistic#, a.value, b.value
  2  from v$mystat a, v$mystat b
  3  where a.statistic# = b.statistic#
  4  and a.value != b.value;

STATISTIC#      VALUE      VALUE
---------- ---------- ----------
        15      61728      53456
       188         13         12
       190       3072       2832

Hmmmm

      Rating: 5
Hummm...
      ah-hah  August 19, 2002
Reviewer:  Alex
      Daher  from Brazil
Right!!! Now It's much clearer... :-)

Thank you again, Tom!

      Rating: 4
Very
      interesting...  August 19, 2002
Reviewer:
      A reader

August 16, 2002 Anil Lancy -- Thanks for the question regarding
      "Session ", version Oracle 8.1.5

      You Asked
Dear Tom

We are using Oracle 8i as a database for web development using a
IAS server as the middle layer and Active server pages as our
front end. The moment we disconnect a session and query the
v$session we can see that the session is getting disconnected
and shows as inactive but why it is still there.

At the same time I try to do from Oracle sqlplus and disconnect
the session entry is deleted, is there any solution that you can
help.

Regards
Anil Lancy Sequeira

      and we said...
We use connection pooling in the middle tier to avoid the high
cost of physically connecting/disconnecting.  the middle tier is
doing a dbms_session.reset_package call to reset the session
state without tearing it down.

If you wait long enough, the sessions will "time out" and go
away.

It is for performance, it makes a *huge* difference.  The time
to logon could be longer then the time to generate your page.
Review &
      Followup

      Rating:
      4
dbms_session.reset_package with VPD?  February 16,
      2002
Reviewer:  Jack Wells  from Boca Raton, FL USA

We've found that we have to turn off connection pooling for mod_plsql apps that
use VPD / FGAC because the context doen't get reset consistently (bug?).  Would
using the dbms_session.reset_package before "setting context" enable us to turn
connection pooling back on?

          Followup:

That will not reset the context, just de-instantiate PLSQL code.

Your routine that sets up the context could be responsible for clearing it
first.  In that fashion, you can use connection pooling again.

      Rating:
      5
session  August 16, 2002
Reviewer:
      Mo  from Reston, USa
Tom:

How dod you do this in 8i

thanks

          Followup:

same as in 9i.  The same as I described above.

      Rating: 5
Dedicated
      turned into Shared connections  August 16, 2002

Reviewer:  Alexandre  from Brasil
I have two machines, on of them running IAS9i and the other running Oracle
Database 8.1.7.3.0 EE (MTS configuration).
I´ve upgraded from OAS 4.0.8 to IAS9i. I did not make any modifications at
database connection files (tnsnames.ora, sqlnet.ora) nor even to these files on
the machine running IAS9i, and after this upgrade all the connections turned
from SHARED to DEDICATED consuming lots of machine memory resources. I do not
have (on application side) no one tnsnames entry configured explicitly with
SERVER=DEDICATED.
If these information is sufficient to know what is going on, I´d like to know if
there is any parameter in Apache files to turn client connections back to SHARED
on database...

Thanks a lot

          Followup:

It is all in the tnsnames.ora file, server=shared should be part of the tns
connect string.  check it out.  (ias undoubtably is in a different oracle home
then from before, make sure you are using the RIGHT tnsnames as well)

      Rating: 4
Dedicated
      turned into Shared connections   August 16, 2002

Reviewer:  Alexandre  from Brasil
Yes, we are using the correct tnsnames.ora but I am not expliciting
SERVER=SHARED in tnsnames entry because, as I said, we used the same
tnsnames.ora file with OAS 4.0.8 and all client conections used to be SHARED...
Do I have to explicit SERVER=SHARED at tnsnames.ora file entry working with
IAS9i to turn DEDICATED client connections to SHARED client connections on a
database configured as MTS ???

Thanks again...

Alexandre

          Followup:

Try it -- see if it solves your issue.

August 17, 2002 Ruben -- Thanks for the question regarding
      "Parameter p1,p2,p3", version 8.1.7.3

      You Asked
Hi tom ,

if we have next excerpt from trace file :

WAIT #41: nam='db file sequential read' ela= 0 p1=5 p2=15468 p3=1
WAIT #41: nam='db file sequential read' ela= 0 p1=5 p2=15704 p3=1
WAIT #41: nam='db file sequential read' ela= 0 p1=5 p2=15965 p3=1
WAIT #41: nam='db file sequential read' ela= 0 p1=5 p2=15967 p3=1
WAIT #41: nam='db file sequential read' ela= 0 p1=5 p2=15969 p3=1
WAIT #41: nam='db file sequential read' ela= 0 p1=5 p2=15978 p3=1
WAIT #41: nam='db file sequential read' ela= 0 p1=5 p2=15987 p3=1
WAIT #41: nam='db file sequential read' ela= 0 p1=5 p2=16014 p3=1
WAIT #41: nam='db file sequential read' ela= 0 p1=5 p2=16023 p3=1

I know that parameter p1 is the file#, parameter p2 is the block# and
parameter p3 is number of blocks read.

if p1 belongs to /app/oradata/prod001/master_01.dbf, and we
have following tables in there : table_a, table_b and table_c.
How could I know what table belongs what block (p2)?

thanks !!

      and we said...
see dba_extents

select owner, segment_name
 from dba_extents where file_id = P1
   and p2 between block_id and block_id+blocks;
August 17, 2002 Logan -- Thanks for the question regarding "Size
      of SQL*AREA in shared_pool", version 8.1.6

      You Asked
Tom,

The information below is from an Oracle Apps 11i database. The size of SQLAREA
is around 800MB out of a total shared_pool of 1GB. 800MB sounds quite huge to
me? Is it because we are not using bind variables in any of custom programs? How
do I find it out?

SQL> show sga

Total System Global Area 1323839168 bytes
Fixed Size                    69312 bytes
Variable Size            1158340608 bytes
Database Buffers          163840000 bytes
Redo Buffers                1589248 bytes
SQL> select * from v$sgastat;

POOL        NAME                            BYTES
----------- -------------------------- ----------
            fixed_sga                       69312
            db_block_buffers            163840000
            log_buffer                    1572864
shared pool free memory                 112939064
shared pool miscellaneous                 2178508
shared pool PL/SQL DIANA                 17901772
shared pool State objects                  411464
shared pool PL/SQL MPCODE                36041952
shared pool PX msg pool                    193488
shared pool KQLS heap                    20644928
shared pool PX subheap                      22092
shared pool trigger defini                 119028
shared pool enqueue_resources              360000
shared pool PLS non-lib hp                   2096
shared pool table definiti                  57636
shared pool message pool freequeue         231152
shared pool joxlod: in ehe                  82144
shared pool type object de                  16332
shared pool KGK heap                        15096
shared pool joxs heap init                   1232
shared pool table columns                   68408
shared pool transactions                   392480
shared pool db_block_buffers              2720000
shared pool dictionary cache             14626848
shared pool fixed allocation callback         960
shared pool view columns d                   4996
shared pool trigger inform                   1360
shared pool KGFF heap                      191944
shared pool library cache                84392088
shared pool sql area                    811576440
shared pool processes                      230400
shared pool sessions                       844800
shared pool db_block_hash_buckets          451144
shared pool ktlbk state objects            188320
shared pool trigger source                  34544
shared pool event statistics per sess     1356800
java pool   free memory                  46067712
java pool   memory in use                 3936256

38 rows selected.

      and we said...
Apps is a different beast -- due the sheer volume of PLSQL and SQL is uses, the
shared pool in that environment tends to be large.

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1163635055580
for a technique to discover if you are missing bind variables in your
applications.
August 17, 2002
      -- Thanks for the question regarding
      "ANALYZE PL/SQL CODE ", version 8.1.7

      You Asked
Hi Tom,

For analyzing a SQL we can use set autotrace on to figure out the explain plan .
Is there any tool available which  we can use to get the explain plan for a
script containing pl/sql block (other than tkprof )  .

      and we said...
DBMS_PROFILER is a source code profiler builtin to the database.

Search this site for

dbms_profiler

for examples.  Or if you have my book "Expert one on one" I have a full section
on using it in there as well.

It is not the same as "explain plan" for sql -- but only because it is "code",
not sql.  It is for PLSQL what sql_trace+tkprof are for SQL.
August 17, 2002 Nandagopal -- Thanks for the question regarding
      "Sample Pro*C/C++ Compile Error", version 8.1.7

      You Asked
HiTom,

I am trying to compile the Sample program of Pro*C from Sun salaries after
installing Oracle Pro*C and getting the following errors.

In Sun the default gcc is installed in /opt/sfw director and
not installed any optional package in SUN.

Can you please help me as how to Successfully compile this Pro*C sample program.

Thanks you very much.

gcc -xO2 -Xa xstrconst -XF -mr -xarch=V8 -xcache=16/32/1:1024/64/1
xchip=ultra -D_REENTRANT -K PIC _DPRECOMP -I.
I/oracle/app/oracle/product/8.1/precop/public
I/oracle/app/oracle/product/8.1/rdbms/public
I/oracle/app/oracle/product/8.1/rdbms/demo
I/oracle/app/oracle/product/8.1/plsql/public
I/oracle/app/oracle/product/8.1/network/public
DSLMXMX_ENABLE -DSLTS_ENABLE -D_SVID_GETTOD -C sample1.c
gcc: PIC: No such file or directory
gcc: unrecognized option '-Xa'
gcc: unrecognized option '-K'
gcc: language chip=ultra not recognized
***Error code 1
make: fatal error: Command failed for target 'sample1.o'
Current Working directory /oracle/app/oracle/product/8.1/precop/demo/proc
***Error code 1
make: fatel error: Command failed for target 'sample1'

      and we said...
The supported C compiler on Sun is the optional Sun package SPARCWorks.  The
makefiles we ship are setup for THAT compiler.

Now, that aside, I use gcc myself like this on solaris:

$ make
proc ireclen=255 lines=yes
include='(/export/home/ora817/proc/lib,/export/home/ora817/rdbms/demo,/export/hom
e/ora817/rdbms/public)' iname=t

Pro*C/C++: Release 8.1.7.2.0 - Production on Sat Aug 17 09:35:46 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

System default option values taken from:
/export/home/ora817/precomp/admin/pcscfg.cfg

gcc -I. -I/export/home/ora817/precomp/public -g
-I/export/home/ora817/rdbms/demo -I/export/home/ora817/rdbms/public
-I/export/home/ora817/proc/lib -c t.c

gcc -o t -L/export/home/ora817/precomp/lib/ -L/export/home/ora817/lib/ -t -o t
\
t.o -L/export/home/ora817/lib -lclntsh   `cat
/export/home/ora817/lib/sysliblist` -R/export/home/ora817/lib -laio    -lm
-lthread
ld: warning: option -o appears more than once, first setting taken

Review &
      Followup

      Rating:
      5
  March 14, 2003
Reviewer:
      A reader  from UK

August 17, 2002 chirayu -- Thanks for the question regarding
      "displaying only columns with values in them", version 8.1.6

      You Asked
Dear Tom,
   I have a empmast table having some 100 odd columns in it.
   Many of the table column are empty for most of the employees.
   Can I write a sinqle query in such a way that only the columns
   that have values in it are displayed.

   for eg. say for empno = 100.
     Of the 100 columns that I have the details availabe to me
     are only of empno, firstnm, middlenm, surname, marital_status columns.
     The rest of the columns are null.

   Here I would like to run a query in such a way that only these columns
   are displayed for this empno. for an other empno there might be some
   more details available.  what i want is my query should dynamically
   display all the columns who have some values in it.

   Thanx in advance
   chirayu sutaria

      and we said...
There is no "query" that can do this (every row has a fixed number of columns --
the same as the row before it and the row after it)

This is a problem for the thing that prints the result set to deal with.  In
sqlplus, you can use this technique (which also has the nice effect of printing
DOWN the page, great for wide results)

scott@ORA817DEV.US.ORACLE.COM> create or replace procedure print_table( p_query
in varchar2, p_nulls in boolean default true )
  2  AUTHID CURRENT_USER
  3  is
  4      l_theCursor     integer default dbms_sql.open_cursor;
  5      l_columnValue   varchar2(4000);
  6      l_status        integer;
  7      l_descTbl       dbms_sql.desc_tab;
  8      l_colCnt        number;
  9  begin
 10      execute immediate
 11      'alter session set
 12          nls_date_format=''dd-mon-yyyy hh24:mi:ss'' ';
 13
 14      dbms_sql.parse(  l_theCursor,  p_query, dbms_sql.native );
 15      dbms_sql.describe_columns
 16      ( l_theCursor, l_colCnt, l_descTbl );
 17
 18      for i in 1 .. l_colCnt loop
 19          dbms_sql.define_column
 20          (l_theCursor, i, l_columnValue, 4000);
 21      end loop;
 22
 23      l_status := dbms_sql.execute(l_theCursor);
 24
 25      while ( dbms_sql.fetch_rows(l_theCursor) > 0 ) loop
 26          for i in 1 .. l_colCnt loop
 27              dbms_sql.column_value
 28              ( l_theCursor, i, l_columnValue );
 29                          if ( p_nulls OR l_columnValue is not null )
 30                          then
 31                  dbms_output.put_line
 32                  ( rpad( l_descTbl(i).col_name, 30 )
 33                  || ': ' ||
 34                  substr( l_columnValue, 1, 200 ) );
 35                          end if;
 36          end loop;
 37          dbms_output.put_line( '-----------------' );
 38      end loop;
 39      execute immediate
 40          'alter session set nls_date_format=''dd-MON-rr'' ';
 41  exception
 42      when others then
 43        execute immediate
 44            'alter session set nls_date_format=''dd-MON-rr'' ';
 45        raise;
 46  end;
 47  /

Procedure created.

scott@ORA817DEV.US.ORACLE.COM> drop table t;

Table dropped.

scott@ORA817DEV.US.ORACLE.COM> create table t ( x int, y int, z int );

Table created.

scott@ORA817DEV.US.ORACLE.COM> insert into t values ( 1, null, null );

1 row created.

scott@ORA817DEV.US.ORACLE.COM> insert into t values ( 1, 1, null );

1 row created.

scott@ORA817DEV.US.ORACLE.COM> insert into t values ( 1, 1, 1 );

1 row created.

scott@ORA817DEV.US.ORACLE.COM> insert into t values ( null, 1, null );

1 row created.

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> exec print_table( 'select * from t', FALSE );
X                             : 1
-----------------
X                             : 1
Y                             : 1
-----------------
X                             : 1
Y                             : 1
Z                             : 1
-----------------
Y                             : 1
-----------------

PL/SQL procedure successfully completed.

scott@ORA817DEV.US.ORACLE.COM>

August 17, 2002 steve -- Thanks for the question regarding "best
      way to update a blob", version 8.1.7

      You Asked
first of all , I think you should block the domains that are causing you
problems, it shows a great disrespect to you and the service you provide if
people cannot even check their email addresses are correct before submitting a
question.

anyway my question.
I have the following code that shoves  graphics into a database
(updatePhotoContent),it consists of an insert for the photo data then  updates
the sizes later
, i would like to do it in one "hit", currently i have to insert the photos then
get there sizses back from the database, and update the size fields.
this is twice as much to go wrong and not clean.

also, i notice that in the docs it states that if the lob is less than about 4k
it gets put in line, bigger than this it is put out of line.
so if it is in line and it is 20 bytes, does it still take up 4k?, and if it is
out of line and it is 5k does it actually take 8k?

addPhoto is called to add a blank record
updatePhotoContent (this is a routine that is used to upddate an exsisting photo
or fill the blank record  added by addPhoto

public static long addPhoto(long sectionId, Photo photo, Connection c) {
        System.out.println("AS.addPhoto(): " + photo);
        Section section = null;
        long id = 0;
        try {
            String sql = "Select graphics_table_seq.nextval from dual ";
            PreparedStatement st = c.prepareStatement(sql);
            ResultSet rset = st.executeQuery(); // Execute Query
            if(rset.next()) /*this is always true*/ {
                id = rset.getLong(1);
            }
            sql = "insert into photo_store(id,section_id, name, description)
values(?,?,?,?)";
            st = c.prepareStatement(sql);
            st.setLong(1, id); // Bind the photo id
            st.setLong(2, sectionId); // Bind the photo section id
            st.setString(3, photo.getName()); // Bind the photo name
            st.setString(4, photo.getDescription()); // Bind the photo
description
            rset = st.executeQuery(); // Execute Query
        }
        catch(Exception ex) {
            ex.printStackTrace();
        }
        return id;

*********the following is the code that needs to be simplified***********

 public static void updatePhotoContent(long photoId, InputStream is1,
InputStream is2, Connection c, String[] theDetails) {
        System.out.println("AS.updatePhotoContent(): " + photoId);
        Section section = null;
        BufferedOutputStream bos1 = null;
        BufferedOutputStream bos2 = null;
        try {
            String sql = "Select content,thumb_nail from photo_store where
deleted<>1 and id=? for update of content,thumb_nail";

            //         Statement st = c.createStatement();
            PreparedStatement st = c.prepareStatement(sql);
            st.setLong(1, photoId); // Bind the photo id

            ////Oracle
            ResultSet rset = st.executeQuery(); // Execute Query

            //           ResultSet rset = st.executeQuery();
            oracle.sql.BLOB blob_pic = null;
            oracle.sql.BLOB blob_thm = null;
            int n = 0;
            if(rset.next()) /*this is always true*/ {
                blob_pic = (oracle.sql.BLOB)rset.getObject(1);
            }
            blob_thm = (oracle.sql.BLOB)rset.getObject(2);
            bos1 = new BufferedOutputStream(blob_pic.getBinaryOutputStream());
            bos2 = new BufferedOutputStream(blob_thm.getBinaryOutputStream());
            if(blob_pic != null) {
                byte[] buf = new byte[ MAXBUFSIZE ];
                n = 0;
                while((n = is1.read(buf, 0, MAXBUFSIZE)) != -1) {
                    bos1.write(buf, 0, n);
                }

                //now write out the thumbnail
                if(blob_thm != null) {
                    n = 0;
                    while((n = is2.read(buf, 0, MAXBUFSIZE)) != -1) {
                        bos2.write(buf, 0, n);
                    }

                    //now update the size of the thumb & picture as well as the
xy size
                    sql = "Update photo_store set
picxsize=?,picysize=?,pic_size=dbms_lob.getlength(content),thm_size=dbms_lob.getl
ength(thumb_nail) where deleted<>1 and id=?";

                    //st = c.createStatement();
                    st = c.prepareStatement(sql);
                    st.setString(1, theDetails[ 1 ]); // Bind the size
                    st.setString(2, theDetails[ 2 ]); // Bind the size
                    st.setLong(3, photoId); // Bind the photo id
                    rset = st.executeQuery();
                    bos1.flush();
                    bos2.flush();
                    buf = null;
                    if(bos1 != null) {
                        try {
                            bos1.close();
                        }
                        catch(IOException e) {
                            ;
                        }
                    }
                    if(bos2 != null) {
                        try {
                            bos2.close();
                        }
                        catch(IOException e) {
                            ;
                        }
                    }
                    is1.close(); // this should not be here cos it was not opend
in this routine.
                }
            }
        }
        catch(Exception ex) {
            ex.printStackTrace();
        }
        finally {

            // still try to close the channel
            if(bos1 != null) {
                try {
                    bos1.close();
                }
                catch(IOException e) {
                    ;
                }
            }
            if(bos2 != null) {
                try {
                    bos2.close();
                }
                catch(IOException e) {
                    ;
                }
            }
        }
    }

I'm storing the length because, I thought  diddling with the clobs/blobs via the
 dbms.lob package would incur extra time. ( like if i want to retrieve the sizes
of all the pictures ( 100,000's, i thought it would be faster than 100,000'
calls to dbms.lob, also it is going to be a multi user package)
if you are telling me that it is not the case, then i can remove the code & save
the time and space in the table, of storing the info.

I was also going to use it later for  some sort of analytical information
relating to the pictures.
I cannot find a lot of information on the efficency of the dbms package and
don't know how it gets the size of the stored data.
does it already store the length or des it have to scan the lob to calculate the
length?

      and we said...
Ok, that makes sense -- if you are going to query up 100k of them at a time,
storing the length as an attribute, rather then using dbms_lob, will be more
performant.

I'll make a couple of comments.  Instead of coding this:

try {
            String sql = "Select graphics_table_seq.nextval from dual ";
            PreparedStatement st = c.prepareStatement(sql);
            ResultSet rset = st.executeQuery(); // Execute Query
            if(rset.next()) /*this is always true*/ {
                id = rset.getLong(1);
            }
            sql = "insert into photo_store(id,section_id, name, description)
values(?,?,?,?)";
            st = c.prepareStatement(sql);
            st.setLong(1, id); // Bind the photo id
            st.setLong(2, sectionId); // Bind the photo section id
            st.setString(3, photo.getName()); // Bind the photo name
            st.setString(4, photo.getDescription()); // Bind the photo
description
            rset = st.executeQuery(); // Execute Query
        }

Do this:

    CallableStatement cstmt = connection.prepareCall(
        "begin insert into t(x) values (t_seq.nextval) return x into ?; end;" );

    cstmt.registerOutParameter( 1, java.sql.Types.INTEGER );
    cstmt.executeUpdate();

    int newVal = cstmt.getInt( 1 );
    System.out.println( newVal + " assigned on input" );

Saves a trip to the DB.  Get the sequence back from the insert, don't select it
and then insert it.

As for the lengths -- just use the Java IO routines to query the filesystem to
discover the size of the file before you read it.  Then, you can do the UPDATE
.... returning blob1, blob2 trick just like I did with the sequence (eg: update
the lengths -- get the blobs back -- in one call instead of select for update,
and then update)

If the blob is less then 4k, we store it inline using only the storage needed.
If we store it out of line, it'll take some integral multiple of the chunksize
bytes of storage (eg: if the chunksize is 16k and you store 5k, it'll take 16k
of storage)
Review & Followup

      Rating:
      4
  August 15, 2002
Reviewer:  steve
       from china
thanks
 i didn't think of executing the code segment to save time and the trip to the
DB, quite obvious once it's pointed out. ( thanks again for pointing out the
wood)
as for the file system, i could get the size that way, either by generating the
image then reading the size back ( takes memory), using the file utl, seemed a
bit wasteful and extra error trapping , for something i could do  better in the
S.Q.L

          Followup:

I don't understand the second comment?

Your input stream is an already existent file isn't it?

      Rating:
      3
  August 16, 2002
Reviewer:  steve

Yes you are correct i already have a stream open, and afer thinking about it,
i suspect that i had a concern about the platform it (java client) was running
on, i was not clear if  running the code on a different platform would return a
different  file size.

E.G mac. if you pass a file to a mac, the mac adds a resource fork, ( info about
the file to the start of the file), which changes the size of the file.)
sometimes as much as 30-40k
I thought it was more consistent to use something i knew would work ( oracle), I
could have tested it on every platform, but that would be timeconsuming and
messy.
( oracle jdbc already runs differently on my mac than on windows)

          Followup:

Then, you'll have to do this secondary update if you cannot get java to tell you
the number of bytes you'll be reading from the static file that exists on the
file system (In C, this would be trivial -- I would fopen, fseek, ftell and then
frewind and read the file -- I canot believe that Java cannot be used to tell
you the size of a static file you will be reading, seems incomprehensible)

      Rating: 4
Bulk
      Commit?  November 13, 2003
Reviewer:  VInnie
       from Orlando
Tom,

Can your original answer wish details how to eliminate a trip to the dB be
configured for many inserts at a time & only commit when desired?  If so do you
have an example?

August 17, 2002 Vijayakumar -- Thanks for the question regarding
      "What is the limit on Sending bulk emails from Oracle DB using SMTP.",
      version 8.1.6

      You Asked
Hi,
   I am trying to send bulk email reminders (number of emails to be generated
may vary between 1000 to a maximum of 10000) all users who have not completed a
survey from the oracle database using SMTP. Each email content is unique as it
contains some user specfic information.
   I have coded to run as a DBMS_JOB and is running is successfuly to prove the
working principle. I also have a mailin database to handle all the returned
emails (non delivery) in lotus notes.

   One of the main concern that everyone has is that it might JAM the mail
gateway, due to the large volume of emails being generated. Hence, I am trying
to send emails in blocks of 100 users.
   Is there any guideline as to how many emails can be sent at a time?
Is there a better way to implement this.

Thanks

Vijayakumar Belaguli

      and we said...
I send over 25,000 emails a day from my server inside Oracle on a busy day.

We use utl_smtp.
We use dbms_job

We cannot JAM the email gateway at all - this is because only N dbms_job's will
be running concurrent and YOU control N (n is 36 or less in 8i and before, 1000
or less in 9i and later)

I run with N=3.  No jamming of email that way.  The emails are sent one after
the other, 3 at a time at most.  No worries there (if your email gateway cannot
handle that load, you need to upgrade that 8088 to a modern processor)

Review &
      Followup

      Rating: 4
Sending emails
      from Database using SMTP via DBMS_JOB  August 15, 2002

Reviewer:  vijayakumar belaguli  from Singapore

Thanks for your reply.  It has relieved me some worry that I had. In fact I have
only one DBMS_JOB to send jobs for sending emails to one category of people. So,
In total I only have 3 categories and hence only 3 jobs running at any given
time.
     My main concern is In one DBMS_JOB run cycle I am trying to send 100 emails
one after the other and go to sleep for say about 15 minutes and then again send
the next 100 emails. Is this approach the right way to go. My main constraint is
that I need to finish sending all the emails on the same day. Can I increase to
greater than 100 for each run.
Can you enlighten on this?
Note that is a E10K sun solaris WEBSERVER open to public access.

          Followup:

I wouldn't wait 15 minutes, Personally, I would schedule a job per email to be
sent (wait, that is what I do ;)

That way, they just go out as fast as I schedule them -- one after the other.

      Rating: 4
What is the
      limit on Sending bulk emails from Oracle DB using SMTP.", version
      8.1.6  August 16, 2002
Reviewer:  Vijayakumar
      Belaguli  from Singapore
Hi Tom,
       Thanks for your reply. Do you still recommend to sending emails by
submitting the job repeatedly for every email you send? Will there not be an
overhead, particularly, if trying to fetch the reciepients address based on
several table joins and certain conditions (dynamic data). Since I am using
cursor to get the block of 100 reciepients at a time and then sending it in one
DBMS_JOB scheduled run, it would be more efficient than the
one you have suggested.
You comment please.

Thanks
Vijayakumar Belaguli

          Followup:

It is upto you -- only you know all of the constraints of your system.  In my
system, the emails are generated independently of eachother so submitting each
one by itself is "natural".

The problem with doing 100 at a time could be that the first 99 succeed and then
the SMTP server fails for whatever reason.  Now, you'll re-email those 99 guys
again later.  Singleton DBMS_JOBS can help make SMTP "transactional" (eg: each
send is distinct from every other send)

Perhaps the thing that is batching up 100 at a time now just queues 100 separate
jobs, each tasked with sending a single email. If you have my book "Expert one
on one" I show how to use dbms_jobs in an efficient way (with bind variables),
something you would need to do if you choose this path.

August 17, 2002 vinod -- Thanks for the question regarding
      "searching files in a directory in OS using intermdia with AND OR NOT NEAR
      operators ", version 8.1.7

      You Asked
Dear  Tom

 Hello

Can I  use intermedia  text search of Oracle 8.1.7 for NT for
1) AND,OR,NOT search on any directory of  harddisk.  ie I will specify location
( lke c:\orahome\docs) which contains files of any type like *.html ,*.doc *.rtf
etc or even we can restrict files of two types (html and doc or html and rtf  )
and then I will specify  keywords in AND and OR  ,or NOT Combinations.
Similarly there is a intermedia operator NEAR BY  can the same can also be used
like  AND,OR ,NOT .

Here  further I require  that the paragraphs containing that  keywords given in
AND ,OR ,NOT should be merged in a single file (of html or doc type) .

I hope i will get a positive reply.

Thanks
vinod

      and we said...
1) You will have indexed the files in the directory -- you will have told
intermedia the names of the files you want to be able to search and we will have
indexed them.

You can then search them at will.

Yes, you can use "near".

We do not "merge" individual paragraphs from multiple documents together, we
return the fact that the document has that text.  The concept of a paragraph
doesn't apply here.
Review & Followup

      Rating:
      4
  August 17, 2002
Reviewer:  vinod
       from india
No my problem is to search all files of particular type like *.doc or *.rtf or
*.htm and even we can merge the same using util_file package some one told me.

          Followup:

You have to register the documents in intermedia (it needs to be told of their
existence).

"merging" them is something you would have to accomplish on your own.  You can
use intermedia to convert ALL of the registered documents into HTML which would
be easier for you to parse then the proprietary .doc format or the complex rtf.

August 17, 2002 Leonardo -- Thanks for the question regarding
      "decimal separator when using to_char", version 8.1.5

      You Asked
Hi Tom! After hours searching the archives, I couldn´t find an answer. So...

My Oracle DB Server is a 8.1.5, and its defaults to nls english (date format,
number, etc.). As I´m working on a Portuguese project, I set the language to
portguese and territory to Brazil. Ok, all dates came in dd-mm-yyyy and numbers
uses a comma as decimal separator.

BUT I have a view that concatenates some text with a number (ex: 'Total: ' ||
to_char(12345.67, '999,999.99') these is a sample that shows the same result) as
a column.

When I select this column from this view I get as result the text concatenated
with the value, wrong formatted!
(ex: it shows 'Total: 12,345.67' instead of 'Total: 12.345,67'). It seems to not
be using the nls settings!

Why? And How to fiz it?

Thanks in advance for your time!

Leonardo Zacche
Rio de Janeiro - Brazil

      and we said...
Use D and G, don't use , and .    Using a comma and a dot will get you a comma
and a dot.  Using D and G will get you the decimal separator and group
separator:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from nls_database_parameters;

PARAMETER                      VALUE
------------------------------ ----------------------------------------
NLS_LANGUAGE                   AMERICAN
NLS_TERRITORY                  AMERICA
NLS_CURRENCY                   $
NLS_ISO_CURRENCY               AMERICA
NLS_NUMERIC_CHARACTERS         .,
NLS_CHARACTERSET               WE8ISO8859P1
NLS_CALENDAR                   GREGORIAN
NLS_DATE_FORMAT                DD-MON-RR
NLS_DATE_LANGUAGE              AMERICAN
NLS_SORT                       BINARY
NLS_TIME_FORMAT                HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT           DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT             HH.MI.SSXFF AM TZH:TZM
NLS_TIMESTAMP_TZ_FORMAT        DD-MON-RR HH.MI.SSXFF AM TZH:TZM
NLS_DUAL_CURRENCY              $
NLS_COMP                       BINARY
NLS_NCHAR_CHARACTERSET         WE8ISO8859P1
NLS_RDBMS_VERSION              8.1.7.3.0

18 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select to_char( 1234.532, '9g999d9999' ) from
dual;

TO_CHAR(123
-----------
 1,234.5320

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set nls_numeric_characters =
',.';

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select to_char( 1234.532, '9g999d9999' ) from
dual;

TO_CHAR(123
-----------
 1.234,5320

ops$tkyte@ORA817DEV.US.ORACLE.COM> select to_char( 1234.532, '9,999.9999' ) from
dual;

TO_CHAR(123
-----------
 1,234.5320

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review &
      Followup

      Rating: 3
Doubt
      ?  August 17, 2002
Reviewer:  A reader

What is the Q :
"
BUT I have a view that concatenates some text with a number (ex: 'Total: ' ||
to_char(12345.67, '999,999.99') these is a sample that shows the same result) as

a column.
When I select this column from this view I get as result the text concatenated
with the value, wrong formatted!
(ex: it shows 'Total: 12,345.67' instead of 'Total: 12.345,67'). It seems to not

be using the nls settings!"

My question on the Question itself is : Is it not doing the correct thing here
what is the problem.
The given Format is to_char(12345.67, '999,999.99') and
the selected column is  'Total: 12,345.67'  which is compatible to the given
format.
  So why is this a problem in the first place.

Correct me if I am wrong or did the get the Question wrong.

          Followup:

They want the NLS "group separator" to be used where ever a comma is in the
format and the NLS "decimal separator" to be used where ever a dot is in the
format.

In order to do that, you use G and D in the format.

      Rating: 5
Thanx for
      clearing our doubt  August 17, 2002
Reviewer:
      A reader
U r Great.

August 18, 2002 atul -- Thanks for the question regarding "How to
      find Indexes dropped", version 8.0.4

      You Asked
Sir,

Generally if developer comes to you and saying yesterday this query was working
fast today it's very very slow..

Normally problem is somebody drop index on a table..
As a DBA it's very time consuming to find out which index got dropped etc...

I have wrote a script which will tell newly created/dropped indexes from
yesterday..

But i'm not satisfied with this..

Do you have any ideas/script which will accomplish this to save time and trouble
of a DBA?????

thanks.
atul

      and we said...
One could simply enable AUDITING and audit relevant events.  You would have a
comprehensive audit trail of who did when and when.  Then, you can track them
down and say "why???"

That is my suggestion.

(actually, why do developers have the ability to do this in a production system?
where is your change control process?)
Review &
      Followup

      Rating:
      5
  August 17, 2002
Reviewer:  atul
       from india
Sir,

As i know auditing requires space and also performance will also go down
slightly..

I just want to reviews status of indexes from yesterday and today..

so is there any other way or script to do this???

I'm giving my script below...
--------------------------------

clear
#get the sysdate
p=`echo "set heading off;\nset feedback off;\nset pages 0;\nselect sysdate from
dual;\nexit;"|sqlplus -s /`

#get the index list
p1=`echo "set heading off;\nset feedback off;\nset pages 0;\nspool
/user/appl/dbdesign/att/$p.txt;\nselect
table_name||'-'||index_name||'-'||column_name||'-'||column_position||'-'||index_o
wner from dba_ind_columns where index_owner not in ('SYS','SYSTEM')
order by table_name;\nspool off;\nexit"|sqlplus -s /`

cd /user/appl/dbdesign/att

t=`ls -lt | grep -v "total" | wc -l`

if [ t -ge 3 ]
then
ls -lt | tail -1 | awk '{print "rm -fe " $9}' > /tmp/a1.ksh
ksh /tmp/a1.ksh
else
echo "No"
fi
diff *.txt | egrep "<" > /tmp/indremoved.txt
echo "Following indexes are Removed"
cat /tmp/indremoved.txt
diff *.txt | egrep ">" > /tmp/indaddesd.txt
echo "Following indexes are added"
cat /tmp/indaddesd.txt
---------------------------

Ur suggestions plz...

Thanks.
atul

          Followup:

In order to judge what is different today from yesterday you will have to (gasp)
consume storage.  What *better* place to have this storage be then in a
database.

I will stand firm here -- auditing.  You are talking about auditing DDL and tell
me, how often will this percieved performance issue hit you then?  (not often).
Sure, today you say "I want to compare today against yesterday" but rest assured
two weeks from now you'll want to compare today with last week.

Auditing = right answer = such a trivial amount of data, probably less then your
alert log = it is in the database, which was built to hold data.

August 19, 2002 Sanjai -- Thanks for the question regarding
      "UTF8", version 8.1.7

      You Asked
Hi Tom,

In one of our projects we need to make our application support multiple
languages. Our database's character set is UTF8. Since some of the languages
like chinese can have alphabets that can take upto 5 characters our DBAs are
asking us (developers) to increase all the column lengths by 5 times. If the
column size is 20 then they are asking us to make it 100.

My question was in that nececssary ?  Since the database is UTF8 do we still
need to increase the column lengths or the database can handle that seamlessly.

Thanks a lot.

      and we said...
Yes, it is necessary in Oracle8i and before.

In Oracle9i, you can use the syntax:

create table t
(  x varchar2( 20 CHAR ),
   ...
)

and we'll take care of it.  x varchar2(20) sets aside 20 BYTES, not 20
characters.  varchar2(20 CHAR) in 9i and up lets you put 20 CHARACTERS in there,
not just 20 bytes.
Review & Followup

      Rating:
      5
UTF8  August 18, 2002
Reviewer:
      sanjai
Thanks for your prompt response. Just one more clarification.

In Oracle9i by using varchar2( 20 CHAR ) will the perforamce be same as using
varchar2(20) in Oracle8i ?

          Followup:

Trick question.

you would have to compare varchar2(20 char) with varchar2(100) or varchar2(120).

It'll perform like they would.  a varchar2(20) might only be able to hold 3 or 4
characters whereas the varchar2(20) would be able to hold 20.

It is a syntactic "niceness", we do the multiplication your DBA's are telling
you to do now.

      Rating:
      5
UTF8  August 19, 2002
Reviewer:
      sanjai
Thanks a lot, Tom.

August 19, 2002 sukumar -- Thanks for the question regarding
      "Crating standby database on different OS platform", version 8.1.6

      You Asked
Hi Mr.TOM

We ahve Digital unix server for oracle database (production)
version 8.1.6.

can i create standby database on windowNT4 server from digital unix server?

if yes what i changes i should do in normal procedures.

With advance Thanks

S Sukumar

      and we said...
Not until Oracle9iR2 (release 2) where we have a logical standby.

Before that, we have a physical standby -- the standby is a bit for bit, byte
for byte replica of the production.  You must be using the same version of
Oracle on the same hardware and operating system.
Review &
      Followup

      Rating:
      3
  August 18, 2002
Reviewer:  WH Chan
       from Shenzhen, China
I did a test before. You can build a standby database
on NT4 while your production database is on win2k, vice
versa. But the oracle version must be the same.

          Followup:

I flipped a coin.

It landed heads up.

To be sure, I did it again.

It landed hedads up.

Therefore -- coins will land heads up always.  (not).

The supported configuration would be win NT4 to win NT4, and win 2k to win 2k.
It is clearly documented:

<quote>
o You must use the same version and release of the operating system on the
primary and standby hosts. The standby host can, however, use a different
directory structure.

o You should use the same version, release, and patch of the Oracle RDBMS
for
the primary and standby databases so that failover operations are not
compromised.
</quote>

so you actually have it backwards.

      Rating:
      4
  August 18, 2002
Reviewer:  Sukumar
       from Chennai, INDIA
Thank U for the immediate resonse, Mr.TOM.

But i have one NT4 server with sufficient disk space for holding my entire
production database which is in Unix server.

I would like to make use of this NT server for making backups.

Can u suggest to make this NT server as production database
when my current unix database server fails without taking much time.

Can i use database replication to my NT server from unix server ?
If yes kindly let me know whow to replicate it to NT server .

Note :
According to yr suggesion, i have to go for new Unix
digital server which would cost more for me.

          Followup:

High availability and data survivability is something you pay for, it costs
money.  The more you put into it, the more you get out of it.

Replication (advanced replication) is a terribly expensive (in terms of system
overhead and maintenance) approach to disaster recoverability.  If you think a
new machine would be expensive...

My suggestion to you would be to look at Oracle9i Release 2, consider upgrading
-- in order to use logical standby.  Especially relevant to you since you are
using wholly unsupported software right now, today (and ADDING something like
replication to that mix would be a truly bad idea, you'll be upgrading anyway
after the first support call).

August 19, 2002 Edgars -- Thanks for the question regarding
      "Storing queries in database", version 9.0.1

      You Asked
Hello Tom,

I would like to save some queries outside my applications executable, is this a
normal practice to save queries in database tables BLOB field and than
application, just select query from there, prepares it and executes? I can not
use VIEWS because there are bind variables. Thank you!

      and we said...
I would not call it "normal", no.

I would put the queries into stored procedures that return result sets.  Then
just call the stored procedure from your application.

See

http://asktom.oracle.com/~tkyte/ResultSets/index.html

Review
      & Followup

      Rating: 4
But what
      if?  August 19, 2002
Reviewer:  Edgar Chupit
       from Latvia
Hello Tom,

But what if I don't know exact count of queries and don't know exact names at
programs creation time. Suppose I have something like combobox that have query
associated with each value. Values for comboboxes I get from database, do you
suggest that in this case I should also select function name instead of query?
It is possible and it sure will work, but why does this approach is better than
storing queries in database? Thank you!

          Followup:

Yes.  Your combo box should call a stored procedure to get a result set.  You
don't select a function name - you will RUN a stored procedure.

It is better because

o the queries are syntax checked when you compile them. (less chance of runtime
error)

o less round trips to the database.  Instead of 1) get query 2) parse query 3)
bind query 4) execute query 5) fetch query 6) close query... you will 1) get
parsed, bound and executed query 2) fetch query 3) close query

o better control over security.  User running application needs EXECUTE on
procedure, does NOT need SELECT on table

o automagic dependency mechanism.  Your procedures will clearly (in the data
dictionary clearly) be dependent on the tables.  Now, when you ask "who would be
affected by me changing name from 80 to 100 characters" -- you can run a query
and we'll tell you.

o you can add custom auditing later (since it is a procedure after all, you can
introduce complex logic in there)

and so on.

August 19, 2002 Zahid -- Thanks for the question regarding "Forms
      entry in Product_User_Profie table", version 8.1.7

      You Asked
What is the exact entry for program to stop query or any other command.

Please tell me about product user profiles table and valid keywords to enter in
this table .

      and we said...
fortunately, it is documented:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a82950/ape.htm#623300
....
August 19, 2002 Karl -- Thanks for the question regarding
      "Storing And Retrieving Blobs interfaced with VB", version 8.1.7

      You Asked
From a vb application that i have created, regarding the production of ID cards
for students, i have stored all student photos in a file on a server. This works
fine but i want to store them in an oracle database - because this is where all
the student information (excluding photos) are stored anyhow. However, i am
unaware of the procedure in storing and retrieving blobs in oracle and how i
could incorporate this within my VB program. I have searched loads of sites for
this and someone pointed me to this site for guidance. I have searched your site
and although there are questions on blobs i do not think there is one as
specific as this(and if there is, it relates to JAVA - which unfortunately i
know very little about).

This is the table below that i am hopefully going to store the blobs within
(Hopefully)!
 Name                            Null?    Type
 ------------------------------- -------- ----
 DOMAIN                          NOT NULL VARCHAR2(30)
 OWNER_REF                       NOT NULL NUMBER(10)
 BLOB_NAME                       NOT NULL VARCHAR2(30)
 BLOB_TYPE                       NOT NULL VARCHAR2(10)
 BLOB                                     LONG RAW
 PRINTED_DATE                             DATE
 PRINTED_NO                               NUMBER(3)
 DESCRIPTION                              VARCHAR2(50)
 FILE_ID                                  NUMBER(10)
 CREATED_BY                      NOT NULL VARCHAR2(30)
 CREATED_DATE                    NOT NULL DATE
 UPDATED_BY                               VARCHAR2(30)
 UPDATED_DATE                             DATE
 SP_VISIBLE                               VARCHAR2(1)

Is there and help you can give, or any other documents that you could point me
in the direction of? I do realise that you do not use VB, but hey, it is worth a
try.

      and we said...
See

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/index.htm
There is actually a document right there in the documentation set:

 Oracle8i Application Developer's Guide - Large Objects (LOBs) HTML PDF

Subsets of  the Oracle8i Application Developer's Guide - Large Objects (LOBs)
manual are available in PDF only for the following language interfaces:

PL/SQL    C (OCI)    C/C++ (Pro*C/C++)    Pro*COBOL    Visual Basic (Oracle
Objects for OLE)    JAVA

August 19, 2002 kiril -- Thanks for the question regarding "crash
      recovery and media recovery.", version 8.1.7

      You Asked
Hi Tom
I have an argue with a colleague about media and instance recovery.
Core of argue is when we need  media recovery.

Q1) If we have a folloing situation ( ver 8.1.7 NT 4.0) :
  DB is working in NOARCHIVELOG mode with 4 redo log 2Mb each.
T1 have 7 million rows 10k.
 Statement S1 is updating 2,5 million rows.
 DB will use log1, log2,log3, log4 and again log1, log2 and after that power
failure.....!
 After starting Oracle instance will start automatic crash recovery. I think
after crash recovery is done(all online redo filed have been applied)we need a
media recovery because there are owerwritten log files?
Am I right ?
Please, make a short comment.
thanks ,
kiro

      and we said...
You are wrong.

The only time you need media recovery is when you have a corruption or lost
disk.

If you want the long comment -- get my book "Expert one on one" -- I cover this
topic (how we are able to reuse redo log files).  It is the complex interaction
between REDO and UNDO that is in play here.
Review &
      Followup

      Rating:
      4
  August 19, 2002
Reviewer:  kiro

Tom
Sorry I wrote in wrong way my opinion.
I think after media failure will start crash recovery from online redo logs.
After applying of redo logs, because there is no commit will start rollback.
After rolling back of entries from redo logs Oracle will roll back entries from
RBS segments. I think I am right !?
Thanks

          Followup:

still technically wrong.

we never rollback from redo logs.  We only redo from redo logs.  We rollback
only from rollback (undo)

August 19, 2002 John -- Thanks for the question regarding "update
      stmt", version 8.1.6

      You Asked
tom,

update emp set sal = sal where empid = 1000;

does the above statement really updates or leave it as left hand and right hand
columns are same.

thanks

      and we said...
it really updates.

You can tell this for sure by looking at the redo generated by these two
statements:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t as
  2  select rownum x, rpad( 'x', 2000, 'x' ) y from all_users;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace on statistics
ops$tkyte@ORA817DEV.US.ORACLE.COM> update t set  x=x;

43 rows updated.

Statistics
----------------------------------------------------------
          2  recursive calls
         60  db block gets
         20  consistent gets
         15  physical reads
      11048  redo size
       1010  bytes sent via SQL*Net to client
        791  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
         43  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM> update t set  y=y;

43 rows updated.

Statistics
----------------------------------------------------------
          0  recursive calls
         83  db block gets
         19  consistent gets
          0  physical reads
     182988  redo size
       1010  bytes sent via SQL*Net to client
        791  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
         43  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off
ops$tkyte@ORA817DEV.US.ORACLE.COM>

You can see the update of the 2000 byte column generated proportionally more
redo then the number.  The update is "real"
Review &
      Followup

      Rating: 5
can we avoid
      this anyway  August 19, 2002
Reviewer:  john

thanks for that.
can we tell oracle any way to skip updation other than skipping that column in
the update statement.

          Followup:

skipping that column in the update statement is in fact the ONLY way.

August 19, 2002 Leah -- Thanks for the question regarding
      "DBMS_UTILITY example", version 8.1.5

      You Asked
During our software build, some database objects
such as packages, views, etc. become invalid after compilation.
You've suggested in one of your q&a's to use
dbms_utility.compile_schema to recompile invalid objects in the
order of their dependencies. Can you please give an example
using the utility for this purpose?

      and we said...
The goal of the compile schema procedure is to attempt to make
valid all INVALID procedures, packages, triggers, views, types,
and so on in a schema.  This procedure works (in 8.1.6 and
before but not 8.1.7) by using the
SYS.ORDER_OBJECT_BY_DEPENDENCY view which is supposed to return
objects in the order they depend on eachother.  If we compile
the objects in the order this view returns the data ¡V at the
end, all objects should be valid.  This procedure runs the ALTER
COMPILE command as the user who invoked the procedure (invokers
rights).

It should be noted that COMPILE_SCHEMA demands you pass in a
case sensitive username ¡V if you call:

scott@TKYTE816> exec dbms_utility.compile_schema( ¡¥scott¡¦ );

It is probable that nothing will happen (unless you have a lower
case user named scott.  You must pass in SCOTT.

There is however another issue with compile_schema in versions
of the database between 8.1.5 to 8.1.6.2 (that is all 8.1.5 and
8.1.6.0 and 8.1.6.1 versions).  If you have a Java enabled
database, this will introduce some recursive dependencies into
your system.  This will cause COMPILE_SCHEMA to raise the error:

scott@TKYTE816> exec dbms_utility.compile_schema( user );
BEGIN dbms_utility.compile_schema( user ); END;

*
ERROR at line 1:
ORA-01436: CONNECT BY loop in user data
ORA-06512: at "SYS.DBMS_UTILITY", line 195
ORA-06512: at line 1

If you encoutner this error, we can create our own
COMPILE_SCHEMA procedure that behaves exactly as the real
COMPILE_SCHEMA.  We can do this by compiling the objects in any
order we feel like it.  It is a common misconception that we
must compile objects in some specific order ¡V we can in fact do
them in any old order and end up with the same outcome we would
have had ordered by dependency.  The logic is:

ƒá Pick any invalid object from a schema that we have not yet
tried to compile
ƒá Compile it
ƒá Goto step one until there are no more invalid objects that we
did not try to compile once

It is that simple ¡V we need no special ordering.  That is
because a side effect of compiling an invalid object is that all
invalid objects it depends on will be compiled in order to
validate this one.  We just have to keep compiling objects until
we have no more invalid ones (well, we might have invalid ones
but that would be because they cannot be successfully compiled
no matter what).   What we might discover is that we need only
to compile a SINGLE procedure to have 10 or 20 other objects get
compiled.  As long as we don¡¦t attempt to manually recompile
those 10 or 20 other objects (that would invalidate the first
object again) we are OK.

Since the implementation of this procedure is somewhat
interesting, we¡¦ll demonstrate it here.  We need to rely on an
invokers rights routine to do the actual ¡§alter compile¡¨
command ¡V however we need access to the DBA_OBJECTS table to
find the ¡§next¡¨ invalid object and report on the status of the
just compiled object.  We do not want the invoker of the routine
necessarily to have to have access to DBA_OBJECTS.  In order to
achieve this, we will use a mixture of Invokers rights routines
and Definers rights routines.  We need to make sure that the top
level routine, the one called by the end user, is the invokers
rights routine however to ensure that roles are enabled and all.

Here is my implementation of a COMPILE_SCHEMA.  The person who
runs this script must have had SELECT granted to them on the
SYS.DBA_OBJECTS view directly (refer to the Invokers Rights
chapter for details on why this is).  Since this is a SQLPlus
script, with some SQLPlus directives in it ¡V I¡¦ll put the
script in this time, not the results of actually running the
script.  I am using a SQLPlus macro variable to fill in the
schema name as we compile objects.  I am doing this because of
the invokers rights routine (need to fully qualify objects if
they should always access the SAME table regardless of who is
running it) and the fact that I personally do not like to rely
on public synonyms.  The script will be given to you in pieces
below with commentary in between:

column u new_val uname
select user u from dual;

drop table compile_schema_tmp
/

create global temporary table compile_schema_tmp
( object_name varchar2(30),
  object_type varchar2(30),
  constraint compile_schema_tmp_pk
  primary key(object_name,object_type)
)
on commit preserve rows
/
grant all on compile_schema_tmp to public
/

We start the script by getting the currently logged in users
username into a SQLPlus macro variable.  We will use this later
in our create or replace procedures below.  The temporary table
is used by our procedures to ¡§remember¡¨ what objects we have
attempted to compile.  We need to use ¡§on commit preserve
rows¡¨ because of the fact that we are going to do DDL in our
procedure (the alter compile command is DDL) and DDL commits.
Next, we can start in on the procedures we need:

create or replace
procedure get_next_object_to_compile( p_username in varchar2,
                                      p_cmd out varchar2,
                                      p_obj out varchar2,
                                      p_typ out varchar2 )
as
begin
    select 'alter ' || object_type || ' '
           || p_username || '.' || object_name ||
          decode( object_type, 'PACKAGE BODY', ' compile body',
                  ' compile' ), object_name, object_type
      into p_cmd, p_obj, p_typ
      from dba_objects a
     where owner = upper(p_username)
       and status = 'INVALID'
       and object_type <> 'UNDEFINED'
       and not exists ( select null
                          from compile_schema_tmp b
                         where a.object_name = b.object_name
                           and a.object_type = b.object_type
                      )
       and rownum = 1;

    insert into compile_schema_tmp
    ( object_name, object_type )
    values
    ( p_obj, p_typ );
end;
/

This is a definers rights procedure that accesses the
DBA_OBJECTS view for us.  This will return ¡§some¡¨ invalid
object to be compiled ¡V as long as we have not yet attempted to
compile it.  It just finds the first one.  As we retrieve them,
we ¡§remember¡¨ them in our temporary table.  Next we have our
invokers rights routine that will actually do the compilation.
This also shows why we needed the ¡§column u new_val uname¡¨
directive above ¡V we need to physically insert the OWNER of the
temporary table in here to avoid having to use a synonym.  Since
we do this dynamically upon install ¡V it makes it better then a
synonym:

create or replace procedure compile_schema( p_username in
varchar2 )
authid current_user
as
    l_cmd  varchar2(512);
    l_obj  dba_objects.object_name%type;
    l_typ  dba_objects.object_type%type;
begin
    delete from &uname..compile_schema_tmp;

    loop
        get_next_object_to_compile( p_username, l_cmd, l_obj,
l_typ );

        dbms_output.put_line( l_cmd );
        begin
            execute immediate l_cmd;
            dbms_output.put_line( 'Successful' );
        exception
            when others then
                dbms_output.put_line( sqlerrm );
        end;
        dbms_output.put_line( chr(9) );
    end loop;

exception
    when no_data_found then NULL;
end;
/
grant execute on compile_schema to public
/

And that is it ¡V now you can go into any schema that is able to
compile some objects and execute:

scott@TKYTE816> exec tkyte.compile_schema('scott')
alter PROCEDURE scott.ANALYZE_MY_TABLES compile
Successful

alter PROCEDURE scott.CUST_LIST compile
ORA-24344: success with compilation error

alter TYPE scott.EMP_MASTER compile
ORA-24344: success with compilation error

alter PROCEDURE scott.FOO compile
Successful

alter PACKAGE scott.LOADLOBS compile
Successful

alter PROCEDURE scott.P compile
Successful

alter PROCEDURE scott.RUN_BY_JOBS compile
Successful

PL/SQL procedure successfully completed.

So, that shows me the objects it attempted to compile and the
outcome.  According to the above ¡V we compile 7 objects, 2 of
which failed and 5 of which succeeded.  We compiled them in any
order, the order was simply not relevant.  This procedure should
work in all situations.

Review &
      Followup

      Rating:
      5
Questions  August 19, 2002
Reviewer:
      Jeff Yuan  from AZ USA
After installing 9.2.0.1, I got more than 10 hundred invalid objects in the SYS.
I used your method in your wonderful book (and here) to compile the invalid
objects, and still got about 150 invalid objects in SYS. Three kinds of errors
in my SqlPlus screen:

1. alter TYPE SYS.ODCIARGDESC compile
ORA-02311: cannot alter with COMPILE option a valid type with type or table
dependents

Question: How can I manually compile this object? and what cause the problem?

2. alter PACKAGE BODY SYS.DBMS_AQ compile body
ORA-00922: missing or invalid option

Action: "alter package DBMS_AQ compile body;" compiled it.
Question: Is there a bug in your procedure?

3.alter VIEW SYS._ALL_REPCOLUMN_GROUP compile
ORA-00911: invalid character

Action: "alter VIEW "_ALL_REPCOLUMN_GROUP" compile;" compiled it.
Question: Need "" on the name with special chars?

I am more interested in solving the problem in Question 1. Could you please take
a look on it? I use 9i rel 2.

Thank you very much.

Jeff Yuan

          Followup:

> oerr ora 2311
02311, 00000, "cannot alter with COMPILE option a valid type with type or table
dependents"
// *Cause:  An attempt was made to ALTER with COMPILE option a type that is
//          valid and has type or table dependents.
// *Action: No need to perform this operation.

I love that action.  I've always wondered why people are so gung ho to have this
"perfect" no invalid objects state.  I've always just let it fix itself.

Let that type be, it'll fix itself IF and WHEN you need it.

#2 -- looks like there is:

create or replace
procedure get_next_object_to_compile( p_username in varchar2,
                                      p_cmd out varchar2,
                                      p_obj out varchar2,
                                      p_typ out varchar2 )
as
begin
    select 'alter ' || object_type || ' '
           || p_username || '.' || object_name ||
          decode( object_type, 'PACKAGE BODY', ' compile body',
                  ' compile' ), object_name, object_type
      into p_cmd, p_obj, p_typ
      from dba_objects a

should be

create or replace
procedure get_next_object_to_compile( p_username in varchar2,
                                      p_cmd out varchar2,
                                      p_obj out varchar2,
                                      p_typ out varchar2 )
as
begin
    select 'alter ' ||
          decode( object_type, 'PACKAGE BODY', 'PACKAGE', object_type ) || ' '
           || p_username || '.' || object_name ||
          decode( object_type, 'PACKAGE BODY', ' compile body',
                  ' compile' ), object_name, object_type
      into p_cmd, p_obj, p_typ
      from dba_objects a

3) another fix -- need quoted identifiers:

create or replace
procedure get_next_object_to_compile( p_username in varchar2,
                                      p_cmd out varchar2,
                                      p_obj out varchar2,
                                      p_typ out varchar2 )
as
begin
    select 'alter ' ||
          decode( object_type, 'PACKAGE BODY', 'PACKAGE', object_type ) || ' "'
           || p_username || '"."' || object_name || '" ' ||
          decode( object_type, 'PACKAGE BODY', ' compile body',
                  ' compile' ), object_name, object_type
      into p_cmd, p_obj, p_typ
      from dba_objects a

will do that.

      Rating: 5
alter type body
      --> alter type  August 19, 2002
Reviewer:
      Jeff Yuan  from AZ USA
Hi Tom,

Thanks a lot again for your help.

I just found an issue with TYPE BODY. "alter type body xxx compile body;" should
be "alter type xxx compile body;".

One example is "alter type XMLGENFORMATTYPE compile body;"

Regards,

Jeff

August 19, 2002 saritha -- Thanks for the question regarding
      "First comma in a string", version 8.1.7

      You Asked
Hi Tom

  From a string I have to find the first comma and remove it

eg: UC , This is a test,

the output should be
   UC This is a test,

Thank you

saritha

      and we said...
scott@ORA817DEV.US.ORACLE.COM> select x,
  2             decode( sign(instr(x,',')),
  3                     1, substr(x,1,instr(x,',')-1) ||
substr(x,instr(x,',')+1),
  4                     x )
  5    from t;

X                         DECODE(SIGN(INSTR(X,',')),1,SUBSTR(X,1,INSTR(X,','
------------------------- --------------------------------------------------
UC , This is a test,      UC  This is a test,
Hello World               Hello World
,Hello World              Hello World
,,,,,,Hello World         ,,,,,Hello World
Hello World,              Hello World

scott@ORA817DEV.US.ORACLE.COM>
August 19, 2002 Scott -- Thanks for the question regarding
      "Question about where results are kept in memory", version 8.1.7

      You Asked
Tom,

A couple of us at work were wondering how oracle can return query results for
large result sets.

For example say I have a table of 2Million records and I do a select * from this
table.

Suppose this is a simple java program which opens a connection and executes the
query.  Assume the client can control when the client will fetch the next batch
of results by hitting the space bar.

Where does Oracle build the resultset for this query.  Obviously it cannot store
2M rows in memory, therefore, how does Oracle return the the correct data while
changes are happening on the table. (I don't mean read consistency or anything
to do with rollback or SCN) but rather how does Oracle knows where to find the
data that comes next in my resultset.  How does it find the 100 and 115 rows to
return to the client.

How does a sort change this?  Will the results be maintained in temp segments
and returned from these objects or will it use the same mechanisn when no sort
was involved.

Hopefully, I made sense, however, I get the feeling I was not all that clear.
In short how does a cursor know where to find the next batch of rows to return
to the client if all 2M query result rows are not kept query in memory to be
returned to a user.

Thanks
Scott.

      and we said...
We do not build the result set anywhere in general.

If you do select * from one_billion_row_table -- we fetch the data from the
table as needed -- the results start coming back IMMEDIATELY not after we've
fetched the last row.

For certain types of queries we might build part of the result set or all of it
(depends).  This would take place in RAM upto sort_area_size bytes and then we
would start paging that out to TEMP.

We find rows 100 to 115 by maintaining a state, called a cursor, on the server.
It tells us where we left off and where to begin.

Review &
      Followup

      Rating: 5
Still
      unsure  August 19, 2002
Reviewer:
      A reader
Thanks for your quick reply.

Maybe I am wasting my time trying to understand this but how does the cursor
keep track of what it has returned and what it needs to return.  Are there any
issues to be concerned with keeping a cursor open and not fetching from it all
at once?

For example say I open a cursor which return a billion rows.  I get a first
batch and display them in a list box.  If the user scrolls the list box I start
getting more data if not the cursor sits open waiting to get the next set of
data.    Is there anything I need to be concerned about with this situation.
How does the cursor know how to ignore rows it has already returned.

Thanks again.

          Followup:

It just knows -- say you were doing a full scan, it knows it needs to read 1,000
extents and that it was currently processing the first extent and has gotten as
far as block 55.  Now, to get the next fetch done, it just needs to pick up
where it left off.

You do not need to worry about how the cursor actually does this, just accept as
fact that the job of the cursor is to maintain enough state to give you the
right answer.

The one thing you need to "worry" about is ora-01555 if you are updating the
table you have the cursor open on.  If your DBA didn't size your rollback
segments properly, they will cause you to get an error (maybe) after some
extended period of time.

      Rating: 5
What is
      happening  August 19, 2002
Reviewer:  Jim
      Kennedy  from Portland, OR USA
The term "result set" really refers to the conceptual set of data that you asked
for.  You do not actually have to get the entire set on the client/web side.
You can get the data as you need it.  Some other RDBMS's want  you to get the
entire result set from the RDBMS to the client or middle tier.  This is usually
due to a different concurrency model from Oracle.  In Oracle you don't have to
download the entire result set to the client or middle tier and close it ASAP.

If you look at some of the API's for retrieving data (eg OCI) you find that you
have to explicitly retrieve rows and you don't just say x=
fetchEntireResultSet(...);

August 19, 2002 Srinivas -- Thanks for the question regarding "I
      want to export the oracle table data to a plain text format with out using
      spool.", version 8.0

      You Asked
Hi,
I want to export the oracle table data to a plain text format with tab
separtated with the coordinates i mention using a PL/SQL code not with Spool.

Thanks
Srinivas

      and we said...
see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:459020243348
http://asktom.oracle.com/~tkyte/flat/index.html
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:68212348056
they are C, sqlplus and PLSQL approaches in order.
August 19, 2002 Peter -- Thanks for the question regarding
      "Nested Tables - accessing objects returned from a subquery", version
8.1.7

      You Asked
Question: Unable to reference nested-table objects returned from a subquery -
how to qualify them?

Basic Sample Record:
KEY_ID KEY_CONTEXT GROUPING     MD_NESTEDTABLE (NAME | VALUE PAIRINGS)
I123   PRI         Element Met  SEGMENT_UUID     I123A
                   adata        PRIMARY_PARENT   I100
                                PUB_CODE         FLLB
                                volume.break     1
                                IMMEDIATE_PARENT
                                ELEMENT_NAME     topic
                                abbv             UNIFORMCC

Query:
SELECT md_ntab
 FROM metadata_tab MT, TABLE(MT.md_ntab) MN
 WHERE MT.key_context = 'PRI' AND
       MT.grouping = 'Element Metadata' AND
       MN.name = 'PUB_CODE' AND
       MN.value = 'FLLB'

This gives me a collection of objects for FLLB, which I loop through and put
into a Java struct.  Each struct's attributes are then looped through and other
conditions are checked.  Eventually, I am just left with the values '1' (from
volume.break) and 'I123A' (from SEGMENT_UUID) (and 12 other records like it).
It works, but I know it's not efficient - need more of the work done within
Oracle.

Sample Record Returned (+ 137 other records...only 13 records match my real
criteria - which I figure out using Java/jdbc):
METADATA_NTABTYP(
   METADATA_OBJTYP('ELEMENT_NAME', 'topic'),
   METADATA_OBJTYP('IMMEDIATE_PARENT', ''),
   METADATA_OBJTYP('PRIMARY_PARENT', 'I100'),
   METADATA_OBJTYP('PUB_CODE', 'FLLB'),
   METADATA_OBJTYP('SEGMENT_UUID', 'I23A'),
   METADATA_OBJTYP('volume.break', '1'),
   METADATA_OBJTYP('abbv', 'UNIFORMCC'))

Trying do something like this...
  1  SELECT t FROM (SELECT md_ntab t
  2                   FROM metadata_tab m, TABLE(m.md_ntab) n
  3                   WHERE m.key_context = 'PRI'              AND
  4                         m.grouping    = 'Element Metadata' AND
  5                         n.name        = 'PUB_CODE'         AND
  6                         n.value       = 'FLLB')
  7*   WHERE t.metadata_objtyp.name = 'volume.break'
SQL> /
WHERE t.metadata_objtyp.name = 'volume.break'
                        *
ERROR at line 7:
ORA-00904: invalid column name

This would return me collection of objects (13) for FLLB with volume.break
information only - not all FLLB records...but I can't seem to access each
METADATA_NTABTYP and its attributes (METADATA_OBJTYP).

Ultimately, I only want the 'SEGMENT_UUID' value and the 'volume.break' value
for each record of 'PUB_CODE' FLLB

I am trying to do this all in SQL - seems as if I'll have to do it in PL/SQL
though...

I'm a bit of an Oracle newbie...I haven't worked much in PL/SQL or with OR -
just learning.

Thanks,
Pete
New York

Design:
CREATE OR REPLACE TYPE metadata_objtyp AS OBJECT
(
  name        VARCHAR2(20),
  value       VARCHAR2(500),
  version     NUMBER,
  status      VARCHAR2(1),
  create_date DATE
);
/

CREATE OR REPLACE TYPE metadata_ntabtyp AS TABLE OF metadata_objtyp;
/

CREATE TABLE metadata_tab (
  key_id         VARCHAR2(33),
  key_context    VARCHAR2(10),
  grouping       VARCHAR2(30),
  create_date    DATE,
  md_ntab        metadata_ntabtyp,
    PRIMARY KEY (key_id, key_context, grouping)
  )
  NESTED TABLE md_ntab STORE AS meta_ntab (
    (PRIMARY KEY (nested_table_id, name, value, version))
    ORGANIZATION INDEX COMPRESS
  )
  RETURN AS LOCATOR
/

      and we said...
Do you mean like this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace type myScalarType as object
  2  ( nm varchar2(20),
  3    vl varchar2(20)
  4  )
  5  /

Type created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace type myTableType as table
of MyScalarType
  2  /

Type created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int, y myTableType )
nested table y store as y_tab
  2  /

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values
  2  ( 1, myTableType( myScalarType( 'volume.break', '1' ),
  3                    myScalarType( 'ELEMENT_NAME', 'topic'),
  4                    myScalarType( 'PUB_CODE', 'FLLB' )
  5                  ) );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values
  2  ( 2, myTableType( myScalarType( 'volume.break', '2' ),
  3                    myScalarType( 'ELEMENT_NAME', 'xtopic'),
  4                    myScalarType( 'PUB_CODE', 'BLLS' )
  5                  ) );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select t.y
  2    from t, TABLE(t.y) t2
  3   where t.x = 1
  4     and t2.nm = 'PUB_CODE'
  5     and t2.vl = 'FLLB'
  6  /

Y(NM, VL)
---------------------------------------------------------------------------------
--------------------------------------------------
MYTABLETYPE(MYSCALARTYPE('volume.break', '1'), MYSCALARTYPE('ELEMENT_NAME',
'topic'), MYSCALARTYPE('PUB_CODE', 'FLLB'))

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select *
  2    from TABLE( select t.y
  3                  from t, TABLE(t.y) t2
  4                 where t.x = 1
  5                   and t2.nm = 'PUB_CODE'
  6                   and t2.vl = 'FLLB'
  7              )
  8  /

NM                   VL
-------------------- --------------------
volume.break         1
ELEMENT_NAME         topic
PUB_CODE             FLLB

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select *
  2    from TABLE( select t.y
  3                  from t, TABLE(t.y) t2
  4                 where t.x = 1
  5                   and t2.nm = 'PUB_CODE'
  6                   and t2.vl = 'FLLB'
  7              ) T3
  8    where T3.nm = 'volume.break'
  9  /

NM                   VL
-------------------- --------------------
volume.break         1

ops$tkyte@ORA817DEV.US.ORACLE.COM>

Review &
      Followup

      Rating: 3
What if the
      subquery returns multiple records?  August 16, 2002

Reviewer:  Pete Krueger  from Rochester, NY USA

SELECT *
  FROM TABLE(SELECT m.md_ntab
               FROM metadata_tab m, TABLE(m.md_ntab) n
               WHERE m.key_context = 'PRI' AND
                     m.grouping = 'Element Metadata' AND
                     n.name = 'PUB_CODE' AND
                     n.value = 'FLLB') t
  WHERE t.name = 'volume.break'
SQL> /
SELECT * FROM TABLE(SELECT m.md_ntab
                    *
ERROR at line 1:
ORA-01427: single-row subquery returns more than one row

I tried changing:
  WHERE t.name = 'volume.break'
TO
  WHERE 'volume.break' IN t.name

But I get the same error - any suggestions?

Thanks a ton!
-Pete

          Followup:

You mean like this then:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int, y myTableType )
  2  nested table y store as y_tab
  3  /
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index y_idx on y_tab(nm,vl)
  2  /
Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values
  2  ( 1, myTableType( myScalarType( 'volume.break', '1' ),
  3                    myScalarType( 'ELEMENT_NAME', 'topic'),
  4                    myScalarType( 'PUB_CODE', 'FLLB' )
  5                  ) );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values
  2  ( 2, myTableType( myScalarType( 'volume.break', '2' ),
  3                    myScalarType( 'ELEMENT_NAME', 'xtopic'),
  4                    myScalarType( 'PUB_CODE', 'FLLB' )
  5                  ) );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;
Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select t.x, t2.nm, t2.vl
  2    from T, TABLE(t.y) T2
  3   where t.x in ( select t.x
  4                    from t, TABLE(t.y) t2
  5                   where t.x in ( 1, 2 )
  6                     and t2.nm = 'PUB_CODE'
  7                     and t2.vl = 'FLLB' )
  8     and nm = 'volume.break'
  9  /

         X NM                   VL
---------- -------------------- --------------------
         1 volume.break         1
         2 volume.break         2

      Rating: 3
I'm
      experimenting on it now but using your example...  August
      16, 2002
Reviewer:  Pete Krueger  from NY USA

Using your example, I would just add this (SEGMENT_UUID):

insert into t values
 ( 1, myTableType( myScalarType( 'volume.break', '1' ),
                   myScalarType( 'ELEMENT_NAME', 'topic'),
                   myScalarType( 'SEGMENT_UUID', 'I123'),
                   myScalarType( 'PUB_CODE', 'FLLB' )
                 ) );

1 row created.

insert into t values
 ( 2, myTableType( myScalarType( 'volume.break', '2' ),
                   myScalarType( 'ELEMENT_NAME', 'xtopic'),
                   myScalarType( 'SEGMENT_UUID', 'I124'),
                   myScalarType( 'PUB_CODE', 'FLLB' )
                 ) );

And the results I'm working for are:

VL                    VL
-------------------- --------------------
1                    I123
2                    I124

Alternatively, I could make do with:

NM                   VL
-------------------- --------------------
volume.break         1
SEGMENT_UUID         I123
volume.break         2
SEGMENT_UUID         I124

But maybe a join can be done somehow on the values.

I'll keep testing!  Thanks!

-Pete

          Followup:

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values
  2  ( 1, myTableType( myScalarType( 'volume.break', '1' ),
  3                    myScalarType( 'SEGMENT_UUID', 'I123'),
  4                    myScalarType( 'ELEMENT_NAME', 'topic'),
  5                    myScalarType( 'PUB_CODE', 'FLLB' )
  6                  ) );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values
  2  ( 2, myTableType( myScalarType( 'volume.break', '2' ),
  3                    myScalarType( 'SEGMENT_UUID', 'I124'),
  4                    myScalarType( 'ELEMENT_NAME', 'xtopic'),
  5                    myScalarType( 'PUB_CODE', 'FLLB' )
  6                  ) );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select t.x,
  2             max( decode( t2.nm, 'volume.break', t2.vl, null ))
"volumn.break",
  3             max( decode( t2.nm, 'SEGMENT_UUID', t2.vl, null )) SEGMENT_UUID
  4    from T, TABLE(t.y) T2
  5   where t.x in ( select t.x
  6                    from t, TABLE(t.y) t2
  7                   where t.x in ( 1, 2 )
  8                     and t2.nm = 'PUB_CODE'
  9                     and t2.vl = 'FLLB' )
 10     and nm in ('volume.break','SEGMENT_UUID')
 11   group by t.x
 12  /

         X volumn.break         SEGMENT_UUID
---------- -------------------- --------------------
         1 1                    I123
         2 2                    I124

ops$tkyte@ORA817DEV.US.ORACLE.COM>

QED

      Rating: 4
It's
      close...probably good enough...but not perfect  August 19,
      2002
Reviewer:  Pete Krueger  from NY USA
It returns 138 rows instead of 14 that I'd like - the 124 unwanted records just
have an empty volume number (I could probably make due with this and in java
just say if the volume number is null or an empty string, go to the next
record).

It returns all FLLB records because all records will have a SEGMENT_UUID, but
only some will have volume.break.

Instead of:
        name IN ('volume.break', 'SEGMENT_UUID')

I need: 'If I have a volume.break, include the SEGMENT_UUID...else skip the
record'

At least I don't have to put the Oracle objects into Java structs and loop
through each objects' attributes - that helps a lot.

If you have any last advice/suggestions, that'd be great!

Thanks a lot Tom - it's been a good learning experience and I look forward to
following your future discussions.

Pete

          Followup:

select * from
(
select t.x,
       max( decode( t2.nm, 'volume.break', t2.vl, null )) "volumn.break",
       max( decode( t2.nm, 'SEGMENT_UUID', t2.vl, null )) SEGMENT_UUID
  from T, TABLE(t.y) T2
 where t.x in ( select t.x
                  from t, TABLE(t.y) t2
                 where t.x in ( 1, 2 )
                   and t2.nm = 'PUB_CODE'
                   and t2.vl = 'FLLB' )
   and nm in ('volume.break','SEGMENT_UUID')
 group by t.x
)
where "volumne.break" IS NOT NULL
/

QED*2

August 19, 2002 Scott -- Thanks for the question regarding
      "Stress test ", version 8.1.7

      You Asked
The background:

I am in the beginning stages of developing a suite of stress test scripts.
Amongst other things, the purpose is to generate statistics on performance of
the Oracle RDBMS on various platforms.  Ultimately, we want to make intelligent
recommendations to prospective clients re hardware purchases.  Eventualy, they
will be deploying our application on the hardware we recommend.

Ultimately, we would like to run a tool like LoadRunner against our application,
against the various types of hardware.  However, cost constraints, time
constraints (bla bla bla) dictate the need for another short term strategy.

Using scripts has many benefits, the main one being the ability to send them to
the hardware vendor and have them send back the results.  From there, we begin
to get a better picture, ie - the intelligent recommendation.

What I have looked into:

Jonathon Lewis's book (Building Efficient Databases) has an excellent chapter on
Testing to Destruction - with some good recommendations on what should be
considered.

Searched your site - mentions LoadRunner and creating scripts.

The Question(s):

Are you aware of any other sources of information?  In an ideal world, I'd like
to have access to:

1.  Scripts that will pin CPU.
2.  Scripts that will generate high I/O
3.  Scripts to put Rollbacks and Redo (and consequently archiving)through their
paces.
4.  (anything else I should be looking into?)

If you aren't aware of specific scripts, could you recommend how I should
proceed?

Please note, I am not necessarily looking for you to write me the scripts;
however, I'd appreciate it if you could point me in the right direction.

Any recommendations you could make are appreciated.  Thanks

      and we said...
In an ideal world you would have scripts that test YOUR APPLICATIONS
performance.

High CPU load scripts -- so what, in real terms what would the results of this
mean to you (besides "absolutely nothing")

High IO scripts -- see above.

And so on.

You either need to rely on www.tpc.org (standard HARDWARE benchmarks) or build
your own test harness that mimics your applications performance and workload.
Nothing else is relevant.
Review & Followup

      Rating: 5
Right on the
      button  August 19, 2002
Reviewer:  Paul
      Dubar  from Australia
You are so right. There is little value in generating high I/O, CPU, memory
consumption if the application profile is inconsistent with reality. The
objective isn't to generate high anything, it's to observe resource utilisation
as your application load profile increases or varies wrt typical transaction
load.
Tools like Loadrunner are very good for capturing and replaying transaction
loads representative of user activity in an application environment specific to
an installation. From this you can extrapolate to some degree growth capacity
before reaching the limitations of your hardware resources.
Stress testing allows you to verify that specific platform configurations can
support specific load profiles with specific application configurations - to the
point of diminishing returns if so desired.
Volume testing allows you to verify how data volume and distribution may impact
transaction performance.
The majority of applications are deployed in disimilar configurations. One set
of test results invariably doesn't suit all installations; it can only serve as
an indication or reference point, based on what someone has deemed to be a
typical configuration.

August 19, 2002 pauline -- Thanks for the question regarding "add
      log_archive_dest_2 on another server", version 8.1.7.4

      You Asked
Tom,
If we have log_archive_dest_1="location=/appl/oracle/admin/product1/arch"
in the server A in which the instance of product1 sits there, and we want to
have log_archive_dest_2="location=/appl/oracle/admin/product2/arch"
for the instance of product1 but this place is on another server B,
how can I implement it?

Thanks.

Paulien

      and we said...
You would be using a standby database configuration to do this.  We'll transmit
the redo over the network.

See

http://otn.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76995/toc.htm
for details on the standby database.  You need not have an actual "standby"
running there, but it'll be the infrastructure necessary to do the remote
archival

Review &
      Followup

      Rating: 4
Very good point
        August 19, 2002
Reviewer:  Pauline
       from NY,USA


Subject | Article | Last Updated (East coast time)
Oracle Forms | article only | Aug 20, 2002 08:28 am
Table Space | article only | Aug 20, 2002 08:36 am
Is there a means of acquring Column Scheme info using a Select statement rather than "DESC" | article only | Aug 20, 2002 10:58 am
efficient use of BLOB | article only | Aug 20, 2002 02:06 pm
Query with NVL on the where | article only | Aug 20, 2002 09:29 pm
User Authentication. | article only | Aug 21, 2002 07:26 am
Prediction of next value based on existing data. | article only | Aug 21, 2002 07:50 am
Drop index before insert? | article only | Aug 21, 2002 08:29 am
regd. search using Intermedia | article only | Aug 21, 2002 08:57 am
how otn can improve my company | article only | Aug 21, 2002 09:01 am
Autoextensible datafiles | article only | Aug 21, 2002 09:08 am
Problem exporting more than 16,000 rows using Query Builder | article only | Aug 21, 2002 09:13 am
Tuning | article only | Aug 21, 2002 09:20 am
Oracle Internals | article only | Aug 21, 2002 10:59 am
Optimizing the query | article only | Aug 21, 2002 01:30 pm
Fetching Ref Cursors into Scalar Objects to Build a Nested Table | article only | Aug 21, 2002 04:03 pm
How to test a Database Link in a trigger? | article only | Aug 21, 2002 08:53 pm
How do I make this dynamic SQL to use bind variable? | article only | Aug 21, 2002 08:56 pm
column wise formattiing | article only | Aug 22, 2002 08:24 am
Host Command Problem in Forms6i (Forms 6.0.8.8.0 Ver) | article only | Aug 22, 2002 09:01 am
Importing Thai Characters are turning into Junk | article only | Aug 22, 2002 09:04 am
Sql file location | article only | Aug 22, 2002 09:07 am
v$log first_change# not same as checkpoint_change# in v$datafile | article only | Aug 22, 2002 09:08 am
DUPLICATE SYS Constraints | article only | Aug 22, 2002 09:11 am
How to generate PDF output from Stored Procedure | article only | Aug 22, 2002 09:36 am
Miscellaneous | article only | Aug 22, 2002 10:22 pm
SQL Query | article only | Aug 23, 2002 07:10 am
Writing more than one block - v$filestat info | article only | Aug 23, 2002 06:59 pm
Bad Policy or not | article only | Aug 23, 2002 07:02 pm
About Indexes | article only | Aug 23, 2002 07:22 pm
Aggregate function KEEP | article only | Aug 24, 2002 12:37 pm
Quick Search/Google Search on multiple text fields in multiple tables | article only | Aug 24, 2002 12:40 pm
Read only tablespaces. | article only | Aug 24, 2002 12:45 pm
Not Exists : Intermedia and others | article only | Aug 24, 2002 01:04 pm
About Indexes | article only | Aug 24, 2002 01:32 pm
deployment | article only | Aug 24, 2002 02:10 pm
CHECK FOR CURSORS | article only | Aug 24, 2002 07:38 pm
Recover until change | article only | Aug 25, 2002 09:09 am
HTML/SMTP Dynamic when email opened | article only | Aug 25, 2002 09:12 am
transactions that does not generate data in rollback segments | article only | Aug 25, 2002 06:00 pm
Transaction Question | article only | Aug 26, 2002 07:57 am
"Java procedure for host calls on Unix environment", | article only | Aug 26, 2002 08:12 am
pl sql update array processing | article only | Aug 26, 2002 08:30 am
Advantages of using Objects | article only | Aug 26, 2002 09:19 am
Large update.... | article only | Aug 26, 2002 01:40 pm
Size of pl/sql tables | article only | Aug 26, 2002 01:48 pm
use of intermedia | article only | Aug 26, 2002 07:36 pm
How to implement versions | article only | Aug 26, 2002 07:40 pm
row(s) 1 - 48 of 48
August 20, 2002 Sajid -- Thanks for the question regarding "Oracle Forms", version 9.0.1
You Asked
Hi Tom,
Could you please tell me if there is any latest version of Oracle Developer
2000. Couple of years back i used to work on it. But then i moved to web-based
system. Now i want to see how it has changed during these period.
I have Oracle 9.0.1 database installed on my machine. What do i have to install
for that?

I would appreciate if you can give me the usefull links for more information
about the same.

Thanks in advance,

Sajid Anwar
London
and we said...
http://otn.oracle.com/
has the documentation
has the software for download
has discussion forums for the products
has examples
has technical notes/whitepapers
has tons of stuff

Developer 2000 is forms 9i now.  No more client server versions -- just 3 tier.
Review &
      Followup

      Rating:
      4
thanks  August 20, 2002
Reviewer:  Sajid
      Anwar  from London
thanks a lot Tom.

Regards,
Sajid Anwar
London

August 20, 2002 Sajid -- Thanks for the question regarding "Table
      Space", version 9.0.1

      You Asked
Hi Tom,
First of all thanks for help about HOW TO CREATE A NEW DATABASE. I was going
through the link you gave it to me yesterday. I found few things very confusing.
Please point me if i am wrong.
1) Every database has an init.ora file. And in this file, we save the settings
like DB_NAME, INSTANCE_NAME, SERVICE_NAME and all.
I always confuse with these 3 DB_NAME, INSTANCE_NAME, SERVICE_NAME. Could you
please explain in one or two lines.

2) What is the role of TABLESPACE in database? No where i found any reference to
it in database creation.

3) Whats the UNDO TABLESPACE?

Thanks in advance for your help.

Sajid Anwar
London

      and we said...
1) there is a database -- it is a set of files.  that is the db_name, it goes
with the files.

there is an instance.  an instance is a set of processes and a shared memory
segment (explicitly does not include database files).  An instance can mount and
open a single database.  A single database can be mounted and opened by many
instances at the same time in a cluster (this is known as RAC -- real
application clusters -- there is a concepts guide for that on OTN if you are
interested in reading more)

A service is a collection of instances, a logical collection.  It is used at the
networking layer.  If a client connects to a service, we'll connect them to one
of the instances that make up the service -- all of which are typically mounting
the same database.  This is for scalability and availability.  You do not
connect to an instance (which may not be available due to a crash) you connect
to a service and we'll load balance you over the available instances all of
which have full read write access to the database.

2) concepts guide -- read it, learn it, live it.  This is a well documented
concept.

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/nav/docindex.htm#index-CON
3) see #2

Review &
      Followup

      Rating: 5
You are
      great  August 20, 2002
Reviewer:  Sajid
      Anwar  from London
Tom, you are simply genius.

Thanks a million.

Regards,
Sajid Anwar
London

August 20, 2002 Burton -- Thanks for the question regarding "Is
      there a means of acquring Column Scheme info using a Select statement
      rather than "DESC"", version 8.1.6.0.0

      You Asked
Dear Tom:

Were I to want the description of a table, I could within SQL type in the
statement "DESC MYTABLE_NAME;".  However, there are some Oracle ADO providers
which do NOT support the "DESC " syntax, so this is a problem if one wants to
access such information from inside an executable.

Is there then an alternate way using a SELECT statement whereby I could acquire
a list of Column names and data types for a given table?

I know for Indexes, one can determine the columns by a statement such as:
SELECT COLUMN_NAME FROM USER_IND_COLUMNS WHERE INDEX_NAME = 'MYINDEX"

Therefore, I am hoping logically that one could use a SELECT statement to
uncover the columns for a table.  Is there such a way?

Thank you for your kind attention to my question.  I look forward to your e-mail
response.

Sincerely,

Burton G. Wilkins

      and we said...
Here is the fancy answer -- but it shows how you can do it in PLSQL straight
out.  This just lets you get a result set back that is the results of the
describe:

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace type myScalarType
  2  as object
  3  ( cname          varchar2(30),
  4    datatype_name  varchar2(30),
  5    maxlen         number,
  6    precision         number,
  7    scale          number,
  8    nullable  varchar2(1)
  9  )
 10  /

Type created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace type myTableType as table
of myScalarType
  2  /

Type created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function describe( p_query
in varchar2 ) return myTableType
  2  authid current_user
  3  as
  4      l_data    myTableType := myTableType();
  5      l_desc  dbms_sql.desc_tab;
  6      l_cur   number;
  7      l_ncols number;
  8  begin
  9      l_cur := dbms_sql.open_cursor;
 10      dbms_sql.parse( l_cur, p_query, dbms_sql.native );
 11      dbms_sql.describe_columns( l_cur, l_ncols, l_desc );
 12      dbms_sql.close_cursor( l_cur );
 13
 14      l_data.extend( l_ncols );
 15      for i in 1 .. l_ncols
 16      loop
 17          l_data(i) := myScalarType( l_desc(i).col_name, NULL,
l_desc(i).col_max_len,
 18                                     l_desc(i).col_precision,
l_desc(i).col_scale, 'Y' );
 19          if ( NOT l_desc(i).col_null_ok )
 20          then
 21              l_data(i).nullable := 'N';
 22          end if;
 23          select decode(l_desc(i).col_type,
 24                           1, decode(l_desc(i).col_charsetform, 2,
'NVARCHAR2', 'VARCHAR2'),
 25                           2, decode(l_desc(i).col_scale, null,
 26                                   decode(l_desc(i).col_precision, null,
'NUMBER', 'FLOAT'), 'NUMBER'),
 27                           8, 'LONG',
 28                           9, decode(l_desc(i).col_charsetform, 2, 'NCHAR
VARYING', 'VARCHAR'),
 29                          12, 'DATE', 23, 'RAW', 24, 'LONG RAW',
 30                          69, 'ROWID',
 31                          96, decode(l_desc(i).col_charsetform, 2, 'NCHAR',
'CHAR'),
 32                         105, 'MLSLABEL',
 33                         106, 'MLSLABEL',
 34                         111, 'OBJECT_TYPE',
 35                         112, decode(l_desc(i).col_charsetform, 2, 'NCLOB',
'CLOB'),
 36                         113, 'BLOB',
 37                         114, 'BFILE',
 38                         115, 'CFILE',
 39                         121, 'OBJECT_TYPE',
 40                         122, 'OBJECT_TYPE',
 41                         123, 'OBJECT_TYPE',
 42                         178, 'TIME(' ||l_desc(i).col_scale|| ')',
 43                         179, 'TIME(' ||l_desc(i).col_scale|| ')' || ' WITH
TIME ZONE',
 44                         180, 'TIMESTAMP(' ||l_desc(i).col_scale|| ')',
 45                         181, 'TIMESTAMP(' ||l_desc(i).col_scale|| ')' || '
WITH TIME ZONE',
 46                         231, 'TIMESTAMP(' ||l_desc(i).col_scale|| ')' || '
WITH LOCAL TIME ZONE',
 47                         182, 'INTERVAL YEAR(' ||l_desc(i).col_precision||')
TO MONTH',
 48                         183, 'INTERVAL DAY(' ||l_desc(i).col_precision||')
TO SECOND(' ||
 49                               l_desc(i).col_scale || ')',
 50                         208, 'UROWID',
 51                              'UNDEFINED') into l_data(i).datatype_name
 52            from dual;
 53      end loop;
 54
 55      return l_data;
 56  end;
 57  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from TABLE( cast( describe('select *
from scott.emp') as myTableType ) );

CNAME      DATATYPE_NAME       MAXLEN  PRECISION      SCALE N
---------- --------------- ---------- ---------- ---------- -
EMPNO      NUMBER                  22          4          0 N
ENAME      VARCHAR2                10          0          0 Y
JOB        VARCHAR2                 9          0          0 Y
MGR        NUMBER                  22          4          0 Y
HIREDATE   DATE                     7          0          0 Y
SAL        NUMBER                  22          7          2 Y
COMM       NUMBER                  22          7          2 Y
DEPTNO     NUMBER                  22          2          0 Y

8 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

Note that the DECODE is gratuitously "borrowed" from the text of
DBA_TAB_COLUMNS and might have to be altered from release to release!

Review &
      Followup

      Rating: 4
An alternate to
      the DESC statement.  August 20, 2002
Reviewer:
      Burton G. Wilkins  from Los Angeles, CA
Thank you Tom.  This looks brilliant.  I can't wait to try it out.

August 20, 2002 Sudip -- Thanks for the question regarding
      "efficient use of BLOB ", version 8.1.7

      You Asked
 Hi All,
One of my tables has a BLOB column, in which I will store Excel Files. Now this
particular Column will be updated very frequenty. I have taken the precaution of
putting the BLOB column on a separate tablespace. All my tablespaces are LMT. I
have increased FREELISTS on this table to 10.
What should be the optimal value of PCTUSED, PCTFREE parameter?

Regards
Sudip

      and we said...
pctused and pctfree do not have any meaning with regards to BLOBS.  A blob
allocates space in a series of CHUNKS (you controlled the chunk size when you
created it).  If you for example, set the chunksize to 32k and you have an 8k
block -- then a blob will always allocate blocks for itself in multiples of 4
blocks.  If your blob is 32k + 100 bytes for example, it will consume 8 blocks
(4 for the first 32k chunk, 4 for the remaining 100 bytes).  The 3 empty blocks
at the end of the blob are dedicated to that blob -- they are not on a freelist
anywhere.

Remember also that there is block overhead so my example of 32k + 100 bytes is
too simplistic.  32k of blob data will not fit on 4-8k blocks, that simple 32k
blob will consume 2 x 4-8k blocks due to block overhead.

When a blob "shrinks" -- it will give up free space in blocks of CHUNK size
bytes.  eg: in that 32k + 100 byte example, if you later trim the blob back to
16k -- it'll give up the one 32k chunk back for reuse.
Review &
      Followup

      Rating:
      5
  March 18, 2002
Reviewer:  Sudip
      Sarkar  from India
Thanks Tom, It is indeed a very useful information

      Rating: 5
LOB CHUNK size
      and index clause  August 20, 2002
Reviewer:
      vinci  from USA
Hi Tom,
I had two questions.

1.If i have to follow a Uniform, locally managed tablaspace strategy for an LOB
column in a table(LOB in a seperate tablespace than the table data), then the
uniform extent size shud be equal to the CHUNK size?
My datablock size is 4k (Solaris, Oracle 8.1.7.4).
We normally use standard extent sizes for tablespaces based on what size the
tables are going to be. For example if a table size is between 96k and 128m then
all tables like that go into one tablespace which will have a NEXT or UNFIROM
extent size of 128k ....and so on.
But in case of a LOB inside a table...should NEXT or UNIFORM be equal to CHUNK
size for that LOB tablespace?(which is different from teh general convention
that we follow). What is your advice on this?
Even if i continue with the same 128kextent size allocation for teh LOB
tableapsce also ... does CHUNK size matter?(say CHUNK size is 4k). What wud be
ur recommendation.

2.Oracle documentiation says not to mention index clause for LOB tablespace as
of oracle 8i. Even if i use it, oracle will ignore it.
But can i just specify the index name atlaest(for maintenance/standards
following)? Will it have any effect on how oracle handles the LOB and its index?

Thanks

          Followup:

1) no, that would be not a good idea.  You want a couple of chunks in an extent
-- the rule would be "make the extent size a MULTIPLE of the chunk size".

An extent/ chunk would be a little over zealous, you want N chunks / extent.
You get to pick N -- it depends on the ultimate sum of the sizes of the lobs
you'll be storing -- again, shooting for 1000 or less extents

2) you cannot control it (technically).  The name should have no effect.  The
following is not documented but appears to work (but could stop working
unpredicably!)

ops$tkyte@ORA920.US.ORACLE.COM> CREATE TABLE T (X NUMBER, Y CLOB)
  2  LOB (Y) STORE AS
  3  ( INDEX foo )
  4  /

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> CREATE TABLE T2 (X NUMBER, Y CLOB)
  2  /

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select index_name, index_type, table_name from
user_indexes;

INDEX_NAME                     INDEX_TYPE                  TABLE_NAME
------------------------------ ---------------------------
------------------------------
FOO                            LOB                         T
SYS_IL0000030677C00002$$       LOB                         T2

ops$tkyte@ORA920.US.ORACLE.COM>

Use at your own risk for that reason.  It might be best to just leave it named
by the system.  It is not like you can alter it or do anything to it.

      Rating:
      5
  August 20, 2002
Reviewer:  vinci

Thanks Tom...

August 20, 2002 Prince -- Thanks for the question regarding
      "Query with NVL on the where", version 8.1.7.4

      You Asked
Dear Tom,

I have the following query,

select a.* from TableA a
WHERE   (A.CNY#, NVL(A.LOCATION#,0)) IN
                        (       SELECT  SA.CNY#, SA.LOCATIONKEY
                                FROM    mylocA SA
                                WHERE   SA.SESSIONKEY   = 'KEY1'
                                UNION
                                SELECT  SB.CNY#, SB.LOCATIONKEY
                                FROM    mylocB SB
                                WHERE   SB.SESSIONKEY   = 'KEY1'
                         );

What is the best way of re-writing this query to use the index on cny#,
location# on TableA.

Note: In my version of oracle SE, fuctioanl index is no an option.

      and we said...
no rewrite needed:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table tablea ( cny# int, location#
int, data varchar2(500) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table myloca ( cny# int, locationkey
int, sessionkey varchar2(5) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table mylocb ( cny# int, locationkey
int, sessionkey varchar2(5) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index tablea_idx on
tablea(cny#,location#);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.* from TableA a
  2  WHERE      (A.CNY#, NVL(A.LOCATION#,0)) IN
  3                          (       SELECT  SA.CNY#, SA.LOCATIONKEY
  4                                  FROM    mylocA SA
  5                                  WHERE   SA.SESSIONKEY   = 'KEY1'
  6                                  UNION
  7                                  SELECT  SB.CNY#, SB.LOCATIONKEY
  8                                  FROM    mylocB SB
  9                                  WHERE   SB.SESSIONKEY   = 'KEY1'
 10                           );

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   NESTED LOOPS
   2    1     VIEW OF 'VW_NSO_1'
   3    2       SORT (UNIQUE)
   4    3         UNION-ALL
   5    4           TABLE ACCESS (FULL) OF 'MYLOCA'
   6    4           TABLE ACCESS (FULL) OF 'MYLOCB'
   7    1     TABLE ACCESS (BY INDEX ROWID) OF 'TABLEA'
   8    7       INDEX (RANGE SCAN) OF 'TABLEA_IDX' (NON-UNIQUE)

ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table tablea compute statistics;

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.* from TableA a
  2  WHERE      (A.CNY#, NVL(A.LOCATION#,0)) IN
  3                          (       SELECT  SA.CNY#, SA.LOCATIONKEY
  4                                  FROM    mylocA SA
  5                                  WHERE   SA.SESSIONKEY   = 'KEY1'
  6                                  UNION
  7                                  SELECT  SB.CNY#, SB.LOCATIONKEY
  8                                  FROM    mylocB SB
  9                                  WHERE   SB.SESSIONKEY   = 'KEY1'
 10                           );

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=5 Card=1 Bytes=304)
   1    0   NESTED LOOPS (Cost=5 Card=1 Bytes=304)
   2    1     VIEW OF 'VW_NSO_1' (Cost=6 Card=2 Bytes=52)
   3    2       SORT (UNIQUE) (Cost=6 Card=2 Bytes=60)
   4    3         UNION-ALL
   5    4           TABLE ACCESS (FULL) OF 'MYLOCA' (Cost=1 Card=1 Bytes=30)
   6    4           TABLE ACCESS (FULL) OF 'MYLOCB' (Cost=1 Card=1 Bytes=30)
   7    1     TABLE ACCESS (BY INDEX ROWID) OF 'TABLEA'
   8    7       INDEX (RANGE SCAN) OF 'TABLEA_IDX' (NON-UNIQUE)

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

Review &
      Followup

      Rating: 4
Thanks
      Tom.  August 21, 2002
Reviewer:
      A reader
I got confused becaz of the NVL.

August 21, 2002 John -- Thanks for the question regarding "User
      Authentication.", version 8.1.7

      You Asked
Tom;

We are running Oracle 8.17 on AIX 4.3.

We use operating system authentication to validate our production id's.

In some situation's we would like to signon to our production id's remotely
through sqlplus from a NT workstation.

Is it possible to signon to an userid (ie:PROD1)that is defined to oracle as
'identified by external' from a workstation on the network where your signed on
as 'USER1'.

Thanks in advance.

      and we said...
Yup.

I have:

ops$tkyte@ORA817DEV.US.ORACLE.COM> show parameter authen

NAME                                 TYPE    VALUE
------------------------------------ ------- ------------------------------
os_authent_prefix                    string  ops$

and then create my user as:

create user ops$tkyte identifed by top_secret_password;

Now I can:

sqlplus /

locally and

sqlplus ops$tkyte/top_secret_password@db

remotely....

Like this:

system@ORA817DEV.US.ORACLE.COM> create user ops$tkyte identified by pw;

User created.

system@ORA817DEV.US.ORACLE.COM> grant dba to ops$tkyte;

Grant succeeded.

system@ORA817DEV.US.ORACLE.COM> connect /
Connected.
system@ORA817DEV.US.ORACLE.COM> @login
system@ORA817DEV.US.ORACLE.COM> set termout off
ops$tkyte@ORA817DEV.US.ORACLE.COM> connect ops$tkyte/pw@ora817dev.us.oracle.com
Connected.
ops$tkyte@ORA817DEV.US.ORACLE.COM> @login
ops$tkyte@ORA817DEV.US.ORACLE.COM> set termout off
ops$tkyte@ORA817DEV.US.ORACLE.COM>

(my login.sql puts the username in the prompt -- like doing a show user)
Review &
      Followup

      Rating: 3
Does this
      always work?  August 20, 2002
Reviewer:
      Matt  from Australia
What if the os_authent_prefix is null?

SQL> show parameter os_authent_prefix

NAME              TYPE   VALUE
----------------- ------ -----
os_authent_prefix string

This doesn't seems to work the same if the value is NULL.

          Followup:

That is right, its gotta be set so we KNOW what an os authenticated account
looks like.

Yes, this always works -- given that the os_authent_prefix is setup.

August 21, 2002 Peter -- Thanks for the question regarding
      "Prediction of next value based on existing data.", version 8.1.7

      You Asked
Hello Tom
I have the task of trying to predict the trend of a value
given a table of existing ones. I just need to know whether the trend is
increasing or decreasing.
For example:
Given a table called predict
create table predict(x number(2),t date);
SQL> insert into predict values (1,sysdate);
SQL> insert into predict values (2,sysdate);
SQL> insert into predict values (3,sysdate);
SQL> insert into predict values (4,sysdate);
SQL> select * from predict;

         X T
---------- -------------------
         1 2002-08-20-13:59:00
         2 2002-08-20-13:59:08
         3 2002-08-20-13:59:18
         4 2002-08-20-13:59:27

The next value for this can be predicted to be 5 and so
I can return 'increasing'
This can be done by comparing each value with the previous one
but for a very large table this computation may be slow.

function isIncrease( p_value1 in number,
                     p_value2 in number,
                   ) return varchar2
    as
    begin
        if (p_value1 > p_value2) THEN
            return 'TRUE';
        else
            return 'FALSE';
        end if;

end iSIncrease;

I think that I will have to use PL/SQL but I'm wondering if correct use of the
SELECT statement can help speed up computation.

Can you advise on the appropriate method?

      and we said...
Analytic functions to the rescue once again.  Using the LAG or LEAD function,
you can do this fairly easily.  With lag and lead you can reach backwards N rows
(default N=1) or forwards N rows in the result set without having to do a self
join.

Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from predict;

         X T
---------- --------------------
         1 20-aug-2002 08:39:17
         2 20-aug-2002 08:39:19
         3 20-aug-2002 08:39:21
         4 20-aug-2002 08:39:23

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select x,
  2         case when x > one_before then 'increasing'
  3              when x = one_before then 'no change'
  4              when x < one_before then 'decreasing'
  5              else                     'unknown'
  6         end,
  7         case when x > one_after then 'increasing'
  8              when x = one_after then 'no change'
  9              when x < one_after then 'decreasing'
 10              else                     'unknown'
 11         end
 12    from (
 13  select x,
 14         lag(x)  over ( order by t ) one_before,
 15         lead(x) over ( order by t ) one_after
 16    from predict
 17         )
 18  /

         X CASEWHENX> CASEWHENX>
---------- ---------- ----------
         1 unknown    decreasing
         2 increasing decreasing
         3 increasing decreasing
         4 increasing unknown

ops$tkyte@ORA817DEV.US.ORACLE.COM>

Review &
      Followup

      Rating:
      5
Prediction  August 20, 2002
Reviewer:
      Peter Jones  from Switzerland
Unbelievable. thanks again.

      Rating: 5
Prediction -
      the use of CASE  August 20, 2002
Reviewer:
      Peter Jones  from Switzerland
Hello Again
I guess the use of CASE in this example means that
the select command will not go into a PRODECURE
or FUNCTION?

          Followup:

well, sort of a trick question as the use of the analytic functions would do
that first.

Until 9i yes.

See:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3027089372477
for the reason and two easy solutions (views or dynamically opened ref cursors
-- both of which will make this solution fully available in plsql)

      Rating: 5
Prediction and
      ref cursor  August 20, 2002
Reviewer:  Peter
      Jones  from Switzerland
I tried the views and although it worked it was quite slow.
The dymac ref cursor from your example woule have columns which do not specified
names (the CASE columns).
If in the dynmaic ref cursor I give an alias for the column then is a
compilation error. How can I get around this?

open :y for 'select x,
           case when x > one_before then 1
                when x = one_before then 0
                when x < one_before then -1
                else                     9
           end '||a||'
     from (
   select x,
          lag(x)  over ( order by t ) one_before
    from predict)'

    loop
     fetch  :x into rec;
     exit when :x%NOTFOUND;
     dbms_output.put_line(rec.a);
     end loop;

          Followup:

Name them then.

do not concatenate A in -- just put the name in there

open :y for 'select x,
           case when x > one_before then 1
                when x = one_before then 0
                when x < one_before then -1
                else                     9
           end INFORMATION '
     from (
   select x,
          lag(x)  over ( order by t ) one_before
    from predict)'

you now have a column information.

      Rating: 3
Prediction -
      PLS-00302: component 'INFORMATION' must be declared  August
      21, 2002
Reviewer:  Peter Jones  from Switzerland

Still a problem I'm afraid. If I do as you say I get the
error  PLS-00302: component 'INFORMATION' must be declared
when trying to use INFORMATION in the following loop.

loop
     fetch  y into rec;
     exit when y%NOTFOUND;
     x := rec.INFORMATION;
     end loop;
     dbms_output.put_line(x);

If I declare INFORMATION to be a number then I get the same.

          Followup:

and just how is rec itself defined?  You must not have put an attribute in there
called "information" then.  Here is an example.  I use a view to more easily
create a template record but then use a specific query in the plsql block (so
the view is not queried from, just used to defined a record easily)

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view template_view
  2  as
  3  select x,
  4         case when x > one_before then 'increasing'
  5              when x = one_before then 'no change'
  6              when x < one_before then 'decreasing'
  7              else                     'unknown'
  8         end one_before_info,
  9         case when x > one_after then 'increasing'
 10              when x = one_after then 'no change'
 11              when x < one_after then 'decreasing'
 12              else                     'unknown'
 13         end one_after_info
 14    from (
 15  select x,
 16         lag(x)  over ( order by t ) one_before,
 17         lead(x) over ( order by t ) one_after
 18    from predict
 19         )
 20  /

View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_rec  template_view%rowtype;
  3
  4      type rc is ref cursor;
  5      l_rc rc;
  6  begin
  7      open l_rc for
  8      'select x,
  9             case when x > one_before then ''increasing''
 10                  when x = one_before then ''no change''
 11                  when x < one_before then ''decreasing''
 12                  else                     ''unknown''
 13             end one_before_info,
 14             case when x > one_after then ''increasing''
 15                  when x = one_after then ''no change''
 16                  when x < one_after then ''decreasing''
 17                  else                     ''unknown''
 18             end one_after_info
 19        from (
 20      select x,
 21             lag(x)  over ( order by t ) one_before,
 22             lead(x) over ( order by t ) one_after
 23        from predict
 24             )';
 25
 26      loop
 27          fetch l_rc into l_rec;
 28          exit when l_rc%notfound;
 29          dbms_output.put_line( l_rec.x || ',' ||
 30              l_rec.one_before_info || ',' || l_rec.one_after_info );
 31      end loop;
 32      close l_rc;
 33  end;
 34  /
1,unknown,decreasing
2,increasing,decreasing
3,increasing,decreasing
4,increasing,unknown

PL/SQL procedure successfully completed.

      Rating: 5
Prediction -
      dynamic cursor defined.  August 21, 2002
Reviewer:
      Peter Jones  from Switzerland
Thanks it works well now. I will have to rememeber this trick in the future as I
like to use this type of cursor
and use GROUP functions which need a column alias.

August 21, 2002 Ping -- Thanks for the question regarding "Drop
      index before insert?", version 8.1.7

      You Asked
I did it in a stored procedure -- this 90K records can be inserts, updates or
deletes (mostly insert), depending on an action flag.

The fact table is partitioned and the indexes involved are local partitioned.
The partitioned is based on a column called transaction date. Right now we have
5 partitions, each with half year's data of about 2G in size. Thanks, Ping

Hi Tom,

There is a very strange thing happened to me: I have a fact table (not very big
1.9 mil records now but will grow) with 9 bitmap indexes. I have to insert some
records (90K) everyday.

The strange thing is it could not finish the insert because the index tbs and
the rollback tbs keep grow until fail out of space. But if I drop those bitmap
indexes, then the insert can proceed and after I recreate those indexes, the
space used for index is less than 10% of the size when the previous insert
failed.

I guess I can drop-insert-recreate indexes, but with the data grows in this
table, recreate will take longer and longer time. SO I want to ask you, from
what I describe can you tell me what is going wrong?

Thanks, Ping

      and we said...
Ok, I'll recommend a book -- Jonathan Lewis's Practical Oracle8i.  He has a
*really* good discussion on this in there, along with tons of other stuff.  If
you don't want to get the book (wrong choice IMHO), you can read an online
article of his on this topic as well
http://www.dbazine.com/jlewis3.html

Basically, you are doing singleton updates/inserts/deletes against this table.
That is really bad for bitmaps -- horrible.  You can disable the index, perform
the operation and then rebuild the local index partitions in parallel
(unrecoverable even) afterwards.  That would be the most efficient approach for
this data warehouse incremental load.

August 21, 2002 Chaitanya -- Thanks for the question regarding
      "regd. search using Intermedia", version 8i

      You Asked
When returning the search results, how to send the location of the keyword in
the content returned by using Oracle 8i, so that in the front end it can be
easily traced and highlighted.

      and we said...
see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:350017644883
we do the markup already. the front end doesn't have to.
August 21, 2002 Astari -- Thanks for the question regarding "how
      otn can improve my company", version 8.1.7

      You Asked
Hello, Tom, this is my second question to you, before this
i've send you a question and i get fast answer from you,

this is my question,

1. i've working a lot on java programming, and right now i'm
   already have a product to sell, my question
   how otn/any oracle web site can help me to promote my program
   with my current membership that almost is free ?

2. i've ever seen an oracle chart portlet that running on Oracle
   aplication server on Linux, from what i see that chart is pretty
   poor on look that's why one of my product is online chart that
   can be linked to oracle database, perhaps you can tell me
   who i should contact in Oracle Company to offer my program ?

thanx for your answer

      and we said...
1) OTN is the Oracle Technical Network.  For technical issues.

Sounds like you are ready to move onto OPN -- the Oracle Partner Network.
Checkout
http://partner.oracle.com/
2) see #1
August 21, 2002 CJ -- Thanks for the question regarding
      "Autoextensible datafiles", version 8.1.6

      You Asked
How much overhead is involved in having autoextensible datafiles? What I mean
is, if the datafiles are 100% full, how much overhead is involved when it has to
autoextend, as opposed to having a pre-created extent?

Thanks for your time and this great service you provide.

      and we said...
I've never measured it exactly however -- it'll be directly proportional to the
amount of time it takes us to initialize the autoextend by amount of disk.

eg: go onto your server and create a tablespace with a 1m datafile.  Now, resize
that datafile and time it.  That'll give you a really good idea.  Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> set timing on
ops$tkyte@ORA817DEV.US.ORACLE.COM> create tablespace testing
  2  datafile '/tmp/testing.dbf' size 1m;

Tablespace created.

Elapsed: 00:00:00.68
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter database datafile '/tmp/testing.dbf'
resize 2m;

Database altered.

Elapsed: 00:00:00.29
ops$tkyte@ORA817DEV.US.ORACLE.COM> c/2/22
  1* alter database datafile '/tmp/testing.dbf' resize 22m
ops$tkyte@ORA817DEV.US.ORACLE.COM> /

Database altered.

Elapsed: 00:00:02.94
ops$tkyte@ORA817DEV.US.ORACLE.COM> c/22/122
  1* alter database datafile '/tmp/testing.dbf' resize 122m
ops$tkyte@ORA817DEV.US.ORACLE.COM> /

Database altered.

Elapsed: 00:00:13.52
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop tablespace testing;

Tablespace dropped.

Elapsed: 00:00:00.31
ops$tkyte@ORA817DEV.US.ORACLE.COM> !rm -f /tmp/testing.dbf

So, fraction of second for a 1meg extend, 3 seconds for a 20meg extend and 14
seconds for a 100meg extend....

ON MY SYSTEM.

Not on your system!  Your mileage will vary.
Review &
      Followup

      Rating: 5
Thank
      you  August 21, 2002
Reviewer:  CJ
       from Albany, NY USA
Excellent. Thanks.

August 21, 2002 Craig -- Thanks for the question regarding
      "Problem exporting more than 16,000 rows using Query Builder", version
      v6.0.5

      You Asked
I am trying to use Query Builder (v6.0.7.0.0 from Oracle Toolkit v6.0.5.34.1
Production) to export more than 16,000 rows of data from a query to an Excel
file. The query exceeds 200,000 rows and we need to extract all of it. I tried
setting the Edit > Preferences > Command Line value for MAXROWS for "All
Documents" to greater than 16,000 but I could still only export 16,000 rows.

Any help would be greatly appreciated.
Thanks,
Craig

      and we said...
Well, since excel is going to quit at 65,536 rows.... (eg: that file, if it were
produced, would not be usable by excel)....

I don't have query builder but I can offer up a couple of tools to extract the
data given a query (eg: export the query from query builder and I can get it out
for you easily)

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:5071466810401
has links to all of them.
August 21,
      2002 Debra -- Thanks for the question
      regarding "Tuning", version 8.1

      You Asked
Tom,

I have run UTLBSTAT and UTLESTAT from two machines. When I view Report.txt in
one report I see that report commands are listed from the SVRMGR> prompt from
another machine I see the report displays the SQL> prompt. I have run UTLBSTAT
and UTLESTAT from SQL*PLUS in both cases. The reason I ask is because the output
is formatted (columns lined up etc.) with the report using SVRMGR.

Do you know of a command that I can use to flush the library cache?

Is there a way outside of using a GUI tool to find out how many times an index
has been hit per session and since the database has been started?

Thank you for your help!

      and we said...
You should not be using bstat/estat -- statspack is the tool for you!  If you
have my book "Expert one on one Oracle" -- see chapter 10, if not, search this
site for statspack or check out

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/server.901/a87503/tools.htm#29125
(which is the 9i doc set but applies).

utlbstat/estat was designed to be run using svrmgrl only, it contains formatting
commands unique to svrmgrl (and I believe you ran it with svrmgrl on the one
machine, not sqlplus -- 100% sure on that actually).

statspack -- that is a sqlplus only tool.

alter system flush shared_pool;  flushes the library cache.

actually, there isn't any way (gui or otherwise) to find out how many times an
index has been hit per session/since startup -- not until 9i Release 2 anyway
when we track segment specific hits.
August 21, 2002 Sandy -- Thanks for the question regarding
      "Oracle Internals", version 8.1.7

      You Asked
Hi Tom,

I'm looking for some solid documentation on Oracle internals, can you please
point me some links.

Regards,
Sandy

      and we said...
Like what internals, Oracle is a big place.  99.999% of tuning and configuring
requires nothing beyond what is in the manuals (and I almost never get to that
0.001% myself)

Have you read and totally *mastered* the concepts guide?  the admin guide?  I
mean -- if I asked you to draw a picture of Oracle on a whiteboard -- showing
all of the processes, how they interact, what they do, whats in the SGA, what
all the files we use are and how they interact and what happens when a client
connects via dedicated / shared server -- that would be really easy to do?
(that is my all time favorite interview question.  Maybe 10% of the people can
actually do it)

There are lots of books out there that claim to be "internals" books, you can
look at them.  There is an internals course taught by Oracle Education as well
-- I just hope before anyone looks at them, they master -- totally master to the
point where they don't need to look at the concepts guide -- the contents of the
concepts guide.

Review &
      Followup

      Rating:
      2
  August 21, 2002
Reviewer:  Anil
       from NYC
Tom, Your comments are not at all in-appropriate.

This is just to answer the question:

The first place to go is ofcourse www.ixora.com.au, run by Steve Adams. Also get
his book. Another good source is Don Burlesons Oracle Internals Magazine.

Beyond these, you have to sweat it out yourself (by trial and error) if you are
not an Oracle insider!

          Followup:

And Caveat Emptor.

Steves site -- above reproach, I go there myself and link to it.  Excellent.
That is due to his methods more then anything else.  He, like Jonathan Lewis
(and i would like to include myself) try to prove scientifically that what we
say is accurate.  Others -- well, caveat emptor.

I was having an email discussion with the author of a series of "tuning tips"
recently (search for dbasupport on this site to see the thread that started it).
 He asked me to comment more fully on all of his tips since I was taking
exception to the ones people asked me to comment on here.  Turns out -- almost
all of them were either inaccurate, incomplete or just 100% wrong.  His comment
back to me:

"I'm really surprising that the Tuning Book was containing so many errors."

Basically, he was just saying what others had said and never bothered to
actually *test* or *prove* any of it.

I would say that you should sweat all of it out -- by trial and error.  Things
change (was is true in version 8.1.5 is not necessarily true in 8.1.7), people
mis-interpret what was intended (this I see all of the time), people mis-apply
one solution to too many problems, and so on.

So, I'll recommend Steve Adams and Jonathan Lewis's material 100%.  But, I'll
betcha the concepts guide is still something that needs to be studied here.

      Rating: 5
RE: Maybe 10%
      of the people...  August 21, 2002
Reviewer:
      Mark A. Williams  from Indianapolis, IN USA
"Maybe 10% of the people can actually do it"...

Boy, I find that surprising/perplexing...  You (That is, Tom) have lots of
experience across a broad range of individuals - why do you think that is?
Oracle is clearly a market leader, so I would think there would be a strong
knowledge base behind it.  I have found the same to be true in our experiences
with external resources (read "consultants/contractors/vendors") as well,
though.

For some seem really good ideas/techniques about using currently available
Oracle tools (some "semi-documented"), I have found hotsos, ixora, and jlcomp to
be excellent resources in addition to AskTom (and Tom's book of course).

Having said that, though, I don't think a strong knowledge of all the
*internals* is required either.  For example, say I know the exact manner by
which Oracle clones blocks in the buffer cache - now what would I do with that
knowledge?  It is kinda like the "gee whiz" info you get when flying.  "This is
the captain speaking we are currently cruising at 33,000 feet..."  what am I
going to do?  Send a recommendation to the cockpit to change altitude?
Doubtful.

Well, another $0.02...

Thanks,

Mark

          Followup:

I used to find it surprising, not anymore.

I still find it perplexing, bothersome.

The reason -- you only have to be the smartest person in the room to succeed, a
rising tide raises all ships.  The better the group of people you work with --
the better you are at what you do and how you do it.

I actually find knowledge of "internals" to be a hindrance for most people --
they hold onto one or two "facts" -- inside knowledge -- and try to apply it all
over the place.  Lack of knowledge of the basics plus knowledge of some
internals is sort of like a doctor knowing how to use a scalpel, but not
understanding human anatomy.  Keep them away from me.  I actually try to avoid
many of the "internals", finding that 80% or more of everything (tuning for
example) is done using common sense, traditional design, applying my knowledge
to the problem.  It is not tweaking the buffer cache cloning, setting "_"
parameters (which everyone just seems to really dig doing for some reason, my
databases -- no underscores beyond trace_files_public)....

August 21, 2002 kumar -- Thanks for the question regarding
      "Optimizing the query ", version 8.1.7

      You Asked
I have a query Q like select * from (
                      select x from q1
                      union
                      select x from q2
                      union
                      select x from q3 )

while optimizing the query, will it improve performance if union all is used
instead of union, if q1, q2 and q3 all gives distinct result?

Is time taken by Q to retreive all data =
time  taken by q1 + q2 + q3 (parallel option not avail.)

Thanks

      and we said...
Yes.

what you have now is:

  DISTINCT( SORT( Q1+Q2+Q3 ) )

using union all would make that

    Q1+Q2+Q3

Review &
      Followup

      Rating: 4
"Optimizing the
      query ", version 8.1.7  August 21, 2002
Reviewer:
      William Lautenberger  from SF, CA
I intend to simplify "where exists here or exists there)"
by using a view "where exists view (here union there)".
Your suggestion union-all will not alter the result of
the exists operation, but will improve performance
by removing Oracle processing steps "DISTINCT(SORT".
The view itself will probably degrade from "where...or"
so I'm happy to get some cycles back.
Thank you.
WRL

          Followup:

Simplify in this case may be synonymous with "de-optimize".

I would leave it as where exists here or where exists there

August 21, 2002 Art -- Thanks for the question regarding
      "Fetching Ref Cursors into Scalar Objects to Build a Nested Table",
      version 8.1.7

      You Asked
Hi, Tom.  Can you point out what I'm doing wrong here?  I'm trying to return the
results of a dynamically built cursor that uses bind variables into a nested
table.

SQL> CREATE OR REPLACE TYPE myScalarType AS OBJECT (
  2      employee_name       VARCHAR2(10)
  3  ,   job_name            VARCHAR2(9)
  4  );
  5  /

Type created.

SQL> CREATE OR REPLACE TYPE myTableType AS TABLE OF myScalarType;
  2  /

Type created.

SQL> CREATE OR REPLACE PACKAGE sal_pkg AS
  2      FUNCTION get_sal_exceeds (p_sal IN NUMBER)
  3      RETURN myTableType;
  4  END sal_pkg;
  5  /

Package created.

SQL> CREATE OR REPLACE PACKAGE BODY sal_pkg AS
  2      FUNCTION get_sal_exceeds (p_sal IN NUMBER)
  3      RETURN myTableType IS
  4          TYPE rc     IS REF CURSOR;
  5          l_rc        rc;
  6          l_query     VARCHAR2(100) :=
  7              'SELECT ename , job FROM emp WHERE sal > :b1';
  8          l_position  myScalarType;
  9          l_all_pos   myTableType := myTableType();
 10          l_i         BINARY_INTEGER := 0;
 11      BEGIN
 12          OPEN l_rc FOR l_query USING p_sal;
 13          LOOP
 14              FETCH l_rc INTO l_position;
 15              EXIT WHEN l_rc%NOTFOUND;
 16              l_i := l_i + 1;
 17              l_all_pos.EXTEND;
 18              l_all_pos(l_i) := l_position;
 19          END LOOP;
 20          CLOSE l_rc;
 21
 22          RETURN (l_all_pos);
 23      END get_sal_exceeds;
 24  END sal_pkg;
 25  /

Package body created.

SQL> SELECT
  2  *
  3  FROM
  4  TABLE(CAST(sal_pkg.get_sal_exceeds(2000)
  5          AS myTableType))
  6  /
TABLE(CAST(sal_pkg.get_sal_exceeds(2000)
           *
ERROR at line 4:
ORA-00932: inconsistent datatypes
ORA-06512: at "APPS.SAL_PKG", line 14
ORA-06512: at line 1

Thanks again,

Art.

      and we said...
You are fetching the two varchar2 columns ENAME and JOB into a single
object_type.

Sort of like trying to fetch two numbers into a single date.  Won't do it.

So, you have two choices basically:

scott@ORA817DEV.US.ORACLE.COM> CREATE OR REPLACE PACKAGE BODY sal_pkg AS
  2      FUNCTION get_sal_exceeds (p_sal IN NUMBER)
  3      RETURN myTableType IS
  4          TYPE rc     IS REF CURSOR;
  5          l_rc        rc;
  6          l_query     VARCHAR2(100) :=
  7              'SELECT myScalarType( ename , job ) FROM emp WHERE sal >
:b1';
  8          l_position  myScalarType;
  9          l_all_pos   myTableType := myTableType();
 10          l_i         BINARY_INTEGER := 0;
 11      BEGIN
 12          OPEN l_rc FOR l_query USING p_sal;
 13          LOOP
 14              FETCH l_rc INTO l_position;
 15              EXIT WHEN l_rc%NOTFOUND;
 16              l_i := l_i + 1;
 17              l_all_pos.EXTEND;
 18              l_all_pos(l_i) := l_position;
 19          END LOOP;
 20          CLOSE l_rc;
 21
 22          RETURN (l_all_pos);
 23      END get_sal_exceeds;
 24  END sal_pkg;
 25  /

Package body created.

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> SELECT
  2  *
  3  FROM
  4  TABLE(CAST(sal_pkg.get_sal_exceeds(2000)
  5          AS myTableType))
  6  /

EMPLOYEE_N JOB_NAME
---------- ---------
JONES      MANAGER
BLAKE      MANAGER
CLARK      MANAGER
SCOTT      ANALYST
KING       PRESIDENT
FORD       ANALYST

6 rows selected.

that selects an object into an object... Alternatively, you can:

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> CREATE OR REPLACE PACKAGE BODY sal_pkg AS
  2      FUNCTION get_sal_exceeds (p_sal IN NUMBER)
  3      RETURN myTableType IS
  4          TYPE rc     IS REF CURSOR;
  5          l_rc        rc;
  6          l_query     VARCHAR2(100) :=
  7              'SELECT ename , job FROM emp WHERE sal > :b1';
  8          l_position  myScalarType := myScalarType(null,null);
  9          l_all_pos   myTableType := myTableType();
 10          l_i         BINARY_INTEGER := 0;
 11      BEGIN
 12          OPEN l_rc FOR l_query USING p_sal;
 13          LOOP
 14              FETCH l_rc INTO l_position.employee_name,
l_position.job_name;
 15              EXIT WHEN l_rc%NOTFOUND;
 16              l_i := l_i + 1;
 17              l_all_pos.EXTEND;
 18              l_all_pos(l_i) := l_position;
 19          END LOOP;
 20          CLOSE l_rc;
 21
 22          RETURN (l_all_pos);
 23      END get_sal_exceeds;
 24  END sal_pkg;
 25  /

Package body created.

scott@ORA817DEV.US.ORACLE.COM> SELECT
  2  *
  3  FROM
  4  TABLE(CAST(sal_pkg.get_sal_exceeds(2000)
  5          AS myTableType))
  6  /

EMPLOYEE_N JOB_NAME
---------- ---------
JONES      MANAGER
BLAKE      MANAGER
CLARK      MANAGER
SCOTT      ANALYST
KING       PRESIDENT
FORD       ANALYST

6 rows selected.

fetch two strings into two strings....

Review &
      Followup

      Rating:
      5
Excellent  August 21, 2002
Reviewer:
      Art Metzer  from St. Paul MN

August 21, 2002 Fernando -- Thanks for the question regarding
      "How to test a Database Link in a trigger?", version 817

      You Asked
Tom,

My mission is create a way to replicate DML events to another oracle server
(8.1.7) by a database link. The gap beetween the servers must be as minimum as
possible.
Triggers seams to be a good choice, but I want to treat the database link
failure, and the timeout period it is extremely long, and the transaction will
not complete.

How do I quickly test the database link and treat the error like a exception for
an example?

Thansk and regards,

Fernando Lima

      and we said...
You don't want to write code to do this AT ALL.

It took us many years and dozens/hundreds of programmers to code replication.
Use it.

Read:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76959/toc.htm
and use it.

I won't be party to re-inventing the wheel here.  This is a well known
technology now, something you buy and use, not make.

August 21, 2002 Prince -- Thanks for the question regarding "How
      do I make this dynamic SQL to use bind variable?", version 8.1.7.4

      You Asked
Tom,

I have the following sample procedure. How do I use the bind variables here,
without using cursor sharing?

Please see, the commented lines in the code.

create or replace procedure p_mysyn (
        l_object all_tables.table_name%type)
as
        l_owner all_tables.owner%type default USER ;
    l_user all_tables.owner%type := replace(l_owner, '_OWNER','_USER');
    l_stmt varchar2(200) := 'create synonym ' || l_user || '.' || l_object || '
for ' || l_owner || '.' ||

l_object ;
--    l_stmt varchar2(200) := 'create or replace synonym :l_user.:l_object for
:l_owner.:l_object' ;
begin
--some logic goes here
  dbms_output.put_line('Stmt : ' || l_stmt);
--    execute immediate l_stmt using l_user, l_object, l_owner, l_object ;
    execute immediate l_stmt ;
end p_mysyn ;

      and we said...
DDL cannot use bind variables.

Period.

DDL never goes into the shared pool, it is never "reusable", hence the argument
for bind variables just does not apply.

Review &
      Followup

      Rating: 5
Thank you very
      much for the information.  August 21, 2002

Reviewer:  Prince
I wasn't aware of "DDL never goes into the shared pool". thanks for the piece of
information.

      Rating: 4
DDL cannot be
      used in Forms with bind variables  August 22, 2002

Reviewer:  Praveen  from Johannesburg, South Africa

Now I understand why it was not possible for me to use DDL in Forms along with
bind variables

August 22, 2002 rajesh -- Thanks for the question regarding
      "column wise formattiing", version 8.16

      You Asked
hi tom

here is the table structure for storing collection details of different
collection agents
for a deposit scheme

coll_agent_no
coll_depo_code
coll_date
coll_amount
coll_mode

total collection amount of different agents will be entered to this table for
all
working days.

ie
0A56
CA
01-AUG-2002
24000
C
.
.
.
0A56
CA
20-AUG-2002
12000
C
.......

One of our clients demand is that they need a perodic statement as below

aget 0A56
Date     01-aug-2002 02-AUG-2002 04-AUG-2002 ....31-AUG-2002
Amount       24000      18000    17000              12000

aget 0A67
Date...
Amount...

For this statement the parameters will be
Agent Code
From date
Till date

Can i design such a report from the above table.(I need to design this  report
with out
using formula colums for all days)

sincerely

rajesh

      and we said...
I do not know what using a formula column means with regards to sqlplus.

anyway, you would query:

select coll_agent_no,
        sum( decode( coll_date,
         to_date('01-aug-2002','dd-mon-yyyy'), coll_amount, 0 ) ) "01-aug-2002",

        sum( decode( coll_date,
         to_date('02-aug-2002','dd-mon-yyyy'), coll_amount, 0 ) ) "02-aug-2002",

        ...
        sum( decode( coll_date,
         to_date('31-aug-2002','dd-mon-yyyy'), coll_amount, 0 ) ) "31-aug-2002"
  from table
where coll_date between to_date('01-aug-2002','dd-mon-yyyy')
                    and to_date('31-aug-2002','dd-mon-yyyy')
group by coll_agent_no
/

August 22, 2002 Ishaq -- Thanks for the question regarding "Host
      Command Problem in Forms6i (Forms 6.0.8.8.0 Ver)", version Forms Version
      6.0.8.8.0

      You Asked
The Problem is How can I run
Host() command in foms6i in Synchronous Mode.
----------------------------------------------------------------
For Example if the Following Commands were run in Forms
5.0.6.8.0 Ver
The Host Command is executed Synchronously.

Host('Dir c:\windows');
Host(Dir d:\Orawin95');
----------------------------------------------------------------
But if the Following Commands were run in Forms 6.0.8.8.0 Ver
The Host Command is executed ASynchronously.

Host('Dir c:\windows');
Host(Dir d:\Orawin95');
----------------------------------------------------------------
I have tried to call Windows applications from Oracle Forms
6.0.x using ORA_FFI.
But this too runs Asynchronously.

Thanks In Advance.

      and we said...
This appears to work:

host( 'command.com /c dir c:\*.* /s' );

Review &
      Followup

      Rating: 3
How to return
      the error message to form console  August 21, 2002

Reviewer:  Riaz Shahid  from PRAL, Islamabad,
      Pakistan
Hello Tom !

I am using Forms 6.0 and using host command to have backup from forms. For this,
i use:

host('exp
'||user||'/'||get_application_property(password)||'@'||get_application_property(c
onnect_string)||' file='||:file_name);

How i would know that backup has been successfull completed or not ?

I wanto to return error (if any) generated by runnig 'exp'. Like:

Export Terminated Successfully Without Warnings.
OR
Export Terminated Successfully With Warnings.

Regards

          Followup:

That isn't a backup, that is an export and is not a true method for backing up a
database (forget for a moment that the method you are using isn't even a
consistent export -- meaning, it might not even be IMPORTABLE given constraints
and foreign keys!)

Hopefully, you do not use this for any data of any importance....

You would redirect EXP's stdout and stderr to a text file.  You would use
text_io to read the text file and report back what you wanted to report back to
the user.

      Rating: 1
How Can i do
      that ?  August 22, 2002
Reviewer:  Riaz
      Shahid  from PRAL, Islamabad, Pakistan
Tom,

Thanks for your quick response. Please give example to how to use stderr,
text_io etc.

Regards

Riaz

          Followup:

See your windows manuals for how to redirect stdout and stderr (eg: using > in
the command.

for text_io examples, just go into forms and do "help" text_io.

They are there.  They are simple (there are things like text_io.fopen, get_line,
close, etc -- very simple text io routines)

August 22, 2002 Dev -- Thanks for the question regarding
      "Importing Thai Characters are turning into Junk", version 8.0.5

      You Asked
My 7.2.2 Database Drive has crashed. I have a daily Import
fiole. When I try to import the same into Oracle 8 (NT) My Thai
characters are being turned into Junk.

Please Help What do I Do ?

I do not know the NLS_Characterset of the Old Database.
The New Database is TH8TISASCII.

Can I import the File without loosing the Thai font Values ?

Please Help

      and we said...
When you do the import -- import should tell you something like:

Export file created by EXPORT:V08.01.06 via conventional path
import done in WE8ISO8859P1 character set and US7ASCII NCHAR
character set
import server uses US7ASCII character set (possible charset
conversion)
export server uses WE8ISO8859P1 NCHAR character set (possible
ncharset conversion)

The line "export server uses" will tell you what the correct
character set should be.
Review & Followup

      Rating: 2
Character set
      not correct  August 22, 2002
Reviewer:  Jan
      Thorshag  from Sweden
I am trying to move a database to a different schema/user but it doesn't handle
our swedish å,ä,ö correctly.
Character set in database is WE8ISO8859P1.

Do I have to use parameter "charset"?
I use version 8.1.7.

          Followup:

make sure when you exported the client had WE8ISO8859P1.  (eg, if you ran on
unix, the default character set is us7ascii, the data would have been
translated).

When you run import -- same thing, make sure NLS_LANG is set to WE8ISO8859P1.

Then, there will be no loss of anything.

      Rating:
      5
  August 23, 2002
Reviewer:
      A reader
Thanks very much for such a quick answer.
Best regards
Jan

August 22, 2002 mohammad -- Thanks for the question regarding
      "Sql file location", version 8.1.6.0

      You Asked
Hi ! Tom,

Thank you for your service.

My question is :

SQL> ed abcd.sql - this file is automatically  saved under 'BIN' directory.
But If I want to save automatically all my sql scripts under any other
directory,what should I do?

      and we said...
it is automatically saved into the current working directory.

Just start sqlplus from somewhere else.

If you are on window, you can setup the shortcut to run \oracle\bin\sqlplusw.exe
but "startup" in some other directory.
Review &
      Followup

      Rating: 5
Working
      directory  October 13, 2001
Reviewer:  Nag

Tom

How can I guarantee that whatever I work I do is saved in one of my specified
folders or the bin folder, and not in any other folder.

How can I do this.

Can I know what my current working directory is from sqlplus.

Further, there are two bin directories, one on the server side, where we have
our database, and on the client, from where you are connecting to the database.
And from what I know all the utilities that come with oracle are on the server
side , so that in effect means that we cannot use those utilitties from the
client side . Comment.

          Followup:

You would:

cd directory_name
sqlplus

sqlplus always writes to the current working directory unless you give it a
fully qualified filename to write to.

In windows, you would edit shortcut and change the "start in" directory to
whatever you wanted.

If you just type "host" -- sqlplus will dump you to the prompt in your current
working directory.

You can use the utilities installed on your client.  sqlplus is a utility -- its
on your client and on your server right?  you can use both.

      Rating: 5
Working Dir
        October 13, 2001
Reviewer:  Nag

When I open a file from the sqlplus, which is not in the current working
directory, I find that the current working directory changes.

I want the current working directory to always default to bin on the client
side. Can this be achieved.

Now when I type host from the prompt, it shows me the current working directory
. Fine. I do a CD .. , my current working directory has changed now. Now I exit
from the host window, and Iam back in sqlplus.

I do a file -open  , it opens in the bin directory. What is missing in what  Iam
doing.

From sqlplus , how can I change to the working dir, before I save the file.

My client is on win2000.

          Followup:

which sqplus are you using?  sqlplusw or sqlplus?  Must be sqlplusw.

I myself use only sqlplus and I do not see what you are seeing with regards to
opening a file.  The cwd is the cwd.

Also, when you host out you can discover BUT CANNOT CHANGE the current working
directory.  The host command opens up a sub-shell -- any changes you make in
there (environment, cwd, anything like that) is not made in the parent shell.
This is consistent with regards to the way the operating system has always
worked.  Just like in DOS:

C:\Documents and Settings\Thomas Kyte\Desktop\stuff\9i>cmd
Microsoft Windows 2000 [Version 5.00.2195]
(C) Copyright 1985-1999 Microsoft Corp.

C:\Documents and Settings\Thomas Kyte\Desktop\stuff\9i>cd \temp

C:\temp>exit

C:\Documents and Settings\Thomas Kyte\Desktop\stuff\9i>

I started in stuff\9i, created a sub shell, cd'ed to \temp and when I exit the
sub-shell, I'm back in stuff\9i

As for "from sqlplus, how can i..." you already answered that!  In sqlplusw --
use file open to change it.

      Rating: 4
working
      directory  October 14, 2001
Reviewer:  toaha
       from dubai
it is what I was looking for.
Thanks Tom.Thank you very much.

      Rating: 3
sqlplusw in
      win2000  August 22, 2002
Reviewer:  John G
       from Coventry, UK
Tom,

This very has been a source of irritation for me for over a year now !!

I use both sqlplus from a command window and also sqlplusw.

I am a Consultant DBA and during a development project I tend to store scripts
in many different directories, sometimes quite a "distance" from each other.  I
then want to run them directly i.e. @show_tabs.sql  without typing full or
relative paths.

Now under NT/Win95 you used to be able to go to File..Open and NOT select a
file, click Open button and this would set the current working directory until
you changed it again.

Under Win2000 (and XP I presume) the MS File..Open dialog changed, as did the
sqlplusw behaviour .. grrrrr!

You cannot now set the working directory without either actually opening a file.

I have a number of workarounds but none are quite good enough:
a) put an empty .sql file in each dir and open that with File..Open.  This then
sets the dir for the session similar to the old behaviour.
b) set all relevant paths in the SQLPATH env variable
c) setup several windows shortcuts with the directory in the "Working Directory"
box - Use this anyway as you can set up a shortcut with the user/connect string
set up as well.

Any other ideas ??

It seems that Oracle have always wanted to make SQL*Plus that little bit
annoying to "encourage" the purchase of third party tools 8-) ... although
iSQL*Plus seems to be improving.

Thanks

John

          Followup:

My solution:

use the windows explorer.
right click on a directory and choose CMD prompt here
type sqlplus

Sorry, I only use sqlplus.exe, i never use the gui one.  You should contact
support with this particular issue, to file an enhancement request or a bug.

August 22, 2002 -- Thanks for the question regarding "v$log
      first_change# not same as checkpoint_change# in v$datafile", version
      8.1.7.4, 9.2.0.1

      You Asked
Hi Tom

I am doing some research about the difference of checkpoints between alter
system switch logfile and alter system checkpoint. At every log switch a
checkpoints occurs so:

>select group#, status, FIRST_CHANGE# from v$log;

    GROUP# STATUS           FIRST_CHANGE#
---------- ---------------- -------------
         1 CURRENT                1956755
         2 INACTIVE               1916403
         3 INACTIVE               1936678

>select file#, checkpoint_change# from v$datafile;

     FILE# CHECKPOINT_CHANGE#
---------- ------------------
         1            1956835
         2            1956835
         3            1956835
         4            1956835
         5            1956835
         6            1956835
         7            1956835

however if I do alter system switch logfile I see

>select group#, status, FIRST_CHANGE# from v$log;

    GROUP# STATUS           FIRST_CHANGE#
---------- ---------------- -------------
         1 ACTIVE                 1956755
         2 CURRENT                1956893
         3 INACTIVE               1936678

>select file#, checkpoint_change# from v$datafile;

     FILE# CHECKPOINT_CHANGE#
---------- ------------------
         1            1956835
         2            1956835
         3            1956835
         4            1956835
         5            1956835
         6            1956835
         7            1956835

checkpoint_change# stayed same even the implicit checkpoint occured in a log
switch and to syncronize this I have to do alter system checkpoint

Why a *manual* redo log switch's implicit checkpoint doesnt update my datafile
headers? (an normal log switch does seems to update datafile headers)
Is the checkpoint process different?

      and we said...
These days, the database is almost constantly checkpointing (incremental
checkpoints) as well.  Checkpoints are not the discrete operations of
yesteryear.

The checkpoint fired by a log switch happens in the background, it does not
complete with the switch logfile command necessarily.

Also, there are many types of checkpoints -- the one done by the alter system
switch logfile is a "local" checkpoint.  We update the datafile headers in a
"lazy fashion" if you will -- we batch up and wait for some other IO's to
happen.  The failure to update the data file header is "low cost".

If you play with the log_checkpoint_timeout parameter, you'll control the length
of laziness we will use.

You just didn't wait long enough.  Consider (single user system, I'm the only
one on it):

ops$tkyte@ORA817DEV.US.ORACLE.COM> set numformat 999999999999999
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2          for i in 1 .. 100
  3          loop
  4                  update emp set ename = ename;
  5                  commit;
  6          end loop;
  7  end;
  8  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select file#, checkpoint_change# from
v$datafile;

           FILE# CHECKPOINT_CHANGE#
---------------- ------------------
               1      6532308938862
               2      6532308938862
               3      6532308938862
               4      6532308938862
               5      6532308938862
               6      6532308938862
               7      6532308938862
               8      6532308938862
               9      6532308938862
              10      6532308938862
              11      6532308938862

11 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select group#, status, FIRST_CHANGE# from
v$log;

          GROUP# STATUS              FIRST_CHANGE#
---------------- ---------------- ----------------
               1 CURRENT             6532308938970
               2 INACTIVE            6532308938750
               3 ACTIVE              6532308938860

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter system switch logfile;

System altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select file#, checkpoint_change# from
v$datafile;

           FILE# CHECKPOINT_CHANGE#
---------------- ------------------
               1      6532308938862
               2      6532308938862
               3      6532308938862
               4      6532308938862
               5      6532308938862
               6      6532308938862
               7      6532308938862
               8      6532308938862
               9      6532308938862
              10      6532308938862
              11      6532308938862

11 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select group#, status, FIRST_CHANGE# from
v$log;

          GROUP# STATUS              FIRST_CHANGE#
---------------- ---------------- ----------------
               1 ACTIVE              6532308938970
               2 CURRENT             6532308939077
               3 ACTIVE              6532308938860

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_lock.sleep(60);

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select file#, checkpoint_change# from
v$datafile;

           FILE# CHECKPOINT_CHANGE#
---------------- ------------------
               1      6532308938970
               2      6532308938970
               3      6532308938970
               4      6532308938970
               5      6532308938970
               6      6532308938970
               7      6532308938970
               8      6532308938970
               9      6532308938970
              10      6532308938970
              11      6532308938970

11 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

And it hasn't even caught upto the current log yet.

This is ok, it is normal, it is to be expected.
Review &
      Followup

      Rating:
      4
  August 14, 2002
Reviewer:
      A reader
Hi

if alter system switch logfile triggers a local checkpoint what kind of
checkpoint does a normal log switch  trigger?

          Followup:

I believe it is the same, 99.9% on that.  Shouldn't be any different.  Too lazy
right now to actually test it out ;)

You would have to set up a test that monitor the alerts after setting the
init.ora parameter to log checkpoints to alerts and watch a busy system (my test
system won't switch logs unless I setup lots of activity)

      Rating: 5
alter system
      switch logfile does not trigger checkpoint  August 21, 2002

Reviewer:  A reader  from madrid
hi Tom

from Oracle 9i DBA Fundamentals 2 ILT course it states that checkpoint occurs
when

1. every log switch
2. when fast start checkpointing is set
3. log_checkpoint_interval
4. log_checkpoint_timeout
5. instance shutdown cleanly
6. alter system checkpoint commnad
7. when tablespace is taken offline or online backup starts

since none talks about alter system switch logfile I guess that´s why it doesnt
triggers checkpoint?

          Followup:

log switches trigger checkpoints.  See #1 in your list above -- "every log
switch"

      Rating: 5
I tried your
      test but  August 22, 2002
Reviewer:
      A reader
Hi Tom

I carried out your test in my etst database (I am the only user) but after

exec dbms_lock.sleep(60);

I queried again v$datafile but I still see CHECKPOINT_CHANGE# unchanged....

how so?

          Followup:

As I said -- it is a lazy write, it can take a long long long time.

There is nothing wrong here, it is doing exactly what the database coders coded
it to do.

August 22, 2002 -- Thanks for the question regarding "DUPLICATE
      SYS Constraints", version 8.1.7.1.0

      You Asked
We have a table that have redundant SYS constraints and we are not sure how it
got there. What usually caused this to happen?

Sample output:

SYS_C0010002    AIISPRODUCT_ID
SYS_C0010011    AIISPRODUCT_ID
SYS_C0010873    AIISPRODUCT_ID
SYS_C0010882    AIISPRODUCT_ID
SYS_C0012324    AIISPRODUCT_ID
SYS_C0012333    AIISPRODUCT_ID
SYS_C0012444    AIISPRODUCT_ID
SYS_C0012453    AIISPRODUCT_ID
SYS_C0012693    AIISPRODUCT_ID
SYS_C0012702    AIISPRODUCT_ID
SYS_C0012711    AIISPRODUCT_ID
SYS_C006110    AIISPRODUCT_ID
SYS_C0010007    CUSTTYPE_ID
SYS_C0010016    CUSTTYPE_ID
SYS_C0010878    CUSTTYPE_ID
SYS_C0010887    CUSTTYPE_ID
SYS_C0012329    CUSTTYPE_ID
SYS_C0012338    CUSTTYPE_ID
SYS_C0012449    CUSTTYPE_ID
SYS_C0012458    CUSTTYPE_ID
SYS_C0012698    CUSTTYPE_ID
SYS_C0012707    CUSTTYPE_ID
SYS_C0012716    CUSTTYPE_ID
SYS_C006115    CUSTTYPE_ID
SYS_C0010005    INSETUPFLAG
SYS_C0010014    INSETUPFLAG
SYS_C0010876    INSETUPFLAG
SYS_C0010885    INSETUPFLAG
SYS_C0012327    INSETUPFLAG
SYS_C0012336    INSETUPFLAG
SYS_C0012447    INSETUPFLAG
SYS_C0012456    INSETUPFLAG
SYS_C0012696    INSETUPFLAG
SYS_C0012705    INSETUPFLAG
SYS_C0012714    INSETUPFLAG
SYS_C006113    INSETUPFLAG
SYS_C0013006    LASTMODBY
SYS_C0013005    LASTMODDT
SYS_C0010004    LISTING_ID
SYS_C0010013    LISTING_ID
SYS_C0010875    LISTING_ID
SYS_C0010884    LISTING_ID
SYS_C0012326    LISTING_ID
SYS_C0012335    LISTING_ID
SYS_C0012446    LISTING_ID
SYS_C0012455    LISTING_ID
SYS_C0012695    LISTING_ID
SYS_C0012704    LISTING_ID
SYS_C0012713    LISTING_ID
SYS_C006112    LISTING_ID
SYS_C0010009    LISTINGTYPENAME_ID
SYS_C0010018    LISTINGTYPENAME_ID
SYS_C0010880    LISTINGTYPENAME_ID
SYS_C0010889    LISTINGTYPENAME_ID
SYS_C0012331    LISTINGTYPENAME_ID
SYS_C0012340    LISTINGTYPENAME_ID
SYS_C0012451    LISTINGTYPENAME_ID
SYS_C0012460    LISTINGTYPENAME_ID
SYS_C0012700    LISTINGTYPENAME_ID
SYS_C0012709    LISTINGTYPENAME_ID
SYS_C0012718    LISTINGTYPENAME_ID
SYS_C006117    LISTINGTYPENAME_ID
SYS_C0010003    MAINLISTING_ID
SYS_C0010012    MAINLISTING_ID
SYS_C0010874    MAINLISTING_ID
SYS_C0010883    MAINLISTING_ID
SYS_C0012325    MAINLISTING_ID
SYS_C0012334    MAINLISTING_ID
SYS_C0012445    MAINLISTING_ID
SYS_C0012454    MAINLISTING_ID
SYS_C0012694    MAINLISTING_ID
SYS_C0012703    MAINLISTING_ID
SYS_C0012712    MAINLISTING_ID
SYS_C006111    MAINLISTING_ID
SYS_C0013002    NAMEBUSFLAG
SYS_C0013003    NAMEGOVTFLAG
SYS_C0013004    NAMERESFLAG
SYS_C0010010    OUTPUTFLAG
SYS_C0010019    OUTPUTFLAG
SYS_C0010881    OUTPUTFLAG
SYS_C0010890    OUTPUTFLAG
SYS_C0012332    OUTPUTFLAG
SYS_C0012341    OUTPUTFLAG
SYS_C0012452    OUTPUTFLAG
SYS_C0012461    OUTPUTFLAG
SYS_C0012701    OUTPUTFLAG
SYS_C0012710    OUTPUTFLAG
SYS_C0012719    OUTPUTFLAG
SYS_C006118    OUTPUTFLAG
SYS_C0010008    SERVICETYPE_ID
SYS_C0010017    SERVICETYPE_ID
SYS_C0010879    SERVICETYPE_ID
SYS_C0010888    SERVICETYPE_ID
SYS_C0012330    SERVICETYPE_ID
SYS_C0012339    SERVICETYPE_ID
SYS_C0012450    SERVICETYPE_ID
SYS_C0012459    SERVICETYPE_ID
SYS_C0012699    SERVICETYPE_ID
SYS_C0012708    SERVICETYPE_ID
SYS_C0012717    SERVICETYPE_ID
SYS_C006116    SERVICETYPE_ID
SYS_C0010006    STATUS
SYS_C0010015    STATUS
SYS_C0010877    STATUS
SYS_C0010886    STATUS
SYS_C0012328    STATUS
SYS_C0012337    STATUS
SYS_C0012448    STATUS
SYS_C0012457    STATUS
SYS_C0012697    STATUS
SYS_C0012706    STATUS
SYS_C0012715    STATUS
SYS_C006114    STATUS

I have an additional question not related to this one. Is it safe to run root.sh
after you already a working database? Does it need a relink afterwards.

Thanks again in advance.

      and we said...
I'm going to assume you use EXP/IMP alot and you clearly rely on "un-named
constraints" -- constraints with system generated names.

Here is a short extract from my book "Expert one on one", from the chapter on
IMP/EXP, that details exactly what has happened here:

<quote>
Named versus Default Named Constraints

Here is another issue with regards to system-generated named constraints and
IMP/EXP. I could have called this section Where Did All of These Constraints
Come From? since the prior section was called Where Did My Indexes Go? Here we
start with a table T:

tkyte@TKYTE816> create table t
  2  ( x int check ( x > 5 ),
  3    y int constraint my_rule check ( y > 10 ),
  4    z int not null ,
  5    a int unique,
  6    b int references t,
  7    c int primary key
  8  );
Table created.

tkyte@TKYTE816> select constraint_name name, constraint_type type,
search_condition
  2    from user_constraints where table_name = 'T';

NAME                           T SEARCH_CONDITION
------------------------------ - -------------------------
SYS_C002674                    C "Z" IS NOT NULL
SYS_C002675                    C x > 5
MY_RULE                        C y > 10
SYS_C002677                    P
SYS_C002678                    U
SYS_C002679                    R

6 rows selected.

It has lots of constraints on it – six all together. I'll export it, drop the
table and import it again:

tkyte@TKYTE816> host exp userid=tkyte/tkyte owner=tkyte

tkyte@tkyte816> drop table T;
Table dropped.

tkyte@TKYTE816> host imp userid=tkyte/tkyte full=y ignore=y rows=n

tkyte@TKYTE816> select constraint_name name, constraint_type type,
search_condition
  2    from user_constraints where table_name = 'T';

NAME                           T SEARCH_CONDITION
------------------------------ - -------------------------
SYS_C002680                    C "Z" IS NOT NULL
SYS_C002681                    C x > 5
MY_RULE                        C y > 10
SYS_C002683                    P
SYS_C002684                    U
SYS_C002685                    R

6 rows selected.

Looks normal so far. Lets say however, that we rerun the import for whatever
reason (it failed part way through for example). What we'll then discover is:

tkyte@TKYTE816> host imp userid=tkyte/tkyte full=y ignore=y

Import: Release 8.1.6.0.0 - Production on Tue Mar 20 15:42:26 2001

(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.6.0.0 - Production
With the Partitioning option
JServer Release 8.1.6.0.0 - Production

Export file created by EXPORT:V08.01.06 via conventional path
import done in WE8ISO8859P1 character set and WE8ISO8859P1 NCHAR character set
. importing TKYTE's objects into TKYTE
. . importing table                            "T"          0 rows imported
IMP-00017: following statement failed with ORACLE error 2264:
 "ALTER TABLE "T" ADD  CONSTRAINT "MY_RULE" CHECK ( y > 10 ) ENABLE NOVALIDAT"
 "E"
IMP-00003: ORACLE error 2264 encountered
ORA-02264: name already used by an existing constraint
IMP-00017: following statement failed with ORACLE error 2261:
 "ALTER TABLE "T" ADD  UNIQUE ("A") USING INDEX PCTFREE 10 INITRANS 2 MAXTRAN"
 "S 255 STORAGE(INITIAL 524288) TABLESPACE "DATA" ENABLE"
IMP-00003: ORACLE error 2261 encountered
ORA-02261: such unique or primary key already exists in the table
About to enable constraints...
Import terminated successfully with warnings.

tkyte@TKYTE816> select constraint_name name, constraint_type type,
search_condition
  2    from user_constraints where table_name = 'T';

NAME                           T SEARCH_CONDITION
------------------------------ - -------------------------
SYS_C002680                    C "Z" IS NOT NULL
SYS_C002681                    C x > 5
MY_RULE                        C y > 10
SYS_C002683                    P
SYS_C002684                    U
SYS_C002685                    R
SYS_C002686                    C x > 5

7 rows selected.

We have an extra constraint. In fact, every time we run this, we'll have an
extra constraint added. My named constraint however generates a warning on
screen – you cannot have the same named constraint twice.  The un-named
constraint for “x > 5” on the other hand – gets created again.  This is because
the database just generated a new name for it.

I've seen cases where people have been using EXP on one database, truncating the
data on another, and using IMP to put the data back in. Over time they had
accumulated hundreds of check constraints on many columns. Performance was
starting to go downhill and they wanted to know why. The above is the reason
why. Every time they copied the data, they added yet another bunch of check
constraints, all doing the same work. See what the effect of just a one hundred
will do:

tkyte@TKYTE816> create table t
  2  ( x int check ( x > 5 )
  3  )
  4  /
Table created.

tkyte@TKYTE816> declare
  2      l_start number default dbms_utility.get_time;
  3  begin
  4      for i in 1 .. 1000
  5      loop
  6          insert into t values ( 10 );
  7      end loop;
  8      dbms_output.put_line
  9      ( round((dbms_utility.get_time-l_start)/100,2) || ' seconds' );
 10  end;
 11  /
.08 seconds

PL/SQL procedure successfully completed.

tkyte@TKYTE816> begin
  2      for i in 1 .. 100
  3      loop
  4          execute immediate
  5          'ALTER TABLE "TKYTE"."T" ADD CHECK ( x > 5 ) ENABLE ';
  6      end loop;
  7  end;
  8  /
PL/SQL procedure successfully completed.

tkyte@TKYTE816> declare
  2      l_start number default dbms_utility.get_time;
  3  begin
  4      for i in 1 .. 1000
  5      loop
  6          insert into t values ( 10 );
  7      end loop;
  8      dbms_output.put_line
  9      ( round((dbms_utility.get_time-l_start)/100,2) || ' seconds' );
 10  end;
 11  /
.17 seconds

PL/SQL procedure successfully completed.

Yet another good reason to name your constraints!

Review &
      Followup

      Rating:
      5
  August 22, 2002
Reviewer:  Tina
       from Troy,MI
Thanks for the explanation. It makes sense now. I checked the constraint type
for all of them and all are indeed check constraints. If we do need to redo an
import, do we avoid this by dropping the table, re-creating it and then
importing the data back. What is meant by 'name your constraints'?

          Followup:

You can avoid this by dropping the table and letting IMPORT create it.

By "name your constraints" I mean:

tkyte@TKYTE816> create table t
  2  ( x int check ( x > 5 ),
  3    y int constraint my_rule check ( y > 10 ),

X has an unnamed constraint
Y has a named constraint

The nice thing is if you violate the constraint on Y, you can a nice friendly
message "my_rule violated".  If you violate the constraint on X, you get
"SYS_C02432432 violated" which tells you next to nothing.

      Rating: 4
Very
      interesting  August 22, 2002
Reviewer:
      A reader

      Rating:
      5
Everyday!  August 22, 2002
Reviewer:
      A reader
So far I have learned something new (and beneficial) everyday from this site
Thanks Tom

August 22, 2002 Sampath -- Thanks for the question regarding "How
      to generate PDF output from Stored Procedure", version 8.1.6/9.0.1

      You Asked
Hi Tom!
       Thanks for taking my question . Is there any way we can generate PDF
output from stored procedure ? I would really appriciate
if you give any example .
Thanks in advance,
Sampath

      and we said...
You would have to learn how to write PDF.  It would be quite tedious.

This would be a job for Oracle Reports or some other report generation tool.
Review &
      Followup

      Rating: 3
Thank
      you  August 22, 2002
Reviewer:  Sampath

Hi Tom,
       Thanks a lot for your update . If you could give me
any url where I can find the information on how to write to PDF , I would really
appriciate that .
Thanks,
Sampath

          Followup:

try asking over at

http://www.pdfzone.com/

      Rating: 3
do it with
      java  August 22, 2002
Reviewer:  John
       from Akron, OH
Itext is a very nice(and well documented) open source java tool for writing pdf
files.  You can get it at:
http://www.lowagie.com/iText

      Rating:
      3
OOS  August 22, 2002
Reviewer:
      Reader
Hi Tom a question is bothering me is there such thing called OOS (oracle
operating system) i went to oracle site search for that and products came up
under OOS but still wondering havent heared such thing before still not sure
what is it???

August 22, 2002 Mudassar -- Thanks for the question regarding
      "Miscellaneous", version 8.1.5

      You Asked
Q.1. What is the difference between gets and pins inside the
view v$librarycache
Q.2. If you create a table with multiple freelists and/or
multiple freelist groups, each freelist stores all free blocks
or are the blocks divided amongst the multiple freelists. If the
blocks are divided how is it internally managed?
Q.3. Oracle Education curriculum whenever discusses latches says
the maximum latches should be your CPU*2*3 (Why times two and
times three)
Q.4. When you run the dbverf utility, it tells you something
about influx. What is this influx? (Nowhere in the documentation
it mentions about influx)
Q.5. Is there any way to put a lock timeout so that the other
users time out inside of getting locked forever.
Q.6. Can you refer me some Oracle Database Administration book
from which all my queries could be solved. I already have the
documentation. But the documentation doesnot go beyond a limit.
What if I want some more information or the documentation is not
sufficient? From where can I get that information? In that case
my questions to you would be reduced....

Thanks in advance.
Best Regards,
Mudassar Shahzad

      and we said...
1) see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:465420331879
2) the blocks are split up across the freelists.  The purpose of
multiple freelists is to promote even more concurrency.  I'm not
sure how they internally divy them up.  Those sorts of
algorithms change from release to release.

3) Thats the MAX for DB_BLOCK_LRU_LATCHES (an init.ora
parameter).  The maximum of (CPU_COUNT x 2 x 3) ensures that the
number of latches does not exceed twice the product of the
number of CPUs and the number of buffer pools.

4) see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:354017844316
5) No, in order to prevent a DML statement from blocking
indefinitely, you should "select for update NOWAIT" the rows you
want to modify.

6) Are you familar with
http://metalink.oracle.com/?
 It is full of support notes
containing this information in more depth.
Review &
      Followup

      Rating:
      4
  August 21, 2002
Reviewer:
      A reader
Hi Tom,

Can you help to understand this?

I have a database with 8.1.7.3. and here you have what I wrote in my init.ora

db_block_buffers = 102400         # 800M
db_block_lru_latches = 30
buffer_pool_keep = ("buffers: 21760","lru_latches: 5")
buffer_pool_recycle = ("buffers: 38400","lru_latches: 10")

When I do a show parameter in the database the values for the lru_latches and
the db_block_lru_latches are different from the init.ora file.

SQL_FRLS>show parameter buffer

NAME                                 TYPE    VALUE
------------------------------------ ------- ------------------------------
buffer_pool_keep                     string  buffers: 21760, lru_latches: 5
buffer_pool_recycle                  string  buffers: 38400, lru_latches: 1
db_block_buffers                     integer 102400
db_block_lru_latches                 integer 48

1-Why was the value for db_block_lru_latches set to 48 if I put 30 in the
init.ora file?
2-Why was the value for the lru_latches set to 1 (in the case of recycle) if I
put 10 in the init.ora file?
3-Taking into account that I have 4 cpu's, should I set the db_block_lru_latches
to 8?

Thanks for your help

          Followup:

check further down in the init.ora, see if there is another block of init.ora
settings there for these parameters (we take the last ones)

      Rating:
      4
  August 22, 2002
Reviewer:
      A reader
Hi Tom,

Thanks for answering me.
I checked twice the init.ora and there is only one line for the
db_block_lru_latches and that is the one which says 30.
What do you think about this? What can it be?

Thanks

          Followup:

Contact support for this one, I don't have a machine big enough to allocate that
much right now.  Sorry.  (that and the versions of oracle i have on my machines
right with me now don't have that parameter anymore, it is obsoleted)

August 23, 2002 Shankar -- Thanks for the question regarding "SQL
      Query", version 8.1.7

      You Asked
Hi,

In my project I have a query problem.In the Database table, against
rate_dates I have rates for a particular commodity but the rates are entered as
and when they get the rates like on Jan8 th an entry will be there but he next
entry may be on Jan 20 th  and not continuously.

Now, my problem is I need to retrieve this data for a year starting from Jan 1
to Dec 31 if the values are present they have to come and if the values are not
there then 0 to be there.

After this the resultset to look like this:

1-Jan       1000.50 ( value is present in hte table)
2-Jan         0     (value not present in the table)
3-Jan         0
4-Jan       100
.
.
.

30-Dec      0
31-Dec     2000

I need your suggesstion in this

Thanks,

ShankarBalaji

      and we said...
start with a query to give you every day in the year:

select to_date( '01-jan-2002', 'dd-mon-yyyy' ) + rownum -1  dt
  from all_objects
 where rownum <= last_day( to_date('01-dec-2002','dd-mon-yyyy') ) - to_date(
'01-jan-2002', 'dd-mon-yyyy' ) + 1;

Then, use that in an inline view:

select a.dt, nvl( b.rate, 0 )
  from ( select to_date( '01-jan-2002', 'dd-mon-yyyy' ) + rownum -1  dt
  from all_objects
 where rownum <= last_day( to_date('01-dec-2002','dd-mon-yyyy') ) - to_date(
'01-jan-2002', 'dd-mon-yyyy' ) + 1 ) A,
    your_table B
where a.dt = b.dt (+)
/
Review &
      Followup

      Rating: 4
SQL
      Query  May 20, 2002
Reviewer:  Shankarbalaji
       from NY, USA
Thanks a lot and it is very useful for the project progress.

      Rating: 4
Very
      Useful  May 20, 2002
Reviewer:  Madhuri
      Bondre  from India, Ambernath
I used the same to get the distinct months and years for a period. with the
distinct clause

ex. jan 2001, feb 2001 .....dec 2001

it is very useful

      Rating: 5
Great Query
        May 21, 2002
Reviewer:  Sunil Kumar
       from Hongkong
Hi Tom !

Really it is a good query. Now lots of porblem can be solved by taking the help
from this query.

      Rating: 3
Using Indexes
      skip rows  May 21, 2002
Reviewer:  Hrishab

Tom,

   I am using query as
SQL> select count(1) from big_table;
13000 rows selected.
SQL> select count(1) from big_table where some_col > '.';
12000 rows selected.
some_col is varchar column with unique index on it.
Why there is difference of count when I use index?

Thanks,

          Followup:

both queries might be using an index.

The answer to your question "why do they return a different number" is because
there are apparently 1000 rows such that some_col <= '.' or some_col is NULL.

      Rating:
      3
Limitations?  May 21, 2002
Reviewer:
      VJ  from Boston, MA USA
The in-line view query uses all_objects, this could be limitation to extend this
operation further as no. of rows in the all_objects will vary depending on the
case and security setups.

          Followup:

to generate the days in a year -- not a worry.

To generate the days in a decade -- start to worry.

      Rating:
      4
Wow  May 22, 2002
Reviewer:  Chandra
      S.Redy  from Hyd,India
This is very nice approach.It must be helpful a lot to me.

      Rating: 3
What is
      some_col < '.'  May 28, 2002
Reviewer:
      Alexander
With Reference to the following question

  i would like to know what is some_col < '.' bcos as per my knowledge the
some_col > '.' will forcefully use the index. In that case how the query using
some_col < '.' process.

Tom,

   I am using query as
SQL> select count(1) from big_table;
13000 rows selected.
SQL> select count(1) from big_table where some_col > '.';
12000 rows selected.
some_col is varchar column with unique index on it.
Why there is difference of count when I use index?

Thanks,

Followup:
both queries might be using an index.

The answer to your question "why do they return a different number" is because
there are apparently 1000 rows such that some_col <= '.' or some_col is NULL.

          Followup:

I do not understand what you are trying to ask there?  (your keyboard is
broken/breaking -- it is dropping important letters from some words - not
consistently, just sometimes).

      Rating: 3
Solution
      without using ALL_OBJECTS  May 31, 2002
Reviewer:
      Kim Berg Hansen  from Denmark
An alternative solution for those who need more dates than number of rows in
all_objects :

-- create a collection type

create type date_array_type as table of date;
/

-- create a function returning dates in a collection

create or replace function get_date_array(
   parm_start_date date,
   parm_end_date date
)
   return date_array_type
is
   start_date     date := trunc(parm_start_date);
   end_date       date := trunc(parm_end_date);
   out_date_array date_array_type := date_array_type();
   array_idx      pls_integer := 0;
begin
   if start_date is not null and
      end_date is not null   and
      end_date >= start_date then

      out_date_array.extend(end_date - start_date + 1);

      while start_date <= end_date loop
         array_idx  := array_idx + 1;
         out_date_array(array_idx) := start_date;
         start_date := start_date + 1;
      end loop;
   end if;

   return out_date_array;
end get_date_array;
/

-- Use like this :

select to_char(date_table.column_value,'YYYY-MM-DD') "Date"
from table(cast(get_date_array(sysdate-5,sysdate) as date_array_type))
date_table;

Date
----------
2002-05-26
2002-05-27
2002-05-28
2002-05-29
2002-05-30
2002-05-31

-- Or like this :

select distinct to_char(trunc(date_table.column_value,'IW'),'YYYY-MM-DD') "ISO
Week"
from table(cast(get_date_array(sysdate-30,sysdate) as date_array_type))
date_table;

ISO Week
----------
2002-04-29
2002-05-06
2002-05-13
2002-05-20
2002-05-27

-- Or in a join :

select to_char(date_table.column_value,'YYYY-MM-DD') "Date", emp.ename
from
table(cast(get_date_array(to_date('1981-02-18','YYYY-MM-DD'),to_date('1981-02-24'
,'YYYY-MM-DD')) as date_array_type)) date_table, emp
where emp.hiredate(+) = date_table.column_value;

Date       ENAME
---------- ----------
1981-02-18
1981-02-19
1981-02-20 ALLEN
1981-02-21
1981-02-22 WARD
1981-02-23
1981-02-24

Hope this can be of use to somebody.

          Followup:

And in 9i, we would use pipelined functions like this:

ops$tkyte@ORA9I.WORLD> create or replace function get_date_array(
  2     parm_start_date date,
  3     parm_end_date date
  4  )
  5     return date_array_type
  6  PIPELINED
  7  is
  8     start_date     date := trunc(parm_start_date);
  9     end_date       date := trunc(parm_end_date);
 10  begin
 11     if start_date is not null and end_date is not null
 12     then
 13        for i in 0 .. end_date-start_date
 14        loop
 15                   pipe row (start_date+i);
 16        end loop;
 17     end if;
 18     return;
 19  end get_date_array;
 20  /

Function created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> select sysdate-5, sysdate, t.* from TABLE(
get_date_array( sysdate-5, sysdate ) ) t;

SYSDATE-5 SYSDATE   COLUMN_VA
--------- --------- ---------
26-MAY-02 31-MAY-02 26-MAY-02
26-MAY-02 31-MAY-02 27-MAY-02
26-MAY-02 31-MAY-02 28-MAY-02
26-MAY-02 31-MAY-02 29-MAY-02
26-MAY-02 31-MAY-02 30-MAY-02
26-MAY-02 31-MAY-02 31-MAY-02

6 rows selected.

more efficient -- don't have to build up an entire array (less ram, lots less.
If there aren't enough rows in all_objects, you are building a HUGE array).
Also, it allows for more "interactive" processing -- that is, the function
get_date_array doesn't have to complete before you can get your first row back
-- the pipe row builtin "suspends" the procedure and lets other parts of the
query run.

      Rating: 5
I'm definitely
      beginning to like 9i  June 03, 2002
Reviewer:
      Kim Berg Hansen  from Denmark
Thank you for that improvement.

It seems to me that every day I learn something new and smart in 9i - just got
to get our 8i upgraded soon as possible :-)

      Rating:
      5
  June 27, 2002
Reviewer:  Rohit
      Gulati  from Ambala,India
Hi
        I have a transaction table for the items which are purchased or sold. A
item is purchased several times in the year. The table has all the information
regarding the movement of items. I want the latest purchase record for each item
from the table through a single sql command and i want all the fields to be
displayed.

Regards

          Followup:

select *
  from ( select a.*, row_number() over (partition by ITEM
                                        order by DATE_FIELD desc) rn
           from a )
 where rn = 1
/

assuming you have some field ITEM that reprensents the key of the item itself
and some DATE_FIELD you are able to sort by to find the "last purchase record"

      Rating: 4
Why not to self
      join ?!  August 23, 2002
Reviewer:  Yogesh
      Purabiya  from India
In one of reviews, refering to many many days, you said,

"to generate the days in a year -- not a worry.

 To generate the days in a decade -- start to worry. "

But, doing self-join of ALL_OBJECTS we get millions of rows, like :

select to_date( '01-jan-2002', 'dd-mon-yyyy' ) + rownum -1  dt
  from all_objects
 where rownum <= last_day( to_date('01-dec-2002','dd-mon-yyyy') ) - to_date(
'01-jan-2002', 'dd-mon-yyyy' ) + 1;

Do you think that is to be avoided ?

Yogesh Purabiya

          Followup:

I'd be worrying that a self join (cartesian product) might not be efficient, try
it and see.

Me, I count on all_objects having a couple of thousand rows -- probably enough
for a decaded (version dependent).  sure a cartesian product would fix that but
you would have to cartesian product every time just in case -- not sure i like
that too much.

      Rating: 4
Why not to self
      join ?!  August 23, 2002
Reviewer:  Yogesh
      Purabiya  from India
Sorry that I sent the previous Re-View without actually adding the real
self-join. Ignore that, and read this, please.

In one of reviews, refering to many many days, you said,

"to generate the days in a year -- not a worry.

 To generate the days in a decade -- start to worry. "

But, doing self-join of ALL_OBJECTS we get millions of rows, like :

select to_date( '01-jan-2002', 'dd-mon-yyyy' ) + rownum -1  dt
  from all_objects, all_objects   /* SELF-JOIN */
 where rownum <= last_day( to_date('01-dec-2002','dd-mon-yyyy') ) - to_date(
'01-jan-2002', 'dd-mon-yyyy' ) + 1;

Do you think that is to be avoided ?

Yogesh Purabiya

August 23, 2002 Steven -- Thanks for the question regarding
      "Writing more than one block - v$filestat info", version 8.1.6

      You Asked
While looking at the v$filestat view, I noticed that most of the
datafiles have a 1:1 ratio of phyblkwrt/phywrts, but sometimes
there is a larger number of phyblkwrt compared to phywrts.  What
causes this, and is this behavior limited to a certain type of
segment - i.e. temporary, index, table etc.

      and we said...
If you look at the definition of these two columns in the Reference Manual:

PHYWRTS NUMBER Number of times DBWR is required to write

PHYBLKWRT NUMBER Number of blocks written to disk; which may be the same as
PHYWRTS ifall writes are single blocks

When PHYBLKWRT is > PHYWRTS that means we got to do some multi-block IO (thats a
GOOD thing).  We could batch up the writes and do in 1 IO what would take more
then 1 IO otherwise.

It purely depends on the nature and distribution of the data in the files, it is
not limmited to one type of file but might be observed more often on data(table)
and temporary segment -- more so then indexes (which are complex data structures
built a block at a time)
Review & Followup

      Rating:
      4
  April 21, 2001
Reviewer:  Pauline
       from U.S.A.

      Rating:
      4
  August 23, 2002
Reviewer:  John
       from Universal City, TX
The most of my datafiles have the ratio of less than 2 for the blocks/read.
According to the
document, I should set DB_FILE_MULTIBLOCK_READ_COUNT to a higher number. But the
db has
the default number 8. why the I/O so bad?
TIA

          Followup:

huh?

      Rating: 1
"huh?
      "  August 23, 2002
Reviewer:  A reader

What I asked was how do you think the i/o rating, which is less than 2 blocks
per read. and How could I do better?

          Followup:

show me how you are getting this "rating", the huh stems from the fact that I
don't really know what you are saying yet.  Be a little more elaborate in your
information.

do you actually DO operations (like full scans) that would do multi-block io?
If not, then you are doing single block IOs (index reads and table access by
rowid)

      Rating: 5
RE:
      Huh  August 23, 2002
Reviewer:  Mark A.
      Williams  from Indianapolis, IN USA
Force full table scans if you really want to get more blocks per read!  Index
accesses are typically single block reads.  If your systems does alot of "access
by index rowid" operations, your ratio of reads:blocks will be closer to 1:1,
would it not?  Why do you feel you need to have more blocks per read?

Unsolicted $0.02...

- Mark

      Rating:
      3
  August 23, 2002
Reviewer:  John

The infor based on what:
I had done the b/estat on the 8.1.7.3 database:

SVRMGR> Rem I/O should be spread evenly accross drives. A big difference between
SVRMGR> Rem phys_reads and phys_blks_rd implies table scans are going on.
SVRMGR> select table_space, file_name,
     2>        phys_reads reads, phys_blks_rd blks_read, phys_rd_time read_time,
     3>        phys_writes writes, phys_blks_wr blks_wrt, phys_wrt_tim
write_time,
     4>        megabytes_size megabytes,
     5>        round(decode(phys_blks_rd,0,0,phys_rd_time/phys_blks_rd),2)
avg_rt,
     6>        round(decode(phys_reads,0,0,phys_blks_rd/phys_reads),2)
"blocks/rd"
     7>  from stats$files order by table_space, file_name;
TABLE_SPACE                    FILE_NAME
READS      BLKS_READ  READ_TIME  WRITES     BLKS_WRT   WRITE_TIME MEGABYTES
AVG_RT     blocks/rd
------------------------------ ------------------------------------------------
---------- ---------- ---------- ---------- ---------- ---------- ----------
---------- ----------
CIV_EXTERNAL                   /p01/oracle/RESUMIX/civ_external.dbf
        5          5          1          3          3          0         52
   .2          1
CIV_EXTERNAL                   /p01/oracle/RESUMIX/civ_external1.dbf
       98        354         11          3          3          0        105
  .03       3.61
CIV_EXTERNAL_IDX               /p07/oracle/RESUMIX/civ_external_idx.dbf
        3          3          0          3          3          0         52
    0          1
CIV_EXTERNAL_IDX               /p07/oracle/RESUMIX/civ_external_idx1.dbf
        3          3          0          3          3          0        105
    0          1
DATA                           /p05/oracle/RESUMIX/data01.dbf
        3          3          0          3          3          0         21
    0          1
RBS01                          /p03/oracle/RESUMIX/rbs01.dbf
        7          7          1        379        379         51        262
  .14          1
RBS01                          /p03/oracle/RESUMIX/rbs02.dbf
      660        660         12      15076      15076       1905       1049
  .02          1
RBS01                          /p03/oracle/RESUMIX/rbs03.dbf
        7          7          4          7          7          2       2097
  .57          1
RESUMIX_DATA                   /p06/oracle/RESUMIX/RESUMIX_DA01.dbf
    36800      44137       2032       2000       2000        217        210
  .05        1.2
RESUMIX_DATA                   /p06/oracle/RESUMIX/RESUMIX_DA02.dbf
    68744      86837       4940       6735       6735        940        262
  .06       1.26
RESUMIX_DATA                   /p06/oracle/RESUMIX/RESUMIX_DA03.dbf
    55963      86182       3177       2391       2391        368        524
  .04       1.54
RESUMIX_DATA                   /p06/oracle/RESUMIX/RESUMIX_DA04.dbf
    53812      83904       2416       2653       2653        363        524
  .03       1.56
RESUMIX_DATA                   /p06/oracle/RESUMIX/RESUMIX_DA05.dbf
   105890     194626       7968       5071       5071        786       1049
  .04       1.84
RESUMIX_DATA                   /p06/oracle/RESUMIX/RESUMIX_DA06.dbf
    21972      43982       1204       1158       1158        117        524
  .03          2
RESUMIX_DATA                   /p06/oracle/RESUMIX/RESUMIX_DA07.dbf
    25180      48964       1666       1538       1538        166        524
  .03       1.94
RESUMIX_DATA                   /p06/oracle/RESUMIX/data_08.dbf
      177        367         84         72         72          7       1049
  .23       2.07
RESUMIX_IMAGE                  /p05/oracle/RESUMIX/RESUMIX_IM02.dbf
      633        633        160        602        602         74        419
  .25          1
RESUMIX_IMAGE                  /p05/oracle/RESUMIX/RESUMIX_IM08.dbf
      164        164         59        146        146         13       1049
  .36          1
RESUMIX_IMAGE                  /p05/oracle/RESUMIX/RESUMIX_IM09.dbf
      124        124         39         87         87          7       1049
  .31          1
RESUMIX_IMAGE                  /p05/oracle/RESUMIX/RESUMIX_IM10.dbf
       13         13          6          3          3          0       1049
  .46          1
RESUMIX_IMAGE                  /p06/oracle/RESUMIX/RESUMIX_IM01.dbf
      852        852        217        852        852        101        524
  .25          1
RESUMIX_IMAGE                  /p06/oracle/RESUMIX/RESUMIX_IM08.dbf
      338        338        134        332        332         32        839
   .4          1
RESUMIX_IMAGE                  /p07/oracle/RESUMIX/RESUMIX_IM03.dbf
     1379       1379        570       1285       1285        137       1049
  .41          1
RESUMIX_IMAGE                  /p07/oracle/RESUMIX/RESUMIX_IM04.dbf
     1631       1631        640       1593       1593        170       1049
  .39          1
RESUMIX_IMAGE                  /p07/oracle/RESUMIX/RESUMIX_IM05.dbf
     1223       1223        393       1121       1121        109       1049
  .32          1
RESUMIX_IMAGE                  /p07/oracle/RESUMIX/RESUMIX_IM06.dbf
      119        119         36        119        119         15        524
   .3          1
RESUMIX_IMAGE                  /p07/oracle/RESUMIX/RESUMIX_IM07.dbf
       36         36         10         36         36          4        524
  .28          1
RESUMIX_IMAGE                  /p07/oracle/RESUMIX/RESUMIX_IM09.dbf
       15         15          6          3          3          0       1049
   .4          1
RESUMIX_IMAGE                  /p07/oracle/RESUMIX/RESUMIX_IM10.dbf
       12         12          7          4          4          0       2097
  .58          1
RESUMIX_INDEX                  /p04/oracle/RESUMIX/RESUMIX05.dbf
    30317      60782       9116      15879      15879       2538       1049
  .15          2
RESUMIX_INDEX                  /p04/oracle/RESUMIX/RESUMIX_IN01.dbf
     5121       7667       1843       2735       2735        364        210
  .24        1.5
RESUMIX_INDEX                  /p04/oracle/RESUMIX/RESUMIX_IN02.dbf
     5674       8158       2253       3364       3364        386        210
  .28       1.44
RESUMIX_INDEX                  /p04/oracle/RESUMIX/RESUMIX_IN03.dbf
     4547       9441       1492       2596       2596        337        210
  .16       2.08
RESUMIX_INDEX                  /p04/oracle/RESUMIX/RESUMIX_IN04.dbf
     9233      14739       3480       6457       6457        870        419
  .24        1.6
RESUMIX_INDEX                  /p04/oracle/RESUMIX/RESUMIX_IN05.dbf
    32202      34792      16506      30753      30753       5905       1049
  .47       1.08
SOARS                          /p07/oracle/RESUMIX/soars.dbf
        3          3          0          3          3          0         21
    0          1
SYSTEM                         /p05/oracle/RESUMIX/system01.dbf
     1022       1483        353         21         21          1         52
  .24       1.45
SYSTEM                         /p05/oracle/RESUMIX/system02.dbf
     1855       2549        542        182        182         52        210
  .21       1.37
TEMP                           /p02/oracle/RESUMIX/temp02.dbf
      430       4180          0        724       4071          0        524
    0       9.72
TEMP                           /p02/oracle/RESUMIX/temp_01.dbf
      554       5530          0        819       5510          0        524
    0       9.98
USERS                          /p01/oracle/RESUMIX/users01.dbf
        3          3          0          3          3          0         21
    0          1

          Followup:

well, besides being "not very readable", b/estat are out of vogue.  Statspack
rules.

but you haven't answered the important question.

do you full scan.  do you full scan alot.  dropping all of your indexes will
make that "ratio" go up -- but much like a cache hit ratio, that would be
meaningless and counter productive in most cases.

Looks like TEMP is the only place we do multi-block IO meaning your OLTP
application "resumix" is using 100% index access and trying to get multiblock IO
from that would be similar to getting blood from a stone.

This blind "gotta tune these ratios" is something best left for the birds.

      Rating:
      5
Thanks.  August 23, 2002
Reviewer:
      John
I am new to site. Need to figure how the application does. So I was not able to
answer your question.

There is another performance index out from the report.txt. It is the time spent
on read blocks. For the most my datafiles, they are in single digit ms per
block( such 5ms/block). How do you think this performance index?

August 23, 2002 Dennis -- Thanks for the question regarding "Bad
      Policy or not", version 8.1.6.3

      You Asked
Tom,

I thought I remembered reading somewhere in a reply that it did not matter
anymore if table extents and index extents were in the same tablespace.  Based
on how Raid stripping works, and the way the disk reads are in multi-user
environments, it seems that such a statement would be true.  I think that the
only thing you would have to watch out for are "hot tables" or "hot indexes",
but in general you should store all 64K objects regardless of type together.
I've spent almost all morning searching for various things on your site and not
read what I thought I saw so long ago.

Can you offer up your expert opinion on whether or not it can be a good practice
to store indexes and tables together based on extent size (note:  LMT in use,
uniform).

Thanks a lot.  Sorry if this question exists in another form...I've tried
separate tables and indexes, create index, and table segments with index
segments and I don't know what else.

Seems I had another question or two saved up but now I don't remember them.
Drat it all.  Stupid Monday!! :)  Guess I'll wait in the queue until I remember
them again :)

Dennis

      and we said...
don't separate them for performance reasons, there aren't any

http://asktom.oracle.com/~tkyte/google.html
is a very very very big thread but makes some (sometimes) amusing reading.

Separate them by size, separate THINGS (things, not tables vs indexes) to
achieve even IO.  Separate them because it makes you feel better.  Don't do it
because "you must", it is a myth.
Review &
      Followup

      Rating: 5
As always,
      thanks  August 19, 2002
Reviewer:  Dennis
       from Missouri, USA
Thanks Tom!

Hm.  I try to rate on the usefulness of the answer regardless of whether or not
it agrees, but it seems that I am giving you an extra boost b/c I'm happy that
it agrees with me.  Stupid biased opinions influencing us like that.

I thought I was on the right track, but have been flogged via email since I
brought it up.  It's a staff meeting agenda item now, but I wanted to be sure I
wasn't spouting rumor.  I did a little test case with the table and index split
up (only 180K rows :( ) and then with the table and index together, and the
table and index together was a whole .5 seconds elapsed faster (probably just
Noise...I only ran it once so I don't have a robust test case).  Of course, I
realize that the SAN caching plays a role in this, but I think that's another
reason you want to group them up together. It makes sense to cache your whole
file of 64K objects vs caching half of two files and having potential reads into
the cache to find your object (theoretical).

Thanks for giving a clear cut answer.   I should've asked this morning when I
saw my chance, but was sure it was out here somewhere.  I just couldn't find it,
and I'm sure you get more important questions.

Dennis

      Rating: 5
hm.
      backups  August 19, 2002
Reviewer:  Dennis
       from Missouri, USA
Tom,

I finally got through that Google thread (it was entertaining at times...are you
sure nobody pulled a knife?).

It brought up some questions...I guess I haven't dealt with this before, or most
likely we are implementing something wrong.  Why is it feasible and good to
separate tables and indexes for backup purposes?  Is this assuming hot backups?

I can't imagine that it makes a difference if you are doing cold backups.
However, being denied the right to use RMAN, I don't know the impact of it for
hot backups, and if I want to change our methodology to forget about segregating
segment types, I must understand the full impacts.  Leave it to you to deliver a
bonus nugget of information (thanks!).  Yes I know, get it and play with it at
home, but I try to make the family more important than the computer, even if the
computer is more fun sometimes!!  So, I don't even have Oracle installed at
home.  I'm a bad dba in this respect.

Thanks,
Dennis

          Followup:

Well, one theoretical reason could be that technically -- you can get away
without backing up indexes if you really wanted.  I mean - after all, you can
just recreate them.

Me -- I would never dream of doing that however, when you need to restore -- you
want it to happen *fast*.  Rebuilding an index tablespace isn't going to be as
fast generally as plopping down your datafiles and recovering them (that is, if
you can even find the scripts to recreate all of those little guys again).

Otherwise, it wouldn't make any difference at all.  To a backup -- a block is a
block is a block.  The block is either empty or not empty and RMAN will either
back it up or not based on that (an OS file copy will just back it up
regardless)

It has nothing really to do with backups.

(i don't have Oracle installed at home either but fortunately I do have this
really good connection straight into the oracle network so -- its not any
different from being at work ;)  telnet, vi and a web browser, what more do you
need?

      Rating: 4
no performance
      reasons, but management reasons?  August 19, 2002

Reviewer:  Freek D  from Belgium
Hi,

I have read the thread and at a certain point there was said that you could be
better ("for management reasons") to seperate data and index into different
tablespaces. But they didn't specify this.

Do you know what reasons they where talking about? (if there are any)

          Followup:

It is like I said:

"Separate them because it makes you feel better."

Some people feel it is more "manageable" -- me, I feel almost the opposite in
many cases.  It is one of those "what are you used to, what makes you feel good"
decisions.  There is no wrong answer really -- it is the theory that you must
separate them (like that guy who compared data and index to matter and
anti-matter -- feeling that things would explode if you dared put them together)
that is wrong here.

      Rating: 5
Thanks again
      Tom  August 20, 2002
Reviewer:  Dennis
       from Missouri, USA
That was mentioned in the thread...about not restoring them and instead
recreating them (sorry I didn't realize that I had all the answers in front of
me).  You are correct, these days it is pretty hard to beat an OS file copy,
especially if you can take advantage of a high speed switch to bring it back
from the archives.

I agree with your response to Freek D.  The reason we have them separated here
was because everyone bought into the myth.  Now they don't want to separate them
because they still feel it is bad (despite my infallible once run test case
which showed it was faster (ok that is sarcastic :O ).  Plus they are not buying
into it being easier to manage.  They think that it is going to make things
harder to have them all together.  But the GUI tools we use sort things by
object type, not tablespace...which makes it real easy to just drop indexes, no
matter what tablespace they are in, or with whom their bedfellows are.

It took me two years to convince them that there would be no performance impact
from using LMT for all of our OLTP stuff that had predefined initial and next
extents (the big fear was what size do we use..they couldn't see that it didn't
matter)...as part of our 9iR2 upgrade, we are forced to convert (thank you for
making it where you can't have DMT if system is LMT :) ), and so I am trying to
further evolve us.  It makes things simple (why have multiple tablespaces of 64k
objects when theoretically you could store all of them in one tablespace), but
they are convinced doing this will be extremely time consuming.  My thoughts are
that the import can dump everything into the default tablespace (which is
conveniently the 64k tablespace) and then afterwards see what you think has too
many extents and move it to a bigger tablespace, if you don't have the time to
query dba_segments ahead of time to see what needs to be in the bigger
tablespaces and precreate the tables there, and run an index script after the
import to put them in the bigger tablespaces.  It seems so simple.  Of course,
then you have to see which disks are your hot disks and try to balance them out,
but we should be doing that with hot disks now anyways.  Should be part of our
everyday life.

heh, I wish I had the ability to tunnel into work (wait a sec...what am I
saying?).  They experimented with it for a month here, but no word on if we will
go that way.  As we are situated right now, we aren't even able to get our email
off the web (we use Lotus Notes...and Lotus Mail Server (version 4.x)..ack!!!).
We are thinking about proposing the good switch to Oracle Mail Server (since we
already have it), but that is going to cause us to have to get a server under
the new licensing costs, and they are afraid that it might be too expensive (vs
13+ mail servers, the licensing cost of all the software, and the time of 6
people to do upgrades when there is a new version of Notes...not to mention all
the wasted duplicate storage on the server backups).  Unfortunately here they
want to diversify the software we use because they fear putting all their eggs
in one basket...which is a silly notion with something as powerful and massive
as the Oracle software.  We've already made the investment to use your database
engine, we need to start using all the other stuff we get with it, and make
Oracle tools and software part of our living strategy (the fear is "what if we
suddenly want to go to SQL Server or whatever" - I won't even go into why they
shouldn't even consider that and use it as an excuse...nothing is going to work
anyway if we switch, so why not use what we have to full advantage?  ok..I
lied..I'm rambling..sorry).  But I'm not a manager, so I don't know what I'm
talking about.  One day we might be all fancy like that (using the web), but
it'll be a while.

Thanks for your insight Tom, I appreciate it once again.

Dennis

      Rating: 5
Some more
      questions  August 22, 2002
Reviewer:
      A reader
Hello Tom,

we are in the process of building a multi TB of the datawarehouse.

Currently we are planning to simulate a test environment.
The biggest question we have is the database layout.

The DBA group feels that we should follow OFA and based on that we want 10
filesystems each of 100G.

The tables are divided into small, medium , large

The first three filesystems(this can be less than 100G) will hold redo, system,
rollback, small and medium tables
and other files such as control files etc

The remaining 7 will have the data and index segments stored
together in a single file of tablespace.
So each day of week will have 10 tablespaces with 10 datafiles (we have 10 huge
partitioned tables) per day.
And this will always go into single filesystem each day.

The sys.admin group considers that instead of having 10 filesystems, it is
better to have a single huge filesystem as this gives a scope to expand and it
would be easier
for them to backup and restore.

What are your thoughts regarding this? Is there any good discussion which we can
see about this?

are there any performance metrics which we can follow what is a good layout
structure.

Please advise....

          Followup:

Well, this is beyond where I normally go -- this is more of a Jonathan Lewis or
Steve Adams question...

Me, I sort of like the the concept of a single, infinite disk (for those last 7
you talk of).  It's what I did on my last benchmark.  It was just so easy.  the
guys from HP just moved stuff around on me and I never knew, was never bothered.

But -- I also don't work the hardware aspects of systems very much, so take this
with the proverbial grain of salt.

      Rating:
      5
Benchmarking  August 23, 2002
Reviewer:
      A reader
Tom,

Thank you very much for your response. I have a question
when you say benchmarking.. what exactly do you do?

          Followup:

It varies.  but basically you are trying to test two real world scenarios in
your environment to see which will work best.

The simpliest form I use is:

http://asktom.oracle.com/~tkyte/runstats.html
The more complex is a custom test with lots of users.

      Rating: 3
What about
      Optimal Flexible Architecture ?  August 23, 2002

Reviewer:  Sanjay  from Colorado, USA
This thread really confused me for two different opinions by respected oracle
experts. Please don't take this as a blame. In Oracle 9i, DBA HandBook (by
oracle press-authored by Kevin Loney), on page number 92, in the third
paragraph, it says "Index segements should not be stored in the same tablespace
as their associated data tables, since they have a great deal of concurrent I/O
during both data manipulation and queries.", and here Tom says "it is a myth".
Now how do I decide which books/material/author to follow ?

          Followup:

benchmark it.

Read the thread I pointed you to as well.

Howard Rogers
Connor McDonald
Mark Townsend
Pete Sharman
Jonathan Lewis

those are pretty respected people in the field as well.

Think about HOW indexes are actually used and then put that against the
statement "since they have a great deal of concurrent I/O during both data
manipulation and queries.".   Now, does that statement make sense?  Is there
concurrent IO to index and table data during DML/select?  Rhetorical question --
no, the IO is in fact serial in nature.  Read the index to find the table data.
 Index then data, one after the other.  Serial....

I give a seminar on developing successful applications (you can get the slides
from
http://asktom.oracle.com/~tkyte/Aotc.zip
.  In there I have a slide that says "Question Authority".  Unless and
until the referenced book has an explicit benchmark -- some sort of scientific
proof, evidence that what they say is true, question its validity.

Now -- do that with EVERYTHING you read (hey, even I make mistakes when I just
answer off the cuff and don't set up an explicit testcase from time to time!)

Read this:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4951966319022
as well.  The quote from the "author" of the suspect material even said to me
"I'm really surprising that the Tuning Book was containing so many errors."  He
had basically posted a list of 50 some odd "tuning" tips -- 80% or more of which
were wrong, bad advice, or totally misleading.  He got them all from tuning
books.

Caveat Emptor.

      Rating: 5
Lewis's remark
      in that Google thread  August 23, 2002
Reviewer:
      Jan van Mourik  from Houston, TX, USA
Hope this is OK for this thread...
Somewhere in that Google thread Lewis gives some guidelines for what to put
where, one of them is
"Create one rollback tablespace per device"

I've been wondering if there's a difference between creating one rbs tablespace
with a datafile on each device, and his "one rollback tablespace per device"?

          Followup:

The end goal is similar -- getting that IO spread across all devices.  striping
a single infinite disk (apparently infinite -- single mount point, tons of
underlying disks) would do similar work for us.

It would be much the same if it achieves the end goal of even IO (reads and
writes) across the devices.

August 23, 2002 Om -- Thanks for the question regarding "About
      Indexes", version 8.1.7

      You Asked
Hi Tom,

I 'm sorry as I wanted to ask

1) How to find out when a INDEX WAS REBUILD ?

2) Could you please explain with an example as how DDL statements
   generate Redo or Rollback info.

Many thanks

Best regards,
Om

      and we said...
1)

scott@ORA817DEV.US.ORACLE.COM> select object_name, last_ddl_time from
user_objects where object_type = 'INDEX';

OBJECT_NAME                    LAST_DDL_TIME
------------------------------ --------------------
EMP_DEPTNO_IDX                 07-aug-2002 10:10:55
EMP_IDX                        07-aug-2002 10:13:50
EMP_PK                         09-aug-2002 08:17:38
JOB_IDX                        06-aug-2002 20:39:29
T_PK                           06-aug-2002 13:55:43

scott@ORA817DEV.US.ORACLE.COM> alter index emp_deptno_idx rebuild;

Index altered.

scott@ORA817DEV.US.ORACLE.COM> select object_name, last_ddl_time from
user_objects where object_type = 'INDEX';

OBJECT_NAME                    LAST_DDL_TIME
------------------------------ --------------------
EMP_DEPTNO_IDX                 23-aug-2002 19:12:00
EMP_IDX                        07-aug-2002 10:13:50
EMP_PK                         09-aug-2002 08:17:38
JOB_IDX                        06-aug-2002 20:39:29
T_PK                           06-aug-2002 13:55:43

scott@ORA817DEV.US.ORACLE.COM>

2) sure, this shows a create table generated redo (the redo is for the DATA
DICTIONARY entries in this case).  Then, I show that an alter table drop column
for example generates lots of redo and undo -- that is to protect the table
whilst the drop column takes place.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table msg ( msg varchar2(4000) );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4  and a.name = 'redo size'
  5  /

NAME                                VALUE
------------------------------ ----------
redo size                           43704

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t
  2  as
  3  select * from all_objects;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4  and a.name = 'redo size'
  5  /

NAME                                VALUE
------------------------------ ----------
redo size                           86512

ops$tkyte@ORA817DEV.US.ORACLE.COM>

now, since the only thing that happened between the two querys on v$mystat was
the create table, it was the thing that generated the redo in our session...

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger alter_trigger
  2  after alter on schema
  3  begin
  4    for x in ( select used_ublk
  5                 from v$transaction
  6                where addr = ( select taddr
  7                                 from  v$session
  8                                where sid = ( select sid
  9                                                from v$mystat
 10                                               where rownum = 1 ) )
 11             )
 12    loop
 13          insert into msg values ( 'generated ' || x.used_ublk || ' blocks of
rollback' );
 14    end loop;
 15  end;
 16  /

Trigger created.

that trigger will just let me use a single session to monitor the undo for a
given statement.  I could just use two sessions but this is easier for a
demonstration perspective...

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t drop column object_name;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from msg;

MSG
---------------------------------------------------------------------------------
--------------------------------------------------
generated 304 blocks of rollback

ops$tkyte@ORA817DEV.US.ORACLE.COM>

there it is -- 304 blocks of rollback for that drop column

ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4  and a.name = 'redo size'
  5  /

NAME                                VALUE
------------------------------ ----------
redo size                         5577840

ops$tkyte@ORA817DEV.US.ORACLE.COM>

and the corresponding redo to boot

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop trigger alter_trigger;

Trigger dropped.

August 24, 2002 -- Thanks for the question regarding "Aggregate
      function KEEP", version 9

      You Asked
Tom:

     Could you give an example using the aggregate function KEEP last or first?.
Can i have the same result by using analytical functions?

Thank in Advance
Pablo

      and we said...
Well, yes and no.  When you use KEEP with an aggregate -- you get a single
result for the group by set.  When you use KEEP with an analytic function, you
get the result by the partition window.

See

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96540/functions45a.htm#1129681
they have an example of using keep with an aggregate and an analytic function
right there.
August 24, 2002
      Frankie -- Thanks for the question
      regarding "Quick Search/Google Search on multiple text fields in multiple
      tables", version 8.1.7

      You Asked
We have an action item tracking system where an action item (task) can be
assigned to 0 to many people.   This is a dynamic  system, where data is changed
frequently.

We would like a "Quick Search"/"Google Search" (using 1 search field that the
user searches from) capability where the user can enter a keyword from multiple
text fields of the action item(emphasis_item table), associated item status
history of the action item (emphasis_item_Status table) or the even the person's
last name and/or first name that has been assigned responsiblity for completing
the action item.

Example Data form:
Quick Search: _________________________ [search}

Example Data Queires
Quick Search: Jane Doe [search]   - lists all action items where Jane Doe is the
responsible person
Quick Search: Doe cabinet red[search]  - this one show all the action items
where the person name(last, first, middle) =

"Doe" was assigned an action whose action item task desc/other text fields
contained the word "cabinet". Note: Red and
cabinet might not be listed together in the actual text or in the same text
field

We already have a very complicated "Advanced Search" page that lists dozens of
text fields (e.g. Action Item Description, Closure Criteria, Responsible Person,
Assigned Date, Due Date, Status Desc(s)).  We would like to simplify this for
our users with ONE Quick Search field/button that they can do EVERY kind of
search from.

Any pointers (which Oracle tool -- intermedia???? is intermedia index as current
as data???,  working examples that are similar to the description above (it
would be nice to have an automated script that can be run to load a working
examples).  Any help would be appreciated.

We are on Oracle 8.1.7 and restricted to using this version at this time.

Here are some of the tables and text fields that we would like to use this one
"Quick Search/Google type Search" on:
desc plng.emphasis_item_Vw
Name                                      Null?    Type
----------------------------------------- -------- ----------------------------
EMPH_ITEM_ID                              NOT NULL NUMBER(9)
EMPH_ITEM_NO                              NOT NULL VARCHAR2(255)
EMPH_ITEM_TITLE                                    VARCHAR2(2000)
EMPH_ITEM_PROB_TYPE_CODE                  NOT NULL VARCHAR2(30)
EMPH_ITEM_DESC                                     VARCHAR2(2000)
EMPH_ITEM_STATUS_CODE                     NOT NULL VARCHAR2(30)
EMPH_ITEM_ASSIGNED_DATE                            DATE
EMPH_ITEM_CLOSED_DATE                              DATE
EMPH_ITEM_DUE_DATE                                 DATE
EMPH_ITEM_EST_CMPLT_DATE                           DATE
EMPH_ITEM_PROB_IMPACT                              VARCHAR2(2000)
EMPH_ITEM_PROB_RESOLUTION_PLAN                     VARCHAR2(2000)
EMPH_ITEM_PROB_EVID_OF_CMPLT                       VARCHAR2(2000)
EMPH_ITEM_RISK_ACHV_OBJ                            VARCHAR2(2000)
EMPH_ITEM_RISK_DEF                                 VARCHAR2(2000)
EMPH_ITEM_RISK_ABATE_SUM                           VARCHAR2(2000)
EMPH_ITEM_RISK_ABATE_PLAN                          VARCHAR2(2000)
EMPH_ITEM_RISK_MAJ_CNTR_TO_RSK                     VARCHAR2(2000)
EMPH_ITEM_RISK_FLR_CONSQ                           VARCHAR2(2000)
EMPH_ITEM_RISK_LKLHD_FACTOR                        NUMBER(12)
EMPH_ITEM_RISK_FACTOR_RATING                       NUMBER(12,3)
EMPH_ITEM_RISK_TECH_FLR_CONSQ                      NUMBER(12)
EMPH_ITEM_RISK_SCH_FLR_CONSQ                       NUMBER(12)
EMPH_ITEM_RISK_COST_FLR_CONSQ                      NUMBER(12)
EMPH_ITEM_RISK_PRIORITY                            NUMBER(12)
EMPH_ITEM_RISK_ABATPLN_APRV_DT                     DATE
CONTROLLING_PROJECT_NAME                  NOT NULL CHAR(15)
EMPH_ITEM_CONTROL_NO                               VARCHAR2(25)
ASSIGNED_PROGRAM_ID                       NOT NULL NUMBER(9)
DESIGNATING_ORG_ID                        NOT NULL NUMBER(9)
EMPH_ITEM_STOPLIGHT_CODE                           VARCHAR2(30)
SEND_REMINDER                             NOT NULL VARCHAR2(1)
DBMS_CREATE_USER                          NOT NULL VARCHAR2(30)
DBMS_CREATE_DATE                          NOT NULL DATE
DBMS_MODIFY_USER                          NOT NULL VARCHAR2(30)
DBMS_MODIFY_DATE                          NOT NULL DATE
DBMS_CLOSED_DATE                                   DATE

ev-MESDATA> desc plng.emphasis_item_assigned_vw
Name                                      Null?    Type
----------------------------------------- -------- ----------------------------
EMPH_ITEM_ASSIGNED_ID                     NOT NULL NUMBER(9)
EMPH_ITEM_ASGN_RESP_TYPE_CODE             NOT NULL VARCHAR2(30)
EMPH_ITEM_ASSIGNED_DATE                            DATE
ASSIGNED_TO_ORGANIZATION_ID                        NUMBER(9)
ASSIGNED_TO_PERSON_ID                              NUMBER(9)
ASSIGNED_EMPH_ITEM_ID                     NOT NULL NUMBER(9)
ASSIGNED_TO_PERSON_PRIMARY_IND            NOT NULL VARCHAR2(1)
DBMS_CREATE_USER                          NOT NULL VARCHAR2(30)
DBMS_CREATE_DATE                          NOT NULL DATE
DBMS_MODIFY_USER                          NOT NULL VARCHAR2(30)
DBMS_MODIFY_DATE                          NOT NULL DATE

ev-MESDATA> desc plng.person_vw
Name                                      Null?    Type
----------------------------------------- -------- ----------------------------
PERSON_ID                                 NOT NULL NUMBER(9)
PERSON_LAST_NAME                          NOT NULL VARCHAR2(255)
PERSON_FIRST_NAME                         NOT NULL VARCHAR2(255)
PERSON_MIDDLE_NAME                                 VARCHAR2(255)
PERSON_TYPE_CODE                          NOT NULL VARCHAR2(30)
PERSON_MANAGER_INDICATOR                  NOT NULL CHAR(1)
PERSON_FAX_PHONE                                   VARCHAR2(2000)
PERSON_WORK_PHONE                                  VARCHAR2(2000)
PERSON_WORK_EMAIL                                  VARCHAR2(2000)
PERSON_ROLE_CODE                                   VARCHAR2(30)
SEND_REMINDER                             NOT NULL VARCHAR2(1)
BEMSID                                             NUMBER(10)
DBMS_CREATE_USER                          NOT NULL VARCHAR2(30)
DBMS_CREATE_DATE                          NOT NULL DATE
DBMS_MODIFY_USER                          NOT NULL VARCHAR2(30)
DBMS_MODIFY_DATE                          NOT NULL DATE

ev-MESDATA> desc plng.emphasis_item_status_vw
Name                                      Null?    Type
----------------------------------------- -------- ----------------------------
EMPH_ITEM_STATUS_ID                       NOT NULL NUMBER(9)
EMPH_ITEM_STATUS_DATE                     NOT NULL DATE
EMPH_ITEM_STATUS_DESC                              VARCHAR2(2000)
STATUSED_EMPH_ITEM_ID                     NOT NULL NUMBER(9)
DBMS_CREATE_USER                          NOT NULL VARCHAR2(30)
DBMS_CREATE_DATE                          NOT NULL DATE
DBMS_MODIFY_USER                          NOT NULL VARCHAR2(30)
DBMS_MODIFY_DATE                          NOT NULL DATE

      and we said...
Give this:

...
Example Data form:
Quick Search: _________________________ [search}

Example Data Queires
Quick Search: Jane Doe [search]   - lists all action items where Jane Doe is the

responsible person
Quick Search: Doe cabinet red[search]  - this one show all the action items
where the person name(last, first, middle) =

"Doe" was assigned an action whose action item task desc/other text fields
contained the word "cabinet". Note: Red and
cabinet might not be listed together in the actual text or in the same text
field

........

It sounds like you would have to definitely have a "smart" parser behind this
quick search -- a custom piece of code you write, since your requirements are
very unique and specific.

I mean -- a search engine doesn't tell the difference between "jane doe" and
"doe cabinet red".  What happens when  foreign person gets hired whose name is
actually "doe cabinet"?

I can give you quick search abilities, like we have here on asktom, using a
simple AND or OR'ing of terms but anything as specific as you ask for will
require alot of custom code with your business smarts built into it by
definition.
August 24, 2002
      -- Thanks for the question regarding "Read
      only tablespaces.", version 8.1.7

      You Asked
Hello Tom,

I am trying to move a datafile(to different location). But I am doing at
tablespace level.

Instead of offlining it, I made it to read only and tried to rename
a file ( a prior copy has been made ) . But I get an error message
that a file is being in used and hence cannot be renamed.

My question is:

Since the tablespace is marked read only and the SCN freezed why should it not
allow me to move that file to some other location ?

Thanks,

      and we said...
because the file is IN USE.

You have to stop using it before we'll let you rename it.

You'll have to offline the tablespace, rename it, online it.

We cannot have something ACCESSING the file whilst you are RENAMING the file
(moving it effectively). What would happen if we did.....
August 24, 2002 -- Thanks for the question regarding "Not Exists
      : Intermedia and others", version 816

      You Asked
Hi,
   1)If in  a normal query I use
say for eg select * from emp where not(empno = 123 or empno = 234).
It does not use the index so query takes a long time.
How to tune such not equal to queries ?
   2) Is there a better approach to search for a not exists text criteria using
Contains clause .
 When I use "not" Contains clause (for some text entered by user to search
records not having that particular text))the Query does a Full table Scan , no
Index is used.For 20 million rows this takes a lot of time. How to tune (write
alternative) or write such queries.

Thanx.

      and we said...
1) why do you believe USING an index would be faster.  I can force that to
actually use an index and guess what -- it'll run SLOWER!!!

why, why why does everyone in the world seem to think "if my query doesn't use
an index, I am in big trouble and it is running slower then it would with an
index".  It is just not true, not at all!!!

big_table@ORA817DEV.US.ORACLE.COM> select count(*) from big_table;

  COUNT(*)
----------
   1459904

Now, you tell me -- which is the *better* plan?

select *
  from big_table
 where NOT ( object_name = 'FOO' or object_name = 'BAR' )

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch    97320     25.37      25.11      20033     116039        105     1459776
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    97322     25.37      25.11      20033     116039        105     1459776

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 440

Rows     Row Source Operation
-------  ---------------------------------------------------
1459776  TABLE ACCESS FULL BIG_TABLE

********************************************************************************

select /*+ INDEX( big_table OBJECT_NAME_IDX ) */ *
  from big_table
 where NOT ( object_name = 'FOO' or object_name = 'BAR' )

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.01          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch    97320     63.39     289.45      26748    1551690          0     1459776
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    97322     63.40     289.46      26748    1551690          0     1459776

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 440

Rows     Row Source Operation
-------  ---------------------------------------------------
1459776  TABLE ACCESS BY INDEX ROWID BIG_TABLE
1459777   INDEX FULL SCAN (object id 40982)

Me, I would rather avoid that index like the PLAGUE.

2) show me the query you are running perhaps and we can take a look -- remember,
include all relevant details (such as -- what it is exactly you have indexed).
we'll see what we can do.
August 24,
      2002 Om -- Thanks for the question
      regarding "About Indexes", version 8.1.7

      You Asked
Hi Tom,

Thanks for all your help.

Today my  Question is

1)How can we find out that when exactly a particular INDEX
or any other Object was last analyzed ?

2) Why DDL statements do not generated Redo or rollback information.

Best regards,
Om

      and we said...
1) umm, did you look at the LAST_ANALYZED columns in user_indexes, user_tables?

2) they do.  they most certainly do.
Review &
      Followup

      Rating: 5
WHAT HAPPENS IF
      THE TABLE OR INDEX IS NOT ANALYZED  August 23, 2002

Reviewer:  john
tom,

does't the optimizer give wrong 'cost' values when you explain plan for query if
some of the tables in the query are not analyzed?

because i have written a query that uses tables from some other schema.. i got
the explain plan then i see in the plan all tables are using their indexes.the
total cost was 192

now i analyzed all my current schema objects. then i run and see the explain
plan then i see most of the other schema and our schema table's indexes are not
used.

and the cost climbed to very very very hughe number

i saw the last analyzed column for the tables in other schema, there is no data
available...

is the cost climbed because of the other tables are not analyzed or anything
else?

please help.

          Followup:

Sure, if the table is not analyzed, we use a documented set of rules to
determine what statistics to use for that object.

the cost is a relative number, you cannot really compare the costs between two
different queries with two different inputs.

recommendation: either analyze everything or analyze nothing but be aware that
the next release of Oracle (after 9iR2) won't even have a rule based optimizer!

August 24, 2002 chet -- Thanks for the question regarding
      "deployment", version 8.1.7

      You Asked
Hi Tom,

  Why the value of CPU is getting high when I deploy my SQLJ programs to the
database?

$top
2:46pm up 30 days, 5:49, 2 users, load average: 1.01, 0.40, 0.47
86 processes: 82 sleeping, 4 running, 0 zombie, 0 stopped
CPU states: 98.4% user, 1.5% system, 0.0% nice, 0.0% idle
Mem: 517220K av, 412432K used, 104788K free, 587184K shrd, 112984K buff
Swap: 72252K av, 1744K used, 70508K free 105932K cached
PID 5733
USER oracle
PRI 14
NI 0
SIZE 59460
RSS 58M
SHARE 44028
STAT R
LIB 0
%CPU 98.0 ---------------> too busy
%MEM 11.4
TIME 0:32
OMMAND oracle

Tanks...

      and we said...
umm, compiling java is cpu intensive perhaps?

It is really OK to have the cpu being used.  You cannot put cpu cycles "in the
bank".  If you don't use them -- you can never use them.

Its GOOD that it pegs the CPU -- it was not 100% meaning -- there was idle
capacity.  anything under 100% is great.

A machine that runs at 5% -- thats oversized.

A machine the continously runs at 100% -- thats undersized.

the closer to 100% you are without hitting it -- thats perfection.

You deploy very infrequently - compiling java (compiling anything) is a cpu
intensive process.  It should consume the CPU during the deploy.
Review &
      Followup

      Rating: 2
not
      oversized  September 14, 2001
Reviewer:
      A reader  from New York
A machine the continously runs at 100% -- thats undersized and not oversized.

          Followup:

Thanks -- I was typing too fast, meant to say "A machine that runs at 100%
continously -- undersized".  I updated my response.

      Rating:
      2
deployment  September 19, 2001
Reviewer:
      chet
thank you very much...

That was Linux on the machine with PIII, 1CPU, 512 RAM.

It is very quickly when I change to the DB server which is W2K on the machine
with PIII,2CPU,1.5G RAM.

Does it depend on the machine?

      Rating: 4
Collect usage
      of CPU within the database  August 23, 2002

Reviewer:  Marc  from Switherland
Hi Tom

I try to do a statement like this: select cpu_idle from v$...;

Is there a SQL statement, where I can see the actual usage of the CPU ? Many
thanks for your answer.

Kind Regards
Marc

          Followup:

we don't measure idle time, use sar or vmstat as the OS level for that.

      Rating: 1
Collect amount
      of CPU usage in a Select Statement  August 24, 2002

Reviewer:  Marc  from Switzerland
Hi Tom

Thanks for quick feedback, but you haven't answered the important question: Is
there a SQL statement, where I can see the actual usage of the CPU ?

I am looking a way to collect CPU specific data as I can do for disk I/O in
v$filestat or v$sysstat.

One solution is to exectue an external unix procedure in PL/SQL, but again, I
examine a way to do it with SQL.

Nice Weekend
Marc

          Followup:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from v$sysstat where upper(name)
like '%CPU%';

STATISTIC# NAME                                CLASS      VALUE
---------- ------------------------------ ---------- ----------
         8 recursive cpu usage                     1       1355
        11 CPU used when call started            128      15493
        12 CPU used by this session                1      15488
       177 parse time cpu                         64        356
       211 OS User level CPU time                 16          0
       212 OS System call CPU time                16          0
       213 OS Other system trap CPU time          16          0
       219 OS Wait-cpu (latency) time             16          0

8 rows selected.

you want timed_statistics to be set on and optionally timed_os_statistics.

(if you new about v$sysstat???)

Anyway, bear in mind, v$filestat shows you our database stats only, it does not
show the OTHER io on those volumnes.

      Rating: 3
Install
      shield  August 24, 2002
Reviewer:  Godwin
       from Ghana
Thanks for your answers. My question in relation to this is that do Oracle have
anything like install shield such that it will help application developers
package their applications (ie Forms,Reports exe files and the Database) unto CD
so that the CD will be used to install the Customised developed application on
other machines?
It is like the VB guys can easily package their applications and deploy it to
other PCs.
Thanks.

          Followup:

http://otn.oracle.com/software/products/osp/content.html
...

August 24, 2002 Munzer -- Thanks for the question regarding
      "CHECK FOR CURSORS", version 8i

      You Asked
Tom:

1.  I have a cursor defined in a Pl/SQL program.  I am using a "FOR LOOP" cursor
to open, fetch and run.  I want to display a "NO RECORDS FOUND" in case no
records were found.  Problem is that it seems I have to put an IF statement
after :

FOR po_rec in po_cur(i_po_number,i_stock_number,i_vendor_code);
LOOP
IF SQL%NOTFOUND THEN
htp.p('no records found');
else
htp.p(po_rec.po_number);
end if
end loop;

This seems to be creating a problem becuase If cursor retrieves 10 records i am
running the IF statement 10 times.

Do you know a better way of doing this?

2.  Do you always have to define parameters in cursors when you have a parameter
in the SELECT statement?

Thank you,

      and we said...
1) that logic won't work either.  The loop will never enter the body of the loop
if there is no data found.

You typically code:

...
   l_found_record boolean default false;
...
begin
   for po_rec in po_cur( .... )
   loop
      l_found_record := true;
      htp.p( ... );
   end loop;
   if ( not l_found_record )
   then
       htp.p( 'no records found' );
   end if;

2) No, i prefer to code like this:

   for x in ( select *
                from po
               where po_number = i_po_number
                 and stock_number = i_stock_number
                 and vendor_code = i_vendor_code )
   loop
      ...

and never declare cursors myself.  I prefer it that way personally.  I find the
code easier to read when the query is staring me straight in the face.  If the
query gets large and complex -- i use a view.
Review &
      Followup

      Rating:
      5
cursor  August 22, 2002
Reviewer:
      munzer  from Reston, USA
TOm:

Basically you can not use cursor%rowcount inside the loop to check if it is 0 or
not?Correct?

          Followup:

if the rowcount was zero, we never executed the loop ;)

      Rating:
      5
cursor  August 23, 2002
Reviewer:
      mo  from Reston, USA
Tom:

1. is there a difference between paramterised cursor and bind variable cursor.

2.
E.g. if I have this implicit cursor:

For x in (select * from table where state='CA')
LOOP
......
END LOOP;

Would you use a bind variable for it like

For x in (select * from table where state=:state)
LOOP
......
END LOOP;

Where do you define the value of this varibale? is it using owa_util.bind
variable inside the loop.

3. same this for explicit cursor. how you define the bind variable.

cursor xyz is select * from table where state=i_state
open xyz
fetch xyz into l_rec
....
close xyz

4. IF you have subqueries in your select do you always use ref cursor.  when do
you use dbms_Sql.

          Followup:

1) they are the same.

2) i would use

for x in ( select * from table where state = 'CA' )

believe it or not.  STATIC sql (sql that does not change at runtime) can use
bind variables for the things that change.  Since everytime you run the above
query, the state is ALWAYS 'CA', you need not use a bind variable for it.

If you did:

   l_query := 'select * from table where state = ''' || p_state || '''';
   open ref_cursor for l_query;

then, you should use a bind variable instead.

3) If the value of i_state changes from execution to execution, use a bind
variable.  If the state is forever and ALWAYS 'CA', just put it in there.

4) a ref cursor has nothing to do with a subquery - I don't see the connection.

If you have my book -- I cover when you use dbms_sql and native dynamic sql in
depth.

static sql -- for everything you can.  never do dynamically that which you can
do statically.

dbms_sql - for when you don't know the number/types of the outputs (eg: if you
use my print_table utility, it uses dbms_sql cause we do not know how many
columns it will fetch).

native dynamic sql - for all other dynamic sql.

      Rating:
      5
cursors  August 24, 2002
Reviewer:
      mo  from Reston, USA
Tom:

Great explanation for cursors.

1.  As you said, if a user is querying by state then you use dynamic sql.  Now
Is this considered to be dynamic
For x in (select * from table where state=p_state)
LOOP
..
END LOOP

or

l_query := 'select * from table where state = ''' || p_state || '''';
   open ref_cursor for l_query;

Will both work since I am not using the :i_state as bind variable.

2.  You say that you use dbms_sql when you do not know the number of output
variables.  DO you mean when you have a query like "select * from table" then
you do not know how many columns there are so you do not use ref cursor.

          Followup:

1) no, no -- that is NOT what I said.

I said:

static sql -- for everything you can.  never do dynamically that which you can
do statically.

dbms_sql - for when you don't know the number/types of the outputs (eg: if you
use my print_table utility, it uses dbms_sql cause we do not know how many
columns it will fetch).

native dynamic sql - for all other dynamic sql.

You would NOT use dynamic sql just because the state value changes from execute
to execute, you would use a SQL statement with a bind variable for stat.

BTW:
l_query := 'select * from table where state = ''' || p_state || '''';
   open ref_cursor for l_query;

does NOT USE a bind variable at all.  You are hard coding the literal value
p_stat in the query.  That is *bad*.

it could be

   l_query := 'select * from table where state = :x';
   open ref_cursor for l_query using p_state;

but it should just be static sql.

2) I mean when you quite simply don't know.

the fact is in this case, you KNOW how many columns 'select * from table' will
return (you own table, table does not vary at runtime, you KNOW exactly how many
columns it will return)

search this site for

dump_csv

to see an example of a routine where you DON'T know -- because the query is
passed to you at run time and can be any query in the world, with any number of
columns.

August 25, 2002 Ramesh -- Thanks for the question regarding
      "Recover until change", version 8.1.7

      You Asked
Hi Tom,
Thanks for the Great work.. Keep it up..
Qn:
RECOVER Database
UNTIL CHANGE integer ;
Here Integer => SCN ..
While recovering in practical scenario.. How do we know the SCN  for
a particular transaction.
Normally we use until time... for incomplete recovery.

If we can't use the syntax practically , Is there any specific reason  for this
?

Thanks in advance

Ramesh

      and we said...
You would use LOG MINER to discover an explicit SCN.

It is in the redo logs.
Review & Followup

      Rating:
      3
  August 24, 2002
Reviewer:  ramesh
       from Atlanta, GA
While doing recovery using archive logs, the database is not opened condition,
How can we check the scn using logs..

Please let us know..

Thanks
Ramesh

          Followup:

log miner, like I said.

You generally use ANOTHER instance to mine the logs, you don't have to (nor is
it even common to) use the same instance to mine them.

      Rating: 4
Way to get SCN
      right now?  August 24, 2002
Reviewer:  Doug
       from CT, USA
I know this isn't a 100% fit with the original question, but isn't there a way
to use a select statement to get the current SCN right now? I forget what it is.

          Followup:

documented - in 9i.

undocumented -- see the link you found below ;)  which is the same link the
original questioner was asking about!

      Rating: 4
for
      Doug  August 25, 2002
Reviewer:
      A reader
http://asktom.oracle.com/pls/ask/f?p=4950:8:1207638::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:2591957673673,%7Bcommitscn%7D

      Rating: 4
Thanks a
      lot  August 25, 2002
Reviewer:  Ramesh
       from ATL,GA

      Rating: 5
another
      way?  September 27, 2002
Reviewer:  Doug
       from CT, USA
How about?
alter system checkpoint;
select CHECKPOINT_CHANGE# from v$database;

August 25, 2002 Meyer -- Thanks for the question regarding
      "HTML/SMTP Dynamic when email opened", version 8.16-9ias

      You Asked
I can use the procedure on this site called HTML_EMAIL to send HTML emails via
SMTP.

I also have a package.procedure that I can run via mod/plsql that creates
dynamic HTML content in my web browser (standard stuff).

Have 2 questions,

1)I want to be able to send an email that displays current dynamic content
(current when the email is opened, not sent), with out the user having to click
a link to get the current content (email checks the server based on URL). I
tried this using inline frames (html <iframe>), and meta refreshes but outlook
default security settings to do not display them, it does work on web email
clients...do you have any insight if this is possible, how to accomplish, or a
trail to send me investigating?

2)As alternative, and for other applications...I want to be have the ability to
take my package.procedure that I run via the web and somehow send that output
into HTML_EMAIL...or get the HTML output from that procedure that I display in
my browser into the HTML_EMAIL procedure...I am not sure of the exact syntax to
call/pass.

THANK YOU,
Meyer

      and we said...
1) nope, i do not.

Seems reasonable that it would be a bad thing security wise.  I personally would
send a simple LINK, period.  Ease of use is one thing -- secure, reliable,
trustworthy -- that is all I want, more then anything (it is why I'm using linux
petty much all around -- I haven't had the need to run virus software since.  Is
there even "virus" software for linux???)

2) see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:347617533333
it shows how to run a mod_plsql routine (it references webdb but that is the
same for all intents and purposes) and send the output to a file.  change the
file to something else and there you go.
Review &
      Followup

      Rating:
      4
progress  August 25, 2002
Reviewer:
      Meyer Tollen  from Baltimore, MD
Tom see code below...you have been a huge help. I have been working on this
non-stop since the moment you advised...I have it working...but...Please help me
make it perfect and understand this better.

1)I am getting web server header info In the buffer output
EXAMPLE: Content-type: text/html Content-length: 13958
(see my workaround for this in the code). How do I turn off the header for this
specific call from inside pl/sql (the fixes I used same issue w/cookies do not
seem to work). If possible please test your solution and provide specific syntax
that works in this specific case because I have been trying a multitude of
things for hours/days. My work around works fine, but I still want to figure
this out.

2)Please explain how htp.htbuf_arr works, what exactly is it, how does my
html/output get there...can I clear it, is it going to be hear for a while
(guess ok) Is more information available.

3)FYI: In your HTML_EMAIL procedure, in the TEXT HEADER section
This line of code:
l_temp   := '--' || l_boundary || chr(13)||chr(10);
Needs to be this:
l_temp := chr(13)||chr(10)||'--' || l_boundary || chr(13)||chr(10);
For it to display HTML (instead of text) w/the following email clients (these
are the only ones I tested):
Outlook, Outlook Express, Outlook Web Access

Code below...THANK YOU!

Meyer

PROCEDURE SEND_SP_EMAIL

is
nm  owa.vc_arr;
vl  owa.vc_arr;
l_thePage       htp.htbuf_arr;
l_lines         number default 99999999;
temp_html      varchar2(30000) := ' ';

begin
nm(1) := 'SERVER_PORT';
vl(1) := '80';
owa.init_cgi_env( 1, nm, vl );

webpack.webproc; --pl/sql procedure norm exec via mod/plsql
owa.get_page( l_thePage, l_lines );
for i in 1 .. l_lines loop
--work around for htp header
if i < 3  and upper(l_thePage(i))  like 'CONTENT%'
then null;
--this is not the header build the HTML
else temp_html := temp_html||(l_thePage(i) );
end if;
end loop;

--send the email below
commit;
html_email('email@domain.com', 'email@domain.com', 'Email Title', 'txt (txt not
used now)', temp_html,'mysmtpserver.mydomain.com','25');
commit;
end;

          Followup:

1) you cannot turn it off -- you have to parse it out, just like you are doing.

2) htp.htbuf_arr is just a "type".  type htbuf_arr is table of varchar2(255)
index by binary_integer.

It is an array.  when you call htp.p, they add a row to the array (they pack it
in there actually).  You can clear it by calling owa.get_page. (or
owa_util.showpage, that'll dump it to the dbms_output buffer).

3) I've never had a problem with any of those email clients -- must be a subtle
difference in the way we are doing stuff.  I always send TEXT and HTML, you are
sending just HTML i believe.  That might have something to do with it.

August 25, 2002 khalid -- Thanks for the question regarding
      "transactions that does not generate data in rollback segments", version
      8.1.7

      You Asked
Hi Tom

I don't beleive my self that i would have the change to ask you.

My question is can i  update or delete records in a table without generating
data in the rollback segments. case i don't care for the data i have it in the
table.

i want to speed up the update or delete by not generation the entries in the
rollback segment.

I know how not to generate redo log entries in the redo log bugger
by altering the tabel to nologging.

Thank you very very much.

      and we said...
Arg.

Maybe you don't care, but I'm sure someone would care if the table you were
working with became 100% corrupt and inaccessible.

You see, when you do an update/delete -- you are modifying a hugely complex data
structure.  There is the table itself, the indexes on it, the fact that others
are accessing it concurrently as well.

A failure during the process would leave this extremely complex set of
structures in a totally, wholly, unknown state.

By the way -- all updates and deletes ALWAYS generate log -- ALWAYS. (so no,
you don't know how to not generate the redo log entries -- there is NO WAY not
to!)  Setting the table to nologging has absolutely ZERO, none, no effect
whatsoever.  read

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:485221567528

So, if you are going to delete MOST of the records or update most of the
records, you may be better off with:

create table temp as <select rows to keep>  -- for a delete
create table temp as <select modified rows> -- for update

and use parallel, nologging on that (no redo, no rollback)

Then, truncate the original table.
Now, set all indexes on the table to "unusable".
Then, insert /*+ append */ into table select * from temp;  (using parallel if
you like)
Now, rebuild the indexes in parallel, nologging.

make a BACKUP immediately after.
Review &
      Followup

      Rating: 5
Thanks
      Tom  August 26, 2002
Reviewer:  khalid
      al-mansour  from Yemen
Thank you very much you clarify rollback and nologging for me.

August 26, 2002 Nag -- Thanks for the question regarding
      "Transaction Question", version 8.1.7

      You Asked
i'M LOGGED IN AS SCOTT AND TIGER....

SQL> truncate table t;

Table truncated.

SQL> desc t;
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 X                                                  NUMBER(38)
 Y                                                  CHAR(30)

SQL> insert into t values (1,'A');

1 row created.

SQL> insert into t values (2,'B');

1 row created.

SQL>
SQL>
SQL> SELECT * FROM T;

         X Y
---------- ------------------------------
         1 A
         2 B

******CONNECT AGAIN AS SCOTT/TIGER  AND QUERY TABLE T. NOW THE PREVIOUS SESSION
DOESNT EXIST, AND WE HAVE NOT COMMITTED THE RECORDS THERE, SO WE SHOULD NOT BE
ABLE TO SEE THE RECORDS IN THIS SESSION..
BUT OOO LALA  I see that I can still see the records.

Why is this so. I should not be able to see the record in my second session
loggedin as scott/tiger.

SQL> CONNECT SCOTT/TIGER
Connected.
SQL> SELECT * FROM T;

         X Y
---------- ------------------------------
         1 A
         2 B

SQL>

Thnak you

      and we said...
Your original "subject" was:

<quote>
Records inserted from the  session, but not committed, why are weable to query
them even if we have not committed those records. And why are we able to query
them even from the second session even though they were not committed in the
second session.
</quote>

I shortened it.  That stuff belongs in the question itself (heck, it is the
question)

When you connected to get the second session, SQLPlus did a disconnect/connect.

By default, when you disconnect in SQLPlus, it commits for you.  Exiting sqlplus
does an implicit commit.

So, yes, you should be able to see them in the second session since they were in
fact committed.

As to why you can see them in the first session -- that is the rule of
transactions "A transaction MAY see changes it has made, no other transaction
may see these changes until the transaction that made them commits".

If you would like to see this rule in action:

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int );
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1 );
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2          dbms_output.put_line( 'start' );
  3          for x in ( select * from t )
  4          loop
  5                  dbms_output.put_line( 'x = ' || x.x );
  6          end loop;
  7          dbms_output.put_line( 'done' );
  8  end;
  9  /
start
x = 1
done
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      PRAGMA AUTONOMOUS_TRANSACTION;
  3  begin
  4          dbms_output.put_line( 'start' );
  5          for x in ( select * from t )
  6          loop
  7                  dbms_output.put_line( 'x = ' || x.x );
  8          end loop;
  9          dbms_output.put_line( 'done' );
 10  end;
 11  /
start
done
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

         X
----------
         1
ops$tkyte@ORA817DEV.US.ORACLE.COM>

See how the second block of code, using an autonomous (separate) transaction
cannot see these changes -- thats the transaction semantics at work.
Review &
      Followup

      Rating: 4
Transaction
      Question  August 26, 2002
Reviewer:  C.P.
      Bhattarai  from Nepal
That means Autonomous Transactions acquire a separate session under the same
user ? and Hence both the sessions are independent of each other.

          Followup:

No - not at all.

There is one SESSION.

There are two TRANSACTIONS.

one session, two separate and distinct transactions.

      Rating: 3
Advice for
      Nag  August 26, 2002
Reviewer:  Basharat
       from Dubai
Nag Please don't start shouting at such basic things, it seems to others as you
found some bug in Oracle. :)

August 26, 2002 Chetan -- Thanks for the question regarding
      ""Java procedure for host calls on Unix environment", ", version 8.1.7

      You Asked
Hi Tom,

Your example of "Java procedure for host calls on Unix environment" works
absolutely fine.

I wanted to use this feature so that at a click of a button on an Oracle form
the user can run Unix shell script and also Oracle Utilities like SQL*Loader, so
the DBA had to give permission on Oracle Home's bin directory.

My DBA is not ready to implement this technology in production environment as he
thinks that there is a serious security hole using this technology. He says one
could write scripts which can remove files from the directory where the
permissions are granted (e.g. Oracle Home's bin directory).

I would like to know what kind of permissions are required to be granted so that
this breach of security could be avoided?

Regards

Chetan

      and we said...
Look at the dbms_java.grant_permission call you have to make.

Only the DBA can run that and he must allow you to run the scripts they want you
to.

Since they OWN the file system as well, you cannot deploy any trojan horses
(they have to deploy the scripts)

Hence, the scenario they setup is not possible -- unless they make it so (by
allowing you to execute ANY script)

Review &
      Followup

      Rating:
      3
  August 25, 2002
Reviewer:  Chetan
       from New Zealand
Will talk with my DBA re this and would let you know.

      Rating: 3
Talk with the
      DBA  August 26, 2002
Reviewer:  Chetan
       from New Zealand
Hi Tom,

I talked to my DBA re doing a script review before deploying it. He says there
could be situations where the DBA would not be able to check the Unix script
before deploying it into production (due to time constraints) and I agree that
this is a valid situation.

Is there any other way where-in the DBA's does not have to do a thorough check
on the Unix script before signing it off for deployment.

Currently there is very little or none the DBA has to do.
The things work in the following fashion,
Let's say I have a script which loads data into the database using sql*loader.
The steps followed are as follows

1. Insert a record into a table called BATCH_JOB
which have the following important columns which are populated, The record is
inserted by the user who has minimum privileges.
e.g.
job_number=1
job_name='LOAD_REF_DATA'
job_status='SUBMITTED'
PARAM1='FARE_BASIS'
PARAM2='Farebasis.csv'
PARAM3.....
PARAM4......
....
....
etc.

Step 2:
A cron is running every minute looking out for a entry of a SUBMITTED job_status
in the batch_job table. The cron is running as a user having the superuser
privileges.

Step 3:

Based on the type of job, a new shell script is spawned which would do the
SQL*Loader process and load data from the file.

Step 4:

Update the Batch_job table with a status of completed.

The shell script that is spawned is unharmful because even if someone changes
the shell script and puts in rm -f $ORACLE_HOME it is not possible to do any
damage to the system.

The only problem in this process is it is not at the click of a button.

The point I am trying to make here is that the DBA do not have to do any
checking whatsoever to the shell script and they are happy the way things are
working -- without there intervention.

So the bottomline is, it is possible to implement Java stored procedures without
really bothering the DBA's?

Kind regards.

Chetan

          Followup:

Huh, they would not have time to check the script before deploying it?  sure
they would -- you quite simply would not deploy it until you checked it out.
 Quite simple isn't it?  You don't put into production that which has not be
through test and QA.  same for any piece of code -- isn't it????  I don't
believe this to be even a teeny tiny bit "valid".

If the DBA delegates this to someone else (your TEST group, your QA group for
example) then they won't have to, sure.

Wow, as I read on -- I'm even more amazed.  The DBA doesn't want to have to do
anything and has setup a method for you to do things willy nilly -- with no
security whatsoever, using ROOT (superuser).  It spawns scripts at will, with no
checking.

Your statement "the shell script that is spawned is unharmful ..." either is
written due to niativity on your part or because something else in this followup
is wrong.  If the script runs as "super user", hmmm -- think about it.  can root
do magical things?  That statement, that it is "unharmful" is 100% false if the
script runs as root.

Yes -- it is possible to implement java stored procedures without dba
intervention.  HOWEVER, if you want to do something that requires a privilege
and they are the only ones that can give it -- they will be involved (by
definition).

If all you do is run sqlldr with these scripts -- you should be able to

o have them use dbms_java to grant you privilege to run SQLLDR (not a script,
not arbitrary code but only $ORACLE_HOME/bin/sqlldr)

o you can use java to run sqlldr directly, right there and then.

In Oracle9i, you would use an external table and just select * from flat_file.

      Rating: 3
Quite
      Conivincing.............  August 26, 2002

Reviewer:  A reader
Hi Tom,

Thank you very much for the answer.

Regards

Chetan.

      Rating: 5
Windows
      ?  October 29, 2002
Reviewer:  A reader

Tom,

 Can you please tell me how to do it on windows .

I want to execute the sqller from sql+ and java sp
I think the sql loader does open but it does not do anything.

 I have a batch file to load the data which contains
sqlldr u/p control= c.ctl data=d.data..

TIA

August 26, 2002 -- Thanks for the question regarding "pl sql
      update array processing", version 8.1.7.2

      You Asked
Hi

I have a pl/sql

DECLARE
   CURSOR c1
   IS SELECT cust_id, segment_value FROM table_x_temp;
   custo           NUMBER;
   varob           NUMBER;
   counter         NUMBER := 0;
   objiddatamart   NUMBER;
BEGIN
   FOR r1 IN c1
   LOOP
      BEGIN
         counter := counter + 1;
         SELECT customer_id
           INTO custo
           FROM table_x_datamart_cust
          WHERE customer_id = r1.cust_id;
         UPDATE table_x_datamart_cust
            SET segment_value = r1.segment_value
          WHERE customer_id = r1.cust_id;
      EXCEPTION
         WHEN NO_DATA_FOUND
         THEN
            BEGIN
               SELECT objid
                 INTO varob
                 FROM table_site
                WHERE x_customer_id = r1.cust_id;
               objiddatamart := sa.crea_objid('x_datamart_cust');
               INSERT INTO table_x_datamart_cust

(objid,dev,segment_value,x_datamart_cliente2site,customer_id)
                    VALUES (objiddatamart,0,r1.segment_value,varob,r1.cust_id);
            EXCEPTION
               WHEN NO_DATA_FOUND
               THEN
                  DBMS_OUTPUT.put_line('no data found'||r1.cust_id);
               WHEN OTHERS
               THEN
                  DBMS_OUTPUT.put_line('others'||r1.cust_id);
            END;
         WHEN OTHERS
         THEN
            DBMS_OUTPUT.put_line('others outer loop'||'R1.CUST_ID');
      END;
      IF counter > 5000
      THEN
         COMMIT;
         counter := 0;
      END IF;
   END LOOP;
   COMMIT;
END;
/

can this use array processing?

it takes like 10 ~ 11 hours to process 1 million rows!

thank you

      and we said...
You know -- in Oracle9i -- this is a single COMMAND.

merge into table_x_datamart_cust t1
using table_x_temp t2
on ( t1.customer_id = t2.cust_id
when matched then
    update set t1.segment_value = t2.segment_value
when not matched then
    INSERT (objid,dev,segment_value,x_datamart_cliente2site,customer_id)
    VALUES (sa.crea_objid('x_datamart_cust'),0,t2.segment_value,
            (select objid from table_site where x_customer_id = t2.cust_id),
             t2.cust_id)
/

In any case -- in 8i and before, it could be done in a pair of statements, an
update and an insert.

Assuming table_x_datamart_cust has a primary key on customer_id:

update ( select t1.segment_value dm_segment_value,
                t2.segment_value new_segment_value
           from table_x_datamart_cust t1,
                table_x_temp t2
          where t1.customer_id = t2.cust_id )
  set dm_segment_value = new_segment_value;

insert into table_x_datamart_cust
(objid,dev,segment_value,x_datamart_cliente2site,customer_id)
select  (sa.crea_objid('x_datamart_cust'),0,t2.segment_value,
            (select objid from table_site where x_customer_id = t2.cust_id),
             t2.cust_id)
  from table_x_temp t2
 where t2.cust_id NOT IN ( select customer_id from table_x_datamart_cust )
/

(make sure to read

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:5081797376327
before attempting that NOT IN please!!! and tune accordingly!!)

that will be ARRAY PROCESSING on steroids (deja vu)...

Review &
      Followup

      Rating: 4
the problem is
        August 26, 2002
Reviewer:  A reader

Hi

the problem is we want to know what rows cant be updated so we need the
dbms_output!

          Followup:

Fine -- you call sa.crea_objid when the row cannot be updated right (yes)

So, write another routine sa.crea_objid_that_calls_dbms_output_as_well and have
it call sa.crea_objid and then call dbms_output.  Have this new routine take the
string sa.crea_objid currently takes ALONG WITH the cust_id you want to print
out.

two statements.  array processing on steroids.

      Rating: 3
exceptions and
      array processing  August 26, 2002
Reviewer:
      A reader
hello tom

I have read the doc about array processing in PL/SQL. It says that in FORALL
statemnets whenever there is an exception the array processing stops (or at
least that´s my understanding).

"DECLARE
   TYPE NumList IS TABLE OF NUMBER;
   depts NumList := NumList(10, 20, 30);
BEGIN
   FORALL j IN depts.FIRST..depts.LAST
      UPDATE emp2 SET job = job || ' (temp)'
         WHERE deptno = depts(j);
         -- raises a "value too large" exception
EXCEPTION
   WHEN OTHERS THEN
      COMMIT;
END;
/

The SQL engine executes the UPDATE statement three times, once for each index
number in the specified range, that is, once for depts(10), once for depts(20),
and once for depts(30). The first execution succeeds, but the second execution
fails because the string value 'Bookkeeper (temp)' is too large for the job
column. In this case, only the second execution is rolled back.
When any execution of the SQL statement raises an exception, the FORALL
statement halts. In our example, the second execution of the UPDATE statement
raises an exception, so the third execution is never done."

I also wonder why it says there has to be 3 executions, aren we performing array
processing here?

          Followup:

You'll want to read through:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3675533064673
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1583402705463
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1176035372649
to see lots more examples (especially the first one, it directly addresses what
I belive to be the crux of your "comment")

There are three seperate executions of the UPDATE (there is after all, 3 inputs
right?  there MUST be three executions).  The array processing allows us to
avoid the expensive context switch from the client(plsql) to the server(the
database kernel).  That is all -- there is still 3 executions, just one context
switch.

      Rating: 3
oops forgot to
      ask  August 26, 2002
Reviewer:
      A reader
I wrote everything except the question! My question is does that mean exceptions
and array processing cant go together?

          Followup:

Nope, read that first link in the list above..  Exceptions -- when used properly
-- are great with array processing

August 26, 2002 Kandasamy -- Thanks for the question regarding
      "Advantages of using Objects ", version 9.2.0

      You Asked
Q1. What are all the advantages of using Object types over packages? (since we
can implement the concepts of object in package itself). And which is best, in
performance, when using in JDBC application?

      and we said...
Well, they can be easier.

Just like a java class is "superior" to a C struct.

A package is like a C struct.  An object type like a java class (or C++ class).

Consider UTL_FILE for example.  The first parameter to utl_file is the file to
work on.  YOU must deal with the file handle, YOU must pass it in to each and
every call.

Now, imagine if UTL_FILE were an object type-- the code would be more like:

declare
   x  utl_file_type := utl_file_type( 'foobar.txt', 'r' );
   y  utl_file_type := utl_file_type( 'output.txt', 'w' );
begin
   dbms_output.put_line( 'line one is ' || x.get_line() );

   y.put_line( x.get_line() );
   y.close();
   x.close();
end;

I suppose it is a matter of style, rather then passing in the context data, you
just instantiate a new instance and it does it for you.

When using JDBC, (or c or sqlplus or vb or <anything goes here, java isn't
special in any regards, just yet another API to the database>) both packages and
object types will perform in an equivalent fashion.
August 26, 2002 NEETI -- Thanks for the question regarding "Large
      update....", version 8.1.5

      You Asked
  USING Oracle 8.1.5 on UNIX Sun Solaris

 I have a 20 column table with 40Million rows. I have to assign
 each row a category out of four different categories(STUDENT,
 HOUSEWIFE,WORKING,GENERAL) on the basis of 3 digit alphanumeric
 mbr_code column in the table.

 The relationship of mbr_code and the category is stored in a
 cat_lookup table. The logic is

   if the mbr_code match is in lookup table then
        assign the category.
   elsif code starts with numeric value then
        assign 'WORKING'
   else
        assign 'GENERAL'
   end if;

  I use a cursor to go row by row in mbr_tbl and run thru the logic

        for mbr_rec in mbr_cur loop

             BEGIN

               select mbr_cat
                  into v_mbr_cat
                 from cat_lookup
                where lookup_val = mbr_rec.mbr_cat;

             EXCEPTION
                  WHEN NO_DATA_FOUND THEN

                      if (substr(mbr_rec.mbr_cat,1,1) >= 48 and
                         substr(mbr_rec.mbr_cat,1,1) <= 57 ) then

                         v_mbr_cat = 'WORKING'

                      else

                         v_mbr_cat = 'GENERAL'

            END;

                   update mbr_tbl
                      set mbr_cat = v_mbr_cat
                     where rowid = mbr_rec.rowid;

                     IF < 5000 rec processed >
                         COMMIT;
                     END IF;

        end loop;

     This process takes 30hrs to complete.Any suggestions to make
     it faster.

      In you book you have also mentioned about the bind variable
      can you suggest how can I use the same in this case to avoid
       hard parse each time.

     Thanks in advance

   Neeti,

      and we said...
Don't do the update.

Use DECODE and have a derived column in a view that people use.

With 40 million rows and 4 values -- there will NEVER be an index on this active
table -- hence -- just decode and NEVER update it.

(never ever ever commit in a for loop -- terrible, horrible, very bad practice.
The only things you can do with that are:

a) make the code run slower
b) inhibit transactional integrity
c) make the code run slower
d) write more code then you should (should be a SINGLE update statement)
e) did i mention - it'll run slower?

bottom line, do not do the update, waste of 30 hours.

If you do, just do it in a SINGLE statement -- much much faster.
Review &
      Followup

      Rating: 4
But what about
      rollback segment  August 21, 2002
Reviewer:
      Basharat  from Dubai
Tom, large updates in a single statement will expand the rollback segment
enormously, and will result in error if it runs short of space. Your comments
please..

          Followup:

Here is are my (consistent, always said this) comments on this (in fact, there
is a chapter in my book "Expert one on one" that says this)

Size your rollback (undo) for the jobs you do.  Undo is an integral component of
the database, it is not something to "conserve", to skimp on, to spend literally
HOURS if not DAYS trying to defeat (as we are here).

Given that a 36gig SCSI disk goes for about USD $200/250 these days it would be
more cost effective to keep a closet FULL of these disks and pull them out
whenever you are tempted to "conserve" on RBS space.  Just attach it to your
system, use it once for this one time operation (which by the way, are never one
time operations - they or something like them are done over and over meaning
this cycle constantly repeats itself) and then throw it away.

Net result: Your transaction will go as fast as it can.  Your data integrity
will be preserved.  You will have spent less money then developing, testing and
ultimately FIXING when it fails -- some large piece of procedural code.

Size your system for the jobs you need to do and this is never an issue.

      Rating:
      3
  August 21, 2002
Reviewer:  Rahul
       from london
TOM WHAT IF THE ROLLBACK SEGMENT FAILS IF U DON'T COMMIT IN INTERVALS, IT MIGHT
FOR 40 MILLION RECORDS.

          Followup:

DON'T SHOUT.  SEE ABOVE.

Now, you tell me -- what happens when the above cursor for loop fails with an
ora-1555 halfway through????

Besides -- my answer here was to not do the update at all -- it is an utter
waste, just select out the proper value in a decode -- like the DBA_TAB_COLUMNS
view does for datatypes.

No storage to think twice about then.   They wanted fast -- I gave them a
solution that works in 0.0000 seconds consistently!

      Rating: 2
Not sure about
      the decode solution.....  August 22, 2002

Reviewer:  Neeti  from TX
Tom thanks for the response.....

Well I am not sure how you are suggesting me to use the
decode in this situation. It's true that I have only 4
categories but there are around 2000 mbr_code in the lookup
table.

Sorry for not following the solution.

          Followup:

try this then

create view v as
select a.*, (select mbr_cat from cat_lookup where lookup_val = a.mbr_cat)
mbr_cat
 from mbr_tbl a
/

      Rating: 1
This will
      ignore all the GENERAL that are not it the table....  August
      26, 2002
Reviewer:  Neeti  from TX
Hi Tom,

Sorry for being a pain. But if I use the view would'nt
it going to ignore all the code which does not exist in
the lookup table and has to go in 'GENERAL'

          Followup:

If you mean this tidbit of code:

                      if (substr(mbr_rec.mbr_cat,1,1) >= 48 and
                         substr(mbr_rec.mbr_cat,1,1) <= 57 ) then
                         v_mbr_cat = 'WORKING'
                      else
                         v_mbr_cat = 'GENERAL'

Well, that confuses me.  Since it'll ONLY work if substr( mbr_rec.mbr_cat,1,1)
is '0', '1', '2', ...., '9'.  It'll fling an exception otherwise -- so, it
either ALWAYS returns 'GENERAL' (since a single character from the substring is
NEVER between 48 and 57 ), just use NVL()

create view v as
select a.*, nvl((select mbr_cat from cat_lookup where lookup_val = a.mbr_cat),
'GENERAL' )
mbr_cat
 from mbr_tbl a
/

Or, if it needs to be more complex -- think about it..... Maybe:

create view v as
select a.*, nvl( (select mbr_cat from cat_lookup where lookup_val = a.mbr_cat),
                  case when substr( .... ) then 'GENERAL'
                       else ....
                  end )
mbr_cat
 from mbr_tbl a
/

get fancy!

scott@ORA817DEV.US.ORACLE.COM> select deptno, dname, nvl( (select ename from emp
where emp.deptno = dept.deptno and rownum=1), 'do not know' )
  2    from dept
  3  /

    DEPTNO DNAME          NVL((SELECT
---------- -------------- -----------
        10 ACCOUNTING     CLARK
        20 RESEARCH       SMITH
        30 SALES          ALLEN
        40 OPERATIONS     do not know

scott@ORA817DEV.US.ORACLE.COM> select deptno, dname, nvl( (select ename from emp
where emp.deptno = dept.deptno and rownum=1),
  2    case when dept.deptno between 35 and 45 then 'it is that pesky deptno 40'
  3         else 'i do not know'
  4    end)
  5    from dept
  6  /

    DEPTNO DNAME          NVL((SELECTENAMEFROMEMPWHE
---------- -------------- --------------------------
        10 ACCOUNTING     CLARK
        20 RESEARCH       SMITH
        30 SALES          ALLEN
        40 OPERATIONS     it is that pesky deptno 40

scott@ORA817DEV.US.ORACLE.COM>

August 26, 2002 Irina -- Thanks for the question regarding "Size
      of pl/sql tables", version 8.1.6

      You Asked
Dear Tom, thank you for help.
Would you be so kind as to explain, how can we define admissible size of pl/sql
tables?
How does ORACLE behave when there is not sufficient memory for pl/sql table,
i.e. what happens when
indexed-by)    p(100..00) := 12
  or
    nested)    q.extend; q := q_type(12);
and memory is not there?
Is it possible to handle this error? Will server work after storage_error
exception (ORA-6500)?
Is any difference in allocation memory for nested table and table indexed by
binary_integer?

Thank you.

      and we said...
You'll get one of:

/       4030-4039 Reserved for Memory Manager errors
/
04030, 00000, "out of process memory when trying to allocate %s bytes (%s,%s)"
// *Cause:  Operating system process private memory has been exhausted
// *Action:

04031, 00000, "unable to allocate %s bytes of shared memory
(\"%s\",\"%s\",\"%s\",\"%s\")"
// *Cause:  More shared memory is needed than was allocated in the shared
//          pool.
// *Action: If the shared pool is out of memory, either use the
//          dbms_shared_pool package to pin large packages,
//          reduce your use of shared memory, or increase the amount of
//          available shared memory by increasing the value of the
//          INIT.ORA parameters "shared_pool_reserved_size" and
//          "shared_pool_size".
//          If the large pool is out of memory, increase the INIT.ORA
//          parameter "large_pool_size".

depending on your configuration (MTS/dedicated server).  you should be able to
catch that exception -- but you'd be pretty much dead in the water at that
point.

nested tables are not SPARSE so if you use slots 1, 50 and 100 -- you'll have
100 slots allocated.

plsql index by tables are SPARSE, so if you use slots 1, 50 and 100 you'll have
3 slots allocated.

if you always use contigous elements -- they are pretty much the same.
Review &
      Followup

      Rating:
      5
  May 23, 2001
Reviewer:  Helena
      Markova  from Bratislava, Slovakia

      Rating:
      4
  August 26, 2002
Reviewer:
      A reader
Tom,
With regards to the process memory related eorrors, could you please validate
and explain the following advice found in some internal documentations at my
work.

Thanks.

========
This is happening because your process memory is getting exhausted. The possible
solutions would be

- Increase the ulimit ( virtual memory ) to unlimited.
% ulimit -a would give you the current settings.

- Decrease the SGA size, if it is set to a very large value. This is required if
you are hitting the address space limits.

- You may also try decreasing the parameters like sort_area_size ,
bitmap_merge_area_size , create_bitmap_area_size .

- You can also free up the unused PGA memory by running the procedure
dbms_session.free_unused_user_memory..

If your application uses lots of PL/SQL tables, it might be a good idea to
initialize them to NULL when not used. This will force the garbage collection
and there by freeing up the process memory.
==================

          Followup:

I suppose you are asking this in reference to the error:

> oerr ora 4030
04030, 00000, "out of process memory when trying to allocate %s bytes (%s,%s)"
// *Cause:  Operating system process private memory has been exhausted
// *Action:

Yes, those are all valid.  Most of the explanations seem obvious:

o ulimit, the OS can limit artificially the amount of ram you or oracle can
allocate in a process.  If set too low, you won't get very far.  this will raise
it.

o the SGA consumes memory -- if you make the SGA smaller, there will be more
room for your processes to allocate memory

o by decreasing the _size parameters, you decrease the amount of memory your
dedicated/shared server will attempt to allocate.  Less memory used = less
chance of running out

o seems evident, free memory means less need for allocating more memory....

Setting a table to NULL effectively frees the memory used by that table (only
needed for PLSQL tables defined OUTSIDE of a procedure/function -- in a package)

      Rating:
      4
  August 26, 2002
Reviewer:
      A reader
Thanks for the lightning fast response! If possible, could you also explain the
negative effects of following these tips(like reducing SGA size etc). How do we
decide what will be the optimal size? I know it is not a simple question, but if
you could please suggest what to look for....

Thanks again,

          Followup:

Not really -- but apply logic and common sense to it.  Think it through...

Consider -- reducing SGA for example:

o smaller buffer cache
o smaller redo log buffer
o smaller shared pool

that could negatively impact performance HOWEVER, if the SGA was so large in
proportion to physically available ram that you need to make it smaller -- it
was too large to begin with!

      Rating: 4
Pl explain
      more  August 26, 2002
Reviewer:  VIkas
      Sharma  from Delhi India
Hi Tom,
--* Setting a table to NULL effectively frees the memory used by that table
(only needed for PLSQL tables defined OUTSIDE of a procedure/function -- in a
package) --**

Kindly elaborate it more. Do you mean to say that if you declare a plsql table
in a procedure  for eg.
Procedure p1 is
  Type t is table of number;
  V1    t := t();
Begin
  For j in 1..1000 loop
  V1.extend;
  V1(j) :=  j;

  End loop ;
End;

Will de-allocate the memory once the procedure call is finished. And it will be
in the memory if this procedure is  in a package. Coz the package variable are
stored in the memory till the session is active.

Regards,

Vikas Sharma

          Followup:

when the plsql table goes out of scope -- it is freed, definitely.

A plsql table in a production/function goes out of scope when you return, hence
you typically do not need to "free" it.

A plsql table in a packge -- outside a procedure/function -- never goes out of
scope and hence, when you are "done with it", you can free it if you like.

It is NOT that the procedure is in a package -- it would be that the TABLE
itself was defined in a package OUTSIDE of a procedure:

create package body foo
as

   type myTableType is table of number;

   I_Never_go_out_of_scope    myTableType;   <<<<==== never goes out of scope,
                                             if you fill it up, it'll stay full
                                             till you empty it.

procedure bar
is
   I_go_out_of_scope_upon_return myTableType; <<<<===== auto-freed when the proc
                                                        returns.

      Rating:
      4
  August 26, 2002
Reviewer:
      A reader
"if the SGA was so large in proportion to physically available ram that you need
to make it smaller -- it
was too large to begin with! "

What percent of the physical RAM can be the max size for SGA without affecting
performance?

          Followup:

anywhere between 0 and 99% depending.

Yes, 0 and 99%.  Depends on what you are doing, how many users you have, what
else is on the box, what configuration you are using (shared vs dedicated
server), etc etc etc.

      Rating:
      5
Thanks  August 27, 2002
Reviewer:  Vikas
      Sharma  from Delhi India
Thank you very much.
For this valuable information abt pl/sql table in pacakges.

Regards
Vikas Sharma

August 26, 2002 sreekanth -- Thanks for the question regarding
      "use of intermedia", version 8.1.7.3.0

      You Asked
Tom,

Thanks for your help for my last question.

We are in initial stage of designing a web application.  It is basically kind of
project tracking system. where they can enter notes and issues for the project
and upload the documents(like text,word, excell, pdf ect.).  And the ability to
search using keywords.

My dilema is wheather to save these files in database or in a os file directory.
If saved in database will it be fast like streaming from the file.?

When user searches on a key word, i would like to search in these documents. Is
this possible with intermedia?

Please advise me.

thanks
sreekanth.g

      and we said...
Me, I put it all in the database.  Backup, recovery, data integrity, single
repository, ease of maintenance, lack of having to worry about some numskull
erasing these files cause they take space, lack of worrying about when will the
directory break due to too many files, etc.  They are all infinitely improved
when it is in the database.

Yes, intermedia allows you to index and search all of those document types.
Review &
      Followup

      Rating: 4
use of
      intermedia"  August 23, 2002
Reviewer:
      DBBird  from usa
Greate advice! I have more question.
How do you search if you store the document in database?
Do you query a particular column to search for a keyword? if you do, is there
any performance impact?

Thanks

          Followup:

see

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/inter.817/index.htm
it shows how to use intermedia.

As for the performance question, is there a performance impact?  yes, it runs
faster then scanning each document each time ;)

      Rating:
      5
intermedia  August 23, 2002
Reviewer:
      mo  from Reston, VA
TOm:

If you are searching a varchar2(1000) column for a keyword do you need
intermedia or the "select * from table where column like 'word%' " will work.

Would it help to index a varchar2 column or not?

Thanks

          Followup:

if you use "where c like 'something%'" -- a conventional index will work

If you use "where c like '% something %'", you'll want intermedia (and to use
contains instead of like)

      Rating:
      5
internedia  August 24, 2002
Reviewer:
      mo  from Reston, VA
TOm:

1.  I am confused. let us say I am looking for a varchar2(1000) column that has
a word column. I would do
select * from table where col like 'oracle%';

how would the result be different if i did
select * from table where col like '% oracle %'

2.  Would not indexing a varchar2(1000) column slows down update and insert
operations considerable due to the index.

THank you

          Followup:

1) select * from table where col like 'oracle%'

will find all rows in the table where the column COL started with oracle.

It will find:

COL
--------------
oracle is cool
oracle is great
oracle's the database for me

It will NOT find

COL
---------------------
if you are smart, you use oracle -- really

the other will find only the last one.

2) sigh, nothing but nothing in this world is free.  consider however that you
insert a row once.  You might be querying it up 1,000,000 times.  If the index
can help you avoid 1,000,000 full table scans and find the rows efficiently --
the trade off is MORE then worth it.

      Rating:
      5
intermedia  August 24, 2002
Reviewer:
      mo  from Reston, USA
TOm:

OK I see.

1. If I have a stored procedure that has embedded sql
"select * from table where col like '% oracle %'
it will not work.

I have to ask the dba to turn intermedia on and the nlearn how to use intermedia
to query database?

Correct.

          Followup:

ARG -- yes, yes it will "work"

will it use an index, unlikely.

will is work -- most certainly.

But, if you would like it to be fast, efficient, flexible -- yes, you will have
to "learn" intermedia (which is learning how to issue create index and how to
use the contains clause in a where condition).

Everytime you use this site, you are using intermedia when you search.

      Rating: 5
Read the
      following  August 25, 2002
Reviewer:  Sikandar
      Hayat Awan  from Pindi Bhattian - Pakistan
If you are interested to learn about intermedia then please visit the link,

http://orafaq.cs.rmit.edu.au/faqctx.htm

      Rating:
      5
intermedia  August 26, 2002
Reviewer:
      mo  from Reston, USA
Tom:

You say if I want to find

select * from table wher col like '% something %'
I need intermedia

However, I did a test on a small table using the above and I got the records
that have embedded 'something' in them.

Are you basically saying that I am not using an index with this and that the way
to do the above with an index is using intermedia.

Thanks,

          Followup:

I never said "need"

<quote>
If you use "where c like '% something %'", you'll want intermedia (and to
use
contains instead of like)
</quote>

And if you read your OWN reviews and my followups:

<quote>
OK I see.

1. If I have a stored procedure that has embedded sql
"select * from table where col like '% oracle %'
it will not work.
...<snip>

ARG -- yes, yes it will "work"
</quote>

I already told you "select * from table where col like '% oracle %'" will MOST
certainly work.

Now, your exercise for tonight.  Put 1,000,000 rows in there with lots of other
columns.  Now, do your query.  Tell us how it performs.  Then, try it with
interMedia and you'll see why you WANT it.

August 26, 2002 lydia -- Thanks for the question regarding "How
      to implement versions", version 8.1.7

      You Asked
I have an implementation question.
I need to set up an environment used for a real time application
which contains versioning of the data.

- first there is candidate configuration (about 200Mb of data,
spread over about 100 tables) which can be updated.
This can take some days to complete, (configuring means that
multiple transactions are performed)

- secondly there is an active configuration created out of the
candidate at the moment we decide the candidate is configured
completely. The migration from candidate to active should only
take a few seconds.

If we see that the active version does not match the requirement
then it should be possible to "rollback" to the previous active
version, and also the candidate should be "rollbacked" to the
previous active version.

First I was thinking about implementing the candidate as master
tables, and the active version as snapshots.
But I do not know if it is possible to "rollback" a refreshment
of snapshots.

I also do not find an efficient way to "rollback" the candidate
version to the active version.

If you could give me some good ideas how to accomplish this, I
would be very grateful.

Lydia

      and we said...
snapshots won't work for this at all, no "rollback".

Here are some ideas:

o Use public synonyms.  Have many schemas in your database.  The
current "active" schema holds the active tables.  The public
synonyms point to these tables.  All applications use the public
synonyms.  To make a candidate configuration active, simply drop
and recreate the public synonyms.  If you do not like this
configuration and want to go back, simply drop and recreate them
again.  In this fashion, you could keep as many candidate and
"old active" schemas online as you wanted and have any one of
them be active as any point in time.

o Similar to above but use views.  Some people don't like
synonyms.  So, setup a schema called "active" and have it have a
set of views that are simply:

  create view t1 as select * from USER1.t1;

USER1 is the user that owns the "active" configuration.  When
user2 who currently has a candidate should be active, we will
just recreate the view:

  create or replace view t1 as select * from user2.t1;

All applications will be built against ACTIVE.T1 and active.t1
will just be a view that points to the correct object.

In thinking about it, the views are the best way to go probably
as they can control the grants and everything at that level
(just use CREATE OR REPLACE and existing grants will stay in
place).  It would be cleaner that way.

Review &
      Followup

      Rating: 3
Another face of
      this problem  August 24, 2002
Reviewer:  Rui
      Brito  from Portugal
Tom
My application may need to be installed on various schemas. Each of then contain
one businness company. Like every schemas have the same name of tables, views
and packages, how can I grant access to others autorized users to work with this
schemas ?
I create a Role for each schema, with the grants to the objects.
When I create a user to work with a company, I grant the correct Role to him.
After granted the Role, this user have to know the objects by synonymous,
because the code don´t refer the owner of object.
I can´t use public synonyms, because the object name is unique and he are in
various schemas. When I referenced a object, the public synonym is used, unless
a private synonym is defined.
Creating private synonymous for many users and many objects are a problem,
because we made changes frequently (the application as in evolution).
The approach of drop and recreate synonyms don´t apply, because all schemas is
working at same time. They are another approach ?
In another way, who are the objects they need synonyms ? Tables, Viwes,
Triggers, Indexes, Object Types, Package Spec, Package Body, Sequences, etc ?

Best Regards and thanks for your fantastic support
Rui Brito

          Followup:

The answer is

DO NOT use multiple schemas.  Use a single schema with fine grained access
control:

http://asktom.oracle.com/~tkyte/article2/index.html
That way, you have support for more then one business company but only one set
of tables, roles, grants, procedures/packages, etc etc etc to maintain and
manage.

This is exactly how Oracle Corporation does it itself.  We support more then one
company using a single schema using this functionality.

      Rating: 4
New
      situation  August 26, 2002
Reviewer:  Rui
      Brito  from Portugal
Hi Tom
After I send my review, I search more, and I find the alter session set
current_schema and I used it. It´s work fine, except when I open other session
(like run Reports under Forms with Run_Product). In this case I need to set the
schema again in Report ... but OK, it works.
I read your sugestion and my application is also multi-company in one schema!
The problem is, my Client need to backup and restore (imp/exp) companies
individually. Cause that, I´m forced to implement each conpany in correspondent
schema.
I don´t know any solution to bypass this requesit. It´s not easy isolate only
some data in 500 tables to export only one conpany. Are you agree ?

Thanks again
Rui Brito

          Followup:

No worries there.

If you EXP, it'll use SQL to extract the data.  They will only SEE what they
should SEE.  Their export will contain just their data!  No worries there.

Give it a try -- EXP cannot bypass security.  If you export as a normal user
(not as SYSDBA), you'll just get what you should get.


Subject | Article | Last Updated (East coast time)
SET AUTOCOMMIT On | article only | Aug 27, 2002 09:00 am
Recommended way to implement Read Only information | article only | Aug 27, 2002 09:26 am
When is "DICTIONARY-MANAGED TABLESPACE" the correct choice? | article only | Aug 27, 2002 09:42 am
Plustrace role | article only | Aug 27, 2002 09:21 pm
Get Back the Space allocated/taken to/by the table. | article only | Aug 28, 2002 09:27 am
About Index Rebuild | article only | Aug 28, 2002 09:37 am
Resource used by auditing | article only | Aug 28, 2002 12:39 pm
How to calculate Buffer cache hit ratio without db_block_lru_statistics and db_block_lru_extended_statistics | article only | Aug 29, 2002 05:05 pm
OLEDB Provider | article only | Aug 29, 2002 07:41 pm
system tablespace free 0.01% | article only | Aug 29, 2002 08:09 pm
Coexistence of CAST/MULTISET operators and member functions? | article only | Aug 29, 2002 08:11 pm
Your thoughts on thin vs fat clients for applications | article only | Aug 29, 2002 08:19 pm
Locally Managed Tablespace for DW | article only | Aug 29, 2002 08:22 pm
DBA_INDEX contents? | article only | Aug 29, 2002 08:23 pm
Hex to Char | article only | Aug 29, 2002 08:24 pm
General Question | article only | Aug 29, 2002 08:30 pm
Ascii to Hex Conversion ! | article only | Aug 29, 2002 08:32 pm
page 732 strong vs weak ref cursors. | article only | Aug 30, 2002 09:20 am
Index on partition table | article only | Aug 30, 2002 09:52 am
General Stuff | article only | Aug 30, 2002 08:09 pm
Error Running oracle\ora90\demo\schema\order_entry\oe_mail.sql | article only | Aug 31, 2002 10:52 am
Index creation with nologging and recovery | article only | Aug 31, 2002 04:26 pm
DB link not working | article only | Aug 31, 2002 04:50 pm
Hierarchical Query! Stoping at a particular value | article only | Aug 31, 2002 05:23 pm
Tunning an Update statment | article only | Sep 01, 2002 10:13 am
between for varchar2 columns | article only | Sep 01, 2002 10:40 am
Previnting Illegal copies of a database | article only | Sep 02, 2002 10:02 am
Exclusive lock on a file | article only | Sep 02, 2002 10:29 am
How to find the detail oracle error message in the internet? | article only | Sep 02, 2002 10:50 am
How Can I build Dictionary managed tablespace in Oracle9i R2 | article only | Sep 02, 2002 11:02 am
how to find the defination of view or synonym | article only | Sep 02, 2002 11:55 am
Equivalent of over function | article only | Sep 02, 2002 11:58 am
To_date Query | article only | Sep 02, 2002 01:05 pm
tunning delete statement | article only | Sep 02, 2002 01:35 pm
Outer Join why is it so expensive? | article only | Sep 02, 2002 01:37 pm
join/subquery | article only | Sep 02, 2002 02:45 pm
Role of Java inside the DB | article only | Sep 02, 2002 07:10 pm
row(s) 1 - 37 of 37
August 27, 2002 Mohammed -- Thanks for the question regarding "SET AUTOCOMMIT On ", version 8.1.7
You Asked
Hi Tom :
        I am using Set Autocommit on when I am Updating Or deleteing millins of
record .How can I knwo the number of Recorded will be commited  in every 1000 or
10000 or ... etc .
and we said...
You misunderstand.

"set autocommit on" will cause sqlplus to issue COMMIT after each statement.  It
is a silly setting (one that Microsoft would be proud of -- they hate to have
transactions given their locking model)

Don't use it.  It does *not* intermittently commit work in a statement.

You just update the data, you commit.
Review & Followup
Rating: 5
Thanks August 27, 2002
Reviewer: Mohamemd Almoayed from Yemen
I realy thank you for ur fast response ,:)

August 27, 2002 Richard -- Thanks for the question regarding "Recommended way to implement Read Only information", version 9.0.1
You Asked
Tom,
You perform a wonderful service. Please keep up the great work!

We have several tables partitioned by time (fiscal year). What do you recommend
as the best approach for having historical data be read only and current data be
read/write? Basically, we want to be able to append for the current fiscal year,
and lock the prior fiscal years so that they can not be modified. My current
thought is to have the historical data be in a separate table space than the
current data. Does this approach make sense? Are there any new partition
features supporting read only or read/write (as far as I can tell there is not)?
I'm using Oracle 9.0.1 on Windows 2000.
Thanks!
and we said...
Well, if I was using 9iR2 i would do this:

at the end of a "window", I would "create table NEW_HISTORY_DATA compressed
tablespace XXX as select * from existing_table partition(partition_name)"   --
that'll compress by a factor of maybe 3 to 1 or even 12 to 1.

alter table existing_table exchange partition partition name with this new table
(remove the existing partition and replace it with a nice compressed partition)

alter tablespace XXX READ ONLY;

In 9iR1 -- i would just put each partition into a separate tablespace and make
that tablespace READ ONLY when I wanted to freeze it.
Review & Followup
Rating: 5
Implementing Read Only July 26, 2002
Reviewer: Richard Bulan from Cleveland, OH USA
Excellent response. Thank you!

Rating: 5
Use of data_segment_compression August 26, 2002
Reviewer: Cachito from Bolivia
Hi Tom, I agree excellent answer.

First
The compressed option in
create table NEW_HISTORY_DATA compressed
As I understand reading the documentation is
good for any table used for storing and that is not going to be updated or
deleted.

I´m right?

Second, I asked it before, sorry for asking again, but there is a way to put the
tablespaces for java, xml as readonly, the reason is because the time of backing
up for this database is critical and this must be a full offline backup.
So if I can´t reduce the size ( excluding that tablespaces from backup ) I
should have to delete them.

Thanks

Followup:
It is best when there will be no more subsequent updates or deletes on the
PARTITION of the table or the table itself (so, no -- it is not the "table" but
rather the partition in this case...)

If you let me know what a tablespace for "java" is, or what an XML tablespace
is, I'd be glad to answer.

I disagree that it must be a full offline backup.  It should NEVER be an offline
backup in a production system since by defintion you MUST be in archivelog mode
if you value your data and if you don't, you aren't in production so backup time
is meaningless.

Do online backup.  Shutting down your database is criminal.  Don't do it.

Rating: 5
Recommended way to implement Read Only information August 27, 2002
Reviewer: Ram from INDIA
Tom,

Now I am facing  the same problem .,  But  I am using Oracle 8.1.7  standard
edition  on
Windows 2000. Can  you please give me the solution How should I approach .

Thanks

Followup:
You cannot as you have neither table compression nor partitioning. (nor do you
really give enough information about what you want to do actually.  You do have
read only tablespaces, you can put triggers on tables to prevent modifications,
you do have read only views, you do have GRANTS)

Rating: 5
Is it suitable for snapshot table? August 27, 2002
Reviewer: chao from sh
hi, tom:
  For a snapshot site, is it suitable to use segment compression? We refresh the
snapshot every 1 hour, and the segment itself is not big(tables' size is about
50M total),and the refresh itself is quite fast. The application consumes a lot
of cpu,and have a lot of buffer_gets, from statspack,23K/second in one day's
time. And i want to reduce the load on the server.On seeing your idear, i wonder
if segment compression in snapshot segment will reduce the buffer_gets of the
application? After the data is read into memory, will it restore its original
size?
  Thanks for your advice.

Followup:
It may reduce the buffer gets -- the data is not decompressed in the buffer
cache, it is decompressed upon access - so look for slightly higher CPU usage.

Rating: 4
Using table compression October 01, 2002
Reviewer: Ofir Manor from Israel
Just wanted to add one thing. From my experience, when you finish to insert rows
into the current partition and start to insert rows into a new partition, and
you want to recreate the old partition (compressed or not), it is much better to
use an order by clause:
create table NEW_HISTORY_DATA compressed
tablespace XXX as select * from existing_table partition(partition_name)
ORDER BY A,B,C
That way the data is physicly clustered by some meaningful fields, and that can
improve the compression (if the leading fields are common and non unique).
Also, it should improve some of your queries performance significantly (for
example, if you partition by month maybe order each closed partition by
customer_id so all his records will be contained in one or two physical blocks
instead of tens of blocks). You should decide the physical order of the rows in
the rebuilt partition - it is unlikely that the optimal ordering is "just the
order it was inserted over the last month".
You should also consider using "nologging" option of CTAS to save time.

August 27, 2002 Gus -- Thanks for the question regarding "When is "DICTIONARY-MANAGED TABLESPACE" the correct choice?", version 8.1.6.3
You Asked
Dear Tom,

When is "DICTIONARY-MANAGED TABLESPACE" the correct choice for a tablespace?

We are working with a complex decision-support system that features advanced,
multi-master replication, updatable snapshots, and row-level security.  Perhaps
three quarters of the "user" tables are relatively static "reference file"
entities that receive updates infrequently.  The remainder of the user tables
are very dynamic.

Thus, it would seem that "LOCALLY MANAGED TABLESPACE" would be appropriate for
the dynamic tables, and dictionary managed tablespaces for the relatively static
tables.  But what negative effects can we expect if we make them all LOCALLY
MANAGED tablespaces?

And again, what about the "system" tablespaces, like RBS (rollback segments),
SYSTEM, TOOLS, etc.  Couldn't they benefit from LOCALLY MANAGED tablespaces too?

Thanks,

Gus
and we said...
Only when you are creating your SYSTEM tablespace.

Never for any other tablespace.

System cannot be locally managed, all others should be -- there are no drawbacks
to using a LMT.  They are excellent for all tablespaces you create.
Review & Followup
Rating: 4
I agree.. but November 11, 2001
Reviewer: Doug from CT, USA
I have seen a counter argument..
http://www.ixora.com.au/q+a/0104/11095147.htm
from Steve
states this:
---- start of quote ----
The only drawback with locally managed tablespaces is that used-extent
information is not kept in the data dictionary. It must be read from the segment
header blocks (and additional extent map blocks if any) whenever it is required,
including for queries against DBA_SEGMENTS and DBA_EXTENTS. If a tablespaces
with a large number of mostly small segments is locally managed rather than
dictionary managed, then access to these views can cause a lot more physical I/O
and thus impact the cache retention of user data. And if the segments are mostly
constant in size then the risk of the tablespace contributing to ST enqueue
contention if dictionary managed is low. Thus there is little motivation to make
such a tablespace locally managed and a minor performance risk in doing so,
hence my comment.

Why do you prefer that? What is the advantage of the DD managed space over LMT
in this situation?

I would have a preference for dictionary management for any tablespace with a
large number of mostly small constant sized segments. SYSTEM fits that
description, but RBS and TEMP certainly do not - TOOLS and USERS may do, but
probably do not.

--- end of quote

What do you think Tom?

Followup:
I think that for all newly created tablespaces -- they should be LMT's (thats
the default in fact in 9i).

I do not frequently query DBA_SEGMENTS and DBA_EXTENTS -- most systems I would
think would not either.  Maybe once a day or week to get growth stats, but on a
normal system -- not very often.

Statements like "minor performance risk", especially from steve, mean just that
-- and only in the cases where you are querying those views.

Since many (most perhaps) people/installations do not have the rigor needed to
enforce equi-sized extents in a dicitonary managed tablespace (only takes ONE
bad apple to spoil the bushel)  -- I would go with LMTS.

I still vote for LMT's everywhere.

Rating: 4
LMT to Dictionary Managed August 14, 2002
Reviewer: A reader from Atlanta
Hi,
We created LMT for our application. We used uniform allocation of 1M. Since the
application has 3000 tables with zero rows (and these tables are very rarely
used), we are literally wasting 3000M space. And hence we decided to convert the
tablespace back to DMT. After converting the tablespace to DMT (using
dbms_space_admin), however, now even if I create a new object with initial
extent size of 16K, it still gets allocated 1M.

Is this the expected behaviour? Or is there something else I need to do so that
the object gets allocated the space I specified?

Thanks

Followup:
Use system managed extents, they are perfect for applications where some (but an
unknown number of) tables will be empty and others will be various sizes.

The first couple of extents will be 64k, and then they grow in multiples of
powers of 2 from there.

You'll want to create a NEW tablespace and alter table MOVE the objects into it.
(and alter index rebuild to move them after moving the table).

Rating: 4
August 14, 2002
Reviewer: Doug Bradley from CT
Just an additional note - I'm using LMT for SYSTEM in 9.2 w/ no problems so far

Rating: 4
How to avoid wastage of space while using LMT? August 14, 2002
Reviewer: Tony from Australia
Tom,
What is the suggested value for using a LMT with uniform extent management for a
datafile size of 2GB without wasting space i.e., how much should be reserved for
bitmap management? Do we also need to reserve one block, as with traditional
file sizing method i.e, file size +one block?

Followup:
Add 64k for bitmap overhead
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1143634607948
As for extent size -- you need to know the size of the objects you are going to
put in there.
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:870967214635

Rating: 4
LMT for SYSTEM Tablespace too .. August 15, 2002
Reviewer: OrA from India
From Oracle 9i Release 2 , SYSTEM Tablespace can also be a Locally Managed
Tablespace .. infact it is the defaut.. time to say good bye to Dictionary
Managed Tablespace has truly arrived !!!

OrA

Rating: 4
LMT for SYSTEM Tablespace too .. August 15, 2002
Reviewer: OrA from India
From Oracle 9i Release 2 , SYSTEM Tablespace can also be a Locally Managed
Tablespace .. infact it is the defaut.. time to say good bye to Dictionary
Managed Tablespace has truly arrived !!!

OrA

Rating: 5
DBA_SEGMENTS / DBA_EXTENTS August 15, 2002
Reviewer: Connor McDonald from London
"I do not frequently query DBA_SEGMENTS and DBA_EXTENTS -- most systems I would
think would not either."

The main culprits here tend to be monitoring tools.  Heaps of them go thundering
through dba_segments every 'n' mins so they can pop up a little flashing red
light on your screen. Enterprise Mgr springs to mind :-)

Rating: 4
RBS - LMT ??? August 16, 2002
Reviewer: Victor B. from Russia
(Oracle8i.7 EE Release 8.1.7, Solaris )
What will you tell about RBS tablespace in LMT. When I tried to create rollback
segment in such tablespace than I accounted with :
1. SQL>  create tablespace ROLLBACKS logging datafile
'/opt/home/pluto/oracle8/dbs/oradata/oracle8/rollbacks.dbf'
  2   size 10M reuse extent management local;

Tablespace created.

SQL> alter tablespace ROLLBACKS online;

Tablespace altered.

SQL> create rollback segment  R01  tablespace ROLLBACKS
  2   storage (initial 50K next 50K minextents 20 maxextents unlimited);
create rollback segment  R01  tablespace ROLLBACKS
*
ERROR at line 1:
ORA-25151: Rollback Segment cannot be created in this tablespace

2.
SQL>   create tablespace ROLLBACKS logging datafile
'/opt/home/pluto/oracle8/dbs/oradata/oracle8/rollbacks.dbf'
  2   size 10M reuse extent management local uniform size 128K;

Tablespace created.

SQL>  alter tablespace ROLLBACKS online;

Tablespace altered.

SQL> create rollback segment  R01  tablespace ROLLBACKS
  2   storage (initial 50K next 50K minextents 20 maxextents unlimited);

Rollback segment created.

SQL>  select * from dba_rollback_segs;

SEGMENT_NAME  TABLESPACE_NAME INITIAL_EXTENT NEXT_EXTENT MIN_EXTENTS STATUS
------------------------------ ------ ------------------------------ ----------
SYSTEM          SYSTEM            51200         51200           2        ONLINE

R01            ROLLBACKS        1024000         131072          1        OFFLINE
R02               RBS             10240         51200          20        ONLINE

I see that new rollback have 1 extent instead 20 against
R02 which is in DML with 20 extents (as usually).
Thanks. Victor

Followup:
Well, the first one wasn't UNIFORM extents, it was system allocated (not all
extents would be the same size).  Since rule #1 of all rollback segments is "use
the same sized extent", we don't permit you to use that tablespace.  The second
one works because you have uniform extents.

As for what you see -- it is a side effect of using a locally managed tablespace
whereby the STORAGE parameters are interpreted "nicer".

Look -- you asked for 20 50k extents.
That is 1,000k of data.
Depending on your blocksize, that'll be rounded a bit.

That request gets converted into an initial size (1m in your case). So the
storage parameter ( initial 1m ) is the same in this LMT as ( inital 50k next
50k minextents 20 ) -- maxextents unlimited is a truly bad idea for rBS and we
limit you to 32k extents max.

So, we created the RBS with 1m of data which satisfies your request for 20 50k
extents.  If you looked at DBA_EXTENTS and counted the number of extents, you
would see 8.

Here is an example on my system:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create tablespace rbs_demo
  2  datafile '/tmp/rbs_demo.dbf'
  3  size 10m reuse
  4  extent management local uniform size 128k;
Tablespace created.

same as yours..

ops$tkyte@ORA817DEV.US.ORACLE.COM> create rollback segment rbs_demo_01
  2  tablespace rbs_demo
  3  storage (initial 50k next 50k minextents 20 maxextents unlimited);
Rollback segment created.

same create statement...

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec print_table( 'select * from
dba_rollback_segs where segment_name = ''RBS_DEMO_01'' ' )
SEGMENT_NAME                  : RBS_DEMO_01
OWNER                         : SYS
TABLESPACE_NAME               : RBS_DEMO
SEGMENT_ID                    : 7
FILE_ID                       : 12
BLOCK_ID                      : 9
INITIAL_EXTENT                : 1146880
NEXT_EXTENT                   : 131072
MIN_EXTENTS                   : 1
MAX_EXTENTS                   : 32765
PCT_INCREASE                  : 0
STATUS                        : OFFLINE
INSTANCE_NUM                  :
RELATIVE_FNO                  : 12
-----------------

PL/SQL procedure successfully completed.

Here, I got 1.1meg -- it rounded different given my block size, I have 8k
blocks so it is really 20*56k = 1,146,880.  Now, to get 1146880 bytes, we needed
9 * 128k extents..

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*)
  2    from dba_extents
  3   where segment_name = 'RBS_DEMO_01'
  4  /

  COUNT(*)
----------
         9

which is exactly what I got.

So, this is working exactly as expected here.  With LMT's -- don't even BOTHER
with

NEXT
PCTINCREASE
MINEXTENTS
MAXEXTENTS <<<=== totally ignored

Since we'll just use initial next pctincrease and minextents to figure out what
the true INITIAL allocation (not really initial extent, initial ALLOCATION) will
be.  It would be much much more understandable for all of us humans just to put
an initial allocation rather then make us do the rounding up and math in our
heads.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop rollback segment rbs_demo_01;

Rollback segment dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop tablespace rbs_demo;

Tablespace dropped.

Rating: 3
Cannot Create rollback segments in 8.1.7 in LMT August 16, 2002
Reviewer: A reader
Tom,

We get the below error:

Here's the error:

    SVRMGR> create public rollback segment rbs1 tablespace th_rbs01
    storage (initial 1M next 1M  minextents 50 optimal 50M)      2> ;
    create public rollback segment rbs1 tablespace th_rbs01
    *
    ORA-01552: cannot use system rollback segment for non-system tablespace
'TH_RBS01'

Currently we are using 8.1.7.3 version? How to overcome that if we want to use
LMT for Rollback?

Followup:
> oerr ora 1552
01552, 00000, "cannot use system rollback segment for non-system tablespace
'%s'"
// *Cause: Tried to use the system rollback segment for operations involving
//        non-system tablespace. If this is a clone database then this will
//        happen when attempting any data modification outside of the system
//        tablespace. Only the system rollback segment can be online in a
//        clone database.
// *Action: Create one or more private/public segment(s), shutdown and then
//         startup again. May need to modify the INIT.ORA parameter
//         rollback_segments to acquire private rollback segment. If this is
//         a clone database being used for tablspace point in time recovery
//         then this operation is not allowed.

Create another rollback segment in SYSTEM and online it first, then do this,
then get rid of the rbs you created in system.

Rating: 5
LMT vs DMT August 27, 2002
Reviewer: Richard Ignizio from Richester NY
Tom,
I am a DBA and an outside consultant is telling and convincing one of our
application developers that DMT's out perform and are better then LMT's. He is
making us go from 9.2.0 to 8.1.7.4 because he has it stuck in his head that
LMT's are evil. I know that is not true because we have done all of our own
testing and our standard is to use LMT's. They perform the same if not better
than DMT's.
Can you give me more proof to show this consultant the light.
Thanks
Rich

Followup:
Turn it around

Ask this "really smart guy" for the conclusive evidence that shows the rest of
the world is wrong and he is right.

Should be trivial for this really smart guy to show his stuff right?  I mean
that is why he is the "outside consultant".

Ask him to PROVE his point.  If he provides some "points" -- post them here.
I'll gladly rebutt them.

Rating: 4
July 02, 2003
Reviewer: A reader from Costa Mesa, California

August 27, 2002 -- Thanks for the question regarding "Plustrace role", version 8.1.5
You Asked
Hi, Tom

How can I get permission to user to use autotrace.
I have created user and graned him plustrace role,
select any table, select any sequence, roles connect and
select_any_catalog. After set autotrace on came error:

SQL> select count(*) from track;

  COUNT(*)
----------
   1153129

ERROR:
ORA-01031: insufficient privileges

SP2-0612: Error generating AUTOTRACE EXPLAIN report

Statistics
----------------------------------------------------------
          0  recursive calls
          4  db block gets
       2293  consistent gets
          0  physical reads
       1376  redo size
       1078  bytes sent via SQL*Net to client
        668  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          1  rows processed

I want not to grant dba role to user.

Thank you, Oleg
and we said...
Sounds like you have a common PLAN_TABLE in your database and
this user can SELECT from it but not INSERT.

For example, I have a plan_table created and a public synonym on
it.  I then created a user a and did the following:

tkyte@TKYTE816> grant connect, resource to a identified by a;
Grant succeeded.

tkyte@TKYTE816> grant plustrace to a;
Grant succeeded.

tkyte@TKYTE816> grant select on plan_table to public;
Grant succeeded.

tkyte@TKYTE816> connect a/a
Connected.

tkyte@TKYTE816> set autotrace on
tkyte@TKYTE816> select count(*) from dual;

  COUNT(*)
----------
         1

ERROR:
ORA-01031: insufficient privileges

SP2-0612: Error generating AUTOTRACE EXPLAIN report

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

tkyte@TKYTE816> connect tkyte/tkyte
Connected.

tkyte@TKYTE816> grant all on plan_table to a;
Grant succeeded.

tkyte@TKYTE816> connect a/a
Connected.
tkyte@TKYTE816> select count(*) from dual;

  COUNT(*)
----------
         1

tkyte@TKYTE816> set autotrace on
tkyte@TKYTE816> select count(*) from dual;

  COUNT(*)
----------
         1

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'DUAL'

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

tkyte@TKYTE816>
Review & Followup
Rating: 4
Question on redo size in the Statistics August 27, 2002
Reviewer: Indrakala Venugopal from India
Tom,
   Why does redo size appear when you take statistics of queries?
Pasted below are the statistics i got. Can you please tell me the areas i need
to look inorder to improve performance.

Statistics
----------------------------------------------------------
         96  recursive calls
        122  db block gets
     760692  consistent gets
      76553  physical reads
     232656  redo size
    3914384  bytes sent via SQL*Net to client
     615652  bytes received via SQL*Net from client
       9311  SQL*Net roundtrips to/from client
        325  sorts (memory)
          0  sorts (disk)
     139649  rows processed

regards
IK

Followup:
The redo can appear for many reasons -- delayed block cleanout, space management
(dictionary updates)

Caution, smart aleck answer ahead, read at your own caution:

Given the amount of information you supply above in regards to "how to improve
performance" the only answer I can give given the amount of information you
provide is:

Answer) only query DUAL.  DUAL is very fast.

Seriously, read the designing and tuning for performance guide that comes with
the database.  That is a good start.

Rating: 4
August 27, 2002
Reviewer: Tianhua Wu from Atlanta, GA
set autotrace on will open another session that will insert and delete from plan
table.  That is probably why you see the redo.

Followup:
Nope -- that other session would have its redo tracked against ITS session, not
the one it is peeking at.

delayed block cleanout
space managment

those are the two likely culprits

August 28, 2002 Balaji -- Thanks for the question regarding "Get Back the Space allocated/taken to/by the table.", version 8.1.7
You Asked
Hi Tom,
 I think i am lucky to get a chance to post this question after so many of day's
of waiting.
1) We are working on a DatawareHousing Application which is partitioned based on
the branch. For each partition we have a seperate tablespace. When we insert the
records to these tables based on the branch code the data is parked in the
correspoding tablespace. Recently when i deleted the records for a particular
branch and saw the dba_segments table for this table, i still see the space
used. Is there any way to get back the space allocated. I know few options, exp
and imp, truncate and imp only the needed partition or use alter table truncate
partition (if i use this global indexes will become Invalid and it takes lot of
time to rebuild the index). Other than this do we have any other way to get back
the space.
 The following is the information from dba_segments for the tablespace which i
want the space back.
Table Name = ME_CUST_AC_MASTER
Tablespace Name = ME_DTTSTP
Size Occupied = 5.42 MB (Space which i wanted to get back)

2) Is there any way to find num of rows in a index on table without analyzing
the table.

Thanks for your help to the oracle community.
Thanks and Regards
M.Balaji
and we said...
The space is there -- it is just allocated for the exclusive use of that
partition.

5.42 mb?? You have a datawarehouse and are concerned for 5.42mb??? You'll be
rebuilding global indexes in order to reclaim this (measly) 5.42mb of disk
(seems pretty darn teeny tiny to me for all of this work doesn't it??)

To find the rows an index points to, just

select count( c1 || c2 || c3 || c4 ... ) from t;

where c1, c2, c3, c4, ... are the columns in the index itself.

Or, you can

analyze index iname validate structure;
select * from index_stats;

to find the same (and more)
Review & Followup
Rating: 5
Get Back the Space allocated/taken to/by the table August 28, 2002
Reviewer: M.Balaji from Singapore
Hi,
 Thanks for the reply. Yes,we are working on a Datawarehousing application only.
But the data is a slice for 10 day's and for a very small branch. That's why
it's only 5.7 MB. The size of the database is 26 GB for couple of months.
  I find the answer was very usefull for me.

Thanks for the Help to Oracle Community.
Thanks and Regards
M.Balaji

August 28, 2002 Om -- Thanks for the question regarding "About Index Rebuild", version 8.1.7
You Asked
Hi Tom,

Thanks for always helping us.

Just to confirm as I asked you about

"Is it possible to find out the date when exactly an Index was
rebuild"

and you suggested to have a look at LAST_DDL_TIME in user_objects.

This is what I did.

1) create a emp table.
2) An index I1 on emp.
3) deleted few rows from the emp
4) Rebuild the index

But there was no change in the LAST_DDL_TIME.

The database version is 8.1.7 and the table had appr 200 records
during the test.

Could you please confirm as why the LAST_DDL_TIME was not recorded
for the Index ?

Best regards,
Om
and we said...
It works -- you must have done something "wrong" (like not looking at the
seconds in LAST DDL TIME?)

Try this:

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table emp;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table emp as select * from scott.emp;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index emp_idx on emp(empno);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select to_char(last_ddl_time,'dd-mon-yyyy
hh24:mi:ss' )
  2    from user_objects
  3   where object_name= 'EMP_IDX'
  4  /

TO_CHAR(LAST_DDL_TIM
--------------------
27-aug-2002 15:52:40

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_lock.sleep(2);

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter index emp_idx rebuild;

Index altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select to_char(last_ddl_time,'dd-mon-yyyy
hh24:mi:ss' )
  2    from user_objects
  3   where object_name= 'EMP_IDX'
  4  /

TO_CHAR(LAST_DDL_TIM
--------------------
27-aug-2002 15:52:42

ops$tkyte@ORA817DEV.US.ORACLE.COM> spool off
Review & Followup
Rating: 5
Possible bug... August 28, 2002
Reviewer: Connor McDonald from UK
Bug 1096399 - fixed in one of the 8.1.7 patchsets

hth
connor

Rating: 3
Review on LAST_DDL_TIME August 28, 2002
Reviewer: Om from INDIA
Hi Tom,

Thanks for your help.

But I did the same steps suggested by you.But still the
column was not updated.

Let me paste the contents from the spool file.

SQL> drop table emp5;

Table dropped.

SQL> select name from v$database;

NAME
---------
HC09

SQL> create table emp5
  2  as
  3  select * from emp
  4  where rownum < 1000;

Table created.

SQL> create index IE5 on emp5(ecode);

Index created.

SQL> select to_char(last_ddl_time,'DD-MON-YYYY HH24:mi:ss')
  2  from user_objects
  3  where object_name='IE5';

TO_CHAR(LAST_DDL_TIM
--------------------
28-AUG-2002 09:32:18

SQL> exec dbms_lock.sleep(2);

PL/SQL procedure successfully completed.

SQL> alter index IE5 rebuild;

Index altered.

SQL> select to_char(last_ddl_time,'DD-MON-YYYY HH24:mi:ss')
  2  from user_objects
  3  where object_name='IE5';

TO_CHAR(LAST_DDL_TIM
--------------------
28-AUG-2002 09:32:18

SQL> spool off

Is there any need to set some parameter to make this work.

Please advise.

Best regards,
Om

Followup:
See Connor's comment above -- you are most likely using an unpatched release of
817 -- I have 8174 and can confirm that it works in that release.

Rating: 5
Working on 8.1.7.0.0 without patch August 28, 2002
Reviewer: Sikandar Hayat Awan from Pindi Bhattian - Pakistan
SCOTT > create table emp as select * from scott.emp;

Table created.

SCOTT > create index emp_idx on emp(empno);

Index created.

SCOTT > select to_char(last_ddl_time,'dd-mon-yyyy hh24:mi:ss' )
  2        from user_objects
  3       where object_name= 'EMP_IDX'
  4  /

TO_CHAR(LAST_DDL_TIM
--------------------
29-aug-2002 08:38:39

SCOTT > exec dbms_lock.sleep(2);

PL/SQL procedure successfully completed.

SCOTT > alter index emp_idx rebuild;

Index altered.

SCOTT >
SCOTT >  select to_char(last_ddl_time,'dd-mon-yyyy hh24:mi:ss' )
  2     from user_objects
  3    where object_name= 'EMP_IDX';

TO_CHAR(LAST_DDL_TIM
--------------------
29-aug-2002 08:39:42

SCOTT > drop table emp;

Table dropped.

SCOTT > select * from v$version;

BANNER
----------------------------------------------------------------
Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production
PL/SQL Release 8.1.7.0.0 - Production
CORE    8.1.7.0.0       Production
TNS for 32-bit Windows: Version 8.1.7.0.0 - Production
NLSRTL Version 3.4.1.0.0 - Production

I thing patch is not an issue.

August 28, 2002 Al-KArim -- Thanks for the question regarding "Resource used by auditing", version 8.1.7
You Asked
I have to identify the hardware utilization of turning auditing on.
How much load is it to oracle in turning auditing on as far as cpu
utilization is concerned?
Your reply would be appreciated.
Thanks
and we said...
very very tiny.

only way for you to judge impact:

o benchmark (eg: collect some stats pack snapshots)
o turn it on
o benchmark again
o compare
Review & Followup
Rating: 5
Thanks August 28, 2002
Reviewer: A reader

August 29, 2002 Om -- Thanks for the question regarding "How to calculate Buffer cache hit ratio without db_block_lru_statistics and db_block_lru_extended_statistics ", version 8.1.7
You Asked
Hi Tom,

As you know we do not have db_block_lru_statistics and
db_block_lru_extended_statistics available in oracle 8i database.

Therefore how we can calculate without these parameters as to how
many buffers should be added (if required) to improve the buffer
cache hit ratio in Oracle 8i ?

Many thanks.

Best regards,
Om
and we said...
first, goto
http://www.hotsos.com/
and read "Are you still using cache hit ratios?".

Then ask yourself -- hmm, do I really want to increase a cache hit ratio or do I
want to spend my time lowering my consistent gets?

In any case, you can increase/descrease your block buffers over time -- taking
care to take exact measurements of overall system performance over a period of
time.

I never used the db_block_lru_statistics (inherently unstable on a production
box) rather sized the buffer cache based on the size of the machine, number of
sessions, what else was going on at that same time.  The adjust gradually over
time (after taking observations of what is happening with the current size so I
could see what effect it was or was not having)

Or, you can upgrade to 9i which has the cache advisor -- the ability to
guestimate what your physical io's would be at various cache sizes (and then you
can resize online even!)
Review & Followup
Rating: 5
Buffer cache hit August 30, 2002
Reviewer: Om from INDIA
Hi Tom,

just perfect.This is what exactly I was looking for.

Thanks a lot.

Best regards,
Om

August 29, 2002 Vince Crismer -- Thanks for the question regarding "OLEDB Provider", version Oracle 8.0.5 and 8.1.5
You Asked
Sir,

Greetings.
I would just like to know what is the current release of the
OLEDB Provider for Oracle? Do you have any sample codes for
visual basic on how to use this OLEDB Provider for Oracle.
Thanks.

- Vince Crismer C. Villena
and we said...
See
http://technet.oracle.com/tech/nt/ole_db/
for all of that information and a discussion board for
examples/questions.
Review & Followup
Rating: 5
OLEDB Limitation..???? August 29, 2002
Reviewer: Shailesh Saraff from Pune, India
Hi Tom,

We are using Oracle Provider for OLEDB from Release 2 (9.2.0.1.0) on Windows
2000/XP. I have taken SQL TRACE of following query. This query accepts 100 ID's.
Parses, executes and fetches that many time. Statistics shown below clearly
indicates that PARSE had taken 3.35 seconds elapsed time for 100 records. Where
as EXCUTE, FETCH takes 0.01, 0.02 seconds elapsed time respectively.

Why parsing takes so much of time? How can we avaoid this?

SELECT Name
FROM
TBLTest WHERE Id = :1

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 100 3.16 3.35 0 0 0 0
Execute 100 0.01 0.01 0 0 0 0
Fetch 100 0.02 0.02 0 299 0 99
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 300 3.19 3.38 0 299 0 99

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 19

Rows Row Source Operation
------- ---------------------------------------------------
1 TABLE ACCESS BY INDEX ROWID TBLTEST
2 INDEX UNIQUE SCAN (object id 4590)

Please guide us.

Thanks & Regards,

Shailesh

Followup:
I can only guide you to support to file a performance bug.

I've never ever used OLE as far as I know (sort of impossible on the machines I
program) and have never used OLEDB.  Not seeing the code you wrote -- I don't
know if it is YOUR CODE (eg: I can do the same bad thing in PLSQL, Pro*C, OCI,
JDBC if I code it wrong) or if the OLEDB stuff isn't optimal.

Rating: 3
Followup August 30, 2002
Reviewer: Justin Cave from Boise, ID USA
If you have a support contract with Oracle, I'd log a TAR on this-- it appears
to be a legitimate problem. I believe there's a workaround, however.

The workaround:

Declare a stored procedure, say, get_foo that takes as an IN parameter whatever
you're binding (in this case an integer) and returns a REF CURSOR that is the
result of your select statement. Replace the text of your SQL statement with
"{call get_foo( ? )}" and you should be set.

(potential workaround) I did a experiment along the lines of the one below with
Oracle's ODBC driver and verified that it behaves properly (only 1 parse) when a
prepared select statement is executed multiple times (I used the ODBCTest sample
from the MDAC SDK (url below)). It may be that if you use the Microsoft OLE DB
to ODBC provider with the Oracle ODBC driver that you problems go away, but I
haven't tested that idea.

My code:
create table foo( col NUMBER );

-- Note that I'm just inserting back into foo rather than returning the results
of the select
-- statement like you'd want to do.
create or replace procedure get_foo( value NUMBER )
AS
BEGIN
insert into foo( select * from foo where col = value );
END;
/

What I did:
- Using the "Microsoft OLE DB RowsetViewer", which is one of the sample apps in
Microsoft's MDAC SDK <
http://www.microsoft.com/data>,
I executed row OLE DB API calls with the Oracle OLE
DB provider. When I prepared the statement "insert into foo values( ? )" and
executed it a series of times with different parameters, tkprof shows that all
is well:

insert into foo
values
( :1 )

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 2 0.00 0.00 1 1 6 2
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 3 0.00 0.00 1 1 6 2

Misses in library cache during parse: 0
Misses in library cache during execute: 1
Optimizer goal: CHOOSE
Parsing user id: 35

If I instead use a select statement, "select * from foo where col = ?", I get
the erroneous behavior

select *
from
foo where col = :1

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 13 0.00 0.00 0 0 0 0
Execute 6 0.00 0.00 0 0 0 0
Fetch 6 0.00 0.00 0 10 24 10
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 25 0.00 0.00 0 10 24 10

Misses in library cache during parse: 1
Parsing user id: 35

If I change to use a stored procedure that does the selecting, all is well
again.

BEGIN get_foo(:1); END;

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 3 0.00 0.00 0 0 0 3
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.00 0.00 0 0 0 3

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 35

INSERT INTO FOO SELECT * FROM FOO WHERE COL = :b1

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 3 0.00 0.00 0 4 21 56
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.00 0.00 0 4 21 56

Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 35 (recursive depth: 1)

Justin

Rating: 2
Note this thread is also ongoing on OTN August 30, 2002
Reviewer: Justin Cave from Boise, ID USA
Mr. Villena also asked this question on OTN.  There may be further discussion
there.
http://www.oracle.com/forums/message.jsp?id=1264414&gid=763963

August 29, 2002 Jane -- Thanks for the question regarding "system
      tablespace free 0.01%", version 8.1.6

      You Asked
I do not have DBA background but I recently inherited an Oracle database on NT
that's extensively used for testing. While checking the tablespaces, I noticed
that system ts is only 0.01% free. What does this mean and what should I do to
have more free % on this ts. Any help is appreciated.

Thanks in advance

      and we said...
Well, it might be 0.01% full but it might also be autoextensible.  Use the
following script to see whats really there:

--------------------------------------------------------
-- free.sql
-- usage @free <orderbycolumn>
--       @free 1
-- This SQL Plus script lists freespace by tablespace
--------------------------------------------------------

column dummy noprint
column  pct_used format 999.9       heading "%|Used"
column  name    format a16      heading "Tablespace Name"
column  Kbytes   format 999,999,999    heading "KBytes"
column  used    format 999,999,999   heading "Used"
column  free    format 999,999,999  heading "Free"
column  largest    format 999,999,999  heading "Largest"
column  max_size format 999,999,999 heading "MaxPoss|Kbytes"
column  pct_max_used format 999.9       heading "%|Max|Used"
break   on report
compute sum of kbytes on report
compute sum of free on report
compute sum of used on report

select nvl(b.tablespace_name,
             nvl(a.tablespace_name,'UNKOWN')) name,
       kbytes_alloc kbytes,
       kbytes_alloc-nvl(kbytes_free,0) used,
       nvl(kbytes_free,0) free,
       ((kbytes_alloc-nvl(kbytes_free,0))/
                          kbytes_alloc)*100 pct_used,
       nvl(largest,0) largest,
       nvl(kbytes_max,kbytes_alloc) Max_Size,
       decode( kbytes_max, 0, 0, (kbytes_alloc/kbytes_max)*100) pct_max_used
from ( select sum(bytes)/1024 Kbytes_free,
              max(bytes)/1024 largest,
              tablespace_name
       from  sys.dba_free_space
       group by tablespace_name ) a,
     ( select sum(bytes)/1024 Kbytes_alloc,
              sum(maxbytes)/1024 Kbytes_max,
              tablespace_name
       from sys.dba_data_files
       group by tablespace_name
       union all
      select sum(bytes)/1024 Kbytes_alloc,
              sum(maxbytes)/1024 Kbytes_max,
              tablespace_name
       from sys.dba_temp_files
       group by tablespace_name )b
where a.tablespace_name (+) = b.tablespace_name
order by &1
/

for example on my system it shows:                                    Max
                                                             Max      pct
Tablespa   KBytes     Used    Free   Used      Largest       Kbytes   Used
SYSTEM    201,728  200,960     768   99.6          704   33,554,416     .6

shows that my system tablespace is currently 201m and has 0.4% fre HOWEVER it
can grow to 33gig and hence has only used 0.6% of its potential.

If you find that you really have 0.01% free, you might consider adding more
space by resizing or adding another datafile to SYSTEM.

See the

ALTER DATABASE DATAFILE 'filename' resize    command or
ALTER TABLESPACE ADD DATAFILE ...            command...

Review &
      Followup

      Rating:
      5
  April 01, 2001
Reviewer:  Pauline

The name of the view 'dba_temp_files' in the script should be 'dba_data_files',
right?

      Rating: 4
Datafile
      Size  February 28, 2002
Reviewer:  Venkateswra
      Reddy  from Bangalore, India
Dear Tom

Today I am facing new problem ( I dint face it so far ). Two weeks back through
EM  I am resizing my datafile size from 80 MB TO 8000MB( This has been done by
mistake). While creating datafile system shown outof space. So untile that time
my datafile has been created with 750 MB,(Maximum size on the disk). After that
again resized to 200 mb. Today it has while creating one table from other user
table. Disk out of space came.

My doubt is I am sure about that what this datafile contains not more than 85mb.
Because I have exportated data from my existing user ( HRMS, THIS USER CONTAINS
AROUND 80MB DATA). So there is no chance beyond 85mb of data in my new datafile.

While resing to again 250 mb of datafile .System is showing data contains
morethan what you have specified. It is showing data contgain around 800MB( But
it is not correct).

I would like to resinze datafile If not I want to drop the datafile. But I want
that data what is their in this datafile.Can I export data to another user or
what I can do. Even I have taken export DMP file size is only 800KB only.

It is very very urgent.

Thanks in advance.

Reddy

          Followup:

space is files is allocated by extents.  It is quite possible that the file you
have (the 8000meg on) cannot be shrunk by even a single byte -- even if it only
contains 1meg of data.

Suppose the following is a "map" of the file from byte 0 to 8000mb.  X's are
allocated extents of 1meg, .'s are free space:

.....X........................X.........................X........................

0                                    4000mb             6000mb
8000mb

In that file, which holds 3meg of data, the smallest we can make that file is
6000mb because of that extent that is way out there (search this site for
maxshrink, a script that will tell you the min size each file in your system can
be shrunk to).

You need to move those objects OUT of that file (create a new tablespace, alter
table t move new_tablespace), shrink the file, then move them back and drop that
scratch tablespace.  Remember to rebuild the indexes after the last move back.

Or, just move them, drop this tablespace and files if you like.

      Rating: 4
How about move
      back to it's own tablespace  February 28, 2002

Reviewer:  Lisa  from TX
In Oracle 8.1.6 and up, we can also move the objects back to it's own tablespace
dynamically while user is accessing the application and updating the tables.
Right?

          Followup:

If they are index organized tables -- yes.  You can rebuild these online.

If they are "heap" (normal) tables -- no, you cannot modify them during the
move.

In 9i there is the ability to move the object whilst people read and WRITE it
(online everything)

      Rating: 4
Refer to
      'moving tables across tablespaces'  February 28, 2002

Reviewer:  Lisa
I used the script 'moveall' to build the script to move both tables and indexes
from current tablespace A to current tablespace A.
The script run with no errors (use oracle8.1.6). I checked the tablespace map,
before the extents spread all over the 1 gig bytes tablespace, after the
move/rebuild objects, I am able to resize the tablespace to half of the size
(500M, before it was only able to shrink to 900M).
In this case both the table/ indexes are in tablespace A, when I run the script,
I specified new tablespace is A also.

But your comment said only IOT tables are able to do this online, the regular
tables are not, could you explain why?
and what will happen if the regular table did with this method ?

          Followup:

You can move the tables but during the move, no updates allowed.  The move is
not "online" with regards to the table.

You can rebuild an IOT ONLINE (and hence move it). that allows non-stop DML
during the move.

      Rating: 4
SPACE IN SYSTEM
      TABLESPACE  August 20, 2002
Reviewer:
      A reader
The script that you provided to calculate used% etc for tablespaces..gave me
this on mine:
SYSTEM                270,336      256,784       13,552   95.0       13,552
33,554,416     .8

0.8% used of the total...so it's O.K.

There is another script that I used to see other owners besides sys and system
that have objects in SYSTEM TABLE SPACE:

select owner, segment_name, segment_type
         from dba_segments
         where tablespace_name = 'SYSTEM'
           and owner not in ('SYS','SYSTEM');
it gave me some other users:
OWNER                          SEGMENT_NAME

------------------------------
---------------------------------------------------------------------
OUTLN                          OL$

OUTLN                          OL$HINTS

AURORA$JIS$UTILITY$            AURORA$IIOP$SYSTEM$PROPERTIES

AURORA$JIS$UTILITY$            SNS$BINDINGS$

AURORA$JIS$UTILITY$            SNS$INODE$

AURORA$JIS$UTILITY$            SNS$ATTRIBUTES$

AURORA$JIS$UTILITY$            SNS$REFADDR$

AURORA$JIS$UTILITY$            SNS$PERMISSIONS$

AURORA$JIS$UTILITY$            SNS$SHARED$OBJ$

AURORA$JIS$UTILITY$            JAVA$HTTP$REALM$PRINCIPAL$

AURORA$JIS$UTILITY$            JAVA$HTTP$REALM$ID$

AURORA$JIS$UTILITY$            JAVA$HTTP$REALM$GROUP$

AURORA$JIS$UTILITY$            JAVA$HTTP$REALM$POLICY$

AURORA$JIS$UTILITY$            JAVA$HTTP$REALM$MAPPING$

AURORA$JIS$UTILITY$            JAVA$HTTP$LOG$

OSE$HTTP$ADMIN                 HTTP$LOG$

OSE$HTTP$ADMIN                 EVENT$LOG

OSE$HTTP$ADMIN                 ERROR$LOG

AURORA$JIS$UTILITY$            JAVA$HTTP$DEPLOYMENT$DIGEST$

OUTLN                          OL$NAME

OUTLN                          OL$SIGNATURE

OUTLN                          OL$HNT_NUM

AURORA$JIS$UTILITY$            KEY_C

AURORA$JIS$UTILITY$            SYS_C001012

AURORA$JIS$UTILITY$            SNS$NODE_INDEX

AURORA$JIS$UTILITY$            SYS_IL0000022418C00003$$

AURORA$JIS$UTILITY$            SYS_C001013

AURORA$JIS$UTILITY$            SNS$REFADDR_INDEX

AURORA$JIS$UTILITY$            SNS$PERM_INDEX

AURORA$JIS$UTILITY$            SNS$SHARED$OBJ_INDEX

AURORA$JIS$UTILITY$            HTTP_PRINCIPAL_CON

AURORA$JIS$UTILITY$            HTTP_ID_CON

AURORA$JIS$UTILITY$            HTTP_GROUP_CON

AURORA$JIS$UTILITY$            HTTP_POLICY_CON

AURORA$JIS$UTILITY$            P_MAP_CON

AURORA$JIS$UTILITY$            SYS_LOB0000022418C00003$$

36 rows selected.

Question:
is it O.K. for these to be there.....

Thanks a bunch for being there.....

          Followup:

Yes, it is perfectly OK for those to be there.

      Rating: 5
write a
      procedure to warn free space usage  August 29, 2002

Reviewer:  an  from DE USA
Hi Tom:

I run your script free.sql daily to check our database usage, it's very helpful.
thank you very much.
now I am thinking how I can adopt the free.sql script and write a proceduce that
should
(1) calculate tablespace usage in megabytes, what the remainign percent free is
in that tablespace, and the total available mb (used & free).
(2) the collected data should be stored in a table SPACE_USAGE. and should
contain at minimum the following the tablespace name, date/time, mbused, percent
free, and total mb available.
(3) if a tablespace drops below <= 20% than it should write a message out to the
WARN.log locatied in the adump directory of that database on Unix.

would you please help me how I can write the procedure and pkg about it?
thank you very much as always,
an

          Followup:

Just add trunc(sysdate) to my free.sql (as a column).

create table that holds that output.

then write a procedure that:

inserts into table select * from free.sql;
for x in ( select * from table where used > 80 )
loop
   utl_file -- to write to the file
end loop

use dbms_job to schedule that every day.

      Rating: 5
Free
      tablespace  May 24, 2003
Reviewer:  Abdul
      Wahab  from Jeddah,Saudi Arabia
Dear Tom,
  Thanks a lot for the script you wrote for finding out the freespace. It has
been very useful to me to analyze the tablespaces in my database.
  Thanks again for being there.

August 29, 2002 Art -- Thanks for the question regarding
      "Coexistence of CAST/MULTISET operators and member functions?", version
      8.1.7

      You Asked
Good morning, Tom.

I am studying your book, in particular today chapter 20, "Using
Object Relational Features".

Is it possible to apply CAST and MULTISET operators to a nested table
collection whose object contains a member function or procedure?  If
so, what is the syntax?

SQL> CREATE OR REPLACE TYPE myScalarType AS OBJECT (
  2      ename        VARCHAR2(10)
  3  ,   sal          NUMBER(7,2)
  4  )
  5  /

Type created.

SQL> CREATE OR REPLACE TYPE myTableType AS TABLE OF myScalarType;
  2  /

Type created.

SQL> DECLARE
  2      l_emp_sals        myTableType;
  3  BEGIN
  4      SELECT CAST( MULTISET( SELECT e.ename
  5                             ,      e.sal
  6                             FROM   emp   e
  7                             ORDER BY e.ename )
  8                   AS myTableType )
  9      INTO l_emp_sals
 10      FROM DUAL;
 11
 12      FOR i IN 1..l_emp_sals.COUNT LOOP
 13          DBMS_OUTPUT.PUT_LINE(l_emp_sals(i).ename || ' earns ' ||
l_emp_sals(i).sal);
 14      END LOOP;
 15  END;
 16  /

 ADAMS earns 1100
 ALLEN earns 1600
 BLAKE earns 2850
 CLARK earns 2450
 FORD earns 3000
 JAMES earns 950
 JONES earns 2975
 KING earns 5000
 MARTIN earns 1250
 MILLER earns 1300
 SCOTT earns 3000
 SMITH earns 800
 TURNER earns 1500
 WARD earns 1250

PL/SQL procedure successfully completed.

SQL> DROP TYPE myScalarType FORCE;

Type dropped.

SQL> CREATE OR REPLACE TYPE myScalarType AS OBJECT (
  2      ename        VARCHAR2(10)
  3  ,   sal          NUMBER(7,2)
  4  ,   MEMBER FUNCTION toString RETURN VARCHAR2
  5  )
  6  /

Type created.

SQL> CREATE OR REPLACE TYPE BODY myScalarType
  2  AS
  3      MEMBER FUNCTION toString RETURN VARCHAR2
  4      IS
  5      BEGIN
  6          RETURN (SELF.ename || ' earns ' || SELF.sal );
  7      END;
  8  END;
  9  /

Type body created.

SQL> DECLARE
  2      l_emp_sals        myTableType;
  3  BEGIN
  4      SELECT CAST( MULTISET( SELECT e.ename
  5                             ,      e.sal
  6                             FROM   emp   e
  7                             ORDER BY e.ename )
  8                   AS myTableType )
  9      INTO l_emp_sals
 10      FROM DUAL;
 11      FOR i IN 1..l_emp_sals.COUNT LOOP
 12          DBMS_OUTPUT.PUT_LINE(l_emp_sals(i).toString());
 13      END LOOP;
 14  END;
 15  /

DECLARE
*
ERROR at line 1:
ORA-06550: line 1, column 21:
PLS-00570: different number of columns in the multiset and cast expressions
ORA-06550: line 4, column 5:
PL/SQL: SQL Statement ignored

Thanks, Tom.

Art.

      and we said...
It is a PLSQL issue (fixed in 9i with the common sql parsers).

This shows it won't work statically but does dynamically -- a workaround until
you get to 9i:

ops$tkyte@ORA817DEV.US.ORACLE.COM> DECLARE
  2       l_emp_sals       myTableType;
  3  BEGIN
  4       SELECT CAST( MULTISET( SELECT e.ename
  5                  ,      e.sal
  6                  FROM   emp   e
  7                  ORDER BY e.ename )
  8                AS myTableType )
  9       INTO l_emp_sals
 10       FROM DUAL;
 11
 12       FOR i IN 1..l_emp_sals.COUNT LOOP
 13           DBMS_OUTPUT.PUT_LINE(l_emp_sals(i).ename || ' earns ' ||
l_emp_sals(i).sal);
 14       END LOOP;
 15  END;
 16  /
    l_emp_sals        myTableType;
            *
ERROR at line 2:
ORA-06550: line 1, column 21:
PLS-00570: different number of columns in the multiset and cast expressions
ORA-06550: line 4, column 5:
PL/SQL: SQL Statement ignored

ops$tkyte@ORA817DEV.US.ORACLE.COM> DECLARE
  2       l_emp_sals       myTableType;
  3  BEGIN
  4           execute immediate
  5       'SELECT CAST( MULTISET( SELECT e.ename
  6                  ,      e.sal
  7                  FROM   emp   e
  8                  ORDER BY e.ename )
  9                AS myTableType )
 10       FROM DUAL'
 11       INTO l_emp_sals;
 12
 13       FOR i IN 1..l_emp_sals.COUNT LOOP
 14           DBMS_OUTPUT.PUT_LINE(l_emp_sals(i).ename || ' earns ' ||
l_emp_sals(i).sal);
 15       END LOOP;
 16  END;
 17  /
ADAMS earns 1100
ALLEN earns 1600
BLAKE earns 2850
CLARK earns 2450
FORD earns 3000
JAMES earns 950
JONES earns 2975
KING earns 5000
MARTIN earns 1250
MILLER earns 1300
SCOTT earns 3000
SMITH earns 800
TURNER earns 1500
WARD earns 1250

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> spool off

Review &
      Followup

      Rating: 3
CAST/MULTISET
      operators and member functions  August 29, 2002

Reviewer:  rama  from CT USA
Nice workaound, only why dont the manuals publish these linitations (like only
dynamic method would work in 8. versions)???

          Followup:

They did not anticipate this.

You see - the plsql engine used to use (in 8i and before) its own sql parser.
They write the manuals as they write plsql.  Meanwhile, the server guys are off
expanding SQL - but the plsql guys didn't know about it.

So, when it all comes together -- they had a mismatch in parser functionality.
In the past, this wasn't so bad -- but as things move faster and faster and
faster, this got bad in 8i -- so, in 9i, they use a common parser and exceptions
like this just don't/won't happen anymore.

August 29, 2002 Meyer -- Thanks for the question regarding "Your
      thoughts on thin vs fat clients for applications", version 8.16db-9IAS

      You Asked
Tom,

I am have made full fat applications w/developer but have not run developer on
net. And I have made full web aps (mod/plsql-or-OWA) and created the
cookies/logon, framework...everything from scratch.

I enjoy both types... especially instalation/upgrade of web (just send out the
URL or update one place), and security of client/server (no back button or
history or coding things I take for granted in client/server, for example wrote
a procedure  that draws select boxes in forms that default to the value in the
table).

Do you think we are at a point where everything should be run in a browser, say
for example even a hospital front end system (register patients etc) and why?

If not when would you advise to make web (web meaning dynamic html/javascript
client that runs in browser, not open to entire internet), when would you not?

Thanks,
Meyer

      and we said...
Oh yes, definitely.

I've been there for years actually.

I do not use javascript very much, and not for anything critical (eg: turn off
javascript and I'm OK).  I never use DHTML (and despise sites that do -- I like
to use operating systems other then windoze and boy does it give me heartburn
when the biggest open systems movement in forever is subverted by
microsoft'isms).  I don't put java on the client.

What has to change is the expectation of HOW an application works, responds.
Taking a client server form and trying to put that verbaitim on the web using
HTML is a recipe for SURE disaster.  It'll never fly.  Sure, with some magic
incantation of dhtml, proprietary markup, javascript, etc -- you might be able
to do it but what have you gained?  The ability to really make people upset
(sorry, we only work with MSIE version 6.0.3.2l1.43 patch 523.  Try again.  Oh,
you are using a Macintosh?  sorry -- we don't support that IE version.  Oh, you
are using linux -- bzzzt you lose).

You have to take the requirements (what must this program do) and implement it
using the tools (standard HTML, minimal javascript, light interface).  What you
use at the app server level and beyond is upto you (java, perl, whatever -- me,
I use PL/SQL 100%).  The end application will look different then the client
server one, but it can and will be just as functional (and if you keep the pages
small, just as responsive, or better).

If I can buy all of my books, dvds at amazon
If I can buy gym equipment from outpost
If I can do my expenses
If I can do all of my travel
If I can promote, change, hire, fire
(and the list goes on and on and on)

I cannot imagine an application that could not be there.
Review &
      Followup

      Rating: 4
Clarification
      from me  August 28, 2002
Reviewer:  Meyer
       from Baltimore, MD
Tom,

We think alike...

let me clarify...I realize were not clear.

Dynamic HTML to me meant:
html generated at execute by pl/sql
(not real dhtml)....oops

I also use minimal javascript , almost all pl/sql-to-html...and htp.print w/some
basic javascript (for image swaps etc...all output via htp.p).

Seems similar to your approach. I like keeping everything in the DB, much better
validation.

Would you consider writing the front/end registration system for a hospital to
run this way?

          Followup:

Sure, why not -- I cannot imagine wanting to put some client server application
out there, not in a million years.

      Rating: 4
What about a
      Java "Thick" Client?  August 28, 2002
Reviewer:
      Jer Smith  from Salt Lake City UT
...we're in the middle of writing a clinical application and we've decided on a
three-tiered architecture, _with_ a "thick" client.  By thick in this case we
mean only that it contains executable code on the client machine--not that the
bulk of the business logic is actually implemented anywhere but on the server.
At the same time, most of the application's functionality could also be made
available through a browser, thick PDA client, etc.

It's an important distinction.  I'd hate to try to see an application like CAD
or image manipulation run within the constraints of what you can do with
cross-platform HTML.

Ideally, some day we'd have a "general application engine" that allowed most GUI
features without vendor and OS-specific requirements, instead of something built
piecemeal from something designed to display formatted text.

          Followup:

Ok, CAD/CAM -- drawing programs and the like, agreed -- they'll be on the
desktop as a client application.

A clinical app though, why does it need a thick client?  what does it do that
cannot be done via a web page (this is me showing my ignorance of what exactly a
"clinical" application is/does)

As for your last paragraph -- Forms has been doing that forever.  It started
cross platform, became virturally "windows only" until 1995 and the web when it
returned to its roots and became 3 tiered.

      Rating: 4
Limitations of
      the Web  August 29, 2002
Reviewer:  Jer
      Smith  from Salt Lake City, UT
I guess it has a lot to do with your statement "change the expectation of HOW an
application works, responds."  In many cases that's doable and smart.  In other
cases you're simply giving up too much flexibility in your user interfaces.
Sure, most things are replacable or non-critical, but here's a short list of
features you can't have with W3C standard HTML (if I'm wrong on any of these,
please let me know):
*right-clicking for a context-sensitive menu
*drag-n-drop
*free-form drawing
*connection to local devices
*manipulation of screen elements (moving controls around, as you would in an
IDE)
*keyboard shortcuts
*instantaneous event-based display changes

Do most applications need these features in their user interface?  No, but they
could be potential enhancements to many.   Some applications do, however, and
that trumps the many advantages of having an application be web-based.
Sometimes you simply need or want a more powerful user interface than what the
web can give you right now.

____

I'm pretty unfamiliar with Oracle Forms, but its major difference from what I'm
talking about is that it's not an open standard that anyone can implement on any
platform without cost, like a web browser or text editor is.  There's a reason
you can view web pages on everything from an Amiga to a Zaurus.

          Followup:

Agreed with the first part, 100%

As for the second part -- whats up with this "without cost".  Is someone GIVING
you hardware?  And that T1 connection -- thats *free* right?  And the
development costs -- I mean, you don't pay yourself anything right (you must
just get food from the kindness of strangers?) and so on.  Free -- it ain't
free.

So, do you build your java apps in VI, without any IDE?  ( i would -- i do, but
most do not).  Is your IDE "free"?

Free -- hmmm.   Think about it sometime.

But forms does deploy onto the client as java, you wanted to know about java
clients -- forms does that.

August 29, 2002 Ravi -- Thanks for the question regarding
      "Locally Managed Tablespace for DW", version 816

      You Asked
Dear Tom,

I understand that Locally managed tablespace will improve
performance.  Whether the locally managed tablespaces can be
adopted for DATA WAREHOUSING. I am about to create a database in
816 for dataware house applications. I am thinking to create the
tablespaces except the system as locally managed.

But My concern is, in other databases in my site, we create the
extent sizes in the order of 200M, 500M and all. But the locally
managed tablespaces will create the extents in the order of 64K,
1M, 8M and 64M. So I am thinking that there might be numerous
extents for some tables. Could you give me an idea, whether
locally managed tablespace can be adopted to dataware house
applications.

Thanks in advance
Ravi

      and we said...
Locally managed tablespaces with UNIFORM extent sizing will
create extents in any size you want -- 200m, 500m and all - no
problem.  Don't use autoallocate, use UNIFORM.

Use locally managed tablespaces and do not worry about hundreds
of extents -- they are OK !
Review & Followup

      Rating:
      3
  May 20, 2002
Reviewer:
      A reader
Hi Tom,

Why do you recommend not use the autoallocate option?

Thanks in advance and best regards

Angel

          Followup:

autoallocate is good for applications that create hundreds of tables that are
sometimes used, sometimes not.

For example -- an off the shelf app like Oracle Apps, SAP, etc -- they install
hundreds of tables.  Depending on how you use the software a given table might
be empty or might be 5 gigabytes in size.  They don't know.  Hence, it is really
not possible to size them properly for all cases.  In this case, using
autoallocate makes sense.  All tables start small and grow over time.

In your own development (custom development) -- I cannot imagine you would have
this case.  You either will be using a table or not -- you would not create it
if you did not use it.  Hence, you can size your objects into small, medium and
large tablespaces.  You can use UNIFORM.  I like uniform cause I know exactly
how big each extent will be.

      Rating:
      5
Overhead  May 21, 2002
Reviewer:
      Andre  from Brazil
Ok, but WHAT is the real overhead of using autoallocate ?

          Followup:

who said there was overhead?

It is just not uniform, that is all.  I don't like non-uniform extents for
object I KNOW what the size will be.  autoallocate basically says "I don't have
a clue, it might be small, then again it might be large".  Works for off the
shelf software where tables are sometimes used, sometimes not.  For developed
apps -- the table usage should be well known.

      Rating: 5
What about SYS
      objects (not the SYSTEM tablespace)?  May 21, 2002

Reviewer:  Colin Davies  from Los Angeles, California

I have read several books that recommend moving the SYS.AUD$ table to another
tablespace when planning extensive auditing.  Is it OK to use a locally managed
tablespace for this purpose?  My understanding is that you can use LMT's for any
permanent tablespaces IF they won't have any rollback segments in them.

          Followup:

You can use LMT's for any tablespace except system (until 9ir2 when system is to
be allowed as well).  Even for those RBS tablespaces.

      Rating: 5
For
      Colin  May 21, 2002
Reviewer:  Connor
       from UK
You can use LMT's for anything (and preferably everything).  Some early 8i
versions had some anomalies with rb segs in LMT's, but even these were easy
enough to overcome.  This leaves just SYSTEM - and even this can be an LMT from
9.2

An additional hassle with auto-lmt's is that you cannot predict when a segment
will not be able to extend

      Rating: 4
LMT
        May 21, 2002
Reviewer:  NP  from
      Thailand
Hi, Tom
After reading all this I want to change my DW Tablespace from LMT autoallocate
to Uniform allocation,
Please let us know how to decide a moderate uniform extent size for a medium
load database of size 2 gb spread over 4 disk.
Thanks

          Followup:

2gig is pretty small.

You would set it up by object size -- setting up extent sizes for "small" "med"
and "large".  You would want all segments to be placed in a tablespace such that
they would never have more then say 1,000 extents.

So, maybe for you if you have some really small tables:

64k extent size  -- good for 64k to ~6meg, could goto ~60meg
512k extent size -- good for 512k to ~50meg, could goto ~500meg
1m extent size   -- good for 1m to ~100meg, could goto ~1gig
5m extent size   -- good for 5m to ~500meg, could goto ~5gig

but in your case -- you have just a 2gig database, you could just leave it as is
-- it is small enough that if you ever needed to, you could rebuild the entire
thing in a couple of minutes.

      Rating:
      2
Overhead  May 22, 2002
Reviewer:
      Andre  from Brazil
If "autoallocate" isn't a misnomer, it should offer the advantage of
automatically allocating extents over uniform explicit sizing.

It is easy as 1,2,3... a question of tradeoffs: Why should I manually size my
extents if there's some built-in feature that does that for me ?

The ONLY reasonable explanation is that there must be some kind of "overhead",
be it related to space, performance, contention, whatever...

It's not clear anywhere in the Oracle texts I've gone through.

Could you please clarify that ?

      Rating: 2
Overhead
      (cont'd)  May 22, 2002
Reviewer:  Andre
       from Brazil
Even reading Lewis' book, it's not clear.

He states the "problem" with autoallocate is that some extents may increase in
size and Oracle allocates non-uniform sized extents in this case. This
eventually creates holes.

I may also happen that Oracle allocates smaller extents to fit in the free bit
of the bitmap.

Later on, he says he "hasn't found any good reason" for using AUTOALLOCATE.

I really don't understand that.

The reason is: Oracle does that automatically !

Isn't that a good reason ???

Are you possibly saying that accessing these non-continguous data portions or
managing these scattered exetents creates some "overhead" ?

There are no guarantees the OS block will be contiguous on disk either...

Please help me out on this one.

      Rating: 2
Overhead
      (cont'd)  May 22, 2002
Reviewer:  Andre
       from Brazil
I see only the good old problems interfering with extent sizing, that is:

- Large extents: more unused space
- Small extents: more dynamic allocation

      Rating: 4
I think you're
      missing the point  May 22, 2002
Reviewer:
      Dennis  from Missouri, USA
Andre,

The problem with auto-allocate is what Lewis says.  You have no control over the
next extent size.  The "auto-allocate" can be taken as a misnomer if you
interpret it as "Oracle will allocate extents", which seems to be what you are
doing.  First of all, Oracle allocates the extents already, unless you
specifically force an allocation of an extent.  The difference here is that
Oracle is using an algorithm to "automatically allocate" extents of larger and
larger size.  This is where you can get your holes.  With Uniform LMTs, you will
never have "non-usable" holes, because all the extents will be the same size.

There is no overhead, the bad thing is that you can fragment your tablespace
with a bunch of holes that are not usable, despite the fact that you can clearly
see you have 2 GB (or some other arbitrary number) of "free space" to be
allocated.  Since it will not be contiguous, Oracle will fail to find the space
to allocate an extent.   With Uniform LMTs, the "free space" read out will be
what is usable, because any fragmentation in the tablespace is "good
fragmentation" in the sense that any object in that tablespace can grab an
extent.

As far as the "drawbacks" you see, it all depends on how you use them.  The
technology is such that extent allocation (especially with LMTs) is no longer a
big factor as far as "wasted time" goes.  I think you will find Uniform LMT to
be the best way to go no matter what.  We use different sizing than Tom
specified (objects < 160MB - 160KB extents, >= 160 MB and < 5120 MB (5GB) - 5120
KB (5 MB) extents, and objects over 5120 MB are in 160 MB extent sized
tablespaces), but the theory is still there.

There is not much overhead for extent allocation because the tables are moved
when they hit a certain size we move them.  This allows for a single extent to
contain more rows, so you wind up with longer and longer intervals before the
system allocates an extent.  At first you might have some minor problems if you
pick the wrong size for a table, or it suddenly starts growing rapidly on you,
but this is pretty rare.  I would say unless you have ridiculously small tables
that using an extent size < 160 KB is pointless.

      Rating: 5
Advantages of
      Uniform Size  August 29, 2002
Reviewer:
      A reader
Hi Tom
The principal advantage as I read of using UNIFORM size is that there are not
unwasted segments.

But that are the only advantage could you please give a more detailed
explanation?

Thank you.

          Followup:

unwasted EXTENTS really (a segment being a collection of extents and extents are
what cause fragmentation)

other advantages:

o no fragmentation
o faster (no recursive sql to UET$ and FET$ to update the data dictionary)
o no fragementation
o faster (try creating a dictionary managed table with 1000 extents, then do it
with a LMT)
o no fragmentation
o faster (try dropping that table with 1000 extents now)
o no fragmentation
o faster (try a temporary tablespace in a DMT vs LMT -- benchmark it, its
amazing)

      Rating: 5
Advantages of
      Uniform Size  August 29, 2002
Reviewer:  J
      reyes  from Bolivia
Hi Tom
The principal advantage as I read of using UNIFORM size is that there are not
unwasted segments.

But that are the only advantage could you please give a more detailed
explanation?

Thank you.

      Rating:
      5
  August 30, 2002
Reviewer:  J Reyes

:) Thank you again.

      Rating:
      5
  July 07, 2003
Reviewer:
      A reader

August 29, 2002 wallacel -- Thanks for the question regarding
      "DBA_INDEX contents?", version 8.1.6

      You Asked
Tom:

I am reading oracle8i design and tuning for performance page
8-14

SQL> SELECT INDEX_NAME "NAME", NUM_ROWS, DISTINCT_KEYS
"DISTINCT",
LEAF_BLOCKS, CLUSTERING_FACTOR "CF",
AVG_LEAF_BLOCKS_PER_KEY "ALFBPKEY"
FROM DBA_INDEXES
WHERE TABLE_NAME ="AP_INVOICES_ALL"
ORDER BY INDEX_NAME;
This returns the following typical data:
NAME            NUM_ROWS DISTINCT   LEAF_BLOCKS CF    ALFBKEY
--------------- -------- ---------- -------- -------- ---------
AP_INVOICES_N1  18941    80712      17060    431230   1
AP_INVOICES_N3  14995    2          21403    186450   10701
AP_INVOICES_N4  13196    439859     18669    2889596  1
AP_INVOICES_N5  9734     291        24145    1543140  82
AP_INVOICES_N6  18816    1567987    22708    2579791  1
AP_INVOICES_N9  9216     3          23271    264048   7757
AP_INVOICES_U1  10892    2861077    17074    342793   1
AP_INVOICES_U2  17176    3084212    28910    2499547  1

My question is at what situation
the number of DISTINCT is greater than NUM_ROWS?
it donesn't make sense to me.
since you only have 18941 rows, how can you have 80712 distinct
rows?

also at the same page, it says
Number of leaf blocks. This is the number of I/Os needed to find
the desired rows of data.

this doesn't make sense to me also. since we use index to
narrow down to the leaf, only serveral block is used, not all of
them.
if it says Avg_Leaf_Blocks_per_key, it make more sense.

      and we said...
NUM_ROWS is not used internally by the optimizer and if you use
estimate statistics is not widely accurate.  distinct keys is on
the other hand and is more accurate.  Num_rows in the index
after an estimate is not entirely reliable.

It is poorly worded.

Number of leaf blocks.  This is the number of IO needed to full
scan this index (this is where the data is)

might be a better way to say it.

blevel would be a better judge of how many io's it'll take to
reach a leaf on a index unique scan.

Review &
      Followup

      Rating: 4
Where is
      initial extent unit stored  August 29, 2002

Reviewer:  H  from playsquash@yahoo.com
where is the unit KB,MB,Bytes for initial extent stored in the data dictionary.

Thanks for your help.

          Followup:

it is not, it is always in bytes.

August 29, 2002 Vinnie -- Thanks for the question regarding "Hex
      to Char", version 8.1.5

      You Asked
I have a 3rd party app in which one of the tables stores data as
RAW(1).  When I query the field I can see the data is actually
in HEX format.

SELECT SRCIP from connects;

srcip
-----
8D

My question:

Is there an easy way to convert this to VARCHAR2?

      and we said...
Well, it is not actually stored in HEX -- it is stored in a raw
format (just bytes).  There is an implicit conversion from the
RAW to HEX for display.

Ok, so you want to display this as varchar2 -- there are 2 ways:

o utl_raw
o chr( to_number() ) -- (only because you have 8i)....

Let's do the utl_raw first.  It would look like this:

ops$tkyte@DEV816> select utl_raw.cast_to_varchar2(srcip) srcip,
  2  srcip from connects;

SRCIP SRCIP
----- -----
     8D

UTL_RAW might not be installed on your system -- it comes with
Oracle7.1.6 and up.  To install if you do not have it:

o cd $ORACLE_HOME/rdbms/admin
o connect as SYS or INTERNAL only using SVRMGRL
o @utlraw
o @prvtrawb.plb

Now, if you want to use just "regular" sql functions (will be
faster then utl_raw if you have lots of rows and you don't have
to make sure that utl_raw is installed) you can do this as well:

ops$tkyte@DEV816> select chr( to_number( srcip, 'xx' ) ), srcip
from connects;

CH SRCIP
-- -----
  8D

The 'xx' format is new with 8i -- it can be used to convert a
hexstring to a decimal number or a decimal number to a hex
string...

Review &
      Followup

      Rating: 5
Bingo !!!
      Exactly what i wanted.  May 02, 2001
Reviewer:
      Thevaraj Subramaniam @ Raj  from Kuala Lumpur, Malaysia

I was looking for the proper sequence to add the utl_raw package into the
database. Found it.

      Rating:
      4
  August 29, 2002
Reviewer:
      A reader
similar to Vinnie's question, my 3rd party data is like raw(8) and raw(16)
etc...they store things like account_id etc.
I dont know how they generate those ids and how they store it.
how do i see their data? and display it on the web and sqlplus?
when i use cast_to_varchar2 i get some symbols...is that what was stored
originnally then by that 3rd party?

          Followup:

If it is RAW, I have NO IDEA what was stored by them, none.

You can use rawtohex to see a hex dump -- but to make meaning of that, you need
to ask THEM.

August 29, 2002 atul -- Thanks for the question regarding
      "General Question", version 8.0.4

      You Asked
Sir,

I'm on Digital unix/Oracle 8.0.4..

If i have to forcefully shutdown the server (manually power off) ..
and oracle didn't down..

After rebooting again..
1)are there any chances of corruption in oracle?
2)Shouid i have to shutdown and the startup oracle?

3)As you wrote a book for PROGRAMER'S already,do you have a plan for a book for
ADMINISTRATORS???

Thanks.
atul

      and we said...
1) There is always the chance that the disks will become corrupt in same way
when you have a hardware failure like that.  The data on the disks (through no
fault of ours) could become scrambled -- sure.  That's why unix fsck's and
windows does a check when it was shutdown "abnormally".  That is why you run
production systems in ARCHIVELOG mode and take backups.  We can easily recover
from this using your last backup.

Generally - all should be OK and the db should come up without any issues.  It
is not assured if you just power down.  You might suffer from a media failure at
that point -- necessitating recovery.  Rare, but it can happen.

2) you should just have to startup, you might have to recover, you might have to
recover from backups.

3) My book goes across the boundary.  100% of what is in there is not only
useful to the DBA but stuff they need to know as well.  No, I do not intend to
write a DBA focused book -- that is not where my strenghts or interests lie.
Review &
      Followup

      Rating: 5
When is
      instance recovery initiated  August 29, 2002

Reviewer:  steve  from montreal
Hi Tom,

We had a similar problem occur at a customer site.
Following a power failure, all hell broke loose
(according to 2'nd line support) on the Unix server.
Veritas load was huge because of mirror rebuilding.
Again, according to 2'nd line, Oracle instance
recvery took 4 hours!! This is just for instance
recovery, not media recovery. This sounds absurd.

Now correct me if I'm wrong, but wouldn't the time
it takes to perform an instance recovery be:

  1. The time it takes to get the datafiles into a
     consistent state.

  2. The time to rollback uncommitted transactions

Now, Step 1. would take AT MOST the time it takes
to process the online redo log files - right?
More specifically, it would only take as long as
required to apply the change vectors in the
redo log files since the  last completed checkpoint?

Step 2. is perhaps the one that took a long time??
But 4 hours just doesn't seem to make any sense.

Maybe you can shed some light (as always) on what may
be going on.

Thanks Tom.
Steve

          Followup:

It is neither 1 nor 2.

Now, if veritas was going nuts re-silvering mirrors - think about it. Veritas is
the thing that provides us IO.  If it is somewhat pre-occupied thinking about
this "re-silver these guys" problem -- do you think IO is going to be a huge
bottleneck?

If you are using Oracle EE (enterprise) we will roll forward (eg: apply the redo
log files) and then open the database --- now during this time, svrmgrl (or
sqlplus, whatever you are using) will still be "active" but the database is
accessible before the prompt in those programs returns.  We only need to roll
forward from the logs (which by the way, Veritas needs to give us and we need to
write to datafiles -- if they are hugely busy re-silvering - well......)

So, it could be that you didn't realize the database was ready to go (i remember
the first time my DBA came in about 1 hour after a power outage saying "so
sorry, I don't know whats wrong, the database is taking really long to come up"
and I said -- but Fred, it's been up for over 55 minutes!  He was sitting in
svrmgrl, I was just logged in using it).

Or it could be that veritas was totally overwhelmed.

Or it could be that you have HUGE log files and it took 4 hours to roll forward.

August 29, 2002 OP
      -- Thanks for the question regarding "Ascii to Hex Conversion !", version
      8.1.6.0

      You Asked
Hi Tom,

How are you !

Thanks for your guidance and support from time to
time.

Today my Question is

Is there any function available in ORACLE which can be
used to convert an ascii value of a character to it's
equivalent HEXADECIMAL value and vice versa.

Thanks and Regards
OP Lalwani

      and we said...
  1* select rawtohex( utl_raw.cast_to_raw( 'hello world' ) ) from dual
ops$tkyte@ORA8I.WORLD> /

RAWTOHEX(UTL_RAW.CAST_TO_RAW('HELLOWORLD'))
---------------------------------------------------------------------
68656C6C6F20776F726C64

UTL_RAW might not be installed on your system -- it comes with
Oracle7.1.6 and up.  To install if you do not have it:

o cd $ORACLE_HOME/rdbms/admin
o connect as SYS or INTERNAL only using SVRMGRL
o @utlraw
o @prvtrawb.plb

Review &
      Followup

      Rating: 4
how do i
      reverse it?  August 29, 2002
Reviewer:
      A reader
How do i get 'hello world' back from a raw?

          Followup:

utl_raw.cast_to_varchar2

      Rating:
      4
  August 29, 2002
Reviewer:
      A reader
Continuing from the above question
Here is the sequence...i dont know what data is stored in accounttable in the
emp_id column by my 3rd party.
The column is RAW(8). I need to see this in sqlplus and also display it on web.

SQL>select utl_raw.cast_to_varchar2(emp_id) from accounttable where rownum<3;

UTL_RAW.CAST_TO_VARCHAR2(EMP_ID)
--------------------------------------------------------------------------------
&#9786;     ©Y
&#9787;     ©Y

SQL>select emp_id id from accounttable where rownum<3;

ID
----------------
0100000000001B59
0200000000001B59

SQL>select rawtohex(emp_id) from accounttable where rownum<3;

RAWTOHEX(EMP_ID)
----------------
0100000000001B59
0200000000001B59

SQL>select to_number(emp_id,'xxxxxxxxxxxxxxxxxxxxxxxxxx') id from accounttable
where rownum<3;

        ID
----------
7.2058E+16
1.4412E+17

SQL>column id format 999999999999999999999999999999999
SQL>/

                                ID
----------------------------------
                 72057594037934937
                144115188075862873

SQL>select chr(to_number(emp_id,'xxxxxxxxxxxxxxxxxxxxxxxxxx')) id from
accounttable where rownum<3;

ID
--

SQL>
SQL>

Does this mean that those symbols are the actual data?
They are actually proper numbers that were supposed to be stored in the emp_id
column. How do i see them?

          Followup:

The only thing you can look at is the rawtohex.

I have NO IDEA what they are putting in there -- only they do.  If you can get
them to tell you what is in there, I can tell you how to "see it"

      Rating:
      4
  August 29, 2002
Reviewer:
      A reader
Im sorry...
continuing from teh above question..
i got a dump of that data from the 3rd party,,and i imported it into my
database.(same tables as theirs).Dont know if this is of any relevance.
Also the symbols that is displayed above is not actually what i copied into the
'Enter your review' text box..of this site...dont know how it changed after i
submitted the review. Its a round face....and spaces and a left arrow mark and a
'Y'.

      Rating:
      5
  August 30, 2002
Reviewer:
      A reader

August 30, 2002 -- Thanks for the question regarding "page 732
      strong vs weak ref cursors.", version 8.1.6

      You Asked
tom:

i am reading your book page 732.

i don't understand what is strongly typed REF CURSOR and weakly
typed REF CURSOR.

can you explain more about that?

Thanks

      and we said...
Well, as I said on that page when describing a strongly typed ref cursor "That
is, a ref cursor whose structure is known by the compiler at compile time".

scott@ORA717DEV.US.ORACLE.COM> create or replace package my_pkg
  2  as
  3          type strong is ref cursor return emp%rowtype;
  4
  5          type weak is ref cursor;
  6  end;
  7  /

Package created.

STRONG's structure -- the columns, their datatypes, their lengths, everything --
are well known at compile time.  It can only be used to return rows that look
exactly like the EMP table (doesn't have to be from the EMP table -- just must
be the same STRUCTURE)

WEAK's structure -- not known.  It could be used with:

  open a_weak_one for select * from dual;

or

  open a_weak_one for select * from dept;

Its structure is NOT known at compile time and may actually change from use to
use.
Review
      & Followup

      Rating:
      3
Performance  October 17, 2001
Reviewer:
      Senthil.
Would weakly defined cursor affects performance anyway...compared to strongly
defined.

          Followup:

good question, lets see:

ops$tkyte@ORA717DEV.US.ORACLE.COM> create table run_stats ( runid varchar2(15),
name varchar2(80), value int );
Table created.

ops$tkyte@ORA717DEV.US.ORACLE.COM> create or replace view stats
  2  as select 'STAT...' || a.name name, b.value
  3        from v$statname a, v$mystat b
  4       where a.statistic# = b.statistic#
  5      union all
  6      select 'LATCH.' || name,  gets
  7        from v$latch;
View created.

ops$tkyte@ORA717DEV.US.ORACLE.COM> declare
  2          type strong is ref cursor return emp%rowtype;
  3          type weak   is ref cursor;
  4
  5      l_x     number := 0;
  6      l_start number;
  7          l_strong strong;
  8          l_weak   weak;
  9          l_rec    emp%rowtype;
 10  begin
 11      insert into run_stats select 'before', stats.* from stats;
 12
 13      l_start := dbms_utility.get_time;
 14      for i in 1 .. 1000
 15      loop
 16                  open l_strong for select * from emp;
 17                  loop
 18                          fetch l_strong into l_rec;
 19                          exit when l_strong%notfound;
 20                  end loop;
 21                  close l_strong;
 22      end loop;
 23      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 24
 25      insert into run_stats select 'after 1', stats.* from stats;
 26
 27      l_start := dbms_utility.get_time;
 28      for i in 1 .. 1000
 29      loop
 30                  open l_weak for select * from emp;
 31                  loop
 32                          fetch l_weak into l_rec;
 33                          exit when l_weak%notfound;
 34                  end loop;
 35                  close l_weak;
 36      end loop;
 37          commit;
 38      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 39
 40      insert into run_stats select 'after 2', stats.* from stats;
 41  end;
 42  /
162 hsecs
174 hsecs

PL/SQL procedure successfully completed.

ops$tkyte@ORA717DEV.US.ORACLE.COM> select a.name, b.value-a.value run1,
c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11     and a.name like '%redo%'
 12   order by abs( (c.value-b.value)-(b.value-a.value))
 13  /

NAME                                           RUN1       RUN2       DIFF
---------------------------------------- ---------- ---------- ----------
LATCH.redo writing                                3          2         -1
STAT...redo entries                              16         14         -2
LATCH.redo allocation                            13         25         12
STAT...redo size                              21488      21520         32

So, nothing significant really.  Maybe a 7% decrease in runtime over 1,000
iterations.  No difference in resource usage...

      Rating: 4
page 732 strong
      vs weak ref cursors  August 30, 2002
Reviewer:
      Paul  from UK
Tom,

Thanks a lot for the benchmarking - saved me the trouble!
If there's no/negligible performance benefits in strong vs weak, why bother with
strong at all?

I'm lazy, if I can get away with writing less code, I will!

          Followup:

Strongly typed ones can be "described" -- it is well known what the procedure
would be returning -- some tools will work better with it (eg: layout/GUI toosl
-- they can tell how many columns/types there are).

ops$tkyte@ORA920.US.ORACLE.COM> create or replace package my_pkg
  2  as
  3     type rc1 is ref cursor return emp%rowtype;
  4     type rc2 is ref cursor;
  5
  6     procedure p1( x in out rc1 );
  7     procedure p2( x in out rc2 );
  8     procedure p3( x in out sys_refcursor );  /* New in 9i */
  9  end;
 10  /

Package created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> desc my_pkg
PROCEDURE P1
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 X                              REF CURSOR              IN/OUT
                                RECORD                  IN/OUT
     EMPNO                      NUMBER(4)               IN/OUT
     ENAME                      VARCHAR2(10)            IN/OUT
     JOB                        VARCHAR2(9)             IN/OUT
     MGR                        NUMBER(4)               IN/OUT
     HIREDATE                   DATE                    IN/OUT
     SAL                        NUMBER(7,2)             IN/OUT
     COMM                       NUMBER(7,2)             IN/OUT
     DEPTNO                     NUMBER(2)               IN/OUT
PROCEDURE P2
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 X                              REF CURSOR              IN/OUT
PROCEDURE P3
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 X                              REF CURSOR              IN/OUT

ops$tkyte@ORA920.US.ORACLE.COM>

see, only P1 can TELL us beforehand what'll be returning -- easier for a tool
like forms or powerbuilder to layout a screen to hold the results

      Rating:
      5
  May 22, 2003
Reviewer:
      A reader

August 30, 2002 Andre -- Thanks for the question regarding "Index
      on partition table ", version 816

      You Asked
Hi.
If a have to create partitioned table by range on a column named (COL1  DATE).
The partitioned table was created from a non partitioned table with a non unique
index
on COL1 column.

On the partitioned table , need I create that non unique index on the COL1
column ?
Because, if the partition is ordered by the column specified on the RANGE, there
is
no meaning in create the index . Dont you agree ?

TIA,
André.

      and we said...
No, I disagree -- maybe.  depends on how you use the data.

lets say each partition has 100,000 rows in it and the number of distinct dates
in each partition is 50,000 rows.

do you query:

select * from T where col1 = :x

if so, an index on col1 would be called for.  I'm going to get 2 rows.  I could
either FULL SCAN a partition with 100k rows or I could do an index read to get
the rows.

Now, lets say the partition has 100k rows and there are 10 unique dates in each
partition.  Now, an index that might have made sense PRE-PARTITITIONING does not
make sense any more.  Better to full scan that partition, rather then index
read.

So, it really depends.
Review & Followup

      Rating: 3
Index on a
      specific partition of a table.  August 27, 2002

Reviewer:  Srirama Kiran Kumar  from India

Dear Tom and folks ,

There is a table called as Hours_Archive which holds 5 years of archived data.
It is range partitioned on a column called Archive_Year
(1998,1999,2000,2001,2002).
Data belonging to Archive_Year=2002 is the most used.
Is it possible to create index on Archive_Year only for this partition only ?

Regards
Kiran

          Followup:

sort of.  but not really.  i wouldn't recommend it at all.

you would create an "unusable" index so that the 5 partitions have 5 unusable
index partitions, then rebuild the one you want.  The other 4 would be there and
if any query plan decided that it would be NICE to use the index on them -- that
query would fail since the indexes are unusable (unless you set
skip_unusable_indexes=true).

So, this is one of those things that whilst technically in the realm of
possibility -- isn't really feasible.

      Rating: 3
Work around
      needed please.....  August 29, 2002
Reviewer:
      kiran  from india
Hi Tom,
Thanks for responding. The table which I am referring to has around 100 million
rows of data.
Each partition has 10 million rows. Space wise also each partition takes around
1 GB of space.
So you could imagine how big the index could be if I were to create it on the
whole table (i.e all the partitions). This is the reason why we want to create
index only on the most queried partition.
I hope you understand that we want to save harddisk space. Please suggest a work
around for the same.

Best Regards
Kiran

          Followup:

10,000,000 rows in a gig.  Not bad.

The index would be trivial.  Go for it.

You know, it costs about $250 USD for a 36gig SCSI drive.  You probably expended
more money thinking about this then it would have cost to buy a drive bigger
then you actually need!

      Rating: 3
More
      discussion..  August 30, 2002
Reviewer:
      kiran  from india
Hi Tom,

I gave these rough figures just to emphasize on the size which could be saved if
such a concept (ie. creation of an index on one partition of a table) exists.
I would like to hear from you more on why such a feature
is not / cannot be implemented in a database management system like Oracle.
Time/Disk space statistics/cost is not eactly what i was looking for.

Thanks
Kiran

          Followup:

A partitioned table is a physical thing.  Logically there is but one table.

A table either has an index -- or it does not.

Hey, now that I think about it -- you can in fact do this in a supported way
using a function based index.  Consider:

ops$tkyte@ORA920.US.ORACLE.COM> CREATE TABLE t
  2  (
  3    x int primary key,
  4    collection_year int
  5  )
  6  PARTITION BY RANGE (COLLECTION_YEAR) (
  7    PARTITION PART_95 VALUES LESS THAN (1996) ,
  8    PARTITION PART_96 VALUES LESS THAN (1997) ,
  9    PARTITION PART_97 VALUES LESS THAN (1998) ,
 10    PARTITION PART_98 VALUES LESS THAN (1999) ,
 11    PARTITION PART_99 VALUES LESS THAN (2000) ,
 12    PARTITION PART_00 VALUES LESS THAN (2001) ,
 13    PARTITION PART_01 VALUES LESS THAN (MAXVALUE)
 14  )
 15  ;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into t
  2  select object_id, 1995+mod(rownum,7) from all_objects;

29262 rows created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into t
  2  select -object_id, 1999 from all_objects where rownum <= 1000;

1000 rows created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create index t_idx on t( decode(
collection_year, 1999, x, NULL ) );

Index created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select count(*) from t partition(part_95);

  COUNT(*)
----------
      4180

ops$tkyte@ORA920.US.ORACLE.COM> select count(*) from t partition(part_96);

  COUNT(*)
----------
      4181

ops$tkyte@ORA920.US.ORACLE.COM> select count(*) from t partition(part_97);

  COUNT(*)
----------
      4181

ops$tkyte@ORA920.US.ORACLE.COM> select count(*) from t partition(part_98);

  COUNT(*)
----------
      4180

ops$tkyte@ORA920.US.ORACLE.COM> select count(*) from t partition(part_99);

  COUNT(*)
----------
      5180

ops$tkyte@ORA920.US.ORACLE.COM> select count(*) from t partition(part_00);

  COUNT(*)
----------
      4180

ops$tkyte@ORA920.US.ORACLE.COM> select count(*) from t partition(part_01);

  COUNT(*)
----------
      4180

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> analyze index t_idx validate structure;

Index analyzed.

ops$tkyte@ORA920.US.ORACLE.COM> select lf_rows from index_stats;

   LF_ROWS
----------
      5180

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create or replace view v
  2  as
  3  select t.*, decode( collection_year, 1999, x, NULL ) x_indexed
  4    from t
  5  /

View created.

ops$tkyte@ORA920.US.ORACLE.COM>

See how the index only has data from 1999 in it -- you would use the view to
hide the "complexity" from the end user (and to make it so you can change 1999
to 2000 and 2001 and so on pretty much at will -- as the years change. So, when
you drop and recreate the index with a different decode year, you just fix the
view as well)

      Rating: 4
Useful Work
      around  September 04, 2002
Reviewer:  kiran
       from India
Hi Tom

Pretty interesting work around suggested by you.
Will get back after some more thought.

Regards
Kiran

August 30, 2002 Rennie -- Thanks for the question regarding
      "General Stuff", version 8.1.7.0.0

      You Asked
Tom,
   I have a couple of questions here.

1. If we do not use bind variables in the query would there be any change in the
execution path chosen by the optimizer? This is because the value of the bind
variable will not be known until runtime whereas if we have the value in the
query would it make a difference? I agree that bind variables should be used to
avoid hard parse. But...just curious. Mine is a datawarehousing application and
thus there will not be too many users connected at a time.

2. Iam quoting a particular point from Oracle 8i Designing and Tuning for
Performance (pdf), section "Chosing execution paths for join statements". In
that the author says
"For Join statements with outer join conditions, the table with the outer join
operator must come after the other table in the condition in the join order. the
optimizer does not consider join orders that violate this rule".
He also adds that it applies to both CBO and RBO. Can you please provide more
clarity to this point. I could not see any difference in execution path no
matter what the join order is. Can you please provide some examples.

3. Is there a difference between using 'NOT IN' vs != (No equal to) if there is
only one value? for eg:- SELECT col FROM table WHERE col2 NOT IN ('ABC');

regards
rennie

      and we said...
1) in a data warehouse, bind variables are not as important.  Read through:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1163635055580
bind variables are mandatory in mixed use/ OLTP systems.  In a DW, they are
optional and your plans in general will be better with out them.

2) They are refering to the execution path that will be GENERATED.  The join
order in the execution path we generate is dictated to us when you use an outer
join.

It doesn't have anything to do with how you phrase the query (put the "join
conditions").

3) It would be the same.
Review & Followup

      Rating: 5
about point
      2  August 30, 2002
Reviewer:  A reader

is that saying (I cant understand very well what does that paragraph mean) that
the excution plan will be different in following cases?

select ename, dname
from emp a, dept b
where a.deptno(+) = b.deptno

is different from

select ename, dname
from emp a, dept b
where b.deptno = a.deptno(+)

or different from

select ename, dname
from dept b, emp a
where a.deptno(+) = b.deptno

or different

select ename, dname
from dept b, emp a
where b.deptno = a.deptno(+)

          Followup:

It is saying that the execution plan order of table access is pre-ordained by
the fact you are outer joining.

The actual syntax of the query is not relevant -- a = b,   b = a --  from T1, t2
vs from T2, t1 matters not.  It is the fact that you are outer joining that
tells us "the tables shall be accessed in this order and this order only"

August 31, 2002 Luis -- Thanks for the question regarding "Error
      Running oracle\ora90\demo\schema\order_entry\oe_mail.sql", version
      9.0.1.0.1

      You Asked
I'm reading the "Beginning Oracle Programming" book and am trying to create the
sample schemas (Appendix D, page 1031).  hr_main.sql ran fine, but oe_main will
not and finally disconnects after a series of error messages.  Below is a brief
sample of my session.  Any suggestions would be appreciated.  I'm also pasting
in the oe_main.sql file for your reference.  As a last resort, I'm going to try
typing oe_main in line by line.  Thanks.

SQL> @d:\oracle\ora90\demo\schema\order_entry\oe_main.sql

specify password for OE as parameter 1:
Enter value for 1: order

specify default tablespeace for OE as parameter 2:
Enter value for 2: users

specify temporary tablespace for OE as parameter 3:
Enter value for 3: temp

specify password for HR as parameter 4:
Enter value for 4: human

specify password for SYS as parameter 5:
Enter value for 5: popz71 as sysdba

specify path for log files as parameter 6:
Enter value for 6: d:\spools

DROP USER oe CASCADE
          *
ERROR at line 1:
ORA-01918: user 'OE' does not exist

old   1: CREATE USER oe IDENTIFIED BY &pass
new   1: CREATE USER oe IDENTIFIED BY order
CREATE USER oe IDENTIFIED BY order
                             *
ERROR at line 1:
ORA-00988: missing or invalid password(s)

old   1: ALTER USER oe DEFAULT TABLESPACE &tbs QUOTA UNLIMITED ON &tbs
new   1: ALTER USER oe DEFAULT TABLESPACE users QUOTA UNLIMITED ON users
ALTER USER oe DEFAULT TABLESPACE users QUOTA UNLIMITED ON users
           *
ERROR at line 1:
ORA-01918: user 'OE' does not exist

old   1: ALTER USER oe TEMPORARY TABLESPACE &ttbs
new   1: ALTER USER oe TEMPORARY TABLESPACE temp
ALTER USER oe TEMPORARY TABLESPACE temp
           *
ERROR at line 1:
ORA-01918: user 'OE' does not exist

TO    oe
      *
ERROR at line 10:
ORA-01917: user or role 'OE' does not exist

Connected.
GRANT execute ON sys.dbms_stats TO oe
                                   *
ERROR at line 1:
ORA-01917: user or role 'OE' does not exist

Connected.
GRANT REFERENCES, SELECT ON employees TO oe
                                         *
ERROR at line 1:
ORA-01917: user or role 'OE' does not exist

GRANT REFERENCES, SELECT ON countries TO oe
                                         *
ERROR at line 1:
ORA-01917: user or role 'OE' does not exist

GRANT REFERENCES, SELECT ON locations TO oe
                                         *
ERROR at line 1:
ORA-01917: user or role 'OE' does not exist

rem
rem Header: oe_main.sql 09-jan-01
rem
rem Copyright (c) 2001, Oracle Corporation.  All rights reserved.
rem
rem Owner  : ahunold
rem
rem NAME
rem   oe_main.sql - Main script for OE schema, including OC subschema
rem
rem DESCRIPTON
rem   Creates and populated the Order Entry (OE) and Online
rem   Catalog (OC) Sample Schema
rem
rem NOTES
rem   Run as SYS or SYSTEM
rem   Prerequisites:
rem     Tablespaces present
rem     Database enabled for Spatial and XML
rem
rem MODIFIED   (MM/DD/YY)
rem   ahunold   07/13/01 - NLS Territory.
rem   ahunold   04/13/01 - spool, additional parameter
rem   ahunold   03/29/01 - spool
rem   ahunold   03/12/01 - prompts
rem   ahunold   03/02/01 - NLS_LANGUAGE
rem   ahunold   01/09/01 - checkin ADE

SET ECHO OFF

PROMPT
PROMPT specify password for OE as parameter 1:
DEFINE pass     = &1
PROMPT
PROMPT specify default tablespeace for OE as parameter 2:
DEFINE tbs      = &2
PROMPT
PROMPT specify temporary tablespace for OE as parameter 3:
DEFINE ttbs     = &3
PROMPT
PROMPT specify password for HR as parameter 4:
DEFINE passhr   = &4
PROMPT
PROMPT specify password for SYS as parameter 5:
DEFINE pass_sys = &5
PROMPT
PROMPT specify path for log files as parameter 6:
DEFINE log_path = &6
PROMPT

-- The first dot in the spool command below is
-- the SQL*Plus concatenation character

DEFINE spool_file = &log_path.oe_oc_main.log
SPOOL &spool_file

-- Dropping the user with all its objects

DROP USER oe CASCADE;

REM =======================================================
REM create user
REM
REM The user is assigned tablespaces and quota in separate
REM ALTER USER statements so that the CREATE USER statement
REM will succeed even if the demo and temp tablespaces do
REM not exist.
REM =======================================================

CREATE USER oe IDENTIFIED BY &pass;

ALTER USER oe DEFAULT TABLESPACE &tbs QUOTA UNLIMITED ON &tbs;

ALTER USER oe TEMPORARY TABLESPACE &ttbs;

GRANT CREATE SESSION
,     CREATE TABLE
,     CREATE VIEW
,     CREATE SYNONYM
,     CREATE TYPE
,     CREATE MATERIALIZED VIEW
,     CREATE TRIGGER
,     CREATE SEQUENCE
,     QUERY REWRITE
TO    oe;

REM =======================================================
REM grants from sys schema
REM =======================================================

CONNECT sys/&pass_sys AS SYSDBA;
GRANT execute ON sys.dbms_stats TO oe;

REM =======================================================
REM grants from hr schema
REM =======================================================

CONNECT hr/&passhr;
GRANT REFERENCES, SELECT ON employees TO oe;
GRANT REFERENCES, SELECT ON countries TO oe;
GRANT REFERENCES, SELECT ON locations TO oe;
GRANT SELECT ON jobs TO oe;
GRANT SELECT ON job_history TO oe;
GRANT SELECT ON departments TO oe;

REM =======================================================
REM create oe schema (order entry)
REM =======================================================

CONNECT oe/&pass
ALTER SESSION SET NLS_LANGUAGE=American;
ALTER SESSION SET NLS_TERRITORY=America;
@?/demo/schema/order_entry/oe_cre
@?/demo/schema/order_entry/oe_p_pi
@?/demo/schema/order_entry/oe_p_pd
@?/demo/schema/order_entry/oe_p_whs
@?/demo/schema/order_entry/oe_p_cus
@?/demo/schema/order_entry/oe_p_ord
@?/demo/schema/order_entry/oe_p_itm
@?/demo/schema/order_entry/oe_p_inv
@?/demo/schema/order_entry/oe_views
@?/demo/schema/order_entry/oe_comnt
@?/demo/schema/order_entry/oe_idx
@?/demo/schema/order_entry/oe_analz

@?/demo/schema/order_entry/oc_main

spool off

      and we said...
select error_message from above_text ORDER by something;

;)

old   1: CREATE USER oe IDENTIFIED BY &pass
new   1: CREATE USER oe IDENTIFIED BY order
CREATE USER oe IDENTIFIED BY order
                             *
ERROR at line 1:
ORA-00988: missing or invalid password(s)

ORDER would be an invalid password as it is one of those reserved words.
Review &
      Followup

      Rating: 5
No Keywords as
      Passwords  August 31, 2002
Reviewer:  Luis
       from Lafayette, CA
I sent this question in.  Thank you very much for your help.  The "Beginning
Oracle Programming" book is great.

August 31, 2002 Prince -- Thanks for the question regarding
      "Index creation with nologging and recovery", version 8.1.7.4

      You Asked
I take the backup os the tablespace TSIX at T0.

I create an index with NOlogging at time T1 on TSIX.

Alter the index for logging at T2

Say at T3, I have a failure and I want to recover TSIX

Assume, I did not take the TSIX backup after the index creation.

Now, Can I be able to recover the TSIX as of T3? Can I drop the index, while
doing the recovery?

In summary, how do I recover TSIX to Point T3, from the backup which was taken
at T0?

Thanks,

      and we said...
You cannot recover the index in TSIX as your backup predates its existence and
you have no redo log with which to recreate it.

You'll restore TSIX, recover it -- but the index will be invalid, you'll drop
and recreate the index.

Review &
      Followup

      Rating: 5
Thanks
      Tom!  August 31, 2002
Reviewer:  Prince.

My main quetion was, Can I recover the TSIX or would it error out when it comes
to the point of recovering the index and fails to recover the TSIX further.

Anyway, your answer suggests that I can recover the TSIX but not the index,
which is absoluttlely fine.

          Followup:

That is correct.

August 31, 2002 Sumanta -- Thanks for the question regarding "DB
      link not working", version 8.1.7

      You Asked
We have two Oracle(8.1.7) database server resides in two location. And in both
end Oracle Standered edition is installed.Now we created dblink in both side but
none of it working . Can U pls. tell me where standered edition of oracle
support dblink or not ? If yes pls. mentioned the steps.

Thanks lot.

regards
sumanta

      and we said...
Yes it does.

You give NO information, nothing, nada, zippo.

No command used.
No setup done.
Not even the error message.

geez.

make sure you setup the tnsnames.ora file on the SERVER (not one your client,
must be on the SERVER).

make sure on the server, you can "sqlplus scott/tiger@another_site" using
SERVERS tnsnames.ora file.

then just create database link foo connect to scott identified by tiger using
'another_site';

And if it fails, open a new question and supply some meaningful answer.  My
crystal ball is in the shop this week -- my ability to see your error message
and code without you telling me is somewhat limited without it.

Just a heads up to the other readers.  Supplying information in the question
is crucial.  If you find yourself typing:

"it does not work"
"it was not successful"
"we did all of the steps we were supposed to"

and not much else, I won't be able to answer at all.  "it does not work" or "it
was not successful" must be followed by "and this is what we did .... and this
is the error code we got .... and best of all, here is a cut and paste from
sqlplus just like you use to show us how things happen: ...."

If you say "we did all of the steps...." include those steps.  I haven't any
idea if you did anything close to what you were supposed to!

thanks
August 31, 2002 Prince -- Thanks for the question regarding
      "Hierarchical Query! Stoping at a particular value ", version 8.1.7.4

      You Asked
Tom, this is the query, I posted at google forum. (haven't got any resp there)

http://groups.google.com/groups?q=%22Hierarchical+Query!+Stoping+%22+group:comp.databases.oracle.server&hl=en&lr=&ie=UTF-8&selm=629275ba.0208282045.48b6ba2b%40posting.google.com&rnum=1
I want to get the list of all values starting from the child to the
parent until a certain condition is met.

How do I write a efficient SQL for this?

Ex.
------

create table t (childkey number(80), parentkey number(8), crit char(1));
--crit will be 'T' or 'F' or null.

insert into t values (1,null,null);
insert into t values (4,3,'T');
insert into t values (7,4,'F');
insert into t values (8,7,'T');
insert into t values (11,8,null);
insert into t values (17,11,null);
insert into t values (16,null,null);
insert into t values (14,4,null);
insert into t values (19,14,null);

I want to start at a particular "childKey" and find all its parent till the
first "Crit" = 'T'. if "Crit" is null keep on finding the parent until
the root level is met.

Ex,

if I start with Key = 9, I should get
---------------------------------------
19   14 null   --> no more parents

if I start with Key = 17, I should get
---------------------------------------
17    11    null
11     8    null
8      7    T  --> Crit "T" is met (do not go further)

if I start with Key = 4, I should get
---------------------------------------
4     3    T  --> Crit "T" is met

I worked out the following two ways. Is there any other better and stright
forward way to do this?

Method-1:
---------
select * from t1 where level <=
 (
 select min(level) lvl
 from t1
 where crit = 'T'
 start with childkey = ? --17
 connect by prior parentkey  = childkey
 )
start with childkey = ? --17
connect by prior parentkey  = childkey
union all
select * from t1
where not exists (
  select null from t1
  where crit = 'T'
  start with childkey = ? --17
  connect by prior parentkey  = childkey
  )
and ( crit = 'F' or crit is null)
start with childkey = ? --17
connect by prior parentkey  = childkey

Method-2:
---------
select * from t1 where level <=
 (
 select nvl(min(level),9.999E+125) lvl
 from t1
 where crit = 'T'
 start with childkey = ?? --17
 connect by prior parentkey  = childkey
 )
start with childkey = ? --17
connect by prior parentkey  = childkey

Thanks,

      and we said...
Your comment about 9/19 confuses me.  There is no key = 9, so I assume you meant
19, but 19 doesn't stop there cauuse 19 -> 14, 14 -> 4, 4 -> 3 and 3 has 'T' so
*STOP*....

In any case, this is as easy as this:

ops$tkyte@ORA920.US.ORACLE.COM> select *
  2    from t
  3   start with childkey = 9
  4  connect by ( prior parentkey = childkey and prior nvl(crit,'x') <> 'T' )
  5  /

no rows selected

ops$tkyte@ORA920.US.ORACLE.COM> select *
  2    from t
  3   start with childkey = 19
  4  connect by ( prior parentkey = childkey and prior nvl(crit,'x') <> 'T' )
  5  /

  CHILDKEY  PARENTKEY C
---------- ---------- -
        19         14
        14          4
         4          3 T

ops$tkyte@ORA920.US.ORACLE.COM> select *
  2    from t
  3   start with childkey = 17
  4  connect by ( prior parentkey = childkey and prior nvl(crit,'x') <> 'T' )
  5  /

  CHILDKEY  PARENTKEY C
---------- ---------- -
        17         11
        11          8
         8          7 T

ops$tkyte@ORA920.US.ORACLE.COM> select *
  2    from t
  3   start with childkey = 4
  4  connect by ( prior parentkey = childkey and prior nvl(crit,'x') <> 'T' )
  5  /

  CHILDKEY  PARENTKEY C
---------- ---------- -
         4          3 T

ops$tkyte@ORA920.US.ORACLE.COM>

Just include the constraint on crit in the connect by and the expansion of the
tree "stops".

Review &
      Followup

      Rating: 5
wow!
      Excellent  August 31, 2002
Reviewer:  Prince

I am sorry, when I cut/paste and massage the data, I got it messed.

its so sipme and perfect.

I tried (prior parentkey = childkey and prior crit ='T') earlier, which was
totaly wrong. Your query showed the perfect way.

Thanks

September 01, 2002 Mohammed -- Thanks for the question regarding
      "Tunning an Update statment", version 8.1.7

      You Asked
Hi Tom :
        I am trying to update table by using the following SQL ,but it takes
long time for execution . could u please help us how to Tunne this Sql .

Note : do u think if I use Cursor it will be better than the following SQl

HLR table contains  million of records ,and  NORMALIZED_DUMP  also about 800,000
thousand of record
UPDATE NORMALIZED_DUMP
            SET NORMALIZED_DUMP.BAOC =(select
SUBSTR(HLR.FIELD2,-(LENGTH(HLR.FIELD2)-5),LENGTH(HLR.FIELD2)-5)
                                         from hlr
                                       WHERE
                                        SUBSTR(HLR.FIELD1,1,14) =
'<MODMSUB:MSIN='
                                        AND SUBSTR(HLR.FIELD1,-10,10) =
NORMALIZED_DUMP.IMSI
                                        AND SUBSTR(HLR.FIELD2,1,4)  = 'BAOC' );

with my best regards ,

      and we said...
Is there an index on field1, field2?  If so, something like:

update normalized_dump
   set NORMALIZED_DUMP.BAOC =
   (select SUBSTR(HLR.FIELD2,-(LENGTH(HLR.FIELD2)-5),LENGTH(HLR.FIELD2)-5)
      from hlr
     WHERE HLR.FIELD1 like '<MODMSUB:MSIN=%'
       AND SUBSTR(HLR.FIELD1,-10,10) = NORMALIZED_DUMP.IMSI
       AND HLR.FIELD2 like 'BAOC%' );

will permit the use of the concatenated index on field1, field2 -- whereas your
approach would not.

Alternatively, an index such as:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index fbi on
  2  hlr(SUBSTR(FIELD1,1,14),
  3      SUBSTR(FIELD1,-10,10),
  4      SUBSTR(FIELD2,1,4),
  5      field2);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_stats.set_table_stats( user,
'NORMALIZED_DUMP', numrows => 800000);

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_stats.set_table_stats( user, 'HLR',
numrows => 1000000);

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set
query_rewrite_integrity=trusted;

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set query_rewrite_enabled=true;
Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace on explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> UPDATE NORMALIZED_DUMP
  2     SET NORMALIZED_DUMP.BAOC =
  3       (select
SUBSTR(HLR.FIELD2,-(LENGTH(HLR.FIELD2)-5),LENGTH(HLR.FIELD2)-5)
  4          from hlr
  5         WHERE SUBSTR(HLR.FIELD1,1,14) = '<MODMSUB:MSIN='
  6           AND SUBSTR(HLR.FIELD1,-10,10) = NORMALIZED_DUMP.IMSI
  7           AND SUBSTR(HLR.FIELD2,1,4)  = 'BAOC'
  8  );

0 rows updated.

Execution Plan
----------------------------------------------------------
   0      UPDATE STATEMENT Optimizer=CHOOSE (Cost=3 Card=800000 Bytes=22400000)
   1    0   UPDATE OF 'NORMALIZED_DUMP'
   2    1     TABLE ACCESS (FULL) OF 'NORMALIZED_DUMP' (Cost=3 Card=800000
Bytes=22400000)
   3    0   INDEX (RANGE SCAN) OF 'FBI' (NON-UNIQUE) (Cost=2 Card=2 Bytes=56)

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

Might help quite a bit (just drop the index on HLR when you are done with it,
you could create it UNRECOVERABLE and in parallel to speed it up as well)

Review &
      Followup

      Rating: 3
A doubt here on
      the SUBSTR part  August 31, 2002
Reviewer:  A
      reader  from Bangalore
Tom, my understanding was that only when using the RBO should you avoid
functions on indexed columns. I thought the CBO will use the index even if there
is a function on the column.

          Followup:

Only if the index itself is a function based index which is the second
alternative I demonstrated:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index fbi on
  2  hlr(SUBSTR(FIELD1,1,14),
  3      SUBSTR(FIELD1,-10,10),
  4      SUBSTR(FIELD2,1,4),
  5      field2);

Index created.

Neither the RBO or CBO can use an index on (FIELD1) if you apply a function to
FIELD1 -- eg:

  where substr( field1, 2, 3 ) = 'foo'

CANNOT use an index on FIELD1.  It can use an index on substr(field1,2,3)
however (the CBO can)

September 01, 2002 Nagaraj -- Thanks for the question regarding
      "between for varchar2 columns", version 9.0.1.1.1

      You Asked
Hi Tom,

       When i use BETWEEN operator for numbers  for example

select * from emp where sal between 800 and 1600;

  EMPNO|ENAME     |JOB      |       MGR|HIREDATE |       SAL|
_______|__________|_________|__________|_________|__________|_
   7369|SMITH     |CLERK    |      7902|17-DEC-80|       800|
   7499|ALLEN     |SALESMAN |      7698|20-FEB-81|      1600|
   7521|WARD      |SALESMAN |      7698|22-FEB-81|      1250|
   7654|MARTIN    |SALESMAN |      7698|28-SEP-81|      1250|
   7844|TURNER    |SALESMAN |      7698|08-SEP-81|      1500|
   7876|ADAMS     |CLERK    |      7788|23-MAY-87|      1100|
   7900|JAMES     |CLERK    |      7698|03-DEC-81|       950|
   7934|MILLER    |CLERK    |      7782|23-JAN-82|      1300|

i get an output including 800 and 1600 but when i use between for varchar2
column say ENAME LIKE 'A%' AND 'C%'

i know that character comparisions are performed character by character,based on
the ASCII value equivalent of each character.

select * from emp where ename between 'A%' and 'C%';

   EMPNO|ENAME     |JOB      |       MGR|HIREDATE |
________|__________|_________|__________|_________|____
    7499|ALLEN     |SALESMAN |      7698|20-FEB-81|
    7698|BLAKE     |MANAGER  |      7839|01-MAY-81|
    7876|ADAMS     |CLERK    |      7788|23-MAY-87|

even though ename with C exists it is not retriving the row starting with C . If
i give between 'A%' and 'D%' i will get ename starting with C but not D. I don't
want to give D to get C.Can u suggest me a solution when i say 'A%' and 'C%' i
should get A,B and C also.

Thanks in advance

      and we said...
between 'A%' and 'C%' is NOT using like.  LIKE uses LIKE.  Between uses <= and
>=

try where substr(ename,1,1) between 'A' and 'C'

CL is greater then C%

ops$tkyte@ORA920.US.ORACLE.COM> select * from dual where 'CL' <= 'C%';

no rows selected

ops$tkyte@ORA920.US.ORACLE.COM> c/</>
  1* select * from dual where 'CL' >= 'C%'
ops$tkyte@ORA920.US.ORACLE.COM> /

D
-
X

ops$tkyte@ORA920.US.ORACLE.COM>

That is why you don't get CLERK.
Review &
      Followup

      Rating: 4
between for
      varchar2 columns  September 02, 2002
Reviewer:
      Nagaraj  from Banglore , INDIA
Thanks

September 02, 2002 H.Araghi -- Thanks for the question regarding
      "Previnting Illegal copies of a database", version 9.2

      You Asked
Tom,

We are developing an enterprise software that uses Oracle database. we are
currently using Oracle9i standard Edition.We use C++ and OCI for the most part
and some PL/SQL programs.
Our license fee is based on number of concurrent sessions on the production
database and it is controled by our application. Sometimes the production
database gets copied to another database for testing or training purposes. We
want to prevent them from doing this at least without our knowledge. In other
words, If a database is called PRODUCTION and is located on the server X, we
dont want a copy of this database to be operating on any server with the same or
different SID on the network. Our application should somehow be able to uniquely
identify the database it is connecting.
unfortunately, with an export/import everything is getting copied to the new
database incuding any table we would create for this purpose. My questions are:

1- Is there any identification number in the database that is unique for every
Oracle database and is not modifiable? I know that dbid column in v$database can
be the same on the new database if we use DUPLICATE command from RMAM. Besides,
Oracle has provided a new utility (in 9i 9.2.0) that you can use to change the
dbid.
2- Is there any supplied package that we can use to get some system information
such as CPU serial number or other inside the database? Is there a way to write
such a package?
3- Are you aware of better idea that you could kindly provide?

Thank You

      and we said...
1) no.  there is no unique id.

2) you can use java to get this probably.  You can definitely write a C based
external procedure to do it.

3) well, we do it like this at Oracle -- we trust our customers.  If we have
some serious reason to suspect an abuse of purchased licenses, we might do an
audit.

The only thing I've seen copy protection schemes do is

a) make people mad
b) prevent them from using the software when they should
Review &
      Followup

      Rating: 3
Securing a
      database  September 01, 2002
Reviewer:  David
      Sanchez  from Sydney, Australia
I think this is an administration problem. Access to your production database
should be secure by the securities facilities provided by Oracle and your
operating system. You need to develop polices that restrict who can export or
has access to your system. You should not provide unrestricted access to your
production system.

          Followup:

No, that is not it.

This person makes a product (like Peoplesoft or SAP do). This product runs on an
Oracle database.

They sell this product to their customers who then install, configure and run it
(just like peoplesoft, sap, etc)

They basically want to add copy protection to this enterprise piece of software
(you know, like Microsoft keeps trying to do).  They do not believe their
customers will obey the terms of their license agreement (eg: they will buy 5
licenses but then use it for 500 people or something).

September 02, 2002 Tony -- Thanks for the question regarding
      "Exclusive lock on a file", version 8.1.6

      You Asked
Hi Tom,
This is not a direct oracle question but relating to an application using
combination of unix scripts and oracle 8.1.6. I know you always try to help,
even pointing to a link will be helpful.

I'm using hp-ux 11.0. Is it possible to place an exclusive lock on the file used
by a process/session, as we place a exclusive lock on the table?
Thanks
Tony

      and we said...
You would use a java stored procedure or a C based external routine to do this,
so yes -- it is 100% possible.  But you would have to write the OS specific code
to do so.
September 02, 2002
      -- Thanks for the question regarding "How
      to find the detail oracle error message in the internet? ", version 8.1.7

      You Asked
what does the error "ORA-00205" mean?

      and we said...

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76999/toc.htm

and specifically:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76999/e0.htm#1001897

they are all in the documentation!
September 02, 2002 Jason -- Thanks for the question regarding "How
      Can I build Dictionary managed tablespace in Oracle9i R2", version 9.2.0.1

      You Asked
Because The SYSTEM tablespace is LOCAL managed tablespace.
How Can I build Dictionary managed tablespace in Oracle9i R2?

Can I update the sql.bsq file? how ?

or ORACLE  don't support  Dictionary managed tablespace in Oracle9i R2 any more
from now and later?

Best Rgds

      and we said...
We do support it, but SYSTEM would have to be built that way (eg: custom
database creation)

It is supported, just a truly BAD idea.  Why in the world do you want the type
of tablespace that

o supports getting fragmented really easy
o slows down your performance

???
September 02, 2002 pepo -- Thanks for the question regarding "how to
      find the defination of view or synonym", version 8.1.7

      You Asked
 Hello Tom,
My question is very simple, maybe very easy for you, but I try to find the
answer for weeks. I could not find the anser of this question: " How to find the
defination information of view or synonym".
I know "tab" is a synonym, but I don't know "what is it".

Thanks for your help.

Pepo

      and we said...
select * from all_synonyms where synonym_name = 'TAB';

will tell you TAB is a public synonym that points to SYS.TAB.

To find a view - select * from all_views where view_name = 'VIEWNAME';

See

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76961/toc.htm
and familiarize yourself with the data dictionary!
September 02, 2002 Sajid -- Thanks for the question regarding
      "Equivalent of over function", version 9.0.1

      You Asked
Hi Tom,

I had one request from my colleague to display the commutative salary. eq

ename sal commutative_sal
aa     10    10
bb     15    25
cc     11    36

....
....

I told him about using sum(sal) over (group by ename).

I would like to know, is there anyway other than this function. I mean if
somebody wants to implement this in older version of oracle that doesnt have
ANALYTICAL FUNCTIONS. Or if somebody wants this in SQL Server.

Thanks for your help in advance.

Regards,
Sajid Anwar
London

      and we said...
Yes and no.  It is hugely expensive in terms of processing however (that is why
we have the analytic functions).  So expensive as to be impractical.

see also

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:305416600201
for a trick that can sometimes work (but not in sqlserver.  I'm not very good at
answering "how do i do this in sqlserver" questions ;)
Review &
      Followup

      Rating: 5
thanks a
      lot  September 03, 2002
Reviewer:  Sajid
      Anwar  from London

September 02, 2002 Amer -- Thanks for the question regarding
      "To_date Query", version 8.1.7

      You Asked
Hi TOM

I have just tw questions

1.         I have a large table partitioned on SaleDate field. After some we
observed that most of the queries are on PostDate Column. Now My database is
7x24 operational. It can be very diffcult to have down time and I want to
convert partition from SaleDate to PostDate. How to do it.

2.        I have written a query with where clause on PostDate using
To_Char(POstdate, 'mm/dd/yyyy HH24:MM:SS'). Query works fine for one month data
but when I give range for two month with time it give me no records or
inaccurate results. I changed the query with To_date(.....) it gives me correct
result, but I observed that query takes longer time. I need to improve it. Can u
suggest me something.

I really apperciate ur help -- Your site is my help guide. It help me a lot.
Please reply soon.

Thanks and best Regards,

Amer

      and we said...
1) Oracle9i with the DBMS_REDEFINITION package allows for this to take place
online, while the database is up and running.  Whilst people are
inserting/updating and so on.

Prior to that, the table will have to necessarily be unavailable for
MODIFICATIONS while you do this reorg/rebuild.

2) don't TO_CHAR a date, TO_DATE a char.

  where postdate between to_date( '01/01/2002', 'dd/mm/yyyy' )
            and Add_Months(to_date( '01/01/2002', 'dd/mm/yyyy' ),1)-1/24/60/60;

You were trying to compare STRINGS in a range and that won't work unless the
string was inheritly sortable and yours was not.  You would have had to of used
a format like:

   yyyymmddhh24miss

NOT something that starts with mm/dd  (that is NOT sortable by date!))

but in any case -- try NOT to apply the function to the database column -- turn
the STRINGS into DATES and do it that way.
Review &
      Followup

      Rating: 4
Slow
      performance of To_Date  September 02, 2002

Reviewer:  Mirza Amer Baig  from Pakistan
I agreed and got corrected with your suggestions.

Please tell me why the query is slow while using to_date as compared to to_char?
Your response is greate!!!

Can I use DBMS_REDEFINITION in Oracle 8.1.7?

Thanks for very useful information.

Regards,

          Followup:

Well, lets see

using to_char you got some random (wrong) answer.

using to_date you get the proper, correct answer.

regardless -- the to_char query could have been sped up by select
dbms_random.random from dual you know?

it is pointless to compare that which does not work to that which does.

dbms_redefinition is a *new* 9i feature.  Meaning -- no, it is not in 8i

September 02, 2002 khalid -- Thanks for the question regarding
      "tunning delete statement", version 8.1.7

      You Asked
hi Tom

I am trying to delete 1000000 records from one table containing 10000000

delete
  from log_bad_calls
  where record_id in (select record_id
                      from  lbc_pre )

delete /*+ indix (log_bad_calls PK_LOG_BAD_CALLS) */
  from log_bad_calls one
  where record_id in (select record_id
                      from  lbc_pre tow
                      where
                      tow.record_id = one.record_id)

log_bad_calls contain 10000000 records
lbc_pre  contain 1000000 records

The tow tables has the same structure
The first have index in field record_id whereas the second table doesn’t have
any index

When I obtain the execution plan of the execution I notes that oracle go for
full table access for both.

1. How can I improve the deletion cause it takes hours to execut.
2. Can I enforce the optimizer to apply my hint to him.

Thanks khalid.

      and we said...
To delete 1,000,000 records in a table with a couple of indexes will TAKE SOME
LARGE amount of time.  Serious amounts of time.

The FULL SCAN of LOG_BAD_CALLS is not the limiting factor (it would only take
minutes to full scan that table at most with 10million rows -- I can do a wide
table with 1,000,000 rows in 30 seconds on my laptop!)

It is the DELETE itself.  Can you drop or disable the indexes for the delete?
Then rebuild them in parallel with nologging?

Else, suggest you break lbc_pre into 10 or 20 smaller (non-overlapping) tables
and run 10 or 20 delete sessions concurrently.

INDEXES are the culprit -- not the answer here.  The full scan is NOT the
problem at all.
Review & Followup

      Rating:
      4
  September 02, 2002
Reviewer:
      Khalid  from Yemen
Thansk Tom And..

1.Can I enforce the optimizer to obey my hint.
2.which of the tow delete is better
delete
  from log_bad_calls
  where record_id in (select record_id
                      from  lbc_pre )
or
delete
  from log_bad_calls one
  where record_id in (select record_id
                      from  lbc_pre tow
                      where
                      tow.record_id = one.record_id)

          Followup:

1) why why why??? do you want to make it RUN EVEN SLOWER???  TELL ME, how would
that index help your delete?

2) not possible to say -- if there is no index on lbc_pre, I would definitely
choose the IN.

but, you seemed to have missed my answer which stated:  it is not the access
plan that is consuming time.  It is the fact that you are deleting 1,000,000
rows and you have indexes on the table.  That will take TIME and lots of it.

      Rating: 5
index rebuild
      nologging after big insert/delete, will it require immediate
      backup?  September 02, 2002
Reviewer:
      Joseph  from MD USA
Tom

   Nightly, in my database, a stored procedure to refresh (DELETE/INSERT) lots
of specific rows in a production table is executed.  It takes a long time
because I have couple indexes on the table.  Your suggestions should work well,
however I have a question.

   I plan to ALTER UNUSABLE all the indexes associated with the table and
REBUILD the indexes with NOLOGGING, all in PL/SQL CODE.  That is ok.  The
problem is the 'NOLOGGING'.  Does the database need to have a FULL BACKUP right
away?  I am a developer, and it may not fair well with the DBA, because he
already has a regular backups set up, but the back up does not occur after the
refresh.

Thanks so much

          Followup:

You need to discuss this with your DBA.  The NON-LOGGED operations will not be
recoverable from backup in the event of a failure.  For the indexes -- this is
probably OK (you just rebuild the index). For the table data, if you use a /*+
append */ hint, this may be disastrous -- unless you coordinate with the DBA.

(also bear in mind that with the rebuild of the indexes in a single procedure,
we won't reuse the index space so you'll need 2x the space)

September 02, 2002 Nag -- Thanks for the question regarding "Outer
      Join why is it so expensive?", version 8.1.7

      You Asked
select columns

from

tab1    (1000 rows)
tab2      (800 rows)
tab3      (299 rows)

where tab1.primarykey = tab2.primarykey and
tab23.primarykey =tab2.primarykey(+);

The above is one of the scenarios I'm facing

Tom I have observed that when I removed theouter join the rows returned are 297
and the rows returned with the outer join is 299.

But when I remove the outer join , the query finishes execution in less than 2
seconds, and with the outer join it takes 70 seconds.

Why is it that outer join always proves so expensive? what makes it so
expensive.

( I used the selecting from a select in my columns, to avoid the outer join, but
in the first place why is the outer join so expensive, as I asked above what
makes it so expensive)

Please illustrate your answer.

Thanks

      and we said...
It is an anti-join of sorts.

It forces a specific order of processing tables.  Since your example makes no
sense to me (you join t1 to t2 normally, then you join t23 what is that! to
t2 using an outer join.  it NEVER makes sense to outer join and natural join to
the same table)

We'll do this:

t1
t2
t3

select *
  from t1,t2,t3
 where t1.x = t2.x and t2.y = t3.y(+);

Now, T3 cannot be used as the driving table in this query by definition.
You are asking for all rows from T1 join T2 and if they happen to have a match
in T3, give that at well.  So, T3 cannot be used to "drive" the query -- even
if T3 would be the best choice (this is true because we might have to "make up
rows" in T3 for the outer join and we'd never know we needed to make them up if
we used T3 as the driving table)

Now, turn that into:

select *
  from t1,t2,t3
 where t1.x = t2.x and t2.y = t3.y;

and we've added in new paths for the optimizer to take -- paths that were
unavailable with the outer join.  In your case -- a patch that is available with
the natural join that wasn't available with the outer join is significantly
faster.

It is not that outer joins are ALWAYS slower -- it is that outer joins preclude
the consideration of many access plans and therefore stand a chance of being
significantly slower.
Review & Followup

      Rating:
      5
oops  September 01, 2002
Reviewer:
      Nag
The below is the correct query

select columns

from

tab1    (1000 rows)
tab2      (800 rows)
tab3      (299 rows)
tab4      (100 rows)

where tab1.primarykey = tab2.primarykey and
tab2.primarykey(+) =tab3.primarykey  and
tab4.category='SWE';

If you carefully observe above I dont have a join condition for the tab4. And
yes it does a cartesian product. But still even with this handicap when I remove
the outer join the query executes very fast.

In the example you have given I understand that the table with the outer join
cannot be the driving table, but that is not the case with me, I dont have it as
the driving table.

Before we go any further, can you explain the itnernals of outer join, how do
you make up the rows for the table which is lacking rows in a outer join. Please
explain.

          Followup:

Well, having

where t1.pk = t2.pk
  and t2.pk (+) = t3.pk
  and tb.c = 'SWE'

is just bad code on your part.

Think about it -- if:

  t2.pk(+) = t3.pk

is *needed*, then:

  t1.pk = t2.pk

will just remove it!!! You have just totally wasted cycles, reduced the
available plans.

You could never OUTER join to T2 and have that row in the actual result set due
to the NATURAL join to t1!!!!

Here, you just wrote bad code.

The outer join here was just a plain old BAD idea -- and the above argument
about driving tables and reduced opportunities for plans applies 1000%

As for the last part -- not sure what you are getting at.  If you don't
understand the basic semantics of an outer join, you shouldn't even be using it.

outer join 101:

you have two tables -- emp and dept.  dept has 4 rows (deptno = 10, 20, 30, 40).
 emp has 14 rows but only 3 distinct values for deptno (10,20,30).

You need to write a report that shows the DEPTNO and count of employees for ALL
departments.  This requires an OUTER JOIN since the natural join:

   select d.deptno, count(empno)
     from emp e, dept d
    where e.deptno = d.deptno
    group by d.deptno;

would "lose" deptno = 40.  So, we:

   select d.deptno, count(empno)
     from emp e, dept d
    where e.deptno(+) = d.deptno
    group by d.deptno;

and that simply means "use DEPT as the driving table, for each row in DEPT, find
and count all of the EMPNOS we find.  IF there isn't a match in EMP for a given
DEPTNO -- then "make up" a record in EMP with all NULL values and join that
record -- so we don't drop the dept record"

      Rating:
      3
Probably  September 01, 2002
Reviewer:
      A reader  from Bangalore
Probably,
where tab1.primarykey = tab3.primarykey and
tab2.primarykey(+) =tab3.primarykey  and
tab4.category='SWE';
could give a more meaningful result.

          Followup:

how can you say that?

It gives a wholly DIFFERENT result, but to say "more meaningful?"  No, it would
give a wrong result if the original predicate was the one needed to get the
answer.

      Rating:
      5
How  September 01, 2002
Reviewer:
      Nag
Tom , I'm confused

Please expand on the following , and tell me how will waste cycles occur ...

Think about it -- if:

  t2.pk(+) = t3.pk

is *needed*, then:

  t1.pk = t2.pk

will just remove it!!! You have just totally wasted cycles, reduced the
available plans.

And how should in your opinion this should be rewritten..

Thanks

          Followup:

If we needed to use  t3.pk = t2.pk(+) -- then t2.pk will be NULL and therefore
t1.pk = t2.pk will not be satisified.  HENCE, when we actually outer join t2 to
t3 and "make up a row in t2", we also immediately turn around and throw it out.

THEREFORE, the results of the queries:

where t1.pk = t2.pk
  and t2.pk (+) = t3.pk
  and tb.c = 'SWE'

and

where t1.pk = t2.pk
  and t2.pk = t3.pk
  and tb.c = 'SWE'

are identitical.  By adding the (+) to the first one, all you did was remove
many different possible execution plans.  And given all of your other comments
about the performance of the query with and without the (+) you removed the PLAN
THAT ACTUALLY WORKS BEST from even being considered.

Anytime -- anytime -- you see:

  where t1.x = t2.x(+)
    and t2.any_column = any_value

you know that you can (should, must, be silly not to) remove the (+) from the
query.  Because if we "make up a NULL row for t2" then we KNOW that
t2.any_column cannnot be equal to any_value (it is NULL after all!!)

      Rating: 5
outer
      join  September 01, 2002
Reviewer:  Nag

I asked about outer join because sometimes it is fruitful to go back to basics.
It suddenly gives you a new perspective on a problem.

      Rating: 4
outer
      join  September 02, 2002
Reviewer:  parvez
       from india
select columns
from
tab1    (1000 rows)
tab2      (800 rows)
tab3      (299 rows)
tab4      (100 rows)

where tab1.primarykey = tab2.primarykey and
tab2.primarykey(+) =tab3.primarykey  and
tab4.category='SWE';

If u have one record in tab1 which is not there in tab2 the first join will not
select the record so outer joining tab2 with tab3 is not needed.

      Rating: 4
A
      Reader  September 02, 2002
Reviewer:  Rahul
       from India
Hi Tom

As I think statement below

where t1.pk = t2.pk
  and t2.pk (+) = t3.pk
  and tb.c = 'SWE'

should be like

where t1.pk = t2.pk(+)
  and t2.pk (+) = t3.pk
  and tb.c = 'SWE'

Pls. tell me am i roght or not??

Regards
Rahul

          Followup:

I cannot -- only the original poster of the question can answer that.

Those two predicates answer two entirely different questions.  One would need to
know the question before saying what the answer was.

I can only say that

where t1.pk = t2.pk
  and t2.pk = t3.pk
  and tb.c = 'SWE'

is the same as

where t1.pk = t2.pk
  and t2.pk (+) = t3.pk
  and tb.c = 'SWE'

So if they got the answer right with their predicate, just dropping the outer
join gets the SAME answer -- faster.

      Rating:
      5
rewritten  September 02, 2002
Reviewer:
      Nag
select columns

from

tab1    (1000 rows)
tab2      (800 rows)
tab3      (299 rows)
tab4      (100 rows)

where tab1.primarykey = tab2.primarykey and
tab2.primarykey(+) =tab3.primarykey  and
tab4.category='SWE';

I have rewrote the above as

select columns

from

tab1    (1000 rows)     ...lookup table 1
tab2      (800 rows)    ....lookup table 2
tab3      (299 rows)     ...... loan table
tab4      (100 rows)     ...... category table

where tab3.primarykey = tab1.primarykey and
tab3.primarykey =tab2.primarykey(+)  and
tab4.category='SWE';

The objective of using the outer join in the above rewrittedn query is to return
rows from tab3 even if a matching row is lacking in tab2.

Without the outer join it returns 297 rows, and with the outer join in returns
299 rows.

Does this rewritten query make any sense.

          Followup:

If that is the answer to your question - then yes.  But it is a wholly different
query then you started with!!!  You are joining totally different tables
together.

All of the queries "made sense".  Only you can tell us if you have the relations
proper now.

      Rating:
      5
  September 02, 2002
Reviewer:  Nag

select columns

from

tab1    (1000 rows)     ...lookup table 1
tab2      (800 rows)    ....lookup table 2
tab3      (299 rows)     ...... loan table
tab4      (100 rows)     ...... category table

where tab3.primarykey = tab1.primarykey and
tab3.primarykey =tab2.primarykey(+)  and
tab4.category='SWE'

In the above case , when the first join succeeds , and then the second join
fails , then outer join comes into force and creates a null record and still
returns the record even if the tab2 does not contain a row to satisfy the the
second join. That is how I understand it from the above discussion.

Now when the difference between having the outer join and not having the outer
join is just 2 rows. But as I have been saying the taken is lot more when we
have a outer join.

I have actually totally avoided the outer join by using.

select columns,
tab3.desc_key,
(select tab2.description from tab2 where where tab2.desc_key=tab3.desc_key ),
columns

from

tab1    (1000 rows)     ...lookup table 1
--tab2      (800 rows)    ....lookup table 2
tab3      (299 rows)     ...... loan table
tab4      (100 rows)     ...... category table

where tab3.primarykey = tab1.primarykey and
--tab3.primarykey =tab2.primarykey(+)  and
tab4.category='SWE'

I have used this trick after learning it from your website.

But still I dont understand why it should take so much time to create two null
records when using the outer join. I know you have spent enough time on this
thread, but still if you can come up with an illustration which can more
explicitly explain the scenario .. please kindly do so.

          Followup:

ARG --

compare the plans with and without the outer join.

As I've said (many times) -- the driving table is getting changed here BECAUES
of the outer join and its requirements.

Look at the plan, it should be obvious.

      Rating: 5
Very Very Very
      Very Useful  September 25, 2002
Reviewer:
      Kiran  from India
Hi Tom,

I now really understand way outer join actually works and how it affects the
query's execution path.

I thank you for providing us with the insight.

U r the "TRUE" Oracle Guru :-)

Thanks
Kiran

September 02, 2002 Bindhu -- Thanks for the question regarding
      "join/subquery", version 8.0

      You Asked
table main
==========
fname    fgroup
======   ======
ma        s
ma        s2
ma        e
ma        e9
ma1
ma1       s
ma1       e
ma2
ma2       e8

table subgroupS
===============
fname    grpS
=====    ====
ma        s2
ma1       s

table subgroupE
===============
fname     grpE
=====     ====
ma        e2
ma1       e
ma        e
ma2       e8

result set should be ma1  null
                     ma2  null
                     ma   s
                     ma   e9
Basically i want those that are in table main but not in table subgroupS and
subgroupE.I tried subquery, outerjoin
but something or other fails.

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from main
  2  minus
  3  select * from subgroupS
  4  minus
  5  select * from subgroupE;

FNA FG
--- --
ma  e9
ma  s
ma1
ma2

Review &
      Followup

      Rating:
      3
  January 04, 2002
Reviewer:  Dan
      Kefford  from New Haven, CT USA
Wouldn't the following be more efficient, especially if there were a large
number of rows involved and indexes on all three tables?

SELECT  m.fname,
        m.fgroup
FROM    main m
WHERE NOT EXISTS
       (SELECT  'x'
        FROM    subgroups s
        WHERE   s.fname = m.fname
        AND     s.grps  = m.fgroup)
AND NOT EXISTS
       (SELECT  'x'
        FROM    subgroupe e
        WHERE   e.fname = m.fname
        AND     e.grpe  = m.fgroup)

This is the explain plan from the query above:

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE
   1    0   FILTER
   2    1     TABLE ACCESS (FULL) OF 'MAIN'
   3    1     TABLE ACCESS (FULL) OF 'SUBGROUPS'
   4    1     TABLE ACCESS (FULL) OF 'SUBGROUPE'

For Mr. Kyte's query, the plan is the following:

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE
   1    0   MINUS
   2    1     MINUS
   3    2       SORT (UNIQUE)
   4    3         TABLE ACCESS (FULL) OF 'MAIN'
   5    2       SORT (UNIQUE)
   6    5         TABLE ACCESS (FULL) OF 'SUBGROUPS'
   7    1     SORT (UNIQUE)
   8    7       TABLE ACCESS (FULL) OF 'SUBGROUPE'

Now... if there are indices on all three tables...

SQL> CREATE INDEX main_idx on main
  2  (fname, fgroup)
  3  /

Index created.

SQL> CREATE INDEX subgroups_idx ON subgroups
  2  (fname, grps)
  3  /

Index created.

SQL> CREATE INDEX subgroupe_idx ON subgroupe_dk
  2  (fname, grpe)
  3  /

... the proposed query would execute thusly:

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE
   1    0   FILTER
   2    1     TABLE ACCESS (FULL) OF 'MAIN'
   3    1     INDEX (RANGE SCAN) OF 'SUBGROUPS_IDX' (NON-UNIQUE)
   4    1     INDEX (RANGE SCAN) OF 'SUBGROUPE_IDX' (NON-UNIQUE)

The full scan on main is expected, but now the indices on subgroups and
subgroupe are utilized, though for the other query they are not:

Execution Plan
-------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE
   1    0   MINUS
   2    1     MINUS
   3    2       SORT (UNIQUE)
   4    3         TABLE ACCESS (FULL) OF 'MAIN'
   5    2       SORT (UNIQUE)
   6    5         TABLE ACCESS (FULL) OF 'SUBGROUPS'
   7    1     SORT (UNIQUE)
   8    7       TABLE ACCESS (FULL) OF 'SUBGROUPE'

          Followup:

NULLS -- you forgot about NULLS...  Your query returns the wrong answer....

consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into subgroups  values ( 'ma1', null
);
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> SELECT       m.fname,
  2          m.fgroup
  3  FROM       main m
  4  WHERE NOT EXISTS
  5         (SELECT  'x'
  6          FROM    subgroups s
  7          WHERE   s.fname = m.fname
  8          AND     s.fgroup  = m.fgroup)
  9  AND NOT EXISTS
 10         (SELECT  'x'
 11          FROM    subgroupe e
 12          WHERE   e.fname = m.fname
 13          AND     e.fgroup  = m.fgroup)
 14  /

FNA FG
--- --
ma  s
ma  e9
ma1
ma2

ops$tkyte@ORA817DEV.US.ORACLE.COM> l
  1* select * from main minus select * from subgroups minus select * from
subgroupe
ops$tkyte@ORA817DEV.US.ORACLE.COM> /

FNA FG
--- --
ma  e9
ma  s
ma2

minus deals with NULLS

also -- full scans ARE NOT SCARY THINGS -- repeat -- they are NOT SCARY THINGS

If you have lots of rows in main (lots being a very relative term), you would
end up doing LOTS of random reads on the indexes -- thats not good.

many times -- MINUS quite simply blows away the competition.  Here I looked at a
large table (relatively speaking) "main".  I compared it to the sizes of the
other samples.

There are many ways to write the query.  What you must remember however is that
an INDEX is not always the best answer (see my book -- i blow that idea apart),
sometimes it is.  I do not strive for plans that show "index"... I'm not afraid
of a full scan.  Minus can be very powerful (look at the ease of the query
itself)

IF one of (fname, grps) is not NULLABLE, and one of (fname,grpe) is NOT NULLABLE
and MAIN is very small -- the where not exists might be more efficient.  If
either one of those holds to be not true -- not exists will be slower.

      Rating:
      4
joins/sunquery  January 04, 2002
Reviewer:
      bindhu
It was really good,i had problems with nulls and i thought of decode and having
to eliminate null.Since i was going in loops as to what and why this really
hammered my thought process.

      Rating: 4
problem with
      joins results sets, incorrect?  September 02, 2002

Reviewer:  A reader
Hi

I am trying to do three table join using Oracle 9.2.0.1 Human Resources sample
schema (HR). I was using these three tables:

JOB_HISTORY, DEPARTMENTS and EMPLOYEES

I then ran these queries

select c.employee_id, department_name, first_name, last_name, end_date,
a.department_id, a.job_id
from job_history a, departments b, employees c
where a.department_id = b.department_id
and a.employee_id = c.employee_id

returns 10 rows

select c.employee_id, department_name, first_name, last_name, end_date,
a.department_id, a.job_id
from job_history a, departments b, employees c
where a.department_id = b.department_id
and b.department_id = c.department_id

returns 175 rows

I dont understand why!???

          Followup:

Umm, because you used two totally different predicates?

because you asked two totally different questions?

because you joined the tables using two totally different criteria?

why in the world would you expect anything remotely similar from the two
queries????

      Rating: 4
what am I
      asking in the second query?  September 02, 2002

Reviewer:  A reader
Hi

I understand my first query is asking employee´s jonb history, whata bout the
second...? I thought they are the same :(

          Followup:

Not even close.

LOOK AT THE PREDICATES.

select c.employee_id, department_name, first_name, last_name, end_date,
a.department_id, a.job_id
from job_history a, departments b, employees c
where a.department_id = b.department_id
and a.employee_id = c.employee_id

That joins EMPLOYEES to JOB_HISTORY and JOB_HISTORY to DEPARTEMENTS.

So, for an employee it finds all of their jobs and for that job, the department
name...

select c.employee_id, department_name, first_name, last_name, end_date,
a.department_id, a.job_id
from job_history a, departments b, employees c
where a.department_id = b.department_id
and b.department_id = c.department_id

That joins EMPLOYEES to DEPARTMENTS, DEPARTMENT to JOB_HISTORY

So for every EMPLOYEE it finds the DEPARTMENT information and then gets a list
of everyone who ever worked in that department from JOB_HISTORY.

they are not even remotely the same.  You need to think about what is happening
here -- try to get an understanding.  Look at the join conditions -- how could
they POSSIBLY be the same (unless department_id = employee_id of course)

      Rating:
      4
:-(  September 02, 2002
Reviewer:
      A reader
Hi Tom

any SQL book recommendation? :-(

          Followup:

goto amazon.com and search for

celko sql

      Rating: 5
How do you
      convert words/question into a query?  September 02, 2002

Reviewer:  A reader
Hi Tom

Do you have use any techniques to write queries? For example if someone ask you
for some data how would you convert that question into a SQL query?

          Followup:

I don't know.  If I did -- I would write the best natural language processor
around ;)

It just happens.

      Rating:
      4
  September 02, 2002
Reviewer:
      A reader
I was asking because for example if I have to look following it would take me a
while!

select c.employee_id, department_name, first_name, last_name, end_date,
a.department_id, a.job_id
from job_history a, departments b, employees c
where a.department_id = b.department_id
and b.department_id = c.department_id

That joins EMPLOYEES to DEPARTMENTS, DEPARTMENT to JOB_HISTORY

So for every EMPLOYEE it finds the DEPARTMENT information and then gets a list
of everyone who ever worked in that department from JOB_HISTORY.

September 02, 2002 Meyer -- Thanks for the question regarding "Role
      of Java inside the DB", version 8.16-9ias

      You Asked
Tom,

Recently, at an Oracle Education partner site, I was told that Oracle was
backing off from implementing Java inside the database. Can you please explain
if this was accurate, where it may be coming from... the more specifics w/
history-to-future the better...is the JVM going to be in future releases.

Can you recommend a book for someone with lots of PL-SQL, but not C skills to
start learning Java.

Thanks,
Meyer

      and we said...
What that means is that the placement of EJB's for example inside the database
is being discouraged.  The placement of them in the middle tier (9iAS) is being
encouraged.

It is mostly the J2EE "javaness" that is being de-emphasized in the database.
Java in the database will be playing the role of

o a more sophisticated stored procedure language (more OS features then PLSQL
has or will have)

o something to write extremely complex, extended datatypes in (eg: if you wanted
to write your own TEXT type for example with indexing, summarization, etc -- the
stuff Oracle Text/interMedia do)

You might consider:

http://www.amazon.com/exec/obidos/ASIN/1861006020/
(i have to admit, I was one of the technical reviewers/contributors to this book
and one of the guys who wrote it works for me... but -- as a book written
towards PLSQL developers wanting to learn java, I think it is one of a kind.
Read the reviews and judge for yourself)

Review &
      Followup

      Rating:
      4
Thanks  September 02, 2002
Reviewer:
      Meyer  from Baltimore, MD
Thank you.

      Rating:
      5
java  September 02, 2002
Reviewer:
      mo  from vienna, va
Tom:

1.  Until now I have not encountered a need for using java. it seems most
applications can be done using 100% pl/sql.  when do you really need to use java
stored procedures?

2. oracle already has package utl_smtp for email which is written in java. so
email can still be done using pl/sql except I think being able to attach files
from the database with the email?Correct.

3.  If you are designing a web application would there be a difference from
using Javas stored procedures or JSP.  Is it just different syntax or one is
better than the other.

          Followup:

1) when you need to do something PLSQL cannot.  Sending an email comes to mind
(UTL_SMTP is written in java).  Sending an email with an attachment is really
utterly hard in PLSQL (even with utl_stmp).  It is trivial in java.

Try ftping a file from plsql.

2) yes.

3) JSP = java server page = the ability to co-mingle JAVA and HTML together
(like an ASP or PSP).  java stored procedure = a stored procedure written in
java.  they are entirely different concepts.


Subject | Article | Last Updated (East coast time)
Trcasst Problems | article only | Sep 03, 2002 07:35 am
Fine Grained Access Control | article only | Sep 03, 2002 08:04 am
Temporary Table Questions | article only | Sep 03, 2002 08:12 am
how to detect users and their actions | article only | Sep 03, 2002 08:16 am
Choosing Block Size | article only | Sep 03, 2002 12:30 pm
which is faster? static cursor or dynamic cursor | article only | Sep 03, 2002 12:35 pm
inserts/updates with indexes on the table | article only | Sep 03, 2002 02:27 pm
PL/SQL Tables, Temporary Table or Materialized view. | article only | Sep 03, 2002 03:15 pm
shared pool latch & library cache latch | article only | Sep 03, 2002 03:16 pm
Export, Import of partitioned table | article only | Sep 03, 2002 03:40 pm
Stored Functions - 8i - Apps 11i - Oracle Reports 6.0 | article only | Sep 03, 2002 03:55 pm
oracle developer moving to java | article only | Sep 03, 2002 07:01 pm
DNS name vs IP address in Net8 | article only | Sep 03, 2002 08:19 pm
Misc. | article only | Sep 04, 2002 07:17 am
Clustering 817 database on AIX | article only | Sep 04, 2002 07:45 am
Select Top and Oracle | article only | Sep 04, 2002 08:00 am
MUTATION Trigger | article only | Sep 04, 2002 08:52 am
statment level triggers | article only | Sep 04, 2002 09:27 am
voting | article only | Sep 04, 2002 02:47 pm
calculate rbs required for an DML | article only | Sep 04, 2002 07:22 pm
multiset with group by | article only | Sep 04, 2002 07:24 pm
/etc/system file and SGA | article only | Sep 04, 2002 08:27 pm
How to get the ORA-01652 error code in Unix | article only | Sep 05, 2002 07:30 am
Temporary tables (comparision with SQLServer) | article only | Sep 05, 2002 07:36 am
rewriting a query having an outer join and group by | article only | Sep 05, 2002 08:32 am
copy command vs sql*loader | article only | Sep 05, 2002 10:38 pm
How much of Java do you need to know to use, say, IFS | article only | Sep 06, 2002 07:34 am
UTL_FILE or sql*loader | article only | Sep 06, 2002 02:27 pm
Response Time for materialized | article only | Sep 06, 2002 02:31 pm
Performance issue | article only | Sep 06, 2002 02:45 pm
the explain of my old sql suddenly changed,but i had not change anything. | article only | Sep 06, 2002 03:48 pm
Regarding Password Decryption. | article only | Sep 06, 2002 03:56 pm
rollback and redo generation for autonomous transaction | article only | Sep 06, 2002 03:58 pm
return multiple address to mail_pkg | article only | Sep 06, 2002 07:22 pm
Global variables vs parameters | article only | Sep 06, 2002 07:33 pm
database mounted but not opening | article only | Sep 07, 2002 11:18 am
Using Host, goto and transaction | article only | Sep 07, 2002 06:47 pm
native dynamic sql | article only | Sep 07, 2002 06:48 pm
ESTIMATE GROWING OF TABLES AND INDEXES | article only | Sep 07, 2002 07:12 pm
understanding RAW 10046 trace output | article only | Sep 08, 2002 09:42 am
Duplicate Rows in Forms | article only | Sep 08, 2002 09:43 am
remove datafile from a locally managed tablespace | article only | Sep 08, 2002 09:57 am
Create database link betwent 2 Dbs | article only | Sep 08, 2002 10:04 am
Temporary tablespace | article only | Sep 08, 2002 04:48 pm
How to tell if a table has compression enabled? | article only | Sep 08, 2002 05:16 pm
User defined aggregates and intervals | article only | Sep 08, 2002 05:53 pm
Calling Pl sql stored procedure from Servlet | article only | Sep 09, 2002 08:30 am
General Protection error | article only | Sep 09, 2002 08:57 am
some details on release2 | article only | Sep 09, 2002 09:11 am
SQL statement is different in v$sql | article only | Sep 09, 2002 09:29 am
binding array parameter in dynamic function call | article only | Sep 09, 2002 10:11 am
Uploding text file with utl_file | article only | Sep 09, 2002 10:29 am
Questions on Columns in the ORACLE & FIXED_DATE | article only | Sep 09, 2002 12:32 pm
Temporary Tablespace | article only | Sep 09, 2002 07:33 pm
Triggers vs Packages ! | article only | Sep 09, 2002 07:35 pm
Execute Immediate for a fetch statement | article only | Sep 09, 2002 08:06 pm
row(s) 1 - 56 of 56
September 03, 2002 Tarry -- Thanks for the question regarding "Trcasst Problems ", version 8.1.6.0.0
You Asked
Hi Tom,
(phew, even though i have asktom as my homepage it's not easy to ask you...)
I have a problem with the trcasst utility.
I have enabled tracing and this is what I get after setting the level at
support, i've also tried in vain the admin.
Could you please suggest what might be going on..

C:\>trcasst -s d:\oracle\ora81\network\trace\sqlnet.trc

Trace Assistant Utility for 32-bit Windows: Version 8.1.6.0.0 - Production on
15-AUG-2002 12:29:30

(c) Copyright 1997 Oracle Corporation.  All rights reserved.

    *************************************************************************
    *                        Trace  Assistant  Tool                         *
    *                                TRCASST                                *
    *************************************************************************

TNS-04206: Trace Assistant Internal ERROR: Packet Type
--------------------------------------------------------------------------------
Any clues how to resolve it?
and we said...
I'm good at answering questions about "how do I do something".  "What is the
best way to do this...".  "How does this work"

Support is very good at answering "How do I install this".  "How do I configure
this".  "This feature isn't working (this question)".

They are not good at answering what I do.  I am not good at answering what they
handle best.  That is why it is hard to ask me -- because I get so many
questions that really aren't "how do i", "what is the best way", "how does this
work".... (if I had a nickel for every install question I get even though I make
people read a page that says "I won't answer install questions".... ;)

But anyway.  A 5 second search on
http://metalink.oracle.com/
will turn up this support note:

Article-ID:         <Note:135498.1>
Alias:              SOL:OSS3640
Circulation:        PUBLISHED (EXTERNAL)
Folder:             em.Packs.Diagnostics.V2
Topic:              - - -      Known Problems
Title:              TRCASST GENERATES TNS-04206 TRACE ASSISTANT INTERNAL ERROR:
                    PACKET TYPE
Document-Type:      PROBLEM
Updated-Date:       07-JAN-2002 16:47:03
References:
Attachments:        NONE
Content-Type:       TEXT/X-HTML
Products:           100;
Platforms:          GENERIC;

    * fact: Oracle Server - Enterprise Edition 8.1.6
    * symptom: Unable to run trace assistant
    * symptom: tns-04206 trace assistant internal error: packet type

* cause: There is an extra space after function name in the trace file, which
causes trcasst to fail. Only for packet dumps, trcasst expects funtion names
without a trailing space.

fix:

Remove the trailing space after function name for hex packet dump.

1. Open the sqlnet trace file.
2. Search for nspsend and nsprecv.
3. For each nspsend and nsprecv entry, remove the space between the : and the
hex packet dump.
4. Save edited file.
5. Run Trace Assistant (trcasst -o cli.trc)
Review & Followup
Rating: 4
Thanx much September 03, 2002
Reviewer: Tarry from The Netherlands
Thanx again and sorry for dragging you away from the 'how to' to the support
side.
We're not a direct oracle partner and so it's not possible to get in to the
metallink.
Thanx for your time.

Tarry

Followup:
Become one then.

September 03, 2002 Kwok -- Thanks for the question regarding "Fine Grained Access Control", version 8.1.7/9.0.1
You Asked
Hi Tom:

In your Book "Expert 1to1" page 932, there is a example of the fine grained
access control. Line 25-29, I don't understand the pahse "start with
empno=.......            connect by prior ..... " , why is it use ?

In 9i version,there are new features, secure application role, global
application context and partitioned fine-grained access control. I do not
understand those new features. Can you provide some examples that somehow relate
to the example in your book ?

Thx in advance.
and we said...
The book is "Expert One on One Oracle"

See
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a85397/expressi.htm#1023748
that is quite simply your basic "hierarchical query".  I set the predicate to be
"the empno must be in the set of employees you manage".  In order to get that -
we need to find everyone that works for you, everyone that works for them, and
so on.  We need a hierarchy.

A secure application role -- this is a role that can only be enabled by a stored
procedure.  In the past roles were assigned to people and either you

o just enabled the role
o supplied a password to enable the role.

A secure application role makes it so that you can now write your own logic that
will inspect the environment -- WHO is asking, maybe get a password, whatever
and either enable or disable the role based on whatever inputs YOU decide are
necessary.  It allows you to wrap a role (set of privs) with some logic YOU
provide to determine if the currently logged in session should get that role or
not.

Global application contexts -- this is an application context, just like the
ones you see in my book -- but they can persist across sessions, they can be
shared ACROSS sessions if you like, they can be initialized via OCI or LDAP
callouts.  They are application context's with more features.  The ability to
persist across sessions is especially useful in a web environment where each
page is a new session.  If it takes a second to initialize the context, this can
be a great savings.

Partitioned fine grained access control is the ability to have FGAC by
application.  That is -- the HR application has one policy when accessing table
T, the Fin Apps program has a different policy when accessing table T and
SQLPLUS has yet a third (default) policy when accessing table T.  It is simply
the ability to have a different policy for accessing the same object by
different applications (eg: I can read and write via HR, I can read via fin
apps, I have no access by default in sqlplus)
September 03, 2002 -- Thanks for the question regarding "Temporary Table Questions", version 8.1.7
You Asked
Tom,
  I have a few questions abt temporary table:

  1.  Which type of temporary table is better to use in a multi-user
applications, a global temporary table with on commit preserve rows or
a global temporary table without on commit preserve rows ? Why ?  What is the
difference ?

  2.  Is it logical to create an index for a temporary table ?

  3.  Is it a good practice to truncate tables(permanent/temporary) in a
procedure ? Why?

  4.  How to do a trace on a procedure to check which statement have a bad
performance ?

  Thanks.

Rgds,
Jesse
and we said...
1) they are equally "better".

YOU need to decide based on YOUR needs whether:

a) rows should disappear when you commit
b) rows should stay in there until you log out

regardless of what you pick, they behave identically in a multi-user
environment.  ONLY your session will ever see YOUR data.

2) yes.

3) NO. truncate is DDL.  DDL is expensive.  Truncation is something that should
be done very infrequently.  Now, I don't mean "turn your truncates into
DELETE's" -- that would be even worse.  I mean -- avoid having to truncate or
delete every row in the first place.  Use a transaction based temporary table
and upon commit, it'll empty itself.

4) wouldn't that be neat.  Well, the tools you have available are

DBMS_PROFILER
sql_trace + tkprof

If you have my book "Expert one on one Oracle" -- read chapter 10 and the
section in the appendix on dbms_profiler.  If not, search this site for
dbms_profiler and read
http://asktom.oracle.com/~tkyte/tkprof.html
September 03, 2002 mahbuba -- Thanks for the question regarding "how
      to detect users and their actions", version 8.1.6

      You Asked
How can I find out the following in a Audit table using procedure/function
through a trigger to trace out who has deleted/updated/inserted on a specific
tables:
1.OSUSER
2.TERMINAL
3.ORACLE USERNAME
4.IP ADDRESS OF THE TERMINAL
5.TIME OF ACTION
6.ACTION
7.OLD VALUES AND NEW VALUES
-Please explain in details with examples.

      and we said...
I don't have to explain in detail since the documentation convienently does that
for us.  In fact the Admin Guide spends and entire CHAPTER on nothing but
auditing:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76956/audit.htm#1108
and it gets all of that EXCEPT for old and new values.  If you want that --
you'll be doing it yourself via custom coded triggers -- or if you upgrade to
9i, you can use workspace managment to capture all of that easily for you:

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/appdev.920/a96628/toc.htm

September 03, 2002 Juan Carlos -- Thanks for the question regarding
      "Choosing Block Size", version 9.2

      You Asked
Hi Tom, How are you?
I would like to make you a question that I could´nt answer.
I didn´t found in any place ( forums, metalink, dba fundamentals I, etc.)
a clear guide for choosing the block size, and what can wait from every size.

As you explained in c++ function ( you can get an improvement from 0 to 200%)

I would like you share you experience about changing block size.
For example changing the block size from 2k to 32k in an OLTP that processed x
transanction for seconds there were an improvement of speed of y%.

And if you can a brief explanation that explain when is a good idea to use 2k,
when to use 4k when to use 8k, 16k and 32k. To understand which parameters made
one decide what block size to choose.

Thank you.

      and we said...
It would probably be more like changing the blocksize from 32k to 2k in an OLTP
application improved it.  OLTP -> smaller blocks (due to decreased concurrency
on them, better buffer cache managment).  DSS -> bigger blocks (due to decreased
IO costs and better buffer cache managment).

Benchmarking -- it is what this is all about.  The best thing for you is -- you
don't have to PICK!!!  You can use them ALL at the same time in 9i.  So, setup
some 2k,4k,8k,16k,32k tablespaces and try them out.
Review &
      Followup

      Rating: 5
Study
      case  September 03, 2002
Reviewer:  Juan R

Thank you Tom
But could you share with us something from the real world.
Because all is theory, don´t you know some study case.

Thank you

          Followup:

We use 2k blocks on our TPC-C benchmarks (OLTP)
We use 8k blocks on our TPC-H benchmarks (DSS)

http://www.tpc.org/
No theory there.  But, you have 9.2, you can TEST IT yourself to see what works
best!

      Rating:
      5
  September 03, 2002
Reviewer:
      A reader
:) Thank you

September 03, 2002 Rohil -- Thanks for the question regarding "which
      is faster? static cursor or dynamic cursor", version 9i

      You Asked
hi tom,
"which is faster "a static cursor OR a dynamic cursor(using cursor variables and
REF cursor)" ?
is there any significant difference in execution speeds? (or does it depend on
anything else?)

Given an option of using static and dynamic cursor, which one should be used?

thanx in advance
bye
rohil

      and we said...
STATIC code is always better.

You use dynamic sql only when there is quite simply NO WAY to do it statically.

Static sql has the advantages of

o speed
o ease of coding
o ease of maintenance
o automatic dependency tracking (if you statically reference something, it is
  recorded in the data dictionary and you can query up that fact.  If you
  dynamically reference it -- it is NOT recordable)
o ease of debugging/testing (with dynamic sql, the query isn't generated until
  runtime -- will that query work?  I don't know until i run EVERY possible
  combination.  With STATIC sql I know at compile time that its valid sql)

Always use static sql until you cannot.
Review &
      Followup

      Rating: 4
Statis Vs
      Dynamic Cursor  September 03, 2002
Reviewer:
      Ashok Shinde  from Ipswich, United Kingdom
This has helped me alot. But can you use the static cursor with the datatypes
like long, blob, lob ?
Thanks,
Ashok

          Followup:

Yes, Yes, and finally -- yes.

      Rating: 3
But what about
      this case  September 03, 2002
Reviewer:  Ed
       from UK
Tom,

I saw this SQL statement as a static piece of SQL in a procedure (that works).
Surely the NVLs are a bad idea, and this is a case for using Native Dynamic SQL
even though the static version works.

SELECT forename,
       surnames .... etc
  FROM az_data
 WHERE surname LIKE UPPER(p_surname)||'%'
   AND id = NVL(p_id,id)
   AND cat_code = NVL(p_cat_code,cat_code)
   AND amt BETWEEN NVL(p_low_amt,0) AND NVL(p_hi_amt,9999999999999)
   AND req_date BETWEEN NVL(p_low_date,TO_DATE('20000101','YYYYMMDD'))
AND NVL(p_hi_date,TO_DATE('29990101','YYYYMMDD'))
   AND postcode LIKE UPPER(p_postcode)||'%'
ORDER BY surname, forenames;

          Followup:

This is a case where dynamic sql is called for, PROBABLY, maybe, it depends...

It is quite probable that this statement would run better if the where clause
(which apparently isn't really known until runtime) was generated at runtime.

Now, if the query is using an index on SURNAME or POSTCODE or SURNAME,POSTCODE
-- that would not be the case.  The STATIC sql (as it is) would be better in all
probability.

But yes, thanks for making me tone it down a tad.  Common sense must be applied.
 See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1288401763279
for how I might code this if such an index was not being used (or if ID was
highly selective and indexed for example -- meaning if the query had "where id =
p_id" it should use the index on ID)

      Rating: 5
Query
        September 03, 2002
Reviewer:  San
       from US
Hello Tom,

I have a table to record employee activity,

Table: Employee_activity

EmployeeId   Start_time    End_time(date)  Activity   day
1             9.00 Am       10.00 am        Worked    11/01
1            10.00 Am       11.00 Am        lunch     11/01
2             9.00 Am       12.00 Am        worked    11/01
1             9.00 Am       10.30 Am        worked    11/02
1            11.00 Am       12.00 Am        worked    11/02

I like to get a output like this (record for the unaccounted time),

EmployeeId   Start_time    End_time(date)  Activity   day
1             9.00 Am       10.00 am        Worked    11/01
1            10.00 Am       11.00 Am        lunch     11/01
1            11.00 am        5.00 Pm       Unaccounted 11/01

2             9.00 Am       12.00 Am        worked    11/01
2            12.00 Am        5.00 Pm      Unaccounted 11/01

1             9.00 Am       10.30 Am        worked    11/02
1            10.30 Am       11.00 am     Unaccounted  11/02
1            11.00 Am       12.00 Am        worked    11/02
1            12.00 Am        5.00 pm     unaccounted  11/02

The total working hours is 8.

I am using oracle 8.1.7. Can you please explain me how I can do this in a single
query. If this cannot be done in a single query please suggestion me other
options with example.

Thanks Tom.
Have a blessed and highly favored day.

September 03, 2002 -- Thanks for the question regarding
      "inserts/updates with indexes on the table", version 8.1.6

      You Asked
hi Tom
of course i have already your book its super

1) when we do an insert or update on a table with indexes
when are the indexes being reorganized after the commit or even before the
commit ,which means that the user gets the commit complete and the sql> sql
promt back and continues to work while behind the scene Oracle has some work to
do with the indexes ,and a rollback will have to delete some index slots ,or
until the user doesn't commit Oracle would not touch the indexes

2)I saw in one of your answers (I don't remember which) you're saying "its no
difference if we put the table and its index in the same tablespace" I  always
thought that if you have 2 controllers or 2 disk heads so you put the index
table space on a separate file system then the table's data so you can make use
out of the 2 controllers and gain some performance ?

Thank you in advance

      and we said...
1) the indexes are maintained as the insert or update proceeds.  It is during
the insert/update that they are modified.

2) that would be true IF we accessed the table and the index at the same time.
But since we

  a) read an index so we can
  b) read the table afterwards

it would not matter if the index and table data are on separate devices or not.
I mean, it is not like you are reading them SIMULTANEOUSLY.  You read them
serially.

You would be best off just striping those devices to spread the IO evenly across
them (which is what you are ATTEMPTING to do by putting indexes in one
tablespace and data in another)

Review &
      Followup

      Rating: 4
Simultaneous
      Index/Table access  September 03, 2002
Reviewer:
      Simon Holt  from Dublin, Ireland
Dear Tom,

at risk of being the subject of a "get a new DBA" suggestion...

In a single user environment, it would follow that serial access of an index and
a table segment is occurring, agreed.

But in a multi user environment, where several users are accessing table and
index information simultaneously, surely the separation provides benefit? This
has certainly been the opinion of all the documentation I have read, including
Oracle DBA course notes (admittedly from Oracle 7 days, but nonetheless..),
Kevin Loney's DBA Guide, etc etc

For a (trivial) example, what if:

User A: Performs an indexed access of table A
User B: Performs full table scan of table A

simultaneously where the index segment is in the same tablespace as the table
segment? Unless the tablespace is heavily striped (and for the sake of argument,
perhaps we could assume it is not - many databases I'm called in to look at are
not), this would result in the disk receiving conflicting i/o requests - if the
index were removed to another disk, then at least the i/o requests for the index
segment could be dealt with separately.

Checking out the waits from V$SESSION_WAIT and a more general view from
v$SYSSTAT usually confirms this, though I'm afraid I'm not in a position to
quote observations at this point in time, since I'm some distance from a
database of any description!

Over the last few years, I have seen several systems where wait for I/O has been
very poor, and this because heavily accessed index segments occupy the same
tablespace as heavily accessed table segments on the same device - careful
analysis and monitoring of V$ views, followed by removal of offending objects
and tidying has always solved this (so far!)

I knew it, my career as a DBA is now over ;-)

          Followup:

...
But in a multi user environment, where several users are accessing table and
index information simultaneously, surely the separation provides benefit?....

that was the ORIGINAL idea behind this (behind every myth lie some nugget of
truth shrouded in history) -- to spread IO out.  This was a *relatively* ok way
to spread the IO out.

You could have achieved the SAME benefits by:

create tablespace t1 datafile '/disk1/t1.dbf', '/disk2/t1.dbf';

and then

create table t ( .... ) storage ( initial 512k next 512k pctincrease 0 )
tablespace t1;

create index i on t( ... ) storage ( initial 256k next 256k pctincrease 0 )
tablespace t1;

Now, you have lots of users doing full scans, lots of users doing indexed reads
and you are hitting the disks pretty much evenly (cause we tend to round robin
the extents so table T would be spread across the disks as would index I).

Today -- striping is commonly used.

The goal -- the only goal here -- is even IO distribution.  However you can best
achieve that -- go for it.  It is my experience that indexes are more "cachable"
(eg: cache hit = high) and full scans are not -- so just putting tables on one
and indexes on another wouldn't do it.

So, moving indexes to their own tablespace and associating a single file with a
tablespace is one approach.  Striping is perhaps a better, more fair one.

September 03, 2002 Pranav -- Thanks for the question regarding
      "PL/SQL Tables, Temporary Table or Materialized view.", version 8.1.7.3

      You Asked
Hi,
  I have to delete data from about seven tables for about 100 schemas based on a
join on two table and at the end delete data from one of the join tables too.
Here is what the wury looks like:

This what the join looks like:
vg_rolloff_date := TO_DATE('01-' || TO_CHAR(ADD_MONTHS(SYSDATE,
-39),'MON-YYYY'));

SELECT Col1, Col2, Col3 FROM UserName.Table2
WHERE col_DATE < vg_rolloff_date
AND (Col1, Col2, Col3, col_DATE) NOT IN
 (
    SELECT Col1, Col2, Col3, col_DATE
    FROM User1.Table1
    WHERE col_DATE1 < vg_rolloff_date
    AND   col_DATE2 >= vg_rolloff_date
 );

The delete of tables:
DELETE FROM UserName.Table3 NOLOGGING
WHERE (Col1, Col2, Col3) IN
(
  SELECT Col1, Col2, Col3 FROM UserName.Table2
  WHERE col_DATE < vg_rolloff_date
  AND (Col1, Col2, Col3, col_DATE) NOT IN
  (
    SELECT Col1, Col2, Col3, col_DATE
    FROM User1.Table1
    WHERE col_DATE1 < vg_rolloff_date
    AND   col_DATE2 >= vg_rolloff_date
  )
);
The Delete tables are from Table3 to Table8.
In the end I also have to do this:
DELTE FROM UserName.Table2 NOLOGGING
WHERE col_DATE < vg_rolloff_date
AND (Col1, Col2, Col3, col_DATE) NOT IN
 (
    SELECT Col1, Col2, Col3, col_DATE
    FROM User1.Table1
    WHERE col_DATE1 < vg_rolloff_date
    AND   col_DATE2 >= vg_rolloff_date
 );

This happens every month for about 100 users.  Each has their own set of these
tables.  My question is what would be the best thing to use for the join table.
Should I use PL/SQL Tables, Temporary tables or Materialized view ( Can I put
the vg_rolloff_date formula in Materialized view).  Which would be the best way.

Also with Materialized view if I do Refresh on Demand and Refresh the view
before callin it in the same procedure would I have the latest data or would I
have the data from the transaction before the call to Materialized View.  Thanks
for all the help.
Pranav

      and we said...
Well, first off -- I hate to disappoint you but that "nologging"  you have is a
CORRELATION name -- it is not an option to delete without logging!!!

For example, your:

DELETE FROM UserName.Table3 NOLOGGING
WHERE (Col1, Col2, Col3) IN

is the same as:

DELETE FROM UserName.Table3 Barney_Rubble
WHERE (Col1, Col2, Col3) IN

or

DELETE FROM UserName.Table3
WHERE (Col1, Col2, Col3) IN

There isn't any "nologging" option for a DELETE (or update or insert without an
APPEND hint)

It'll depend on how many rows are in the query:

SELECT Col1, Col2, Col3 FROM UserName.Table2
WHERE col_DATE < vg_rolloff_date
AND (Col1, Col2, Col3, col_DATE) NOT IN
 (
    SELECT Col1, Col2, Col3, col_DATE
    FROM User1.Table1
    WHERE col_DATE1 < vg_rolloff_date
    AND   col_DATE2 >= vg_rolloff_date
 );

as to whether you might consider using a PLSQL table or a global temporary
table.  Say you have just a couple of hundred (hundred, not thousand).  The a:

SELECT Col1, Col2, Col3 bulk collect into l_c1, l_c2, l_c3
  FROM UserName.Table2
WHERE col_DATE < vg_rolloff_date
AND (Col1, Col2, Col3, col_DATE) NOT IN
 (
    SELECT Col1, Col2, Col3, col_DATE
    FROM User1.Table1
    WHERE col_DATE1 < vg_rolloff_date
    AND   col_DATE2 >= vg_rolloff_date
 );

followed by:

forall i in 1 .. l_c1.count
DELETE FROM UserName.Table3 NOLOGGING
WHERE col1 = l_c1(i) and col2 = l_c2(i) and l_c3(i);

could be appropriate, else,
DELETE FROM UserName.Table3 NOLOGGING
WHERE (Col1, Col2, Col3) IN (select * from global_temp_table );

would be.

For the NOT IN -- it'll be best if the columns in the subquery are defined as
NOT NULL, and use the cost base optimizer.  We have better opportunities for
plans that way.

I don't see how a materialized view would be used here, unless you mean for the
"temp table" and there I wouldn't use a materialized view, just a temporary
table.
Review
      & Followup

      Rating:
      4
  August 19, 2002
Reviewer:  Shari
       from Middletown, CT USA
Thanks, Tom. It is very useful. But I can't see why a materialized view wouldn't
be used for this case, even for millions of rows. Pls would you explain that?

          Followup:

why would it be?  what would its purpose be?  can you explain that?

      Rating: 4
materialized
      view  September 03, 2002
Reviewer:  Shari
       from middletown, CT USA
The major purpose to use a MV for this case is to run faster, kind of
performance issue. Can it be much quicker if using a MV instead of a temp table?

          Followup:

If the data needs to be materialized right before the delete -- using an MV or
temporary table would be -- the same.

Since the data in the subquery (which would be the MV) is parameterized (eg:
unknown) the MV would need be built right before the delete - taking the SAME
time a temp table would (maybe even more since it might be logged and an MV is
much more complex then just a temp table)

      Rating: 3
How to specify
      the location of ISNAP_$TABLENAME  September 03, 2002

Reviewer:  Shailandra  from NJ
Hi Tom,

I am creating a partitioned MV. Its creates the MV fine but it creates its
corresponding isnap_$mv to default tablespace. Can this isnap_$mv be partitioned
and also can I specify which tablespace it should be created.

Thanks

September 03, 2002 Morgan -- Thanks for the question regarding
      "shared pool latch & library cache latch", version 8.1.5

      You Asked
Hi Tom,

I have an Oracle 8.1.5.1.0 database running with 610 MB shared pool.
I notice that there are 1 shared pool latch and 19 library cache latches
according to v$latchname and v$latch_children.

To my understanding,the numbers of cache buffer lru chain latches are determined
by db_block_lru_latches paramenter in initsid.ora file.

I am wondering how Oracle determine the number of these two latches.

Thanks,

      and we said...
It is based on size.  Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> show parameter shared_pool;

NAME                                 TYPE    VALUE
------------------------------------ ------- ------------------------------
shared_pool_reserved_size            string  1536000
shared_pool_size                     string  30720000
ops$tkyte@ORA817DEV.US.ORACLE.COM> show parameter shared_pool_size

NAME                                 TYPE    VALUE
------------------------------------ ------- ------------------------------
shared_pool_size                     string  30720000
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from v$latch_children where
name like 'lib%';

  COUNT(*)
----------
         3

ops$tkyte@ORA817DEV.US.ORACLE.COM>

30m -- I have 3.

ops$tkyte@ORA8I.WORLD> show parameter shared_pool_size

NAME                                 TYPE    VALUE
------------------------------------ ------- ------------------------------
shared_pool_size                     string  102400000
ops$tkyte@ORA8I.WORLD> select count(*) from v$latch_children where name like
'lib%';

  COUNT(*)
----------
         5

ops$tkyte@ORA8I.WORLD>

100m, I have 5.

The bigger the shared pool, the more latches we allocate.

Same with the lru latches (a paramter with is obsoleted in the currernt releases
of Oracle -- we set it for you and don't let you touch it anymore)
Review &
      Followup

      Rating: 3
Not a related
      comment  September 03, 2002
Reviewer:  A
      reader  from Bangalore
Tom,
   Iam getting the following error.

SQL> show parameter shared_pool;
unknown SHOW option "parameter"
unknown SHOW option beginning "shared_poo..."

What could be the cause?

regards

          Followup:

You are using a version of sqlplus older then 8i.  8i added "show parameter" to
sqlplus.

September 03, 2002 -- Thanks for the question regarding "Export,
      Import of partitioned table", version 8.1.7

      You Asked
Hello Tom,

We have a huge partitioned table(partitioned by day). The data in this
partitioned table has to be moved to another(development) instance on a regular
basis. We do not need all the days of data but at the most 3 or 4 days of data
at any point in time.

The problem here is we cannot use transportable tablespaces(which we would like
to) as the transportable set is not complete.

Export / Import is time consuming with such huge partitions.
(As with each partition is exchanged with a table and that table is exported.)

What is the best alternative to have just a subset of partitioned table but not
whole?

thanks and regards,

      and we said...
Well, if you do the swap -- you can transport. If you swap a partition P1 of
table T1 with a table XXXX and P1 was in its own tablespace before the swap, you
can transport that tablespace.

Otherwise, you can just export a partition:

$ exp help=y
....
     Example: EXP SCOTT/TIGER GRANTS=Y TABLES=(EMP,DEPT,MGR)
               or TABLES=(T1:P1,T1:P2), if T1 is partitioned table
....

use tables=(T1:P1)

You can also use a WHERE CLAUSE on export as well "query=....." to get a slice
different from a partition.
September 03, 2002 Jay -- Thanks for the question regarding "Stored
      Functions - 8i - Apps 11i - Oracle Reports 6.0", version 8.1.7.3.0

      You Asked
While I see that you don't use Oracle Reports, you probably can answer this
question intuitively anyway.

I am going to be spending a lot of time Modifying Oracle Reports to be used in
Oracle Apps 11i.

Instead of adding tables to the base queries in the report or adding functions
using the report tool, I have created a package to store commonly used
functions.  I am calling these functions

Question:
A) I am worried about performance and
B) Is this idea/practice inherently flawed.

Perceived benefits:
1) Central location for common questions.
2) Easier to append reports and see what the original/standard was.
3) Performance because it is stored in the database server?

Thanks for reviewing my question and for this site which I have only recently
discovered.

      and we said...
a) you should be
b) not necessarily

Functions used in the select (eg: select f(x) from t) are usually OK if there
are hundreds of rows in the result set.  Functions that are used in the
predicate (eg: select * from t where y = f(x) ) can be really bad.

It all depends on how many times they are executed.  You should just pretend
that a stored function will take somewhere between 1/100 and 1/1000 of a second
to call.  You do that 10 times - no problem.  100 times -- no problem.  Do it
100,000 times and *big problem*.

So, just be aware that there will be overhead and there will be times they work
great and times when you have to code around them.

Review &
      Followup

      Rating: 5
Thank
      you!  September 03, 2002
Reviewer:  Jay
      Kiefer  from Windsor, CT USA
Sincerely, Thank You very much (5+ question  - 5+ for site)

May I follow up?
I was thinking select f(x) from t where x is a column(s) of t. The functions may
be nothing more than stored select statments (no procedural logic).

Can you compare/contrast using a function to my other choices.

May other choices (that I know of) are
a)  join the additional tables in From clause
b)  use an Oracle Reports 6.0 function (which I think translates to a trigger)

          Followup:

c) select (select)

eg:

scott@ORA817DEV.US.ORACLE.COM> select dname, (select count(*) from emp where
emp.deptno=dept.deptno) cnt
  2  from dept;

DNAME                 CNT
-------------- ----------
ACCOUNTING              3
RESEARCH                5
SALES                   6
OPERATIONS              0

scott@ORA817DEV.US.ORACLE.COM>

September 03, 2002 kit -- Thanks for the question regarding "oracle
      developer moving to java", version 8.1.7

      You Asked
Hi, tom
been to your site many times and seen you often recommned other books.
being a predominant oracle person, it appears its vital to pick up some java
knowledge.

however, there is always a time constraint and most of my time goes in learning
new oracle ideas. I have been doing oracle for 15 years and even now i call my
self an expert. So I would like to learn java but in terms of getting by with
oralce.

I noticed 1 book you recommended recently, 9i pl/java programming book but it
seems to cover lots of the java in the db which i gather is being replaced by it
being in the application server

do you know of such easy understanding books.

there are so many oracle java books,it be nice your thoughts on them

thanks
kit

      and we said...
No, it doesn't really cover the J2EE java in the database (and thats what is
being de-emphasized)

If you check out the table of contents:

http://www.amazon.com/exec/obidos/tg/stores/detail/-/books/1861006020/contents/ref=pm_dp_ln_b_2/002-4751878-3050444
I think you'll find it is a gentle introduction into Java from a PLSQL/Oracle
developers perspective.  Showing bits and pieces where Java is truly useful in
and out of the database (using java to EXTEND the database).

It concentrates on Java + DATABASE stuff (hey, I helped do the technical
editing, wouldn't have it any other way).

I've read other java books (and I was a C and C++ programmer before this...) and
they just all make my head spin.  I think you'll find this one to be a "gentle"
introduction.  It is ideally suited, perhaps even uniquely suited, to meet your
goal of "So I would like to learn java but in terms of getting by with oralce."

But -- as anyone is free to do -- I rekon there'll be some other suggestions
popping up under this answer anytime now...

Review &
      Followup

      Rating: 3
Thanks for the
      question regarding "oracle developer moving to java", version
      8.1.7  September 04, 2002
Reviewer:  kit
       from england
thanks

September 03, 2002 Doug -- Thanks for the question regarding "DNS
      name vs IP address in Net8", version 8.1.7

      You Asked
What is the overhead in using a DNS name (ie abc.oracle.com) vs 123.123.123.123
in Net8?  Is this resolved at the session level, packet level, or ???

Thanks for your help.

      and we said...
It is resolved by the OS and the OS does the caching, we resolve it via OS calls
each time we need to dereference it.

IMO -- the overhead is neglible on a properly configured system and you should
always use the hostname.
Review & Followup

      Rating: 2
Guys, there is
      a big confusion here.  October 30, 2001
Reviewer:
      Andre Whittick Nasser  from Rio de Janeiro, Brazil
I did not understand if you are referring to hostname vs IP address or DNS vs IP
address.

DNS
===

Typically, when you use adresses with domains like server.acme.com.br you will
use DNS mechanisms external to your system, probably involving naming resolution
in servers located in other regions, possibly other countries, which, logically,
will take some time.

Your machine is configured to "point" to a top-level server that will begin DNS
name resolution.

When your machine encounters something like server.acme.com.br, what it will do
is the following in a hierarquical manner:

1) Find a level 1 DNS server that maps the country domains like
server.acme.com.br (br stands for Brazil).
2) The top, level 1 server will redirect the request to the Brazil's server that
will identify .com as a a domain for commercial organizations.
3) Next, another server will locate Acme company in the .com domain.
4) At last, inside Acme, the machine named "server" will be found.

Note that US DNS addresses do have the country suffix, like ".br". They usually
simply end in .com, .org, .edu, etc

hostnaming
==========

On the other hand, if you are simply mentioning the hostname, that is an alias
that you place in a file called HOSTS (/etc/hosts in UNIX) where you say IP
address www.xxx.yyy.zzz is frinedly know as, say, "server". This, as Tom said,
is immediate.

Instead of issuing: ping www.xxx.yyy.zzz
you just do: ping server

          Followup:

I personally did not find it confusing.  Their terminology might not have been
100%.

The gist of the question was:

should I have tnsnames.ora entries like this:

appsweb4 =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = appsweb4.us.oracle.com)(PORT=1521))
    (CONNECT_DATA = (SID = apps4))
  )

or like this:

appsweb4 =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 1.2.3.4)(PORT=1521))
    (CONNECT_DATA = (SID = apps4))
  )

The first has to be resolved via DNS (which is what they must be using, not many
people use "files" anymore) and the other does not.  my answer -- use the
hostname, not the ip address in the config files.  You'll be hurting later on
when the ip address changes if you don't.

The DNS lookup, as it is cached locally, is pretty darn fast and should not
scare anyone away.  don't use ip's, use hostnames.

      Rating: 5
re: Guys, there
      is a big confusion here.  October 30, 2001

Reviewer:  A reader
> I did not understand...

So?  Is this askandre.oracle.com?

      Rating:
      3
Addresses  October 30, 2001
Reviewer:
      Andre Whittick Nasser  from Rio de Janeiro, Brazil
Yes, if you are talking about pure Net8 configuration, you are correct.

NEVER HARDCODE YOUR IP ADDRESSES !!!

It implies reconfiguring every tnsnames.ora file. Whereas using alias, either
via DNS or through any other centralized naming method, there will be only one
place to change.

As to the caching stuff, if you are accessing always the same database, the IP
address will be in cache, all right.

But if you think of conventional, all-around internet navigation, on the
average, DNS *WILL* incurr additional overhead.

      Rating: 3
Hostname vs. IP
      address  October 30, 2001
Reviewer:  Lin
       from Arlington, VA
Tom,

My first time here. Glad to find a wealth of info you provided. Thanks for your
great service to Oracle users.

Now get back to the topic. We use 8.1.7 on Windows 2000. We have two servers and
configured tnsnames to access the other database from each machine. Recently,
the network admin changed the IP addresses on these servers (hostnames remained
the same). When I tried to connect the 'remote' database using the old
tnsnames.ora file, I got a time out error (ORA-12535). Then I created a new
service name in Net8 using the new IP address instead of hostname, it worked. It
seemed that the hostname did not resolve to the new IP address. This is in
contrast of your follow up comment, that is, use hostname, not IP address when
connecting. Any advice on this issue? Thanks.

          Followup:

In all probability you were not using DNS to resolve that name OR your DNS was
messed up.

Windows/2000 has a hosts file as well -- maybe you had an entry there with the
old ip address.

Next time, try pinging the hostname from windows -- if you cannot ping it,
something is wrong in your network configuration and needs to be fixed.

      Rating: 3
Hostname vs. IP
      address  October 30, 2001
Reviewer:  Lin
       from Arlington, VA
Tom,

My first time here. Glad to find a wealth of info you provided. Thanks for your
great service to Oracle users.

Now get back to the topic. We use 8.1.7 on Windows 2000. We have two servers and
configured tnsnames to access the other database from each machine. Recently,
the network admin changed the IP addresses on these servers (hostnames remained
the same). When I tried to connect the 'remote' database using the old
tnsnames.ora file, I got a time out error (ORA-12535). Then I created a new
service name in Net8 using the new IP address instead of hostname, it worked. It
seemed that the hostname did not resolve to the new IP address. This is in
contrast of your follow up comment, that is, use hostname, not IP address when
connecting. Any advice on this issue? Thanks.

      Rating: 5
if the host
      name or ip changes then where to look  September 03, 2002

Reviewer:  A reader
Tom,

System:
(8.1.7.3.0 running on Windows 2000 advanced server running Oracle Enterprise
Edition)
There has been a talk in our company to change IP address/computer names
of machines.
Some of our database servers are using machine names, some are using static IP
addresses.
1.I am looking for some document (a step by step one) to know what to do when IP
address/Machine name changes on a machine that has Oracle running on it.
2.I was talking to our Network Administrator and he recommended using IP's
(172.16.2.223) instead of machine name (555g121b) because IP's are static.
Your thoughts on this?

TIA

          Followup:

1) there should be nothing that needs to be done.

Only if you hard coded IP addresses into configuration files (they would be the
tnsnames.ora and listener.ora files) would you have to do anything.

Changing the machine name -- that should be OK as long as the HOSTNAME doesn't
change.  If you change the HOSTNAME (the dns name) -- you would have to edit all
of the tnsnames.ora and listener.ora files and fix them up.

2) you need a new network administrator -- that is my thought.  Anyone who calls
themselves a network admin and says "use IP addresses, not hostnames" needs to
go back to class -- they missed the first day apparently.

That would be the dumbest thing you could do.  So, you go put 172.16.2.223 into
all of the configuration files all over the place (all of the clients
tnsnames.ora, your listener.ora).  Great.  Next week they tell you the data
center is moving and taking your machine with it.  Your new IP address is
12.136.32.23.  Now what?  Time to go hunt down all of those files and fix them.
OR -- you could have put 555g121b into the config files -- updated DNS and
wah-lah.  You are back in business and didn't have to do a single thing.

Just read the answer to this original question -- you see my thoughts on this
subject.

      Rating: 5
Why use
      tnsnames?  September 03, 2002
Reviewer:
      Dave  from Ohio
As a side issue, if folks think it is a pain to have to go round redistributing
a new tnsnames.ora file or editing the old one, why are they using them? They
should be considering using Oracle Names/LDAP instead. Add a new instance/delete
an instance/modify a hostname, no problem. Using tnsnames is like some kind of
job-creation scheme for admins.

          Followup:

Agreed, we use LDAP and Oracle nameserver internally ourselves.

      Rating: 5
services.ora
      and snmp_ro.ora  September 03, 2002
Reviewer:
      A reader
Tom,
System:
(8.1.7.3.0 running on Windows 2000 advanced server running Oracle Enterprise
Edition)

Here we use static IP's but we keep changing machine name..
So, that's why the admin suggested to use IP.
Anyway...the machine name is about to change...and I was searching for the
machine name in Oracle directory to see where I need to make a change...
Besides the two that you mention..there are couple more:
services.ora and snmp_ro.ora

services has entries like:
555g121b = (ORACLE_NODE, 555g121b, 555g121b, (PLATFORM=(osName=Windows
NT)(osVersion=5.0)))

listener_555g121b = (ORACLE_LISTENER, 555g121b, (ADDRESS_LIST = (ADDRESS =
(PROTOCOL = TCP)(HOST = 172.16.2.223)(PORT = 1521))))

corp = (ORACLE_DATABASE, 555g121b, (DESCRIPTION=(ADDRESS_LIST = (ADDRESS =
(PROTOCOL = TCP)(HOST = 172.16.2.223)(PORT =
1521)))(CONNECT_DATA=(SID=prod)(SERVER=DEDICATED))), listener_555g121b)

555g121b:80 = (oracle_sysman_webserver, 555g121b,
http://555g121b/server-status,
Apache/1.3.12.0.1a,
E:\oracle\Apache\Apache\conf\httpd.conf, E:\oracle\Apache\Apache)

As you can see..it is a mix of both machine name as well as
IP.

snmp_ro.ora has entries like:

snmp.visibleservices = (listener, prod)
snmp.shortname.listener = LISTENER
snmp.longname.listener = listener_555g121b
snmp.configfile.listener = E:\oracle\network\admin\listener.ora
snmp.oraclehome.listener = E:\oracle
snmp.servicename.listener_555g121b = OracleOraHome81TNSListener
snmp.SID.prod = prod
snmp.oraclehome.prod = e:\oracle
snmp.address.prod =
(ADDRESS=(PROTOCOL=BEQ)(PROGRAM=ORACLE)(ARGV0=ORACLEprod)(ARGS='(DESCRIPTION=(LOC
AL=YES)(ADDRESS=(PROTOCOL=beq)))'))
snmp.remoteaddress.prod = (DESCRIPTION=(ADDRESS_LIST = (ADDRESS = (PROTOCOL =
TCP)(HOST = 172.16.2.223)(PORT =
1521)))(CONNECT_DATA=(SID=prod)(SERVER=DEDICATED)))
ifile = E:\oracle\network\admin\snmp_rw.ora
ifile = E:\oracle\network\admin\sqlnet.ora

Question:
1.services.ora and snmp_ro.ora....do i need to change the machine name to the
new machine name in all the entries..
Aren't these two files recreated when the instance starts..
Well..Services.ora..when services starts...and snmp_ro.ora
when listner starts.

2.O.K...O.K.... I will read up on Oracle Names/LDAP.

TIA

          Followup:

or, just stop using DNS and make up your own hostname... never heard of people
constantly changing HOSTNAMES on you -- ip addresses, yes, hostnames -- no.  I'd
put my foot down after a bit.

      Rating:
      5
clarify..  September 03, 2002
Reviewer:
      A reader
So, I guess I was right about my (1) question that services.ora and snmp_ro.ora
will recreate themselves?

"or, just stop using DNS and make up your own hostname"
How do you do this?

          Followup:

Those are oem and snmp files and to be honest -- I've never used them.  No ideas
but I would guess that they would not recreate themselves.

Plus you have the iAS stuff -- not just the database. No ideas down there.

changing a hostname is *just a really bad idea*.  They have it sdrawkcab where
you work.

To do it yourself hostnames -- /etc/hosts on Unix
(c:\winnt\system32\drivers\etc\hosts or something like that in windows)

September 04, 2002 Vikas -- Thanks for the question regarding
      "Misc.", version 8.1.7

      You Asked
Hi Tom,
Q1. I was very much surprised to see the following behavior or the ref cursor
      Variable1   ref_curcor_type;
      V_number  number;
    Open Variable1 for ‘select deptno,dname from dept ‘;
    Fetch    Variable1 INTO v_number;   -- this fetch does not give
--error as it will give in the case of explicit
--PLS-00394: wrong number of values in the
--INTO list of a FETCH cursor
   Close Variable1;
   Would you please explain me this behavior.
   Q1. I have 15000 rows in a table which statement would be faster
        SELECT * FROM mytab
        SELECT * FROM mytab WHERE ROWNUM <15001
   I tried this one and was astonished to find the later one taking 1/10th the
time of the first? What could be the reason? (before testing I made numerous
inserts and updates on  mytab for about 50000+ rows, which were rolled back.)

Q3 which is faster
(i)    SELECT * FROM mytab
(ii)    SELECT(col1,col2……all the cols) FROM mytab.

Regards,
Vikas Sharma

      and we said...
q1) looks like a harmless bug that is fixed in 9.0 (Oracle9i).  It throws an
error in that release and up.  Does not apply before 8i (as there were no
dynamically opened ref cursors)

q2) Easy, you advanced the HIGH WATER MARK on the table.  The 15,000 rows you
fetched are "at the front of the table".  You added 50,000 rows after them
(extending the table out) and then undid that insert.  The table however has
enough blocks now to 65,000 rows (and there are only 15,000 in there).  When we
full scan -- we have to read every block that might contain data.  Hence, select
* from mytab has to scan all blocks.  The query select * from mytab where rownum
< 15001 -- it gets to STOP when it reads the 15,000th row -- it skips the blocks
that held the other 50,000 rows

You can see this via:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t
  2  as
  3  select *
  4    from all_objects
  5   where rownum <= 15000;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select * from t;

15000 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select * from t;

30000 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> rollback;

Rollback complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

15000 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'T'

Statistics
----------------------------------------------------------
          0  recursive calls
          5  db block gets
       1813  consistent gets
          0  physical reads
          0  redo size
    1788378  bytes sent via SQL*Net to client
     111314  bytes received via SQL*Net from client
       1001  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      15000  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where rownum < 15001;

15000 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   COUNT (STOPKEY)
   2    1     TABLE ACCESS (FULL) OF 'T'

Statistics
----------------------------------------------------------
          0  recursive calls
          4  db block gets
       1192  consistent gets
          0  physical reads
          0  redo size
    1788378  bytes sent via SQL*Net to client
     111314  bytes received via SQL*Net from client
       1001  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      15000  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

See how the number of consistent gets varies? purely due to the HWM of the table
being extended out and the where rownum query getting to abort early.

q3) they are the same.  the second form would be prefered.  protects you from
CHANGE in the future (additional columns, different orders of columns, etc)
Review &
      Followup

      Rating: 4
How to query
      the HWM of the table ?  September 03, 2002

Reviewer:  Kenny  from HK
How can we find out the HWM of the table ?
i tried searching DBA_TABLES but seems not there.

THX.

          Followup:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:231414051079

      Rating:
      4
Thanks,  September 04, 2002
Reviewer:
      Vikas Sharma  from Delhi, India
As always, the way you explain, simply great. Thanks!!

Regards,

Vikas.

September 04, 2002 Colin -- Thanks for the question regarding
      "Clustering 817 database on AIX", version 817

      You Asked
I need to find out how to implement Oracle 817 on an IBM RS6000 cluster, like
which variations of high availability work on AIX 4.3.3 (i.e. parallel server
only, failsafe, standby etc.).
Maybe someone can give me a pointer to some good information source about doing
this so it will really work. Up to now my experience has been mainly with
Windows NT and Sun Solaris Clusters.
Thanx

      and we said...
I asked Marshall Presser our resident RAC / HA guru to look at this and he says:

Failsafe is a Windows only OS failover product.  This is not to be confused with
Oracle Parallel Fail Safe (in 8i) and its follow on product, Oracle RAC Guard
(in 9i).

Standby gives you a different kind of protect than OPS.  OPS protects against
failures of technology within a data center, i.e.  h/w failure of a node, OS
crash, Oracle instance crash.  The OPS model assumes a single storage system
shared by multiple nodes in a cluster.   Standby protects against the loss of
the data center in general, and a disk subsystem in particular.  The standby
model assumes two storage systems, one at a production site and a "mirrored"
copy at a standby site. Should the primary site vanish, processing can be
started on the secondary site.

More information is available at:

http://otn.oracle.com/deploy/availability
http://otn.oracle.com/products/oracle9i/content
My suggestion would be to get a Quick Start from OCS (consulting).  They have
people who do pretty much nothing but OPS and RAC installations and
configurations and understand all the nuances of individual platforms.

September 04, 2002 Jon -- Thanks for the question regarding "Select
      Top and Oracle", version 8

      You Asked
I'm moving over from SQL Server to Oracle and I'm not able to use a Top N
criteria with Oracle (e.g. Select Top 10 * from Customer).  It works fine with
Microsoft stuff, but I can't seem to get it to work with Oracle and it looks
like it might not be supported.

Is there an Oracle Ninja in here anywhere that can clarify this for me ?

Many Thanks,

Jon

      and we said...
select *
  from ( YOUR_QUERY_HERE, INCLUDING ORDER BY )
 where rownum <= :n;

for example:

scott@ORA817DEV.US.ORACLE.COM> select ename, job, sal
  2  from ( select ename, job, sal from emp order by sal desc )
  3  where rownum <= 5;

ENAME      JOB              SAL
---------- --------- ----------
KING       PRESIDENT       5000
SCOTT      ANALYST         3000
FORD       ANALYST         3000
JONES      MANAGER         2975
BLAKE      MANAGER         2850

Or to find the top N within a group:

scott@ORA817DEV.US.ORACLE.COM> select deptno, ename, job, sal
  2  from ( select emp.*, row_number() over ( partition by deptno order by sal
desc ) r
  3           from emp )
  4  where r <= 2;

    DEPTNO ENAME      JOB              SAL
---------- ---------- --------- ----------
        10 KING       PRESIDENT       5000
        10 CLARK      MANAGER         2450
        20 SCOTT      ANALYST         3000
        20 FORD       ANALYST         3000
        30 BLAKE      MANAGER         2850
        30 ALLEN      SALESMAN        1600

6 rows selected.

That got the top 2 compensated people in each DEPARTMENT of emp.

"select top N * from ..." would be a MS proprietary syntax for doing that --
use rownum or row_number in Oracle.
September 04, 2002 Jawahar -- Thanks for the question regarding
      "MUTATION Trigger", version 8.1.7

      You Asked
Hi Tom,

I really appreciated all the help.

Here is what i am doing.
I have table A - which has long columns and
       table B - which has cLOB column
Both Table are identically in structure except Long replace by Clob.

When an insert happens on TABLE A, I want to insert into TABLE B though the
trigger using JAVA (to all to see "TO_LOB" function).
I was using AFTER Trigger ON TABLE A to call the JAVA to insert into table B.

This is a third party application.
I am getting this MUTATION Trigger error.  Is there any work around for this.
Thank you in advance for any feedback if possible.

Thanks,
Jawahar.

      and we said...
Why are you using java?

Anyway, this might be significantly more complex then you imagine.

You see, most applications would INSERT the row into the table and then use the
PIECEWISE API's on the long to "feed" the data to it.  When the insert happens
-- the long data *doesn't exist yet*!!!!

If your 3rd party app does that -- no joy for you.  Here is how you can do it IF
they always insert the long data right in the insert itself:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t1 ( x int primary key, y long
);

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t2 ( x int primary key, y clob
);

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package my_state_pkg
  2  as
  3          type array is table of t1.x%type index by binary_integer;
  4
  5          newones  array;
  6          empty    array;
  7  end;
  8  /

Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create trigger t1_BI before insert on t1
  2  begin
  3          my_state_pkg.newones := my_state_pkg.empty;
  4  end;
  5  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create trigger t1_AIFER after insert on t1
for each row
  2  begin
  3          my_state_pkg.newones(my_state_pkg.newones.count+1) := :new.x;
  4  end;
  5  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create trigger t1_AI after insert on t1
  2  begin
  3          for i in 1 .. my_state_pkg.newones.count
  4          loop
  5                  execute immediate
  6                  'insert into t2
  7                   select x, to_lob(y)
  8                     from t1
  9                    where x = :x' using my_state_pkg.newones(i);
 10          end loop;
 11  end;
 12  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t1 values ( -1, 'Hello world' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t1
  2  select 100000+user_id, username from all_users
  3  where rownum <= 5;

5 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          l_data long := rpad('*',32000,'*');
  3  begin
  4          insert into t1 values ( 0, l_data );
  5  end;
  6  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> column data format a20
ops$tkyte@ORA817DEV.US.ORACLE.COM> select x, dbms_lob.getlength(y),
dbms_lob.substr(y,20,1) data
  2    from t2
  3  /

         X DBMS_LOB.GETLENGTH(Y) DATA
---------- --------------------- --------------------
        -1                    11 Hello world
    100000                     3 SYS
    100005                     6 SYSTEM
    100011                     5 OUTLN
    100027                    10 OPS$ORA817
    100025                     6 CTXSYS
         0                 32000 ********************

7 rows selected.

Review &
      Followup

      Rating: 4
Reason for
      Table Mutation?  September 04, 2002
Reviewer:
      Basharat  from Dubai
Tom, Can you briefly explain circumstances under which table mutation happens
and what is the way around it?

          Followup:

http://asktom.oracle.com/~tkyte/Mutate/index.html

September 04, 2002 chirayu -- Thanks for the question regarding
      "statment level triggers", version 8.1.6

      You Asked
Dear Tom,

 I need your asistance in understanding statement level triggers.
 My queries are

 (1) what constitutes a 'statement' in oracle.
 (2) What can a statement level trigger do and what it cannot.
 (3) please give a simple example of a statement level trigger.

Thanx in advance,
Regards
chirayu sutaria

      and we said...
1) insert update delete

2) it can do anything it wants.  It does not have access to the :new and :old
records (eg: it is not a row level trigger)

3)

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:5260222732521
shows both a BEFORE and AFTER statement trigger.
Review &
      Followup

      Rating:
      4
Excellent  September 06, 2002
Reviewer:
      chirayu sutaria
Excellent as ever, I will keep on asking questions
if you answer like this !!!

September 04, 2002 shi -- Thanks for the question regarding
      "voting", version 8.1.6

      You Asked
Tom:

I am designing a voting system.

requirement is each time after a person vote, she can
see the result.

such as  right now
1 person selected A
10 person selected b
6 person selected c
2 person selected d

and then judy make her select selection A.
we will  give her the result
1/20 selected a
10/20 selected b
6/20 selected c
2/20 selected d.

now I have two choice to maintain the total number of voting.
one is put an after insert trigger, add one each time a
person made she choice.
the other choice is everytime i select count(0) from vote_table.

which method is better(for performance)? any other idea?

Thanks,

      and we said...
Or option 3 - use a materialized views with on commit refresh:

scott@ORA816.US.ORACLE.COM> create      table votes ( data
number );
Table created.

scott@ORA816.US.ORACLE.COM> create materialized view votes_total
  2  build immediate
  3  refresh on commit
  4  as
  5  select 'A' dummy, count(*) cnt
  6    from votes
  7   group by 'A';

Materialized view created.

scott@ORA816.US.ORACLE.COM> insert into votes values ( 1 );

1 row created.

scott@ORA816.US.ORACLE.COM> select * from votes_total;

no rows selected

scott@ORA816.US.ORACLE.COM> commit;

Commit complete.

scott@ORA816.US.ORACLE.COM>
scott@ORA816.US.ORACLE.COM> select * from votes_total;

D        CNT
- ----------
A          1

scott@ORA816.US.ORACLE.COM>
scott@ORA816.US.ORACLE.COM> insert into votes
  2  select rownum from all_users;

12 rows created.

scott@ORA816.US.ORACLE.COM> select * from votes_total;

D        CNT
- ----------
A          1

scott@ORA816.US.ORACLE.COM> commit;

Commit complete.

scott@ORA816.US.ORACLE.COM> select * from votes_total;

D        CNT
- ----------
A         13

scott@ORA816.US.ORACLE.COM>

That'll be more concurrent perhaps then using a trigger at the
row level.

In any case, storing a summary off to the side, if there will be
a sizable number of rows -- is a good idea.

If there are many things to vote on (eg: your table has
thousands of rows but the total count for any one thing will be
in the tens/hundreds) an index and a normal "select count(*)"
would probably suffice as well.  Its hard to say -- I don't know
how many rows you plan on counting....
Review &
      Followup

      Rating:
      2
  May 09, 2001
Reviewer:
      A reader

      Rating: 5
An Example with
      Triggers + Temporary Table please ...  April 29, 2002

Reviewer:  Pascal  from Hamburg,Germany
Hi Tom,

I came across this Voting Logic and was interested to see how could this be
implemented in Oracle, by using Triggers ...
and Global Temporary tables..

I assume HIT rate  of 1 million in 1 Hour.
I assume also i need to update the Count Statistics every 10 seconds..or so  ..

There could be several Voting Options and several users.

Do you suggest storing the Totals for users and Voting Options in a Separate
table from Users or in the same Users table ?

An Example with Triggers  and/OR  Temporary Table  would be greatly appreciated
..

Thanks in advance

pascal

Thanks in advance

Pascal

          Followup:

You would not use global temporary tables for this at all (you sort of want the
data to be persistent after all).

I would not recommend using triggers for this at all.  I probably wouldn't use
on commit refresh either with your requirements -- I would refresh in the
background every 5 to 10 seconds instead.

It would most certainly be in a different table.

Triggers would be the wrong approach.  Materialized views -- the right approach.

      Rating: 5
Help for
      Trigger Implementation ...  May 31, 2002
Reviewer:
      Pasko  from HH,Germany
Hi Tom

Thanks for the response above.
I AM back to this  Voting stuff...
Your Mview Option is Great , but for now i would like  to start with the
Trigger Option

I have the following :

CREATE      TABLE   VOTe_DETAILs (
  ID                   RAW (16)      DEFAULT sys_guid() NOT NULL   ,
  date_into          DATE          NOT NULL,
  user_id              RAW (16)      NOT NULL,
  KEYWORD              VARCHAR2 (16)  NOT NULL,
  COUNTER             INTEGER       NOT NULL,
  CONSTRAINT vt_PK      PRIMARY KEY  ( ID )         ) ;

 Here  user_id and keyword  are foreign keys to other two Tables for
 Users  and  their coresponding keywords .

Here is the table for my Totals:

  CREATE TABLE CURRENT_VOTES (
     user_id      RAW (16)      NOT NULL,
     COUNTER            INTEGER       NOT NULL,
     KEYWORD            VARCHAR2 (16)  NOT NULL,
     last_update          DATE          NOT NULL,
     measuring_section      INTEGER       NOT NULL   --back to this later
     ) ;

Now here is my Trigger which i will use to keep all the Counts for a User_id and
specific Keyword ( i got some sample from salary Total Example from some Oracle
Docs )

Tell me if this trigger will keep track of all the counts  for all users and
keywords  and
insert them properly on the  current_votes table.

Now if i want to also add an Option to Track
specific Measuring Periods( Date_from - Date_to  to seconds )  for a
User_id  ,
how do you recommend i implement it.

If i have say another table for Tracking Measuring Sections  ,
how could i catch and synchronize the  Times beween Measuring Sections  when i
create new ones- say  ms1 , ms2 , ms3 , .....
without loosing any Vote  Detail records.

I can have many parallel Inserts in Vote_detail Table.

CREATE TRIGGER tr_vote_counter_aiudr

AFTER DELETE
OR INSERT
OR UPDATE OF
user_id   , keyword ,  counter  ON  vote_details

FOR EACH ROW

 BEGIN

   IF DELETING OR (UPDATING AND  :old.user_id  != :new.user_id
                            AND  :new.keyword  !=  :old.keyword )
   THEN
   UPDATE current_votes
   SET   tot   =  tot  -  :old.counter
   WHERE user_id  = :old.user_id   and
         keyword   =         :old.keyword   ;

   END IF;

   IF INSERTING OR (UPDATING AND  :old.user_id  != :new.user_id
                                    AND  :new.keyword  !=  :old.keyword      )
   THEN
   UPDATE current_votes
    SET    tot   =  tot   +  :new.counter
    WHERE user_id  = :new.user_id   and
          keyword       =         :new.keyword   ;

   END IF;

   IF ( UPDATING AND  :old.user_id  = :new.user_id
                       AND  :new.keyword     =  :old.keyword      AND
                        :old.counter    !=    :new.counter  )
   THEN
   UPDATE  current_votes
   SET  tot   = tot  - :old.counter  +   :new.counter
   WHERE user_id       =    :new.user_id   and
         keyword       =         :new.keyword   ;

   END IF;

   END;

   Thanks in advance

   pasko

          Followup:

why?  when this can be done in a single line of code -- why?

      Rating:
      5
Summarization  May 31, 2002
Reviewer:
      Andre  from Brazil
I have a similar situation, where I store a MV to generate the output as quickly
as possible. Even avoiding the restrictions in the DW doc -- which I think are
not clear enough -- I get the following error. I can only infer I can only have
join conditions in the WHERE caluse. Could you please shed some light on it ?
I'm using Oracle 8.1.7.

WORK@UNI2OPED> create table t1 (a number primary key, b varchar2(10));

Tabela criada.

WORK@UNI2OPED> create table t2 (c number primary key, d varchar2(10), e number
references t1(a));

Tabela criada.

WORK@UNI2OPED> create materialized view log on t1 with rowid;

Log de view materializada criado.

WORK@UNI2OPED> create materialized view log on t2 with rowid;

Log de view materializada criado.

WORK@UNI2OPED> create materialized view mv
  2  build immediate
  3  refresh fast on commit
  4  as select t1.a a, t1.b b, t2.c c, t2.d d, t1.rowid t1_rid, t2.rowid t2_rid
  5  from t1, t2
  6  where t2.e=t1.a and t1.a=0
  7  /
from t1, t2
     *
ERRO na linha 5:
ORA-12054: cannot set the ON COMMIT refresh attribute for the materialized view

WORK@UNI2OPED> create materialized view mv
  2  build immediate
  3  refresh fast on commit
  4  as select t1.a a, t1.b b, t2.c c, t2.d d, t1.rowid t1_rid, t2.rowid t2_rid
  5  from t1, t2
  6  where t2.e=t1.a
  7  /

View materializada criada.

          Followup:

ON COMMIT

Refresh occurs automatically when a transaction that modified one of the
materialized view's fact tables commits. Can be used with materialized views on
single table aggregates and materialized views containing joins only.

your first MV is not "containing joins only"

      Rating: 5
Thanks, but is
      my Trigger Logic Correct for someone without Oracle EE  June
      01, 2002
Reviewer:  pasko  from HH,Germany

Hi Tom
thanks very much for your prompt response,
It's true that the Materialized view solution is Great,
But i needed to implement this Voting stuff both on Enterprise and Standard
Edition Servers , and so you see i would be stuck when i don't have the
Materialized View enabled..

I just wanted to ask you if my above Trigger Logic wouldn't mess up/miss  my
Total Count.

Is it Correct?

Thanks and have a nice Weekend!

Pasko

          Followup:

Test it and see.  the logic looks "over done" to me -- I don't understand the
checks on the userid and such.  I would just:

if ( updating or deleting )
   decrement based on :old values using nvl(:old.counter,0)
enf if;

if ( updating or inserting  )
   increment based on :new values using nvl(:new.counter,0)
end if;

      Rating: 5
How to
      implement 'Sequence in Parent Scenerio '   September 04,
      2002
Reviewer:  Pasko  from Germany
Hi Tom,

I am back..

I would like to implement Measuring Sections with Voting , such that i could
define voting Sections or Voting rounds for some Customer.

I would like to implement this on a table such as this:

create table vote_config (
cust_id     number  ,
customer    varchar2(30) ,
vote_round  number ,
start_time date ,
end_time date

 ) ;

Alter table vote_config add  constraint  votes_pk
primary key ( cust_id , vote_round ) ;

My requirement is that for each Cust_id ,   vote_round should be increasing and
gap free.
( i think they have something like this in Designer ? (seq in parent ) ,
implemented through triggers , but i am not sure.

How could i implement this and do you think it's a good idea to keep track of
voting rounds this way.

Thanks

          Followup:

increasing and gap free -- he he he.

increasing great.

gap free -- more power to you.  You'll have to write your own sequence object
using a table and data.

      Rating: 5
Any suggestions
      on how to implement Voting Rounds ?  September 04, 2002

Reviewer:  A reader

          Followup:

Not really -- don't know exactly your requirements (not asking for them either
really....)

If voting round means "version" (eg: like a document version, etc) -- then you
are on the right path with a version number pushed down into the requisite
tables.

September 04, 2002 -- Thanks for the question regarding "calculate
      rbs required for an DML", version 8.1.7.0.1

      You Asked
Hi Tom

Is it possible to calculate the size of rollback segment needed for a DML? Or we
have to guess...

      and we said...
It is very hard as it would be a function of the index changes (how many splits
and other things will your DML cause on the index -- some might not cause any --
others thousands).

You can get a good ballpark feel for it by doing something like:

 UPDATE ...... where ..... AND ROWNUM < 500;

and viewing the used_ublk in v$transaction for your session (and then rolling
back).  You can extrapolate up and down from there -- but index maintenance will
be a huge wildcard in this.
Review &
      Followup

      Rating: 4
Just what I was
      looking for! ... but....  September 04, 2002

Reviewer:  Robert  from Memphis, USA
Tom,

Could you please elaborate on 'index maintenance', and how we may be able to
approximate its role in our rollback usage analysis.

Thanks,

Robert.

          Followup:

The best you can do is run the small test I suggest above and count blocks.

The indexes are a wildcard.  Say you have an index on column X and it has values
1, 2, .... 10,000 in it (one to ten thousand).  Now you do an update set x = -x.
 Just imagine what that might do to that index - the block splits as the data
moves around (and every row moves around alot here).

Then, do an update that doesn't move so much....

It is hard to say.

September 04, 2002 -- Thanks for the question regarding "multiset
      with group by", version 8.1.6

      You Asked
I have a table that has about 50 columns and I would like to group by 5 columns.
 Based on this grouping I would like to retrieve all the grouped records that
have a grouping count>1 into a collection with the same fields as the table
definition.

Currently, I have created this query using 2 selects on the same table, one for
the group by select on the 5 columns and the second select for all 50 columns
based on the where clause equal to that of the 5 group by columns.

My problem is this table is very large and I would only like to do one select on
this table.  It would be great if I could group the 5 columns together and the
50 columns of the grouping rows be placed into a collection.  Is this possible?

      and we said...
Ok, pretending that DEPTNO/JOB represent your 5 columns and ename, deptno, job,
mgr represent your 50 columns -- this (i believe) does it for you:

scott@ORA817DEV.US.ORACLE.COM> select *
  2    from (
  3  select ename, deptno, job, mgr, count(*) over ( partition by deptno, job )
cnt
  4    from emp
  5             )
  6   where cnt > 1
  7  /

ENAME          DEPTNO JOB              MGR        CNT
---------- ---------- --------- ---------- ----------
SCOTT              20 ANALYST         7566          2
FORD               20 ANALYST         7566          2
SMITH              20 CLERK           7902          2
ADAMS              20 CLERK           7788          2
ALLEN              30 SALESMAN        7698          4
MARTIN             30 SALESMAN        7698          4
TURNER             30 SALESMAN        7698          4
WARD               30 SALESMAN        7698          4

8 rows selected.

If I've understood the question properly that is.  I'm assuming your current
query looks something like this:

scott@ORA817DEV.US.ORACLE.COM> select ename, deptno, job, mgr
  2    from emp
  3   where (deptno,job) in ( select deptno, job
  4                                                     from emp
  5                                                    group by deptno, job
  6                                                   having count(*) > 1 )
  7  /

ENAME          DEPTNO JOB              MGR
---------- ---------- --------- ----------
SCOTT              20 ANALYST         7566
FORD               20 ANALYST         7566
SMITH              20 CLERK           7902
ADAMS              20 CLERK           7788
ALLEN              30 SALESMAN        7698
WARD               30 SALESMAN        7698
MARTIN             30 SALESMAN        7698
TURNER             30 SALESMAN        7698

8 rows selected.

Another approach is:

scott@ORA817DEV.US.ORACLE.COM> select ename, deptno, job, mgr
  2    from emp
  3   where exists ( select null
  4                                from emp e2
  5                                   where e2.deptno = emp.deptno
  6                                     and e2.job = emp.job
  7                                     and e2.rowid <> emp.rowid )
  8  /

ENAME          DEPTNO JOB              MGR
---------- ---------- --------- ----------
SMITH              20 CLERK           7902
ALLEN              30 SALESMAN        7698
WARD               30 SALESMAN        7698
MARTIN             30 SALESMAN        7698
SCOTT              20 ANALYST         7566
TURNER             30 SALESMAN        7698
ADAMS              20 CLERK           7788
FORD               20 ANALYST         7566

8 rows selected.
Review & Followup

      Rating: 2
Is the first
      approach better than second?  September 04, 2002

Reviewer:  Dmitry  from Russia
I mean that if the table is really large and there is high selective index on
some group by fields, than I think that second approach is better.

I think so because in first case we should do full scan and sort (expensive),
but in the second case we perfom full table scan and index range scan...

What do You think about it?

          Followup:

don't think, benchmark.

why do you believe sorts are expensive.  Have you computed the cost of a logical
IO recently?  hmmm.

I think you benchmark them and see.

Actually, it would be highly unlikely the query would index range scan as
written.  It would be rewritten as a join of the table with the index.  Also,
the selectivity of an index is not meaningful here since WHERE EXISTS stops at
the first occurence (1, 1000, 100000 entries in the index doesn't matter - we
stop at 1)

I knew I would do this... ok, so I benchmarked it.  Here is my table:

big_table@ORA920.US.ORACLE.COM> @desc big_table
Datatypes for Table big_table

                               Data                 Data
Column Name                    Type                 Length      Nullable
------------------------------ -------------------- ----------- --------
OWNER                          VARCHAR2             30          not null
OBJECT_NAME                    VARCHAR2             30          not null
SUBOBJECT_NAME                 VARCHAR2             30          null
OBJECT_ID                      NUMBER                           not null
DATA_OBJECT_ID                 NUMBER                           null
OBJECT_TYPE                    VARCHAR2             18          null
CREATED                        DATE                 7           not null
LAST_DDL_TIME                  DATE                 7           not null
TIMESTAMP                      VARCHAR2             19          null
STATUS                         VARCHAR2             7           null
TEMPORARY                      VARCHAR2             1           null
GENERATED                      VARCHAR2             1           null
SECONDARY                      VARCHAR2             1           null

Indexes on big_table

Index                          Is
Name                           Unique COLUMNS
------------------------------ ------ --------------------------------
BIG_INDEX_1                    No     OWNER, OBJECT_NAME, OBJECT_ID

And here is the TKPROF

select  OWNER, OBJECT_NAME, OBJECT_ID, object_type
  from (select  OWNER, OBJECT_NAME, OBJECT_ID, object_type,
                count(*) over (partition by  OWNER, OBJECT_NAME, OBJECT_ID ) cnt
         from big_table )
 where cnt > 1

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch    62039     53.44     185.89      29100      12704         67      930560
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    62041     53.44     185.89      29100      12704         67      930560

Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 62

Rows     Row Source Operation
-------  ---------------------------------------------------
 930560  VIEW
 930560   WINDOW SORT
 930560    TABLE ACCESS FULL BIG_TABLE

select  OWNER, OBJECT_NAME, OBJECT_ID, object_type
  from big_table t1
 where exists ( select NULL
                  from big_table t2
                 where t2.owner = t1.owner
                   and t2.object_name = t1.object_name
                   and t2.object_id = t1.object_id
                   and t2.rowid <> t1.rowid )

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch    62039     60.23     337.17      36133      18712        286      930560
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    62041     60.23     337.17      36133      18712        286      930560

Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 62

Rows     Row Source Operation
-------  ---------------------------------------------------
 930560  HASH JOIN SEMI
 930560   TABLE ACCESS FULL BIG_TABLE
 930560   INDEX FAST FULL SCAN BIG_INDEX_1 (object id 30854)

YMMV

      Rating: 5
multiset with
      group by  September 04, 2002
Reviewer:  Jasbir
      Kular  from Toronto
Is is possible to select the grouping into a collection?  Something similar to
the following query, assume that an object type called emp_type has been created
with the same definition as the table emp.  Also, the emp_tab collection type
has been created based on emp_type.

select a.deptno,
       a.job,
       cast(multiset(
                select emp_type(
                       b.ename,
                       b.deptno,
                       b.job,
                       b.mgr)
                  from emp b
                 where b.deptno = a.deptno
                   and b.job = a.job
                     ) as emp_tab) collection
from (select deptno, job
        from emp
       group by deptno, job
       having count(*) > 1) a

I would like to avoid using a self join on the table.  Is there a way to use the
query partition clause to do this?

Thanks a million.

          Followup:

Nope.

September 04, 2002 David -- Thanks for the question regarding
      "/etc/system file and SGA", version 816

      You Asked
Tom,

A couple of questions in mind:

1. Is there any file size limitation for the import/export? If
so how do we cope with it?

2. when we prepare the installation of Oracle on Unix, we need
to edit the /etc/system file

set shmsys:shminfo_shmmax=4294967295
set shmsys:shminfo_shmmni=100
set shmsys:shminfo_shmseg=10
set semsys:seminfo_semmni=70
set semsys:seminfo_semmns=200
set shmsys:shminfo_shmmin=1

What all of the 6 memory parameters about? can you explain each
of them for me?
How to determine the values of these parameters?

3. Are there any relationships between these 6 parameters and
the SGA components (ie share_pool_size, database_buffers, log
buffer size....etc ) parameters setting in the init.ora
parameter file?

4. after we install oracle, if we change the parameters in the
system file, is there any bad effects?

5. if we could change these parameters in the system file, do we
also need to change some of the parameters in the init.ora
parameter file?

Thanks very much.

      and we said...
1) if you use export to write to files that support SEEKING (eg:
regular files) then the limit is two gig.  You can either export
to multiple files (see the FILESIZE and FILE parameters for
details) or export to a named pipe (which does not support
seeking) as outlined at:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:122612348063
or export to tape.

2) See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:454420165038
3) definitely.  the shared_pool_size + db_block_buffers *
db_block_size + log_buffer + large_pool_size + java_pool_size
will be a little less then the size of your SGA.  They are the
major "sizers" of the sga.

4) If you lower limits below what we need, we won't run (that
would be bad).  If you raise limits beyond what we need, you'll
consume more resources -- that may be OK or not.

5) perhaps, if you lower the number of semaphores for example,
you may have to decrease PROCESSES in the init.ora.  If you
lower the SHM* /etc/system stuff, you might have to decrease the
size of  your SGA and so on.

Review &
      Followup

      Rating: 3
Follow-up
      Question  September 04, 2002
Reviewer:  Gunjeet
      Singh  from CA, USA
Hi,

 I have a follow-up question on one of the comments :
4) If you lower limits below what we need, we won't run (that would be bad).  If
you raise limits beyond what we need, you'll consume more resources -- that may
be OK or not.

What does "you'll consume more resources" mean ?
Does it mean that more resources will be allocated on the
server machine but Oracle processes will still use only limited resources based
on the init.ora settings ?

For example, if the PROCESSES setting is set to 100
in the init.ora file but SEMMNS is set to 500, what
will be the impact of this on the server machine and
on Oracle ? Similarly for SEMMSL ?

          Followup:

It'll vary by operating system.  But in general, the OS kernel or Oracle will
allocate larger then necessary data structure.

We'll use more RAM in general.

      Rating: 3
Folow-up
      question  September 04, 2002
Reviewer:  Gunjeet
      Singh  from CA, USA
Any idea what will be the behaviour on Solaris ?
Do these resources get allocated when the kernel
reboots, or they get allocated on an on-demand basis ?

          Followup:

depends on the parameter.  files/process -- per process.  procs/user -- on
demand as users log in.  etc....

Time to dig out the old solaris manuals?

September 05, 2002 thierry -- Thanks for the question regarding "How
      to get the ORA-01652 error code in Unix", version Oracle 8i

      You Asked
Hi Tom,

In order to pass an Oracle error to Unix, I use the "whenerror..." statement.
This works fine for some kind of errors (like division by zero, invalid SP name,
...), BUT NOT FOR ALL.
In particular, when the error ORA-01652 (segments too small) is raised in
Oracle, it is not passed to Unix.

Is there a way to raise this error at the Unix level ?

Thanks,
Thierry.

      and we said...
It is working however the status returned to Unix is limited to a single 1
number (in the range of 0..255).

If you run a shell script like this:

#!/bin/csh -v

sqlplus -s / <<EOF
whenever sqlerror exit SQL.SQLCODE
declare
    e exception;
    pragma exception_init( e, -1652 );
begin
    raise e;
end;
/
EOF

echo $status from SQLPlus

sqlplus -s / <<EOF
whenever sqlerror exit SQL.SQLCODE
declare
    e exception;
    pragma exception_init( e, -254 );
begin
    raise e;
end;
/
EOF

echo $status from SQLPlus

sqlplus -s / <<EOF
whenever sqlerror exit SQL.SQLCODE
declare
    e exception;
    pragma exception_init( e, -255 );
begin
    raise e;
end;
/
EOF

echo $status from SQLPlus

sqlplus -s / <<EOF
whenever sqlerror exit SQL.SQLCODE
declare
    e exception;
    pragma exception_init( e, -256 );
begin
    raise e;
end;
/
EOF

echo $status from SQLPlus

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

 you will recieve

$ test.csh
declare
*
ERROR at line 1:
ORA-01652: unable to extend temp segment by  in tablespace
ORA-06512: at line 5

116 from SQLPlus
declare
*
ERROR at line 1:
ORA-00254: error in archive control string ''
ORA-06512: at line 5

254 from SQLPlus
declare
*
ERROR at line 1:
ORA-00255: error archiving log  of thread , sequence #
ORA-06512: at line 5

255 from SQLPlus
declare
*
ERROR at line 1:
ORA-00256: cannot translate archive destination string
ORA-06512: at line 5

0 from SQLPlus

There is nothing we can do about this -- you might use GREP to GREP the ORA-
messages out and just use:

whenever sqlerror exit 1

so you can detect an error.

Review &
      Followup

      Rating:
      5
  September 04, 2002
Reviewer:
      A reader
Tom,

We regularly use "grep ORA- > err.log" on the log files to trap the error
messages. Do you know how we can trap the preceding 2 lines of the line that has
ORA- error message also in the error log.

Eg:

line1
line2
line3
line4 ORA-xxxx
line5

I want to trap

line2
line3
line4 ORA-xxxx

in the error log. Is it possible? Thanks.

          Followup:

write an awk script would be one way.

September 05, 2002 Alfredo -- Thanks for the question regarding
      "Temporary tables (comparision with SQLServer)", version

      You Asked
We are migrating some Stored Procedures from Sybase 11
to Oracle 8i. Most of them use temporal tables like:

SELECT x, y, z INTO #Temp FROM some_table ...
-OR-
CREATE TABLE #Temp ( .... )

Where #Temp is a Temporal Table in a temporal area which
automaticaly drops down when the Stored Procedures ends or could
be erased using "Drop Table #Temp" and is an individual table for
each user.

We can not use the same type of Temporal Table in Oracle 8i. So I
wonder what is the equivalent of this type of table and how we
can create them and manage.

Thank a Lot for your help.

Alfredo Alzamora
Systems Analyst
CONASEV

      and we said...
I believe you mean "temporary tables" -- temporal tables in a database are
another thing entirely (there is actually such a thing -- temporal tables are
tables that can return the answer that existed at a point in time -- you can ask
the table to return the answer that existed at midnight last night, instead of
the answer that exists right now)...

Oracle's temporary tables are similar to temp tables in those
other databases the main exception being that they are 'statically' defined.
You create them once per database, not once per stored procedure in the
database.  They always exist but appear empty until you put data in them.  They
may be SESSION based (data survives a commit but not a disconnect/reconnect).
They may be TRANSACTION based (data disappears after a commit).  Here is an
example showing the behaviour of both.  I used the scott.emp table as a
template:

SQL> create global temporary table temp_table_session
  2  on commit preserve rows
  3  as
  4  select * from scott.emp where 1=0
  5  /
Table created.

the ON COMMIT PRESERVE ROWS makes this a session based temporary table.  rows
will stay in this table until a logoff.  Only I can see them though, no other
session will ever see 'my' rows even after I commit

SQL>
SQL>
SQL> create global temporary table temp_table_transaction
  2  on commit delete rows
  3  as
  4  select * from scott.emp where 1=0
  5  /
Table created.

the ON COMMIT DELETE ROWS makes this a transaction based temp table.  when you
commit -- the rows disappear.

SQL> insert into temp_table_session select * from scott.emp;
14 rows created.

SQL> insert into temp_table_transaction select * from temp_table_session;
14 rows created.

we've just put 14 rows into each temp table and this shows we can 'see' them:

SQL> select count(*) from temp_table_session
  2  /

  COUNT(*)
----------
        14

SQL> select count(*) from temp_table_transaction
  2  /

  COUNT(*)
----------
        14

SQL> commit;
Commit complete.

since we've committed, we'll see the session based rows but not the transaction
based rows:

SQL>
SQL> select count(*) from temp_table_session
  2  /

  COUNT(*)
----------
        14

SQL> select count(*) from temp_table_transaction
  2  /

  COUNT(*)
----------
         0

SQL>

SQL> connect tkyte/tkyte
Connected.
SQL>

since we've started a new session, we'll see no rows now:

SQL>
SQL> select count(*) from temp_table_session
  2  /

  COUNT(*)
----------
         0

SQL> select count(*) from temp_table_transaction
  2  /

  COUNT(*)
----------
         0

SQL>

Instead of executing "select x, y, z into #temp from some_table" you would:

o once per database create "TEMP" as a global temporary table.

o then in your procedures you would simply "insert into temp (x,y,z) select
x,y,y from some_table"

If you really need the temp table to be created in the procedure itself,
Oracle8i release 8.1 makes this much easier to do as well.  Consider the
following example which uses plsql to create, insert into, fetch from and drop a
temporary table -- whose name is not known until run time.  Its almost as easy
as static sql is:

SQL> declare
  2      type mycur is ref cursor;
  3
  4      l_tname     varchar2(30) default 'temp_table_' || userenv('sessionid');
  5      l_cursor    mycur;
  6      l_ename     scott.emp.ename%type;
  7  begin
  8      execute immediate 'create global temporary table ' ||
  9                         l_tname || ' on commit delete rows
 10                         as
 11                         select * from scott.emp where 1=0 ';
 12
 13      execute immediate 'insert into ' || l_tname ||
 14                        ' select * from scott.emp';
 15
 16      open l_cursor for
 17          'select ename from ' || l_tname || ' order by ename';
 18
 19      loop
 20          fetch l_cursor into l_ename;
 21          exit when l_cursor%notfound;
 22          dbms_output.put_line( l_ename );
 23      end loop;
 24
 25      close l_cursor;
 26      execute immediate 'drop table ' || l_tname;
 27  end;
 28  /
ADAMS
ALLEN
BLAKE
CLARK
FORD
JAMES
JONES
KING
MARTIN
MILLER
SCOTT
SMITH
TURNER
WARD

PL/SQL procedure successfully completed.

Bear in mind that both CREATE and DROP are DDL and will commit any outstanding
transactions.

follow up to comment:

thats exactly what I mean.  DDL is performed as:

commit;
ddl;
if success commit
else rollback;

see
http://osi.oracle.com/~tkyte/autonomous/index.html
for a paper on autonomous transactions which may be useful in such a case.

Review &
      Followup

      Rating:
      4
  April 25, 2001
Reviewer:  VJ
       from Boston
Can you explain in detail your last comment that create/drop are DDL and will
commit out outstanding transactions? Does that mean that, in procedure there are
some updates then an DDL is executed dynamically, then updates made earlier will
get commited automatically?

      Rating:
      5
  September 22, 2001
Reviewer:  Anil
      Kumar  from Denver, USA
Excellent,I like most of the articles here.

      Rating: 5
why temp table
      why not a regular table  November 21, 2001

Reviewer:  Nag
Tom , in the example you have given for

"If you really need the temp table to be created in the procedure itself,
Oracle8i release 8.1 makes this much easier to do as well.  Consider the
following example which uses plsql to create, insert into, fetch from and drop a

temporary table -- whose name is not known until run time.  Its almost as easy
as static sql is:
"

Why cant I use a regular table. What is the benefit of using a temporary table.
Even if I use a regular table instead of a temp table, it is temporary in the
sense that we are going to drop it after using it , as you have done in the
program.

So how does using a temporary table here gives more benefit that using a regular
table.

thanks

          Followup:

Nag -- you have "the book" (mine ;) -- check out the chapter on tables.  There i
explain how temp tables generate almost no redo and rollback.  How they
automagically clean themselves out upon commit or session end.  Those are their
advantages.

      Rating: 5
nice
      feature  September 04, 2002
Reviewer:  Rahul
       from India
Hi Tom

When I'm trying to create the gtt with specified columns , it gives me error...

SQL> create global temporary table gtt
  2  on commit preserve rows
  3  (caseno number(9),permissions varchar2(5),iuid number(9),userid number(9));
(caseno number(9),permissions varchar2(5),iuid number(9),userid number(9))
*
ERROR at line 3:
ORA-00922: missing or invalid option

But when I give the command without "on commit preserve rows" , it creates the
table without any error.

SQL> create global temporary table gtt
(caseno number(9),permissions varchar2(5),iuid number(9),userid number(9));

Table created.

So pls. let me know when I created the above table , in which mode it will work
..
i) on commit preserve rows
ii) on commit delete rows

Regards
Rahul

          Followup:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create global temporary table t
  2  on commit preserve rows
  3  ( x int )
  4  /
( x int )
*
ERROR at line 3:
ORA-00922: missing or invalid option

ops$tkyte@ORA817DEV.US.ORACLE.COM> create global temporary table t
  2  ( x int )
  3  on commit preserve rows
  4  /

Table created.

Put it in the right place -- after the column list...

      Rating: 3
Thanks
      But...  September 05, 2002
Reviewer:  Rahul
       from India
Hi Tom

Thanks for the help but you didn't tell me the mode of the table , if we create
it with simple syntax as I send you .

Thanks

          Followup:

well, it is documented:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a85397/statem3e.htm#2061631
and it can be queried out (duration column in user_tables)

It could also be discovered in a trivial fashion by

a) create global temporary table
b) insert a row
c) commit
d) select * from it -- if you get a row, it must be a SESSION based table, if
you don't, it must be a TRANSACTION based table

September 05, 2002 Nag -- Thanks for the question regarding
      "rewriting a query having an outer join and group by", version 8.1.7

      You Asked
Tom

I'm using the outer join below to get deptno 40 in my query results.

SQL> ed
Wrote file afiedt.buf

  1  select ename, empno,dept.deptno, dname
  2  from emp,dept
  3* where emp.deptno(+)=dept.deptno
SQL> /

ENAME           EMPNO     DEPTNO DNAME
---------- ---------- ---------- --------------
CLARK            7782         10 ACCOUNTING
KING             7839         10 ACCOUNTING
MILLER           7934         10 ACCOUNTING
SMITH            7369         20 RESEARCH
ADAMS            7876         20 RESEARCH
FORD             7902         20 RESEARCH
SCOTT            7788         20 RESEARCH
JONES            7566         20 RESEARCH
ALLEN            7499         30 SALES
BLAKE            7698         30 SALES
MARTIN           7654         30 SALES

ENAME           EMPNO     DEPTNO DNAME
---------- ---------- ---------- --------------
JAMES            7900         30 SALES
TURNER           7844         30 SALES
WARD             7521         30 SALES
                              40 OPERATIONS

Now you have advetised that to avoid the outer join we can do the below.

SQL> select empno, ename, (select dname from dept where dept.deptno=emp.deptno),

  2  emp.hiredate
  3  from emp;

     EMPNO ENAME      (SELECTDNAMEFR HIREDATE
---------- ---------- -------------- ---------
      7369 SMITH      RESEARCH       17-DEC-80
      7499 ALLEN      SALES          20-FEB-81
      7521 WARD       SALES          22-FEB-81
      7566 JONES      RESEARCH       02-APR-81
      7654 MARTIN     SALES          28-SEP-81
      7698 BLAKE      SALES          01-MAY-81
      7782 CLARK      ACCOUNTING     09-JUN-81
      7788 SCOTT      RESEARCH       19-APR-87
      7839 KING       ACCOUNTING     17-NOV-81
      7844 TURNER     SALES          08-SEP-81
      7876 ADAMS      RESEARCH       23-MAY-87

     EMPNO ENAME      (SELECTDNAMEFR HIREDATE
---------- ---------- -------------- ---------
      7900 JAMES      SALES          03-DEC-81
      7902 FORD       RESEARCH       03-DEC-81
      7934 MILLER     ACCOUNTING     23-JAN-82

But here in the output we cannot get operations i.e. deptno 40. Then the purpose
using the above trick is defeated. I'm I right. So in what way you benefited
from not using outer join.

2. Does the trick used in second query  make using group by impossible.

Cause I tried and got the following result.

SQL> ed
Wrote file afiedt.buf

  1  select deptno,(select dname from dept where dept.deptno=emp.deptno),
sum(sal)
  2  from emp
  3* group by deptno
SQL> /
select deptno,(select dname from dept where dept.deptno=emp.deptno), sum(sal)
                      *
ERROR at line 1:
ORA-00979: not a GROUP BY expression

SQL> ed
Wrote file afiedt.buf

  1  select deptno,(select dname from dept where dept.deptno=emp.deptno),
sum(sal)
  2  from emp
  3  group by deptno,
  4* (select dname from dept where dept.deptno=emp.deptno)
SQL> /
select deptno,(select dname from dept where dept.deptno=emp.deptno), sum(sal)
                      *
ERROR at line 1:
ORA-00979: not a GROUP BY expression

Using the trick (selecting a select) , at the same time how can I also do a
group by .

Thanks

      and we said...
1) wow, you missed the point I think.....

Of course you cannot use it in your case -- because DEPT, not EMP is the driving
table.

You can use the select (select) trick if your original query was for example:

scott@ORA817DEV.US.ORACLE.COM> select dname, count(empno)
  2    from emp, dept
  3   where emp.deptno(+) = dept.deptno
  4   group by dname
  5  /

DNAME          COUNT(EMPNO)
-------------- ------------
ACCOUNTING                3
OPERATIONS                0
RESEARCH                  5
SALES                     6

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> select dname, (select count(empno) from emp where
emp.deptno = dept.deptno)
  2    from dept
  3  /

DNAME          (SELECTCOUNT(EMPNO)FROMEMPWHEREEMP.DEPTNO=DEPT.DEPTNO)
-------------- ------------------------------------------------------
ACCOUNTING                                                          3
RESEARCH                                                            5
SALES                                                               6
OPERATIONS                                                          0

scott@ORA817DEV.US.ORACLE.COM>

You can SELECT ( select from table you were OUTER JOINING TO )

You are trying to do it the opposite way and that of course won't work.

I advertised a technique that can be applied in the proper circumstances, this
isn't one of them.  So the question is moot as the technique doesn't apply here
at all.

2)

This is how to do what you asked:

scott@ORA817DEV.US.ORACLE.COM> select deptno, dname, sum(sal)
  2    from (
  3  select deptno, (select dname from dept where dept.deptno=emp.deptno) dname,
sal
  4    from emp
  5             )
  6   group by deptno, dname
  7  /

    DEPTNO DNAME            SUM(SAL)
---------- -------------- ----------
        10 ACCOUNTING           8750
        20 RESEARCH            10875
        30 SALES                9400

However, since the subquery is a functional dependency based on deptno, this
would be the right way to code it (eg: group by DEPTNO and *then* do the lookup.
 Don't lookup deptno 10 more then once!):

scott@ORA817DEV.US.ORACLE.COM>
scott@ORA817DEV.US.ORACLE.COM> select deptno,
  2             (select dname from dept where dept.deptno=emp.deptno) dname, sal
  3    from ( select deptno, sum(sal) sal
  4                     from emp
  5                    group by deptno
  6             ) emp
  7  /

    DEPTNO DNAME                 SAL
---------- -------------- ----------
        10 ACCOUNTING           8750
        20 RESEARCH            10875
        30 SALES                9400

scott@ORA817DEV.US.ORACLE.COM>
September 05, 2002 -- Thanks for the question regarding "copy
      command vs sql*loader", version 8.1.7.2

      You Asked
Hi Tom

We are developing a batch job for datawarehouse loading, for other developments,
so far we have always used sql*loader, the process were

1. extract the data using spool in sql*plus
2. ftp the spool file to dwh machine
3. load with sql*loader

we wonder if we use copy from origin to destination (dwh) will this be faster?
since we are avoiding many I/O, ftp and spool. Or copy is not fast in large
tables?

thanx

      and we said...
Unless you were doing a direct path load, you'll find the sqlplus copy command
to be very fast and useful in your case.  I use it to move large amounts of
data.  The one thing to remember however is that the data is pulled from DB1 to
sqlplus to DB2.  So -- saying that -- don't do something like run sqlplus
locally on your pc else all the data will come from DB1 to your pc and then be
sent to DB2.  Run sqlplus locally against either DB1 or DB2 to avoid this extra
network trip.
Review & Followup

      Rating: 4
DETAILED
      EXPLANATION WITH EXAMPLES  February 10, 2002

Reviewer:  mahbuba rahman  from Sharjah,UAE

YOU SAID THAT
The one thing to remember however is that the data is pulled from DB1 to sqlplus
to DB2.  So -- saying that -- don't do something like run sqlplus locally on
your pc else all the data will come from DB1 to your pc and then be
sent to DB2.  Run sqlplus locally against either DB1 or DB2 to avoid this extra
network trip.

could you please explain the above more clearly with examples?

          Followup:

Say database DB1 is on the machine db1.yourorg.com.

Say database DB2 is on the machnie db2.yourorg.com.

Say you sqlplus installed on your pc which is host yourpc.yourorg.com.

If you run the sqlplus copy command on YOUR pc using your copy of SQLPLUS
installed on that pc the data will be transfered to db1.yourorg.com ->
yourpc.yourorg.com -> db2.yourorg.com.

suppose you are "dialed up", this would be very slow.  Suppose you are on a LAN,
this would incurr extra movement of bits on the wire..

Now, if you telnet to db2.yourorg.com and connect locally to db1, the data will
be transferred from db1.yourorg.com -> db2.yourorg.com directly, using whatever
wires connect db1 and db2.  Your PC will not recieve all of the bits of data.
They transfer will go faster.

      Rating:
      4
  February 11, 2002
Reviewer:
      A reader  from India
I think that Tom has given us a tip, to run the sqlplus
session (locally) on the database server (either DB1
or DB2 i.e. either the source database server or the
destination database server) instead of running it on
another (client) PC.

If we run the sqlplus on another (client) PC other than
the source or destination database server, SQLPLUS will
have to "get the data" (over the NETWORK) to the (client)
PC and then "send the data" (again over the NETWORK) to
the destination database server.

If we run run sqlplus session (locally) on the database
server (either DB1 or DB2) only 01 trip is made over the
network to either fetch or send the data.

      Rating: 4
Network latency
      and COPY  February 11, 2002
Reviewer:
      A reader
Hi

I once suggested copy command instead of spooling, ftping and sql*loading
process but there was an performance consultant from Oracle Consulting who said
that since copy works with net8/TCP IP therefore copy would cause much more
network latency than three processes mentioned above and the time taken can be
more than all three processes added together (FTP cause much less latency
accodring to him)

What is your opinion about this?

          Followup:

benchmark it.

that's my opinion.  You would be surprised.  Bear in mind that automating
(scripting) a sqlplus copy command is trivial.  doing the same with a spool,
ftp, sqlload (which means TWO machines have to sort of work together, you have
to put passwords into a .netrc file to automate the ftp and so on) is not.

I find it (sqlplus coyp) to be VERY fast.

But, you should never accept at face value anything -- benchmark it once and see
for real how it works in your environment.

      Rating: 2
New to
      sqllaoder  February 11, 2002
Reviewer:
      Amarsey  from Ghana
Please Tom,
I have followed some examples on how to use sqlloader and seems to be getting
some problems.
Where should i create my control file?
I wrote my control file at the DOS editor and save it with the extension ctl.
ie
LOAD DATA
INFILE 'C:\MYDATA.TEXT'
APPEND
INTO TABLE TABLE1
(FNAME,SNAME,LNAME);
FIELD TERMINATED BY "," OPTIONALLY ENCLOSED BY "''"

Tom ,after i execute the command at dos prompt ie
sqlldr user/password control='c:pop.ctl',
the records inserted into the table1 turns into figures
ie 1,0,0 for each field,
Please give me the simple procedure.
Thanks.

          Followup:

Well, your ctl file isn't even valid so it cannot be loading any data:

LOAD DATA
INFILE 'c:\mydata.text'
APPEND
INTO TABLE TABLE1
FIELDS TERMINATED BY "," OPTIONALLY ENCLOSED BY "'"
(FNAME,SNAME,LNAME)

would be valid.  One would need to see your input file as well (just the first
line or two)

      Rating: 3
copy command vs
      sql*loader  February 13, 2002
Reviewer:  Ted
      Trocky  from Philadelphia, PA
Tom,

Does the copy and "insert into" behave the same way?

insert /*+ append */ into reporting.salesreps  select * from
adm.salesreps@adm_link;

The above code is executed from a sqlplus script on a Unix application server
connected to the target db and pulling data back via a DB link.  Is the copy and
insert equivalent or will the insert bypass the moving of the data to the app
server?

Thanks
Ted

          Followup:

The two are very very different.

Copy will open a connection to each database from the SQLPlus client.  So, if
you are on your PC and have DB1 and DB2 each located on different servers -- the
copy command will have a connection from your PC to DB1, a connection from your
PC to DB2.  It will issue a select statment against DB1, pull the data to your
PC (using array fetching) and then Array insert that data into DB2.  So the data
goes from DB1 -> PC -> DB2.

Also, the copy command will optionally "commit" batches (see the copycommit
setting).  Also, the copy command will ALWAYS work in a logged fashion.

Insert /*+ append */ ... will NOT commit batches.  Also, it can work in a
non-logged fashion (work performed on indexes will always be logged, work
performed on the table may not be)

Insert /*+ append */ into t select * from t@db1 will come to the same result as
the sqlplus copy command HOWEVER the data will flow from DB1 -> DB2.  It will
NOT go to your PC.

      Rating:
      4
  July 03, 2002
Reviewer:  Philip
      Tao  from Sydney, Australia
Hi Tom, we have two databases, one is Oracle 8.0.4 and another one is Oracle 9i.
 If we want to copy data from 8.0.4 to 9i, does 8.0.4 support the copy command?

          Followup:

sqlplus is the tool with the copy command and yes, it was in 8.0.4 (and 7.x as
well)

      Rating: 5
so db link and
      copy have same speed?  September 05, 2002

Reviewer:  A reader
Hi

I was wondering if

insert /*+ APPEND */ select from t1@db is as fast (and performance wise) as a
copy (not running from an intermediate PC)

Or DB LINK is slower?

          Followup:

benchmark it and see.

COPY cannot append.

      Rating:
      5
  September 05, 2002
Reviewer:  Martin
      Guillen  from Argentina.
Hi tom:
From A90842-01:
"The COPY command will be obsoleted in future releases of SQL*Plus. COPY
supports the datatypes listed for the COPY command, but no new datatypes will be
supported."

So, why it will be obsoleted?.
Will be replaced with another tool?.

          Followup:

sqlplus itself is going 3 tier (isqlplus).  different set of features/functions.

insert /*+ append */ into t select * from t@another_place

does what copy does

      Rating: 4
copy does not
      need a db link  September 06, 2002
Reviewer:
      A reader
in some shops db links are used with caution, copy is much more handy!

it would be a bad thing if copy is obsolete

September 06, 2002 Raghu -- Thanks for the question regarding "How
      much of Java do you need to know to use, say, IFS", version 8.1.7

      You Asked
This is a general questions about some of the new features. As soon as I start
to look at a new feature of Oracle, say iFS, and poke around technet, I get a
bunch of java code and jsp. How much of a <i>Java</i> guy does one need to be
before these new features of the <i>database</i> can be exploited? Ditto with
soemthing like being able to make a OAS/9iAS pl/sql-generated webpage accesible
over a cellphone or PDA using WAP. Should be simple - right? But in my
experience, it is not. Perhaps I am way behind everybody else but I still
wonder...

      and we said...
Well, I know enough Java to be dangerous -- I have yet to write a JSP (don't
want to) or a servlet (but I have written a Palm Sync Conduit and many java
stored procedures)....

iFS doesn't require Java, it is a file system.  You don't need to program it
further.  Many of the examples you see are in the building of more
sophisiticated front ends -- user interfaces.  The "language of choice" for many
in that area (for better or worse) is Java.  Can you use PLSQL -- sure, php --
yes, python - certainly, asp -- if you want to.

In 9iR2 -- iFS comes with "Oracle files" -- a front end that gives you out of
the box functionality -- if you like it, no programming even to get a "front
end".

Now, for the WAP access, there is some complexity there but it should be 100% in
the setup, configuration and getting the first page going on the wireless
device.  After that -- it is preetty easy.  It is sort of like getting your
first PLSQL "mod_plsql aka owa" page going.  I remember it taking more then a
minute or two to fully understand how the URL got turned into a stored procedure
call and that procedure would generate a html page that would go back to a
browser.  Once I learned it -- it was "trivial".  Before I learned it -- it was
black magic.

I don't think you are way behind everybody else -- we all have different skill
sets.  Hey -- tons of people are doing "off the shelf apps" implementations.  I
haven't done a full one myself.  Am I behind?  In apps implementations maybe --
but not in the area that I need to be.  I still learn something new each and
every day and the technology is a moving target.  By waiting some things out --
I got to skip "learning" them all together ;)  You learn what you need to know.
Review &
      Followup

      Rating: 5
Nice
      ramblings  June 11, 2002
Reviewer:  Jorge R
       from Bloomington, MN USA
Very nice reflections and ramblings (and I mean it in the best possible way)

I would love to hear more about that Palm Sync Conduit and even see the code,
but I understand this is not the forum.

So, I just wanted to thank you for the words of wisdom.

      Rating: 4
about
      WAP  September 06, 2002
Reviewer:  Inga
       from Russia
Hello Tom!
I'm trying to find out the way to access Oracle DB from PDA or cellular phone
(with WAP). At the moment I realized that it's nessesery to have OAS or (better)
the iAS, then develop lots of pl/sql (or java) procedures, that would generate
dynamic web-pages for a user.
Could you please clarify the "complexity" (you said about) of using these method
for WAP-devices?

Thank you in advance!

          Followup:

Once you understand the technology, it is not hard at all.

http://technet.oracle.com/docs/products/iaswe/content.html
is the docs for the wireless stuff.  Before understanding the technology, it is
black magic and apparently very complex.

September 06, 2002 Sampath -- Thanks for the question regarding
      "UTL_FILE or sql*loader", version 8.1.7

      You Asked
Hi Tom,
       Thanks for taking my question . We have 15 tables each with
250K to 350K records . We truncate them and load from flat files every day using
Sql*loader. After load we run procedures to update these tables from OLTP tables
.My questions are

1) What is the performance gap between UTL_FILE and Sql*loader

2) Is it possible to use  UTL_FILE and load these tables directly from flat
files  including data from OLTP tables to avoid running the procedures to update
these tables later ?
    Say I have empno,ename in flat file and "sal" is in  OLTP table
is it posible to pull it and insert to the target tables .

Thanks in advance,
Sampath

      and we said...
1) for this amount of data, it could be large.  sqlldr will be much faster.

2) yes it is.   PLSQL would read a row, processing row, insert row.

In Oracle9i you have EXTERNAL TABLES (search for that on this site).  They solve
your issue very elagantly.  We can treat a flat file like a table and select
from it -- very fast, very functional, very very cool.
Review &
      Followup

      Rating:
      4
Thanks  August 09, 2002
Reviewer:
      Sampath
Tom,
      Thanks again for your advice .

Sampath

      Rating: 4
How to create
      external tables on fixed length data  September 05, 2002

Reviewer:  Sampath  from USA
Hi Tom,
      I installed 9i and tried external tables as you have adviced on the flat
file which I have created with your dump_csv code . It looks great .
         But I could not find any example or syntax to create External table on
fixed length data with no delimiters or spaces between fields .
     Could you please give me an example ?
Thanks in advance,
Sampath

          Followup:

read up on sqlldr -- you just use POSITION(n:m) in the control file section of
the create table.

      Rating: 3
Please take a
      look at the ERROR  September 06, 2002
Reviewer:
      Sampath  from USA
Hi Tom,
      I have a file emp.txt and it has the following data .

CLARK9000
TOM  9001
HARRY9002
      I created table with the following statement .
*********************************************************
CREATE TABLE admin_ext_employees
                   (ename        VARCHAR2(10),
                employee_id       NUMBER(4))
                    ORGANIZATION EXTERNAL
     (
       TYPE ORACLE_LOADER
       DEFAULT DIRECTORY admin_dat_dir
       ACCESS PARAMETERS
       (
    records delimited by newline
         badfile admin_bad_dir:'empxt%a_%p.bad'
         logfile admin_log_dir:'empxt%a_%p.log'
         DISCARDFILE admin_bad_dir:'empxt%a_%p.dsc'
         FIXED
         missing field values are null
        (ename POSITION (1:5) char,
         employee_id  POSITION (6:9) NUMBER
         )
     )
     LOCATION ('emp.txt')
     )
     PARALLEL
     REJECT LIMIT UNLIMITED
**************************
   This is the error I'm getting now .

SQL> select * from admin_ext_employees;
select * from admin_ext_employees
*
ERROR at line 1:
ORA-29913: error in executing ODCIEXTTABLEOPEN callout
ORA-29400: data cartridge error
KUP-00554: error encountered while parsing access parameters
KUP-01005: syntax error: found "missing": expecting one of: "equal, number"
KUP-01007: at line 6 column 10
ORA-06512: at "SYS.ORACLE_LOADER", line 14
ORA-06512: at line 1
 I created all directories and granted read/write on them to the User creating
external table .

Thanks in advance,
Sampath

          Followup:

the missing goes after fields:

ops$tkyte@ORA920.US.ORACLE.COM> CREATE TABLE admin_ext_employees
  2                     (ename        VARCHAR2(10),
  3                  employee_id       NUMBER(4))
  4                      ORGANIZATION EXTERNAL
  5       (
  6         TYPE ORACLE_LOADER
  7         DEFAULT DIRECTORY admin_dat_dir
  8         ACCESS PARAMETERS
  9         (
 10           records delimited by newline
 11           badfile admin_bad_dir:'empxt%a_%p.bad'
 12           logfile admin_log_dir:'empxt%a_%p.log'
 13           DISCARDFILE admin_bad_dir:'empxt%a_%p.dsc'
 14           fields
 15           missing field values are null
 16          (ename POSITION (1:5) char,
 17           employee_id  POSITION (6:4)
 18           )
 19       )
 20       LOCATION ('emp.txt')
 21       )
 22       PARALLEL
 23       REJECT LIMIT UNLIMITED
 24  /

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> host echo 'CLARK9000' > /tmp/emp.txt

ops$tkyte@ORA920.US.ORACLE.COM> host echo 'TOM  9001' >> /tmp/emp.txt

ops$tkyte@ORA920.US.ORACLE.COM> host echo 'HARRY9002' >> /tmp/emp.txt

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select * from admin_ext_employees;

ENAME      EMPLOYEE_ID
---------- -----------
CLARK             9000
TOM               9001
HARRY             9002

ops$tkyte@ORA920.US.ORACLE.COM>

      Rating: 4
It works
      now  September 09, 2002
Reviewer:  Sampath
       from USA
Tom ,
      Thanks a million . It works now .

Sampath

September 06, 2002 Bharath -- Thanks for the question regarding
      "Response Time for materialized ", version 8.1.7

      You Asked
Tom,
    SELECT NAME FROM DT_BUDGET_MV;

    I am trying to run this SELECT statement which fetches the data from the
materialized view DT_BUDGET_MV. The problem is different machines give different
response time. I am attaching the TKPROF for 2 different machines having the
same configuration.

Client Machine 1
call      count  cpu      elapsed   query        current     rows
-------      ------ -------- --------- ----------     ----------  ----------
Parse     1      0.01     0.08      0              0           0
Execute   1      0.00     0.00      0              0           0
Fetch     487    0.12     0.14        584         21          7276
-------      ------ ------- ---------- ----------     ---------- ----------

total     489    0.13    0.22        584         21          7276

Client Machine 2
call      count  cpu      elapsed    query        current     rows
-------      ------ -------- ---------- ---------- ----------  ----------
Parse     1      0.00     0.12       0             0           0
Execute   1      0.00     0.02       0          0           0
Fetch     38     0.09     0.12       140        21          7276
-------      ------ -------- ---------- ---------- ----------  ----------
total     40     0.09     0.26       140        21          7276

Please advice,
Bharath

      and we said...
Where do you see different times?

You see -- timings are done in 1/100th of a second (1/1000th in 9i).

If some operation takes LESS THEN 1/100th of a second, we don't "count it".  We
time many thousands or even millions of discrete events (we don't time the thing
continously from start to finish as each phase of the query is processed in tiny
little steps).  Hence, the timings you see could easily be off by many
hundredths of a second.

If the parse actually took 0.009 seconds -- we might see ZERO, we might see 0.01
-- depending on when the parse took place (did the parse happen OVER a clock
tick or between them -- if over you get 0.01, if between you get 0.00)

Same with the fetch stuff -- there were hundreds of timed operations in the
fetch, many of which happen in less then 0.01 cpu seconds and may not be timed.

For all intents and purposes -- these numbers are identical (also consider on
one machine we did 584 consistent gets -- on the other 140 -- these were NOT
identical)

Review &
      Followup

      Rating: 5
But
      still  September 06, 2002
Reviewer:  Bharath
       from USA
But the response time is always consistent,in one machine it takes 6(487 Fetches
from TKPROF) seconds and other machine it takes 14(38 fetches from TKPROF)
seconds ,both machines are running under same configuration .Why is that
difference?

          Followup:

Oh, didn't even notice that.

Ok, that is easy.... Your arraysize is the default of 15 on machine 1, and the
arraysize is the non-default value of 200 on machine 2 (look for a login.sql or
glogin.sql on machine 2)

SQL> select 7276/487 from dual;

  7276/487
----------
14.9404517

SQL> select 7276/38 from dual;

   7276/38
----------
191.473684

SQL>

Is how I determined that.... That does definitely lead to less consistent gets
as well -- see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:880343948514
for all of the details on this phenomena.

      Rating: 5
Response Time
      for Materialized View  September 06, 2002

Reviewer:  Bharath  from NYC
Based on no. of fetches, Machine 1 should be slower than Machine 2. But we had
indicated that Machine 2 is slower. We even changed the ArraySize from 15 to 200
(as well as 200 to 15) on Machine 2. There was no change in the output. Machine
1 is always faster than Machine 2.

          Followup:

I'm telling you -- there is no measurable difference between the two!!!

But anyway -- look at the tkprof again,

machine 1 takes  (0.13) cpu (0.22) elapsed
machine 2 takes  (0.09) cpu (0.25) elapsed

machine one was slower.  now what.

September 06, 2002 Partha -- Thanks for the question regarding
      "Performance issue", version 8.1.7.3

      You Asked
Hi, Tom

Can you tell me what is the percentage of boost when we set timed_statistics =
false and audit_trail = false.

Right now we have audit_trail=DB and timed_statistics = true at Instance
level(Production database).

If we turn it off what is the percenttage of performance gain in production
database.

I suggested both the parameters set to false and i told them approximately
20-30% performance gain.

Can you please give me your opinion on this issue.

Thanks

      and we said...
Almost none.  Very little.

Since timed_statistics can be turned on and off these days at will -- it is OK
to leave off (no longer requires a bounce)

Audit_trail = true however, that means you are MISSING out on what could be very
very vital information.  When considering auditing -- "performance" is not part
of the equation as for deciding whether it is on or off (and I question why you
have auditing on at all if it is).  Auditing is something that either a system
MUST have or it doesn't have to have.

You *way* way way, by probably 600%, over estimated this.  timed_statistics is
"noise" and depending on the level of auditing, its impact is very small.
However -- turnning OFF audting can have a huge impact, but not on performance.
On your ability to see who did what and when.
Review &
      Followup

      Rating:
      3
  September 06, 2002
Reviewer:
      A reader

September 06, 2002 zl -- Thanks for the question regarding "the
      explain of my old sql suddenly changed,but i had not change anything.",
      version 8.1.7

      You Asked
the sql is :
select * from a,b where a.seq=b.seq and b.acct_id=1234;

now the explain is
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=RULE (Cost=844 Card=1 Bytes=53)
1 0 SORT (AGGREGATE)
2 1 HASH JOIN (Cost=844 Card=45400 Bytes=2406200)
3 2 TABLE ACCESS (FULL) OF a (Cost=826 Card=2000 Bytes=44000)
4 2 PARTITION RANGE (ALL)
5 4 TABLE ACCESS (BY LOCAL INDEX ROWID) OF b (Cost=17 Card=2270 Bytes=70370)
6 5 INDEX (RANGE SCAN) OF b_1 (NON-UNIQUE) (Cost=13 Card=2270)

this sql executed very fast before,and now very slow, i could not offer the old
explain .

the table "a" has a primary key on seq, table "b" is a partition table, it has a
index on seq, a index on acct_id.

init.ora => optimizer_mode=rule

now after i analyze the two table,this sql seems to ok,but i had never analyze
anytable ago and this sql seems ok, why it suddenly changed to need analyze? i'm
afraid analyze may affect explain of other sql,so ,can you tell me why, and how
to do ? thanks very much.

      and we said...
You are using a partitioned table.

Partitioned tables MANDATE the use of the cost based optimizer.  If your
query touches a partitioned table (or IOT, or this or that or the other thing),
we will use the CBO regardless of the optimizer mode.  You've been using the CBO
all along.

You can see that you are using the CBO in the above explain -- when you have a
COST, CARD and BYTES -- that indicates the CBO is being used.

Here is my theory: someone did change something somewhere -- they analyzed a
table, changed an optimizer related parameters, something.  Your plan changed.

The bottom line is - using features that require the CBO without analyzing is a
sure fire recipe for disaster.   You need to have been analyzing this table all
along.
Review
      & Followup

      Rating: 4
partition
      statistics  September 06, 2002
Reviewer:
      shutian Lin  from Canada
Make sure all partitions have statistics even it is an empty partition.
Otherwise, your excution plan will be different.

Another senario I have experienced (817.2) is when SGA is fragmented, the
parallel reading of multiple partitions will become reading one partition a
time.  Reboot the instance you will see the different performance.

      Rating: 3
ok,i
      see.  September 07, 2002
Reviewer:  zl
       from china
  but it still puzzles me, because nobody had analyzed any
table or changed an optimizer related parameters, i think the reason maybe: the
partitioned table's row number grows too many(>23000000rows),so result in that
case. is my thought right?
  thanks for your answer.

September 06, 2002 Farhad -- Thanks for the question regarding
      "Regarding Password Decryption.", version 8.1.3

      You Asked
Hi,
  Thanks for answering my previous question.
  Well,this time my question is:

Q.How can I decrypt the password of the user incase if he
forgets his password.Is there any method of decryption of
password which Oracle provides apart from changing his password
by alter user command ?

Thanks,
Farhad.

      and we said...
You cannot decrypt that which is not encrypted...

in Oracle, the password is not encrypted (that would imply there
is a decrypt but there is not).  Instead, to validate a
username/password we take them, plus some magic "salt" and hash
it.  This results in a fixed length string of some bytes of
data.  We compare that to the stored hash and if they match --
you are in.  If not -- you are not.

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:95412348059
for more info on this subject.

Review &
      Followup

      Rating: 4
Decryption of
      Oracle Password  September 06, 2002
Reviewer:
      Kiran  from Cincinnati, OH, USA
Tom,

I want to enable password encryption in my application (siebel). To do this I
have to extract the current password. Encrypt it using Siebel's Encyption
Utility and then set the password back to the encrypted password.

Is it possible to extract the current password.
If this is not possible can you suggest any alternative.

thanks

Kiran

          Followup:

first line of my answer was:

You cannot decrypt that which is not encrypted...

no ideas beyond assigning everyone new passwords or asking them to come to some
page and sign up.

September 06, 2002 Mario -- Thanks for the question regarding
      "rollback and redo generation for autonomous transaction", version 8.1.7

      You Asked
Hi Tom,

I would like to ask you three related questions:

1) Say that a session make a DML tatement and it
need a rollback segment, say for example X; the
Recursive Calls made by the shadow process on behalf
of that statement (for example in the DML stmt appear
a sequence, so the sys.seq$ has to be updated) which
rollback segment will it use? Should it be the same: X?

2) An autonomous transaction which rollback segment
use: Is it the same as its 'parent' transaction?

At the outset I have said 'related question'
because we know that a recursive call is nothing
but an autonomous transaction!

3) Tom I have to use your script 'runstats.sql'
(
http://govt.oracle.com/~tkyte/runstats.html
;
between the two statements that I want to compare,
I have to drop and create indexes and I want to do
that with an autonomous transaction; so the redo
generated by the autonomous transaction will it
invalidate the stats?

Thank you Tom for your unvaluable help.

Mario from Rome, Italy

      and we said...
1) recursive sql like that is done in the equivalent of an autonomous
transaction.  It need not be in the same RBS no -- it is a different transaction
all together.

2) not neccesarily, it might, it might not!

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.username, b.segment_name
  2    from v$session a, dba_rollback_segs b, v$transaction c
  3   where a.taddr = c.addr and b.segment_id = c.xidusn
  4  /

USERNAME                       SEGMENT_NAME
------------------------------ ------------------------------
OPS$TKYTE                      RBS_01

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          pragma autonomous_transaction;
  3  begin
  4          insert into t values ( 2 );
  5          for x in ( select a.username, b.segment_name
  6                   from v$session a, dba_rollback_segs b, v$transaction c
  7                  where a.taddr = c.addr and b.segment_id = c.xidusn )
  8      loop
  9                  dbms_output.put_line( x.username || ', ' || x.segment_name
);
 10      end loop;
 11          commit;
 12  end;
 13  /
OPS$TKYTE, RBS_02

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.username, b.segment_name
  2    from v$session a, dba_rollback_segs b, v$transaction c
  3   where a.taddr = c.addr and b.segment_id = c.xidusn
  4  /

USERNAME                       SEGMENT_NAME
------------------------------ ------------------------------
OPS$TKYTE                      RBS_01

ops$tkyte@ORA817DEV.US.ORACLE.COM>

See, before the a-tran, we were using rbs_01, in the a-tran -- rbs_02, back out
of the a-tran -- rbs_01 again (we hadn't committed yet)

3) you'll have to do that in another session, the redo generated by the a-tran
is in the current session otherwise.  You can do this by "pausing" the runstats
script:

exec dbms_alert.register( 'PAUSE' );

declare
    cursor c is select * from dual;
    l_dummy dual.dummy%type;
    l_start number;
    l_run1  number;
    l_run2  number;
    l_msg   varchar2(4000);
    l_status number;
begin
    insert into run_stats select 'before', stats.* from stats;

    l_start := dbms_utility.get_time;
    for i in 1 .. 10000
    loop
        select dummy into l_dummy from dual;
    end loop;
    l_run1 := (dbms_utility.get_time-l_start);
    dbms_output.put_line( l_run1 || ' hsecs' );

    dbms_application_info.set_client_info( 'Waiting...' );
    dbms_alert.waitone( 'PAUSE', l_msg, l_status );
    dbms_application_info.set_client_info( 'Done Waiting...' );

Now, you kick off the script and when v$session says this session is "waiting"
(select username, client_info from v$session) -- you go do what you gotta do and
when you are done, just:

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_alert.signal( 'PAUSE', '' )
PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;
Commit complete.

Now bear in mind!  As I say on the runstats page, If you run this on a
multi-user system, the latch information may be technically "incorrect" as you
will count the latching information for other sessions - not just your session.
This test harness works best in a simple, controlled test environment.

you just created a multi-user environment -- any latching done by the
drop/create index will through off the results.

That just means that you might want to develop a different test harness.  It
would:

a) collect the stats
b) run test 1
c) collect the stats

.... do whatever

d) collect the stats  <<<=== new step
e) run test 2
f) collect the stats

and then view the difference between (C-A)  and (F-D)  instead of just comparing
(C-A) to (F-C) as I do now.

Review &
      Followup

      Rating: 5
Thank you
      again  September 06, 2002
Reviewer:  Mario
       from Rome, Italy
I'll do the test in a single-user environment and I'll bear in mind the good tip
for a multi-user env.

Thank you very much for that most interesting reply!

Cheers

          Followup:

No, you misunderstand

YOU are creating a multi-user environemnt!  If you drop and create stuff in the
middle, there is no avoiding adding in the work done by those operations in the
current runstats -- you would have to modify it (or ignore the LATCH portion of
the report)

      Rating: 5
Thank you again
      and again...  September 06, 2002
Reviewer:
      Mario  from Rome, Italy
Sorry Tom,

now I understand, your clarification is very appropriate.
The drop and create statement get a lot of latchs and so the gathered stats are
almost useless...!

Regards

September 06, 2002 Rick -- Thanks for the question regarding "return
      multiple address to mail_pkg", version 8.1.7

      You Asked
Hi Tom. I'm trying to get your package mail_pkg (found it on this site) to
accept multiple values for the p_to (recipient list).

begin
    mail_pkg.send
    ( p_sender_email => 'me@acme.com',
      p_from => 'Oracle Database Account <me@acme.com>',
      p_to => mail_pkg.array( 'coyote@acme.com', 'roadrunner@acme.com' ),
      p_cc => mail_pkg.array( 'kermit@acme.com' ),
      p_bcc => mail_pkg.array( 'noone@dev.null' ),
      p_subject => 'This is a subject',
      p_body => 'Hello Tom, this is the mail you need' );
end;
is how you were executing it. That part all works, but I cannot seem to wrap my
brain around how to return multiple email addresses from a table. the table has
several rows, each with a separate email address in the contactemail column. how
do I select all of these emails into the variables to pass them to the p_to
array?

I'm sure it's something simple, but I simply cannot get it to work.
I've tried a bunch of things but I'm stuck.

TIA,

Rick

      and we said...
You can just use a select into like this:

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> declare
  2          l_to  mail_pkg.array;
  3  begin
  4          select username BULK COLLECT INTO l_to
  5        from all_users
  6       where rownum <= 10;
  7  end;
  8  /

PL/SQL procedure successfully completed.

or:

declare
   l_to mail_pkg.array := mail_pkg.array();
begin
   for x in ( select .... )
   loop
     l_to.extend;
     l_to(l_to.count) := x.some_field;
   end loop;

Review &
      Followup

      Rating:
      5
sheesh  August 16, 2002
Reviewer:
      Rick  from BC, Canada
how simple! I was looking at infinitely harder solutions, none of which worked
very elegantly (and most not at all!).

Thanks again,

Rick

      Rating: 5
yep, me
      again...  September 05, 2002
Reviewer:  Rick
       from BC, Canada
Requirements have changed somewhat. I am currently using your procedure
html_email and it works great! The only drawback is that I need to send the
identical email to several people at once, so looping through the list is simply
taking too much time. How would I customize the html_email procedure to accept
multiple email addresses ( p_to ) similar to the mail_pkg package, but without
losing the html_email functionality?

Thanks again Tom for this great service and your interesting answers.

Cheers,

Rick

          Followup:

this is in reference to the procedure at:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1739411218448

Well, you would change the code that is:

begin
    l_connection := utl_smtp.open_connection( p_smtp_hostname, p_smtp_portnum );
    utl_smtp.helo( l_connection, p_smtp_hostname );
    utl_smtp.mail( l_connection, p_from );
    utl_smtp.rcpt( l_connection, p_to );

into code that looks like mail_pkg.send:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:255615160805

      Rating: 4
Thanks
      again!  September 06, 2002
Reviewer:  Rick

      Rating:
      5
uhhh...  September 06, 2002
Reviewer:
      Rick  from BC, Canada
I got most of it to go. What I'm doing is using one of your functions
(get_transposed (think it was yours)) to contcatenate the emails into a string,
like:
'joe@abc.com','asdf@asdf.com','asdf@erere.com' etc.
This all works fine. if I cut and paste that into
the ( p_to => ) (like at the top of this page) then it works. Yet when I assign
that string to a varchar2 variable, it returns me the error:

ORA-20002: 553 Invalid address syntax

Any idea why it works when it's pasted manually, but not in a variable?

here's how I'm sending it:

begin
  mail_pkg2.send2
   ('me@notrealemail.com',
    'Oracle Database <me2@notrealemail.com>',
    mail_pkg2.array(p_to),
    mail_pkg2.array(bccme@notrealemail.com),
    'This is a subject',
    'This is the email you sent',
    '<html><body><B> test email</B></body></html>');
end;
/

Cheers,

Rick

          Followup:

Add some dbms_outputs right before the utl_smtp call that pumps out this string
to see what is actually getting there.  Something like:

  dbms_output.put_line( '"' || p_to || '"' );
  for x in ( select dump(p_to) x from dual )
  loop
      dbms_output.put_line( x.x );
  end loop;

(and don't forget to remove the dbms_output after you figure it out since it has
a 255 byte/line limit and will undoubtably cause you heartburn later if you
leave it in!)

      Rating: 5
forgot the
      quotes  September 06, 2002
Reviewer:
      A reader
that should read:

begin
      mail_pkg2.send2
       ('me@notrealemail.com',
        'Oracle Database <me2@notrealemail.com>',
        mail_pkg2.array(p_to),
        mail_pkg2.array('bccme@notrealemail.com'),
        'This is a subject',
        'This is the email you sent',
        '<html><body><B> test email</B></body></html>');
    end;
    /

      Rating: 5
good point of
      dbms_output.put_line  September 06, 2002
Reviewer:
      Rick  from still in BC, Canada
Anyways, I tried it. The results look exactly like what I'm
putting into the string that I am pasting manually:

"'addy1 <fake1@email.com>','addy2 <fake2@email.com>'"

(I've replace the actual email addresses, but you get the gist). I don't see a
problem there. I just cut 'n pasted that line (minus the double quotes) into the
send part of the procedure and it worked fine.

This is very irritating, and I'm sorry if I'm wasting your time.

Thanks,

Rick

;-)

          Followup:

what about the DUMP (maybe there is some magic bad character we cannot see
hiding in there!)

      Rating: 5
here's the
      dump(p_to)  September 06, 2002
Reviewer:
      Rick
Typ=1 Len=65:
39,66,114,105,99,107,32,60,98,97,110,100,101,114,115,111,110,64,80,114,101,115,10
5,110,101,116,46,99,111,109,62,39,44,39,66,114,105,99,107,97,103,97,105,110,32,60
,98,114,105,99,107,108,101,110,97,64,80,114,101,115,105,110,101,116,46,99,111,109
,62,39

I had to concatenate the dump together (substr(p_to,1,65)) and
substr(p_to,66,100 (approx.))

          Followup:

and if you compare that to the dump of the cut and pasted example we see??

      Rating: 5
tested a few
      things  September 06, 2002
Reviewer:  you know
      my name by now...
If I put the separate email addresses inside quotes, like so:
'''adsf@asdf.com','asdf_adsf@asdss.com'''
then it is treated as one long string, and the addresses are rendered invalid
(invalid address error). Could, by concatenating the various email addresses
into a string with this function:

create or replace function concat_emails(p_idnum in number )
return varchar2
is
    l_str  varchar2(1000) default null;
    l_sep  varchar2(1)    default null;
begin
    for get_email_string in (
          select distinct ''''||contactname||' <'||contactemail||'>'||''''
contactinfo
          from contacts
          where contactemail is not null
          and (clientid = p_idnum or tagid = p_idnum)
          and receive_alerts = 'Y') loop
        l_str := l_str || l_sep || get_email_string.contactinfo;
        l_sep := ',';
    end loop;
    return l_str;
end;
/
show errors

possibly be the problem? Seems likely at this point.

how else could I do a bulk send of the email using the mail_pkg.array(p_to); ??

My ignorance is blinding at times....

;-]

      Rating: 5
re:
      dump  September 06, 2002
Reviewer:  Rick

if you mean by the p_to dump, and the dump of the actual string (the one that
works) then they are identical.

If this is taking up too much of your time, just let me know in a private email.

Cheers,

Rick

          Followup:

If they are IDENTICAL all the way down - then, well, I'm stumped.  I don't have
any idea at that point.  If the input to the utl_smtp call is identical byte for
byte -- I don't know what to say.

      Rating: 5
heh
      heh  September 06, 2002
Reviewer:  Rick
       from BC, Canada
sorry about that. I've been pulling my hair out all day, but I think I may have
an idea how to get around it.
What I'm going to do is parse the concatenated string of emails into separate
variables and just pass them to the p_to line, separated by commas. it works
when I put variables in like that, containing only one value.

Oh well, I'm learning lots (mainly at your expense!!)

thanks again,

Rick the pest.

;-]

September 06, 2002 Irene -- Thanks for the question regarding
      "Global variables vs parameters", version forms 6i

      You Asked
Hi Tom,
could you explain advantages and disadvantages
of using global variables and parameters in Forms
in terms of efficiency of application and system resourses.
What would you recommend  for passing values between
blocks and program units in one form.
Thank you.
Irene

      and we said...
Avoid global variables whenever possible (they are OK to some degree in a
package body to maintain state and because they use of them is confined to the
pacakge body).

Global Globals should be avoided whenever you can.  Use them only when you have
to.  They are a BUG just waiting to happen.

Formal parameters are the way to go.
Review &
      Followup

      Rating:
      5
  June 28, 2001
Reviewer:  Helena
      Markova  from Bratislava, Slovakia

      Rating: 3
I Agree with
      Tom  June 28, 2001
Reviewer:  Pichaimani
      Balasubramanian  from San Jose, CA USA
In the past I've encountered some serious bugs because of Global Variables. Its
better to avoid it unless it is very necessary.

      Rating: 4
pacakge body
      global variables  September 05, 2002
Reviewer:
      john
are the variables declared on top of PACKAGE BODY that is the variables that are
global to a package body are also main tain state

i have :
SQL> create or replace package my_pack AS
  2
  3  PROCEDURE P1;
  4  END;
  5  /

SQL> Create or replace package BODY my_pack AS
  2
  3  v1 varchar2(1) := 'N';
  4
  5  PROCEDURE P1 AS
  6  BEGIN
  7   DBMS_OUTPUT.PUT_LINE(V1);
  8   IF v1 = 'N' THEN
  9    V1 := 'Y';
 10   END IF;
 11   DBMS_OUTPUT.PUT_LINE(V1);
 12  END;
 13  END;
 14  /

SQL> EXEC MY_PACK.P1;
N
Y

PL/SQL procedure successfully completed.

SQL> EXEC MY_PACK.P1;
Y
Y

any idea why is this happening

          Followup:

any variable defined outside of a procedure/function is a global variable and
maintains its state for the duration of the session.

      Rating: 4
constatnt
      global variables  September 06, 2002
Reviewer:
      A reader
thanks tom
i understand now that we should try avoiding global varaibles.

in our project, to globally access the constants, we created a package that will
have only constatns declared. other packages can refer these constants and use
them in their body.

now will that create any problem, we are only in the development process, if
that is a problem we can remove it.
please guide.

          Followup:

constants are constants - they are perfect for global variables.  Just define
them as constant!

      Rating: 5
Global
      variables vs parameters  September 06, 2002

Reviewer:  Rama  from CT , USA

September 07, 2002 sushant -- Thanks for the question regarding
      "database mounted but not opening", version 8.1.7

      You Asked
Hi Tom

We are planning to install siebel crm software which uses oracle as a backend.
So we were practicing some dba commands as mentioned in the documentation
on a test database.

As per the requirments we need to have only 1 rollback segment with 500M space.
Initially we had rbs0,rbs1,rbs2,rbs3,rbs4,rbs5,rbs6 rollback segements which
were deleted and in place a new rollback segment called
RBS0 with a intial 500M and next 500M was created in the same tablespace RBS.

Before this operation the test database was functioning smoothly.

Now we get an error
when we try to startup the database

SQL> startup
ORACLE instance started.

Total System Global Area  140122140 bytes
Fixed Size                    75804 bytes
Variable Size              58048512 bytes
Database Buffers           81920000 bytes
Redo Buffers                  77824 bytes
Database mounted.
ORA-01157: cannot identify/lock data file 2 - see DBWR trace file
ORA-01110: data file 2: 'D:\ORACLE\ORADATA\SIEBELDB\RBS01.DBF'

Regards
Sushant

      and we said...
sorry if this sounds harsh but...

You need to get someone there to help you.  You really want to get a DBA, at
least for a couple of days -- to get you started.  Then, you'll see how useful
they are and offer them a job.

You've done so many wrong things, I cannot see it getting better before it gets
worse.

First -- you've apparently erased some database files (RBS01.DBF) but you didn't
tell us you were erasing them!  So, you erased this file -- did you DROP the
tablespace this file was part of first!!!

Not knowing the steps you did, I cannot comment (good thing this is test -- just
start over right) on how to fix.

Also, a single RBS - for an OLTP application.  I certainly hope that the Siebel
docs don't say that - that would be foolish.

A RBS with a 500m initial and 500m next would give you at least 1000meg of RBS
(minextents on RBS is 2).  You never want extents that large anyway.

You were better off with 6 RBS's with much smaller extents and minextents set
high enough to give you sufficient space.

Time for you to "rollback".  Go back to the way it was. (it was after all --
working smoothly, no?)
September 07,
      2002 Mo -- Thanks for the question
      regarding "Using Host, goto and transaction", version 8.1.7

      You Asked
Tom:

1.  When I run "host cmd" in SQL*PLUS I get a smal dos window that display the
results fast that I can not see anything .  Is there anyway to keep that window
open at the same time SQL*PLUS is open? otherwise what is the benefit of it.

2.  Why I do not see PL/SQL code that uses

  goto label

<<label>>

IT seems everyone uses exceptions.  But is not this useful especially when you
have a business logic in an if statement that does not raise an oracle defined
excpetion and you have to define your own exceptin using init_pragma?

3.  If I have one transaction that enters data in 5 tables based on some logic
and I want to commit after entering data successfully into 5 tables or rollback
the whole transaction.

DO you add IF (SQL%rowcount > 0 and (x>5 or z<6) ) before each insert to the 4
tables or do you basically create a boolean variable to track that each step was
executed and then at the end before you commit you do:

If (step1 and step2 and step3 and step4) then
  commit;
else
 rolback;
end if;

Thank you,

      and we said...
1)

SQL> host erase c:\temp\file_i_dont_want.anymore

that would be a "use" for it.  But anyway -- you would have to use the windows
setup that says "don't automatically close the little dos windows".  I don't
remember exactly where you do this -- but this is a windows thing, not a sqlplus
issue really (goto your start menu, Run.... put in the same command.  Then ask
yourself "what is the use of that" -- cause the same exact thing will happen!)

Me, when made to use windows, I always use sqlplus.exe in a DOS window. I never
use that "GUI" version, I see no advantages to it, only downsides.

2) goto's are considered to be a fairly poor programming practice.  This school
of thought (that goto's are not a desirable thing) goes back for as many years
as I can remember.  Languages like Basic, that in the beginning didn't have alot
of control structures, forced the use of GOTO but not since then really.

I don't understand your comment about exceptions at all.  I don't have any idea
really why you say they are "not useful".

3) I would code:

   begin
       insert
       insert
       insert
       insert
       insert
       commit;
   exception
       when others then
          rollback;
          raise;
   end;

That is all.

Review
      & Followup

      Rating:
      5
host  September 07, 2002
Reviewer:
      mo  from reston, va
Tom:

1.  You mean that you from windows go to dos prompt (without exiting windows
completely) and then run sqlplus.exe?  Why is this better than using GUI
sqlplus?

2. I know that goto is a poor prgramming practice even though I never understood
"why" it is? and how it is different than using exceptions?

what i meant is that I see goto useful in things like this:

if (a>5 and z>6) then
   goto program1;
else goto program2;
end if;

<<program1>>
...
<<program2>>
..

instead of:

declare
program1 exception;
program2 exception;

begin
if (a>5 and z>6) then
   raise program1;
else raise program2;
end if;

Exception
WHEN program1 then
....
WHEN program2 then
...

3. Great technique.  This way if any insert fails it will raise an exception and
stop. Correct?

          Followup:

1) yes.  I run a cmd.exe shell and run it from there.

why is this "better"?  I can easily change the colors, the font size, the
scrollback buffer, etc.

Host output doesn't "disappear"

I can Cd to where I want to be before starting sqlplus.

It works *just* like sqlplus on every single other platform.

sqlplusw.exe is sort of a aberration -- it is a unique sqlplus, different from
sqlplus on all other platforms.  I don't like different -- I like homogenous.

2) a goto results in what is known as spaghetti code.  hard to read, sometimes
impossible to understand.

exceptions make it IMPOSSIBLE to ignore errors and many programming errors are
directly caused by ignoring errors.  When an exception comes up one of two
things will happen

a) you will catch it and deal with it.
b) you will ignore it and maybe someone else will catch and deal with it OR the
program will just fail outright which is infinitely prefered to CONTINUEING the
faulty program allowing it to do even more damage.

well-- maybe there is three.

c) you will code a when others that is not followed by RAISE to re-raise the
error.  Now you have just disabled exceptions and you are back to the "bad
programmer".

It looks like you are trying to use gotos as an error mechanism.  Or, even
worse, you are trying to use exceptions as something other then error handling
(eg: you are trying to use exceptions as a control structure like IF/THEN/ELSE
or LOOP).  They are a very poor choice for that.

Exceptions are superior in every way to goto for error handling.  I mean --
suppose you have a procedure P that can have two distinct errors -- how would P
be able to use a goto to notify the caller?  (it cannot).  If you are trying to
use exceptions as a program flow control structure STOP -- that would be a
really bad idea.

3) correct. If you have my book "expert one on one oracle" - i cover this in
great detail.

September 07, 2002 antoine -- Thanks for the question regarding
      "native dynamic sql", version 8.1.7

      You Asked
o/s sun solaris 5.8
oracle 8.1.7

Hi Tom,

Thank you for all your great help and suggestions.

I have a problem with this PL/SQL code. I created a native dynamic sql
statement, but when I tried to pass many arguments in a single string (str_BIND)
it didn't work. If you have some hints or solutions about this or about my code,
feel free to give me some advice.

This procedure is going to be called from java program and it is for a dynamic
search.

Yours sincerely,

Antoine

here's the code:

I look at your code from your book
expert one-on-one on page 711-712

create or replace package my_pkg as

type refcursor_Type is ref cursor;

PROCEDURE spGetPROPword(
v_HOTELPROPERTYCITY IN varchar2 default NULL,
v_HOTELPROPERTYADDRESS1 IN varchar2 default NULL,
v_HOTELPROPERTYADDRESS2 IN varchar2 default NULL,
v_HOTELPROPERTYADDRESS3 IN varchar2 default NULL,
v_HOTELPROPERTYADDRESS4 IN varchar2 default NULL,
v_HOTELPROPERTYSTATE IN varchar2 default NULL,
v_HOTELPROPERTYCOUNTRY IN varchar2 default NULL,
v_HOTELPROPERTYPOSTAL IN varchar2 default NULL,
v_HOTELPROPERTYPHONE1 IN varchar2 default NULL,
v_HOTELPROPERTYPHONE2 IN varchar2 default NULL,
v_HOTELPROPERTYNAME IN varchar2 default NULL,
p_cursor out refcursor_Type) ;

end;

CREATE OR REPLACE package body my_pkg as

procedure spGetPROPword
(
v_HOTELPROPERTYCITY IN varchar2 default NULL,
v_HOTELPROPERTYADDRESS1 IN varchar2 default NULL,
v_HOTELPROPERTYADDRESS2 IN varchar2 default NULL,
v_HOTELPROPERTYADDRESS3 IN varchar2 default NULL,
v_HOTELPROPERTYADDRESS4 IN varchar2 default NULL,
v_HOTELPROPERTYSTATE IN varchar2 default NULL,
v_HOTELPROPERTYCOUNTRY IN varchar2 default NULL,
v_HOTELPROPERTYPOSTAL IN varchar2 default NULL,
v_HOTELPROPERTYPHONE1 IN varchar2 default NULL,
v_HOTELPROPERTYPHONE2 IN varchar2 default NULL,
v_HOTELPROPERTYNAME IN varchar2 default NULL,
p_cursor out refcursor_Type ) IS

str_QUERY varchar2(500);
str_BIND varchar2(500);
tag varchar2(1);
begin

str_QUERY:= 'SELECT * from ODD_PROPERTY_MAINTENANCE';

if (V_HOTELPROPERTYCITY IS NOT NULL) OR (V_HOTELPROPERTYADDRESS1 IS NOT NULL) OR

(V_HOTELPROPERTYADDRESS2 IS NOT NULL)  OR (V_HOTELPROPERTYADDRESS3 IS NOT NULL)
OR

(V_HOTELPROPERTYADDRESS4 IS NOT NULL)  OR (V_HOTELPROPERTYSTATE IS NOT NULL)  OR

(V_HOTELPROPERTYCOUNTRY IS NOT NULL) OR (V_HOTELPROPERTYPOSTAL IS NOT NULL) OR

(V_HOTELPROPERTYPHONE1 IS NOT NULL) OR (V_HOTELPROPERTYPHONE2 IS NOT NULL) OR

(V_HOTELPROPERTYNAME IS NOT NULL) then

 str_QUERY:=str_QUERY || ' WHERE ';
end if;

if (V_HOTELPROPERTYCITY IS NOT NULL)
then
  str_QUERY := str_QUERY || 'HOTELPROPERTYCITY = :a ';
 tag:= 'Y';
 str_BIND := v_HOTELPROPERTYCITY;

end if;

if (V_HOTELPROPERTYADDRESS1 IS NOT NULL) and (tag ='Y')
then
 str_QUERY := str_QUERY || ' and HOTELPROPERTYADDRESS1 =:b ';
 str_BIND := str_BIND || ',' || V_HOTELPROPERTYADDRESS1;
elsif (V_HOTELPROPERTYADDRESS1 IS NOT NULL) and (tag <>'Y') then
 str_QUERY := str_QUERY || 'HOTELPROPERTYADDRESS1 =:b ';
 str_BIND := str_BIND || V_HOTELPROPERTYADDRESS1;
 tag:= 'Y';
end if;

if (V_HOTELPROPERTYADDRESS2 IS NOT NULL)  and (tag ='Y') then

 str_QUERY := str_QUERY || ' and HOTELPROPERTYADDRESS2 = :c ';
 str_BIND := str_BIND || ',' || v_HOTELPROPERTYADDRESS2;
elsif (V_HOTELPROPERTYADDRESS2 IS NOT NULL) and (tag <>'Y') then
 str_QUERY := str_QUERY || 'HOTELPROPERTYADDRESS2 = :c ';
 str_BIND := str_BIND || v_HOTELPROPERTYADDRESS2;
 tag:= 'Y';
end if;

if (V_HOTELPROPERTYADDRESS3 IS NOT NULL) and (tag ='Y') then

 str_QUERY := str_QUERY || ' and HOTELPROPERTYADDRESS3 =:d ';
 str_BIND := str_BIND || ',' || v_HOTELPROPERTYADDRESS3;
elsif (V_HOTELPROPERTYADDRESS3 IS NOT NULL) and (tag <>'Y') then
 str_QUERY := str_QUERY || 'HOTELPROPERTYADDRESS3 =:d ';
 str_BIND := str_BIND || v_HOTELPROPERTYADDRESS3;
 tag:= 'Y';
end if;

if (V_HOTELPROPERTYADDRESS4 IS NOT NULL) and (tag ='Y') then
 str_QUERY := str_QUERY || ' and HOTELPROPERTYADDRESS4 =:e ';
 str_BIND := str_BIND || ',' || v_HOTELPROPERTYADDRESS4;
elsif (V_HOTELPROPERTYADDRESS4 IS NOT NULL) and (tag <>'Y') then
 str_QUERY := str_QUERY || 'HOTELPROPERTYADDRESS4 =:e ';
 str_BIND := str_BIND || v_HOTELPROPERTYADDRESS4;
 tag:= 'Y';
end if;

if (V_HOTELPROPERTYSTATE IS NOT NULL)  and (tag ='Y') then
 str_QUERY := str_QUERY || ' and HOTELPROPERTYSTATE = :f ';
 str_BIND := str_BIND || ',' || V_HOTELPROPERTYSTATE;
elsif (V_HOTELPROPERTYSTATE IS NOT NULL) and (tag <>'Y') then
 str_QUERY := str_QUERY || 'HOTELPROPERTYSTATE = :f ';
 str_BIND := str_BIND || V_HOTELPROPERTYSTATE;
 tag:= 'Y';
end if;

if (V_HOTELPROPERTYCOUNTRY IS NOT NULL) and (tag ='Y') then
 str_QUERY := str_QUERY || ' and HOTELPROPERTYCOUNTRY = :g';
 str_BIND := str_BIND || ',' || V_HOTELPROPERTYCOUNTRY;

elsif (V_HOTELPROPERTYCOUNTRY IS NOT NULL) and (tag <>'Y') then
 str_QUERY := str_QUERY || 'HOTELPROPERTYCOUNTRY = :g ';
 str_BIND := str_BIND || V_HOTELPROPERTYCOUNTRY;
 tag:= 'Y';
end if;

if (V_HOTELPROPERTYPOSTAL IS NOT NULL)  and (tag ='Y') then
 str_QUERY := str_QUERY || ' and HOTELPROPERTYPOSTAL = :h ';
 str_BIND := str_BIND || ',' || V_HOTELPROPERTYPOSTAL;
elsif (V_HOTELPROPERTYPOSTAL IS NOT NULL) and (tag <>'Y') then
 str_QUERY := str_QUERY || 'HOTELPROPERTYPOSTAL = :h ';
 str_BIND := str_BIND || V_HOTELPROPERTYPOSTAL;
 tag:= 'Y';
end if;

if (V_HOTELPROPERTYPHONE1 IS NOT NULL)  and (tag ='Y') then
 str_QUERY := str_QUERY || ' and HOTELPROPERTYPHONE1 = :i ';
 str_BIND := str_BIND || ',' || V_HOTELPROPERTYPHONE1;
elsif (V_HOTELPROPERTYPHONE1 IS NOT NULL) and (tag <>'Y') then
 str_QUERY := str_QUERY || 'HOTELPROPERTYPHONE1 = :i ';
 str_BIND := str_BIND || V_HOTELPROPERTYPHONE1;
 tag:= 'Y';
end if;

if (V_HOTELPROPERTYPHONE2 IS NOT NULL) and (tag ='Y')  then
 str_QUERY := str_QUERY || ' and HOTELPROPERTYPHONE2 =:j ';
 str_BIND := str_BIND || ',' || V_HOTELPROPERTYPHONE2;
elsif (V_HOTELPROPERTYPHONE2 IS NOT NULL) and (tag <>'Y') then
 str_QUERY := str_QUERY || 'HOTELPROPERTYPHONE2 = :j ';
 str_BIND := str_BIND || V_HOTELPROPERTYPHONE2;
 tag:= 'Y';
end if;

if (V_HOTELPROPERTYNAME  IS NOT NULL)  and (tag ='Y') then
 str_QUERY := str_QUERY || ' and HOTELPROPERTYNAME = :k ';
 str_BIND := str_BIND || ',' || V_HOTELPROPERTYNAME;
elsif (V_HOTELPROPERTYNAME IS NOT NULL) and (tag <>'Y') then
 str_QUERY := str_QUERY || 'HOTELPROPERTYNAME = :k ';
 str_BIND := str_BIND || V_HOTELPROPERTYNAME;

end if;

open p_cursor for str_QUERY using str_BIND;

end;

end ;

Here the error I got:

SQL*Plus: Release 8.1.7.0.0 - Production on Fri Aug 30 14:24:14 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected to:
Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production
With the Partitioning option
JServer Release 8.1.7.0.0 - Production

SQL> variable t refcursor
SQL> DECLARE
  2    V_HOTELPROPERTYCITY VARCHAR2(200);
  3    V_HOTELPROPERTYADDRESS1 VARCHAR2(200);
  4    V_HOTELPROPERTYADDRESS2 VARCHAR2(200);
  5    V_HOTELPROPERTYADDRESS3 VARCHAR2(200);
  6    V_HOTELPROPERTYADDRESS4 VARCHAR2(200);
  7    V_HOTELPROPERTYSTATE VARCHAR2(200);
  8    V_HOTELPROPERTYCOUNTRY VARCHAR2(200);
  9    V_HOTELPROPERTYPOSTAL VARCHAR2(200);
 10    V_HOTELPROPERTYPHONE1 VARCHAR2(200);
 11    V_HOTELPROPERTYPHONE2 VARCHAR2(200);
 12    V_HOTELPROPERTYNAME VARCHAR2(200);
 13    VV_HOTELPROPERTYCITY VARCHAR2(200);
 14
 15  BEGIN
 16    V_HOTELPROPERTYCITY := 'Hong Kong';
 17    V_HOTELPROPERTYADDRESS1 := NULL;
 18    V_HOTELPROPERTYADDRESS2 := NULL;
 19    V_HOTELPROPERTYADDRESS3 := NULL;
 20    V_HOTELPROPERTYADDRESS4 := NULL;
 21    V_HOTELPROPERTYSTATE := NULL;
 22    V_HOTELPROPERTYCOUNTRY := 'HK';
 23    V_HOTELPROPERTYPOSTAL := NULL;
 24    V_HOTELPROPERTYPHONE1 := NULL;
 25    V_HOTELPROPERTYPHONE2 := NULL;
 26    V_HOTELPROPERTYNAME := NULL;
 27    --VV_HOTELPROPERTYCITY := NULL;
 28
 29   my_pkg.spGetPROPword ( V_HOTELPROPERTYCITY, V_HOTELPROPERTYADDRESS1,
V_HOTELPROPERTYADDRESS2,
V_HOTELPROPERTYADDRESS3, V_HOTELPROPERTYADDRESS4, V_HOTELPROPERTYSTATE,
V_HOTELPROPERTYCOUNTRY, V_HO
TELPROPERTYPOSTAL, V_HOTELPROPERTYPHONE1, V_HOTELPROPERTYPHONE2,
V_HOTELPROPERTYNAME, :t );
 30  END;
 31  /
DECLARE
*
ERROR at line 1:
ORA-01008: not all variables bound
ORA-06512: at "ABOCTI.MY_PKG", line 169
ORA-06512: at line 29

SQL>

      and we said...
You only ever bind the first variable

open x for y using z;

will only bind a single variable with the value of Z.  If Z = 'a,b,c,d' that is
NOT the same as coding:

open x for y using a,b,c,d;

is it opening X using the string Y with the single value in Z which just happens
to be a comma delimited list.

Here is how to do this:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1288401763279

Review &
      Followup

      Rating: 5
thank again
      Tom  September 02, 2002
Reviewer:  Antoine
      Bocti  from Canada,Montreal
Interesting, I never knew about sys_context()!
Sorry for not searching properly in the archive.

      Rating: 5
REF cursor out
      and intermedia  September 03, 2002
Reviewer:
      antoine Bocti  from Canada, Montreal
Hi Tom,

is it possible with your exemple that you gave me to declare a ref cursor out.

And in the applacation context to put a search with intermedia like for exemple:

if ( p_hotelpropertycity  is NOT NULL ) then
           dbms_session.set_context( 'MY_CTX', 'city',
                                     'contains
hotelpropertycity,'''p_hotelpropertycity%''' ');
           l_query := l_query ||
                ' and ename like
                  sys_context( ''MY_CTX'', ''city'' ) ';
       end if;

thanks.

          Followup:

yes, you can use a ref cursor IN OUT parameter instead of a local variable.

that example you gave is not "intermedia"?  there is no contains....

      Rating: 5
REF cursor out
      and intermedia   September 03, 2002
Reviewer:
      antoine Bocti  from Canada,Montreal
I put this code in expemple instead of your code.

This is the ink you gave me.

CURSOR

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1288401763279

My question was, is it possible in your exemple (that you gave me above
(CURSOR)) to put some intermedia operator like contains to build  a dynamic
search procedure?

If it's possible can you give me an exemple please.

Thank Tom.

          Followup:

hmm, looks like contains doesn't get along with sys_context in 8i...

You'll have to use this technique instead:

      if ( p_ename is NOT NULL ) then
           l_query := l_query ||
                ' and contains( field, :p_ename ) > 0 '
       else
           l_query := l_query ||
                ' and 1 = DECODE(:p_ename,NULL,1,1)';
       end if;

and then when you open the query:

     open refcursor for l_query using p_ename;

      Rating:
      5
thanksTom!  September 04, 2002
Reviewer:
      Antoine Bocti  from Montreal,Canada
I just have a question for intermedia and the use of wildcard(%), what's your
opinion about it?

          Followup:

wildcards slow everything down.  I only do them on structured data (where we
have a CHANCE at maintaining performance)

      Rating:
      5
thanksTom!  September 04, 2002
Reviewer:
      Antoine Bocti  from Montreal,Canada
Hi tom,

with intermedia can I search over multiple table with a single query.

If not, can you create a view or a materialize view to do the query?

What is your point of view?

thanks.

          Followup:

Yes, in fact -- that is EXACTLY what I do here on asktom!

I indexed this procedure:

create or replace procedure index_ask_tom( p_id in rowid, p_lob IN OUT clob )
as
begin
    for x in ( select * from ask_tom.WWC_ASK_INDEXED_QUESTIONS$ where rowid =
p_id )
    loop
        dbms_lob.copy( p_lob, x.text, dbms_lob.getLength(x.text) );
        for y in ( select '<!--  ' || REVIEW_TITLE || ' ' || REVIEWER_NAME || '
' || REVIEWER_LOCATION || ' ' subj,
                          review_comments
                     from ask_tom.WWC_ASK_QUESTION_REVIEWS$ where displayid =
x.displayid )
        loop
            dbms_lob.writeAppend( p_lob, length(y.subj), y.subj );
            dbms_lob.append( p_lob, y.review_comments );
        end loop;
        for y in ( select followup_comments
                     from ask_tom.WWC_ASK_REVIEW_FOLLOWUPS$
                    where displayid = x.displayid )
        loop
            dbms_lob.writeAppend( p_lob, 1, ' ' );
            dbms_lob.append( p_lob, y.followup_comments );
        end loop;
        dbms_lob.writeAppend( p_lob, length(' -->'), ' -->' );
    end loop;
end;
/

that glues the questions with the answers to the comments and my followups (so
when you search, you are searching across all of them).

See:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:440419921146
for a full example.

      Rating: 5
contains
      operator  September 04, 2002
Reviewer:  mo
       from Vienna, USA
Tom:

1.  What do you mean by "structured data". Is it char or varchar2 column types.

"wildcards slow everything down.  I only do them on structured data (where we
have a CHANCE at maintaining performance) "

2.  Does 8i support the contains operator.  How is that different than using the
like % .

Thank you,

          Followup:

1) yes.

2) yes.

read the intermedia documentation that is freely available and you'll learn alot
about its capabilities:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/inter.817/a77063/toc.htm

      Rating: 5
me
      again...  September 04, 2002
Reviewer:  Antoine
      Bocti  from Montreal, Canada
Hi Tom,

So if I want to do a search on two table by exemple:

table brand
id_brand
name
code

table hotel
id_hotel
id_brand
name
number_room

Can I do a select and join brand and hotel and use one index for a field in
brand and and use one index for a field in hotel?

select * from brand A,hotel where contains(a.name,'Hilton') > 0 OR
                        contains(b.name,'hilton by the sea') > 0;

Is it better to create a PLSQL function that returns  the two fields glued
together?

          Followup:

It would be more efficient if you indexed the function that glued the data
together if you frequently search on those two items as if they were a single
item.

      Rating: 5
REF cursor out
      and intermedia   September 05, 2002
Reviewer:
      Antoine Bocti  from Montreal, Canada
Hi Tom,

Is it possible to add a wildcard add the end of  :p_hotelpropertycity in my
code?

I try these :
l_query := l_query ||   ' and contains( hotelpropertycity, :p_hotelpropertycity'
||' ||% ) > 0';

it compile but at the run time it failed!

Original code that work!

if ( p_hotelpropertycity is NOT NULL ) then
           l_query := l_query ||   ' and contains(hotelpropertycity,
:p_hotelpropertycity ) > 0 ';
else
           l_query := l_query || ' and 1 =
DECODE(:p_hotelpropertycity,NULL,1,1)';
end if;

          Followup:

If that is not working just add the
' and contains( hotel.., :p_hotelpropertycity ) > 0'

or

' and 1 = decode( :p_hotel..., null, 1, 1 ) '

and when you open the query use

  USING p_hotelcityproperty || '%';

      Rating: 5
Tom thank you
      so much again!  September 06, 2002
Reviewer:
      A reader

      Rating:
      5
intermedia  September 07, 2002
Reviewer:
      mo  from vienna, va
Tom:

AM I Correct:

1. If you are doing queries on structured data you can get away with regular SQL
statements and using like % operator.
even using varchar2(2000).

2.  When you do text searches for unstructured data like blob, or long you need
intermedia and use contains operator?

          Followup:

1) yes

2) "need" is too strong. WANT is the correct term.

September 07, 2002 ALVARO -- Thanks for the question regarding
      "ESTIMATE GROWING OF TABLES AND INDEXES", version 8.1.7

      You Asked
HI TOM,

I'VE READ SOME QUESTIONS HERE ABOUT ESTIMATE GROWING USING DBMS_SPACE ... BUT I
WOULD WANT YOU GIVE SOME ADVISES ABOUT THIS

I ANALYZE TWO SCHEMAS ON MY DB WITH DBMS_UTILITY.ANALYZE_SCHEMA USING THE
ESTIMATE OPTION (THE COMPUTE OPTION IS TOO HEAVY FOR THE AMOUNT OF DATA THAT I
HAVE). THIS PROCESS IS MADE ALMOST EVERY DAY AND I'VE CREATED A TABLE LIKE
DBA_TABLES WHERE I KEEP ALL OF THE DATA OF EVERY ANALYZE. IN THIS TABLE I HAVE

OWNER
TABLE_NAME
NUM_ROWS
BLOCKS
EMPTY_BLOCKS
AVG_SPACE
CHAIN_CNT
AVG_ROW_LEN
AVG_SPACE_FREELIST_BLOCKS
NUM_FREELIST_BLOCKS
LAST_ANALYZED
NUM_ROWS_ANT   -- NUM ROWS BEFORE THE LAST_ANALYZED
AVG_CRE_ROW    -- AVG(NUM_ROWS-NUM_ROWS_ANT) DAILY GROWING

THE AVG_CRE_ROW IS CALCULATED IN A PROCEDURE THAT TAKES ALL OF THE ROWS FOR A
TABLE.

NOW , IF I HAVE DATA OF EACH ANALYZE ON A TABLE ...

1 NUM ROWS AT THIS MOMENT
2 NUM ROWS BEFORES THE LAST ANALYZE
3 AVG(1-2) AVG OF GROWING DAILY
4 AVG_ROW_LEN
5 BLOCKS
6 EMPTY_BLOCKS
7 AVG_SPACE
8 CHAINT_CNT

(1)      (2)      (3)     (4) (5)     (6)         (7)         (8)
25750390 25750390 3089737 125 232013  12138       1604          0
44581737 25750390 3089737 125 402855      0       1643          0
45524443 44581737 3089737 125 410210   4853       1604          0
46800868 45524443 3089737 125 421683   5588       1604          0
47079443 46800868 3089737 125 424193   3078       1604          0
47378550 47079443 3089737 125 426888    383       1604          0
47378550 47378550 3089737 125 426888    383       1604          0

IS RIGHT TO SAY THAT THE GROWING FOR THIS TABLE COULD BE

AVG_ROW_LEN*AVG_GROWING_DAILY(3)*365 (FOR A YEAR) ????

      and we said...
I personally would look at the growth of the blocks over time, forget about the
row counts and row sizes.  You are not looking at row overhead, block overhead
and the like.

Look at the number of blocks in use and plot the growth of them over time.  That
would be a more accurate estimation.

Also, dbms_utility should be swapped out for dbms_stats,
dbms_utility.analyze_schema is there for backwards compatibility with 7.3
September 08, 2002 Jeff -- Thanks for the question regarding
      "understanding RAW 10046 trace output", version 8.1.7

      You Asked
Tom,

I'm looking to get a better understanding of raw 10046 trace data.
I read the pertaining section in your expert one-on-one book, but still have a
few questions.  (no, it's not going to be a huge multi-part question, just two
related questions)
First, why do I sometimes find two identical statements (same hash id and
statement id) in the trace file.  Shouldn't it just list the additional parsing,
executing, etc. under one listing for the statement.
Second, I understand what a recursive statement is, but if there are one or more
statements with a depth > 0 following a statement, does that mean that ALL those
statements (and their associated waits) that follow apply to the same parent
statement?  (i.e.  is it processed sequentially?)

Thanks.

Jeff

      and we said...
Every parse will cause the statment to appear in the trace file.  TKPROF has an
option to either aggregate same statements together (default) or to report the
timing of EACH statement separately.

If you do the trace with 10046 level 12 (with binds) -- you'll see why it would
be important, you want the timings/stats for each individual execution of the
query with its specific inputs.

As for the recursive one -- no, not necessarily.  You can run a SELECT that does
some recursive SQL -- fetch the first row -- and then start a new query -- and
then start using that SELECT again -- that does some recursive SQL -- and then
start a new query and so on.  For example:

  for x in ( select F(x) from T )
  loop
       select * into y from t2;
  end;

Suppose F(x) is a PLSQL function that does some more SQL.  select F(X) from T
will start -- it'll do recursive sql in F, return a row and then select * into Y
from t2 will happen.   select * from t2 is not recursive sql with respect to
select f(x) from t however.  Then, we fetch the second row and F(x) is executed
yet again -- then select * from t2 and so on.
Review &
      Followup

      Rating: 4
trace data
      followup  September 06, 2002
Reviewer:  Jeff
      Price  from NJ
Thanks.  That clears it up except for one thing.  If the recursive sql
statements aren't listed sequentially following a parent statement, then how can
I find all the recursive statements that belong to a specific parent statement?

          Followup:

You cannot really, consider this code:

=====================
PARSING IN CURSOR #3 len=222 dep=0 uid=566 oct=47 lid=566 tim=53098119
declare
    l_data number;
begin
    for x in ( select f(rownum) r, rownum r2 from all_users where rownum < 10 )
    loop
        execute immediate 'select :x*:x from dual Y$' || x.r2
           into l_data
          using x.r, x.r;
    end loop;
end;
dep=0, top level statement...
=====================
PARSING IN CURSOR #4 len=64 dep=1 uid=566 oct=3 lid=566 tim=53098121
SELECT F(ROWNUM) R,ROWNUM R2   FROM ALL_USERS  WHERE ROWNUM < 10
dep=1, first level recursive sql -- you look backwards in the trace to find
dep=0 to find the "parent" statement, it works for this one
=====================
PARSING IN CURSOR #5 len=29 dep=2 uid=566 oct=3 lid=566 tim=53098121
select sqrt(:x) from dual X$1
dep=2, second level recursive sql, look backwards to find dep=1 and you found
the parent
=====================
PARSING IN CURSOR #5 len=26 dep=1 uid=566 oct=3 lid=566 tim=53098121
select :x*:x from dual Y$1
dep=1, first level recursive -- look backwards for 0 and you find it BUT...
=====================
PARSING IN CURSOR #5 len=29 dep=2 uid=566 oct=3 lid=566 tim=53098121
select sqrt(:x) from dual X$2
dep=2, we KNOW this "parent" is not the immediately prior statement but quite
a few back.  We cannot TELL that from the trace file, you have to "know" that
from the SQL itself.  We don't track that information...
=====================
PARSING IN CURSOR #5 len=26 dep=1 uid=566 oct=3 lid=566 tim=53098122
select :x*:x from dual Y$2
=====================
PARSING IN CURSOR #5 len=29 dep=2 uid=566 oct=3 lid=566 tim=53098122
select sqrt(:x) from dual X$3
=====================
PARSING IN CURSOR #5 len=26 dep=1 uid=566 oct=3 lid=566 tim=53098122
select :x*:x from dual Y$3
=====================
PARSING IN CURSOR #5 len=29 dep=2 uid=566 oct=3 lid=566 tim=53098122
select sqrt(:x) from dual X$4

      Rating: 5
Listing trace
      events sequentially  September 06, 2002
Reviewer:
      Mark Manning  from Atlanta, GA
On every line that begins with "PARSING IN CURSOR", I use a combination of the
timestamp (tim) column and the depth (dep) column to accomplish this.  The tim
value shows at what point something was requested in relation to other requests.
 This not only applies to parse/execute/fetch operations but also for
parse/runtime errors.

I have found this to be VERY handy when researching the behavior of large
complex procedures/packages as well as diagnosing problems.

The metalink doc 39817.1 gives a very good understanding of the contents of
10046 trace files.

As this is one way, I am sure Tom has another (probably simipler) way of
determining this.

Mark M.

          Followup:

whoops -- I just showed it cannot be done! (not reliably anyway)....

Hmm....

      Rating:
      5
frustrating....  September 06, 2002

Reviewer:  Jeff Price  from NJ
Well, that makes sense.  I don't like it, but I guess I'll have to log a
complaint with the oracle kernel developers. ;)
Basically, what I was trying to accomplish was to create a script to parse the
trace file and gather all the associated wait info for each statement, and I
wanted to list all the waits from all recursive sql under the initial (dep=0)
parent statement.  I figured if the dep=0 statement called the sql (directly or
indirectly), then those waits should be associated with it.
But it looks like there's no way to make that association.
Damn... back to the drawing board.

      Rating:
      5
frustrating....yes!  September 06, 2002

Reviewer:  Mark Manning  from Atlanta, GA
Sometime ago, I wrote a program that reads 10046 trace files for the purposes
complementing tkprof with regards to showing:

Summary of waits/cpu time/etc by SQL statement
Building of a "statement tree" by first parsing
Mapping subsequent exec/bind/fetch calls in relation to the session.

Here is the first few PARSING IN CURSOR lines of a trace file from an Oracle
8.1.7.4.1 EE instance running on Windoze 2000 Server.  This trace file is a
level 8 trace of a package that looks at data with given criteria, validates the
rules for said criteria, and performs actions based upon those rules (I excluded
all the Oracle internal SQL from the listing).  The same procedure was executed
six times, each with a different criteria.

PARSING IN CURSOR #1 len=52 dep=0 uid=54 oct=47 lid=54 tim=1958614 hv=2149188146
ad='4422cc8'
PARSING IN CURSOR #3 len=55 dep=1 uid=54 oct=98 lid=54 tim=1958797 hv=3020714499
ad='43c62d4'
PARSING IN CURSOR #3 len=72 dep=1 uid=54 oct=3 lid=54 tim=1960455 hv=3349640452
ad='43c113c'
PARSING IN CURSOR #12 len=68 dep=1 uid=54 oct=3 lid=54 tim=1960877 hv=4225966364
ad='4305aa8'
PARSING IN CURSOR #17 len=33 dep=1 uid=60 oct=42 lid=60 tim=1961741 hv=684019188
ad='42ae3dc'
PARSING IN CURSOR #22 len=68 dep=1 uid=60 oct=42 lid=60 tim=1961825
hv=2508626919 ad='420904c'
PARSING IN CURSOR #33 len=82 dep=1 uid=60 oct=7 lid=60 tim=1962180 hv=188990126
ad='42b8c50'
PARSING IN CURSOR #34 len=54 dep=1 uid=60 oct=3 lid=60 tim=1962196 hv=4137328418
ad='42658b8'
PARSING IN CURSOR #22 len=313 dep=1 uid=60 oct=2 lid=60 tim=1962210
hv=2584885057 ad='41ec77c'
PARSING IN CURSOR #35 len=474 dep=1 uid=60 oct=3 lid=60 tim=1962281 hv=302028352
ad='42653d0'
PARSING IN CURSOR #36 len=129 dep=1 uid=60 oct=3 lid=60 tim=1962396
hv=4027223860 ad='4264248'
PARSING IN CURSOR #22 len=100 dep=1 uid=60 oct=3 lid=60 tim=1962445 hv=851802408
ad='41cf0e8'
PARSING IN CURSOR #37 len=258 dep=1 uid=60 oct=3 lid=60 tim=1962461 hv=47320436
ad='42b61bc'
PARSING IN CURSOR #17 len=43 dep=1 uid=54 oct=3 lid=54 tim=1962560 hv=2132789873
ad='42ec1ac'
PARSING IN CURSOR #39 len=176 dep=1 uid=60 oct=3 lid=60 tim=1962655 hv=365793532
ad='42656c4'
PARSING IN CURSOR #22 len=63 dep=1 uid=60 oct=62 lid=60 tim=1962907
hv=3008036782 ad='418facc'
PARSING IN CURSOR #41 len=301 dep=1 uid=60 oct=3 lid=60 tim=1963438
hv=3432234351 ad='42b8574'
PARSING IN CURSOR #42 len=134 dep=1 uid=60 oct=6 lid=60 tim=1963553
hv=1545559916 ad='4260d6c'

When I run the entire trace file through my program, here is what I get
(excluding the Oracle internal SQL):

Sequential listing of statements.
2149188146:4422cc8                1958614 (USER) PL/SQL EXECUTE
 3020714499:43c62d4               1958797 (USER) ALTER PACKAGE BODY
 3349640452:43c113c               1960455 (USER) SELECT
 4225966364:4305aa8               1960877 (USER) SELECT
 684019188:42ae3dc                1961741 (USER) ALTER SESSION
 2508626919:420904c               1961825 (USER) ALTER SESSION
 188990126:42b8c50                1962180 (USER) DELETE
 4137328418:42658b8               1962196 (USER) SELECT
 2584885057:41ec77c               1962210 (USER) INSERT
 302028352:42653d0                1962281 (USER) SELECT
 4027223860:4264248               1962396 (USER) SELECT
 851802408:41cf0e8                1962445 (USER) SELECT
 47320436:42b61bc                 1962461 (USER) SELECT
 2132789873:42ec1ac               1962560 (USER) SELECT
 365793532:42656c4                1962655 (USER) SELECT
 3008036782:418facc               1962907 (USER) ANALYZE TABLE
 3432234351:42b8574               1963438 (USER) SELECT
 1545559916:4260d6c               1963553 (USER) UPDATE
 624084522:4265bdc                1963570 (USER) UPDATE
 2694197967:413273c               1963578 (USER) ALTER SESSION
 3879039368:41314dc               1963585 (USER) ALTER SESSION
 2725910552:41304ac               1963591 (USER) ALTER SESSION
 1096768091:412fb70               1963598 (USER) ALTER SESSION
1697159799:412ecd0                1963613 (USER) PL/SQL EXECUTE
464775564:3f9ffb8                 2009021 (USER) PL/SQL EXECUTE
 4225966364:4305aa8               2009027 (USER) SELECT
 684019188:42ae3dc                2009096 (USER) ALTER SESSION
 2508626919:420904c               2009104 (USER) ALTER SESSION
 2584885057:41ec77c               2009117 (USER) INSERT
 3778901711:42636fc               2009127 (USER) SELECT
 4289283319:42b6eb0               2009134 (USER) SELECT
 3009714825:42b72d4               2009147 (USER) SELECT
 2349399529:3fa5cb4               2009153 (USER) SELECT
 813450063:4261730                2009170 (USER) UPDATE
 2694197967:413273c               2009183 (USER) ALTER SESSION
 3879039368:41314dc               2009189 (USER) ALTER SESSION
 2725910552:41304ac               2009194 (USER) ALTER SESSION
 1096768091:412fb70               2009200 (USER) ALTER SESSION
 4199142592:42659e8               2009205 (USER) UPDATE
 2694197967:413273c               2009216 (USER) ALTER SESSION
 3879039368:41314dc               2009221 (USER) ALTER SESSION
 2725910552:41304ac               2009226 (USER) ALTER SESSION
 1096768091:412fb70               2009232 (USER) ALTER SESSION
1697159799:412ecd0                2009243 (USER) PL/SQL EXECUTE
3806514016:3f99378                2011824 (USER) UPDATE
1697159799:412ecd0                2011831 (USER) PL/SQL EXECUTE
544032647:3f9cdec                 2014453 (USER) INSERT
1697159799:412ecd0                2014461 (USER) PL/SQL EXECUTE
3629519191:4089c68                2014661 (USER) COMMIT
464775564:3f9ffb8                 2015220 (USER) PL/SQL EXECUTE
 4225966364:4305aa8               2015227 (USER) SELECT
 684019188:42ae3dc                2015296 (USER) ALTER SESSION
 2508626919:420904c               2015303 (USER) ALTER SESSION
 2584885057:41ec77c               2015315 (USER) INSERT
 2349399529:3fa5cb4               2015336 (USER) SELECT
 2694197967:413273c               2015354 (USER) ALTER SESSION
 3879039368:41314dc               2015360 (USER) ALTER SESSION
 2725910552:41304ac               2015365 (USER) ALTER SESSION
 1096768091:412fb70               2015371 (USER) ALTER SESSION
 2694197967:413273c               2015382 (USER) ALTER SESSION
 3879039368:41314dc               2015387 (USER) ALTER SESSION
 2725910552:41304ac               2015392 (USER) ALTER SESSION
 1096768091:412fb70               2015398 (USER) ALTER SESSION
1697159799:412ecd0                2015410 (USER) PL/SQL EXECUTE
464775564:3f9ffb8                 2018177 (USER) PL/SQL EXECUTE
 4225966364:4305aa8               2018184 (USER) SELECT
 684019188:42ae3dc                2018253 (USER) ALTER SESSION
 2508626919:420904c               2018285 (USER) ALTER SESSION
 2584885057:41ec77c               2018297 (USER) INSERT
 2349399529:3fa5cb4               2018318 (USER) SELECT
 62186039:3f97788                 2018334 (USER) LOCK TABLE
 2694197967:413273c               2018350 (USER) ALTER SESSION
 3879039368:41314dc               2018355 (USER) ALTER SESSION
 2725910552:41304ac               2018361 (USER) ALTER SESSION
 1096768091:412fb70               2018366 (USER) ALTER SESSION
 2694197967:413273c               2018377 (USER) ALTER SESSION
 3879039368:41314dc               2018383 (USER) ALTER SESSION
 2725910552:41304ac               2018388 (USER) ALTER SESSION
 1096768091:412fb70               2018393 (USER) ALTER SESSION
1697159799:412ecd0                2018405 (USER) PL/SQL EXECUTE
464775564:3f9ffb8                 2021861 (USER) PL/SQL EXECUTE
 4225966364:4305aa8               2021868 (USER) SELECT
 684019188:42ae3dc                2021937 (USER) ALTER SESSION
 2508626919:420904c               2021967 (USER) ALTER SESSION
 2584885057:41ec77c               2021979 (USER) INSERT
 2349399529:3fa5cb4               2022000 (USER) SELECT
 62186039:3f97788                 2022018 (USER) LOCK TABLE
 2694197967:413273c               2022033 (USER) ALTER SESSION
 3879039368:41314dc               2022039 (USER) ALTER SESSION
 2725910552:41304ac               2022044 (USER) ALTER SESSION
 1096768091:412fb70               2022049 (USER) ALTER SESSION
 2694197967:413273c               2022060 (USER) ALTER SESSION
 3879039368:41314dc               2022066 (USER) ALTER SESSION
 2725910552:41304ac               2022072 (USER) ALTER SESSION
 1096768091:412fb70               2022077 (USER) ALTER SESSION
1697159799:412ecd0                2022088 (USER) PL/SQL EXECUTE
464775564:3f9ffb8                 2029232 (USER) PL/SQL EXECUTE
 4225966364:4305aa8               2029238 (USER) SELECT
 684019188:42ae3dc                2029309 (USER) ALTER SESSION
 2508626919:420904c               2029342 (USER) ALTER SESSION
 2584885057:41ec77c               2029354 (USER) INSERT
 2349399529:3fa5cb4               2029376 (USER) SELECT
 62186039:3f97788                 2029391 (USER) LOCK TABLE
 1519337442:3f8b3f8               2029396 (USER) DELETE
 2640875297:3f7df58               2029403 (USER) SELECT
 2694197967:413273c               2029457 (USER) ALTER SESSION
 3879039368:41314dc               2029463 (USER) ALTER SESSION
 2725910552:41304ac               2029468 (USER) ALTER SESSION
 1096768091:412fb70               2029474 (USER) ALTER SESSION
1697159799:412ecd0                2029483 (USER) PL/SQL EXECUTE

The spacing on the left of the hash value:sql address is meant to show its
recursion depth.  When I step through each statement, it correctly maps to the
order it should run in the package.

Now in your example, I can see where the EXECUTE IMMEDIATE causes issues because
it has to reparse (soft if previously executed) the statement as opposed to
DBMS_SQL.PARSE once and DBMS_SQL.EXECUTE/BIND many times.  However, where you
have a subquery in a for loop that calls a function, it appears to reparse it at
each call as opposed to putting this in a explicit cursor.

I rewrote your example (without the function) using explicit and implicit
cursors in a procedure on a Oracle 9.0.1.1.1 EE instance.

create or replace procedure tracetest as
begin
declare
    l_data number;
    cursor c_select is
        select rownum r, rownum r2
    from all_users
    where rownum < 10;
begin
    for i_select in c_select
    loop
        select i_select.r*i_select.r
         into l_data
    from dual;
    end loop;
end;
end tracetest;

After running the procedure with event 10046 enabled on 9.0.1, I got this:

PARSING IN CURSOR #1 len=70 dep=0 uid=19 oct=42 lid=19 tim=128114344
hv=567314799 ad='7b08cfe0'
alter session set events = '10046 trace name context forever, level 1'
END OF STMT
EXEC #1:c=0,e=0,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=4,tim=128114344
=====================
PARSING IN CURSOR #1 len=23 dep=0 uid=19 oct=47 lid=19 tim=130739344
hv=3047870254 ad='7b091618'
BEGIN tracetest; END;
END OF STMT
PARSE #1:c=15625,e=16000,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=4,tim=130739344
=====================
PARSING IN CURSOR #2 len=61 dep=1 uid=19 oct=3 lid=19 tim=130739344
hv=2765231765 ad='7b1093e0'
SELECT rownum r, rownum r2
    from all_users
    where rownum < 10
END OF STMT
EXEC #2:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=130739344
FETCH #2:c=0,e=0,p=0,cr=5,cu=2,mis=0,r=1,dep=1,og=4,tim=130739344
=====================
PARSING IN CURSOR #4 len=33 dep=1 uid=19 oct=3 lid=19 tim=132786344
hv=2102796684 ad='7b109cd4'
SELECT :b1*:b1
         from dual
END OF STMT
EXEC #4:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=132786344
FETCH #4:c=0,e=0,p=0,cr=1,cu=2,mis=0,r=1,dep=1,og=4,tim=132786344
FETCH #2:c=0,e=0,p=0,cr=7,cu=0,mis=0,r=1,dep=1,og=4,tim=132786344
EXEC #4:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=134833344
FETCH #4:c=0,e=0,p=0,cr=1,cu=2,mis=0,r=1,dep=1,og=4,tim=134833344
FETCH #2:c=0,e=0,p=0,cr=7,cu=0,mis=0,r=1,dep=1,og=4,tim=134833344
EXEC #4:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=136880344
FETCH #4:c=0,e=0,p=0,cr=1,cu=2,mis=0,r=1,dep=1,og=4,tim=136880344
FETCH #2:c=0,e=0,p=0,cr=7,cu=0,mis=0,r=1,dep=1,og=4,tim=136895344
EXEC #4:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=138942344
FETCH #4:c=0,e=0,p=0,cr=1,cu=2,mis=0,r=1,dep=1,og=4,tim=138942344
FETCH #2:c=0,e=0,p=0,cr=7,cu=0,mis=0,r=1,dep=1,og=4,tim=138942344
EXEC #4:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=140989344
FETCH #4:c=0,e=0,p=0,cr=1,cu=2,mis=0,r=1,dep=1,og=4,tim=140989344
FETCH #2:c=0,e=0,p=0,cr=2,cu=0,mis=0,r=0,dep=1,og=4,tim=140989344
EXEC #1:c=15625,e=10250000,p=0,cr=40,cu=12,mis=0,r=1,dep=0,og=4,tim=140989344
XCTEND rlbk=0, rd_only=1

While it does show the parse once, execute/fetch many times, it does not show
the tim value increasing.  This appears to do the same thing on your example as
well.  Hmmmm.  Has something changed with regards to the tim column on 9i?  Here
is an snippet from the recently updated Metalink Doc 39817.1:

    tim        Timestamp.
        Pre-Oracle9i, the times recorded by Oracle only have a resolution
                of 1/100th of a second (10mS). As of Oracle9i some times are
                available to microsecond accuracy (1/1,000,000th of a second).
                The timestamp can be used to determine times between points
                in the trace file.
        The value is the value in V$TIMER when the line was written.
        If there are TIMESTAMPS in the file you can use the difference
        between 'tim' values to determine an absolute time.

Ok, something has changed.  However, I find it hard to believe that all
statements run within 1 microsecond!  So I run it on the 8.1.7 instance (both
the 8.1.7 and 9.0.1 are on the same machine).  Here is the tracefile for 8.1.7:

PARSING IN CURSOR #1 len=71 dep=0 uid=43 oct=42 lid=43 tim=55117728
hv=2762293374 ad='80ab8ac8'
alter session set events = '10046 trace name context forever, level 1'
END OF STMT
EXEC #1:c=0,e=1,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=55117728
=====================
PARSING IN CURSOR #1 len=23 dep=0 uid=43 oct=47 lid=43 tim=55118061
hv=3047870254 ad='80e289d8'
BEGIN tracetest; END;
END OF STMT
PARSE #1:c=0,e=1,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=55118061
=====================
PARSING IN CURSOR #2 len=61 dep=1 uid=43 oct=3 lid=43 tim=55118065 hv=1151295142
ad='817ff0ec'
SELECT ROWNUM R,ROWNUM R2   FROM ALL_USERS  WHERE ROWNUM < 10
END OF STMT
PARSE #2:c=3,e=4,p=4,cr=26,cu=1,mis=1,r=0,dep=1,og=4,tim=55118065
EXEC #2:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=55118065
FETCH #2:c=0,e=0,p=0,cr=14,cu=4,mis=0,r=1,dep=1,og=4,tim=55118065
=====================
PARSING IN CURSOR #3 len=29 dep=1 uid=43 oct=3 lid=43 tim=55118065 hv=974541619
ad='8234aef0'
SELECT :b1 * :b1    FROM DUAL
END OF STMT
PARSE #3:c=0,e=0,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=0,tim=55118065
EXEC #3:c=1,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=55118065
FETCH #3:c=0,e=0,p=0,cr=1,cu=4,mis=0,r=1,dep=1,og=4,tim=55118065
FETCH #2:c=0,e=0,p=0,cr=19,cu=0,mis=0,r=1,dep=1,og=4,tim=55118065
EXEC #3:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=55118065
FETCH #3:c=0,e=0,p=0,cr=1,cu=4,mis=0,r=1,dep=1,og=4,tim=55118065
FETCH #2:c=0,e=0,p=0,cr=19,cu=0,mis=0,r=1,dep=1,og=4,tim=55118065
EXEC #3:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=55118066
FETCH #3:c=0,e=0,p=0,cr=1,cu=4,mis=0,r=1,dep=1,og=4,tim=55118066
FETCH #2:c=0,e=0,p=0,cr=19,cu=0,mis=0,r=1,dep=1,og=4,tim=55118066
EXEC #3:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=55118066
FETCH #3:c=0,e=0,p=0,cr=1,cu=4,mis=0,r=1,dep=1,og=4,tim=55118066
FETCH #2:c=0,e=0,p=0,cr=19,cu=0,mis=0,r=1,dep=1,og=4,tim=55118066
EXEC #3:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=55118066
FETCH #3:c=0,e=0,p=0,cr=1,cu=4,mis=0,r=1,dep=1,og=4,tim=55118066
FETCH #2:c=0,e=0,p=0,cr=19,cu=0,mis=0,r=1,dep=1,og=4,tim=55118066
EXEC #3:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=55118066
FETCH #3:c=0,e=0,p=0,cr=1,cu=4,mis=0,r=1,dep=1,og=4,tim=55118066
FETCH #2:c=0,e=0,p=0,cr=19,cu=0,mis=0,r=1,dep=1,og=4,tim=55118066
EXEC #3:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=55118066
FETCH #3:c=0,e=0,p=0,cr=1,cu=4,mis=0,r=1,dep=1,og=4,tim=55118066
FETCH #2:c=0,e=0,p=0,cr=19,cu=0,mis=0,r=1,dep=1,og=4,tim=55118066
EXEC #3:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=55118066
FETCH #3:c=0,e=0,p=0,cr=1,cu=4,mis=0,r=1,dep=1,og=4,tim=55118066
FETCH #2:c=0,e=0,p=0,cr=19,cu=0,mis=0,r=1,dep=1,og=4,tim=55118066
EXEC #3:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=55118066
FETCH #3:c=0,e=0,p=0,cr=1,cu=4,mis=0,r=1,dep=1,og=4,tim=55118066
FETCH #2:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=55118066
EXEC #1:c=4,e=5,p=4,cr=201,cu=41,mis=0,r=1,dep=0,og=4,tim=55118066

Looking at this trace file, the same query on the same machine takes longer than
1 microsecond.  One important item I would like to point out is the value in the
tim column on the 9.0.1 trace file never changes after I issue the alter session
set events statement (it always shows tim=128114344).  I know several seconds
elapsed between the time I typed the alter session and the exec statement in
SQLPlus.  Possible bug or side effect from the 9i changes?

So what have I learned???

Need to go back to my program and account for EXECUTE IMMEDIATE and "FOR cursor
subquery" behavior.

Need to open an iTar about the tim column value in 9i.

As usual, it is a pleasure to read and share ideas in your forum.

Mark M.

          Followup:

Hey, I purposely did an example to show it couldn't always be done -- you are
right tho -- in a WELL WRITTEN APP -- it won't happen this way (notice that I
made the queries UNIQUE -- thats bad, i used dynamic sql where I didn't need to
-- thats bad, but I did it to show that in general, you cannot do this
dependency stuff)

It must be a windoze specific thingy with the timing however.  On solaris I got:

PARSING IN CURSOR #3 len=33 dep=1 uid=187 oct=3 lid=187 tim=1031354825104976 hv=
2102796684 ad='82b03408'
SELECT :b1*:b1
         from dual
END OF STMT
PARSE #3:c=0,e=847,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=0,tim=1031354825104959
EXEC #3:c=0,e=389,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=1031354825105628
FETCH #3:c=0,e=114,p=0,cr=3,cu=0,mis=0,r=1,dep=1,og=4,tim=1031354825105826
FETCH #2:c=0,e=199,p=0,cr=7,cu=0,mis=0,r=1,dep=1,og=4,tim=1031354825106223
EXEC #3:c=0,e=69,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=1031354825106463
...

that was with:

ops$tkyte@ORA9I.WORLD> select * from v$version
  2  /

BANNER
----------------------------------------------------------------
Oracle9i Enterprise Edition Release 9.0.1.2.0 - Production
PL/SQL Release 9.0.1.2.0 - Production
CORE    9.0.1.2.0       Production
TNS for Solaris: Version 9.0.1.2.0 - Production
NLSRTL Version 9.0.1.2.0 - Production

and similar result on LINUX (which I have overlaid ontop of ALL of my windows
machines finally!!!! Laptop and both desktops...  Got my dual monitors at
1600x1200 each going and loving it -- all unix all the time.  You know, it is
slightly faster too ;) with 9iR2.

      Rating: 5
Very
      true  September 06, 2002
Reviewer:  Mark
      Manning  from Atlanta, GA
Very true.  I was not looking at it from that perspective and was worried that I
was way off base or really lucky.  It is good to know where the "one offs" are.
Thank you for pointing this out.  While your example has undesirable side
effects that a majority of experienced professionals would not implement in a
production environment, it is only a matter of time that I run across one that
is like that.

As for the Windoze, not my call.  At home, I run SuSE (kernel 2.4.18) on Dual
PIII 1.2GHz, 2GB RAM, 6 100GB 7200RPM IDE drives, and Matrox G450 Dual Head each
connected to a 17" Sony monitor.  Been using Linux for years and loving it.
Glad Oracle jumped on the Linux bandwagon.  Faster, more stable, and more
friendly (Unix IS user friendly, it is just picky as to who it is friends with).

One final food for thought.  Two things that came out of Berkley:

1) Unix
2) Acid

Concidence???

Have a good weekend.

      Rating:
      4
ouch!  September 07, 2002
Reviewer:
      Jeff Price  from NJ
Ok, now my head is starting to hurt.
Maybe it can't be oversimplified like this, but I'd like to know, given the
following:

1) PARSING IN CURSOR #1 len=52 dep=0.....
2) PARSING IN CURSOR #3 len=55 dep=1.....
3) PARSING IN CURSOR #3 len=72 dep=0.....
4) PARSING IN CURSOR #5 len=68 dep=1.....

Is it possible for statement 1 to be the parent of statement of number 4??

(...why do I get the feeling the answer to this will be a solid maybe)

          Followup:

Is it posible -- sure.

Yes, but it would take a very specific set of actions.  It could but I would
probably have to write a bit of Pro*C code to do it.  It would take a bit of
setup (eg: cursor #5 would have been opened and cached previously and we are
reusing it) and such but it could happen.

September 08, 2002 RAGUPATHI -- Thanks for the question regarding
      "Duplicate Rows in Forms", version 6.0

      You Asked
Hi Tom,
How are you doing?

Created multiple record based on emp table in forms6.0.
I am entering employee information. first row i am entering empno 1000
,second row i am entering 1001 and third row i am entering 1000.
That time i need to raise a message duplicate row found.

Pls assume this emp table don't have unique or primary key constarint.

Thanks in advance.
Raghu.K

      and we said...
You'll write alot of code then.

I have to assume the table does have a primary key constraint and the database
will tell us of our error as soon as we POST or COMMIT_FORM the data.

Otherwise, you can figure out how to write a ton of unreliable and hard to debug
code -- I would never suggest that course of action.
Review &
      Followup

      Rating: 2
Duplicate Rows
      in Forms  September 08, 2002
Reviewer:
      A.P.Manikandan  from Singapore
Hi,
How about checking duplicates using record groups.
when we designed apps.. 3 years back using Forms 4.5/5.0 we validate duplicates
using Record group. Try it la.

          Followup:

Yah, that is the procedural code I'm talking about.  Tell me, how did you do
that (it involved a loop or two right?)

Not that you can AVOID the primary key on that table anyway right?  I mean, your
form can check your data but not the data of the person next to you.  So, the
table needs to do this check anyway and me being a lazy sort of guy would have
the database do my work for me.

September 08, 2002 -- Thanks for the question regarding "remove
      datafile from a locally managed tablespace", version 8.1.7.4

      You Asked
hi tom

can we remove a datafile from a locally managed tablespace composed of more than
one datafile? In dictionary we cannot because the extents information is in data
dictionary.

For example if I have an empty tablespace with two datafiles, I offline drop one
datafile then all new objects I create should be stored fine in the remaining
datafile since in locally managed tablespace Oracle has to read the file header
to determine free extents in the datafile then if it cannot read the file I
dropped it should be ok no? It will never be able to read the file header hence
it wont read the file I dropped. Unless Oracle reads at tablespace level in LMT?

Am I correct?

      and we said...
A datafile once added is a datafile forever until you drop the TABLESPACE.

You can resize it, you can rename (move) it, you can never ever get rid of it
(until you get rid of the tablespace).

This does not change behaviour between dictionary and locally managed
tablespaces -- it has always been true.
September 08, 2002 Hoang -- Thanks for the question regarding
      "Create database link betwent 2 Dbs", version 8.17

      You Asked
Hi Tom, i have a problem about distributed database. You can tell me about the
way create database link betwent 2 Dbs, create snapshot and replicate with 2 Dbs
(step by step). If you give for me an example about distributed database for
better.
Thank you very much!
Hoang

      and we said...
Even better -- I can point you to the definitive source of all such information:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/index.htm
You want to read the 3 books at the bottom of that page and - if you haven't
already -- read that concepts guide, it is most excellent!

September 08, 2002 khalid -- Thanks for the question regarding
      "Temporary tablespace", version 8.1.7

      You Asked
Hi Tom
I want to create temporary tablespace (for sorting) and assign it to all users
as the default tablespace.
What should I use?
1.Create tablespace temp datafile ‘c:\temp.ora’ size 1m temporary.
Or
2.Create temporary tablespace temp tempfile ‘c:\temp.ora’
Extent management local uniform size 128k;

I prefer to create LMT.

i read the documentation and it says if i use (1) i can not specify 'extent
management local'

Thanks

      and we said...
#2, always use TRUE temporary tablespaces.  the other kind are purely for
backwards compatibility.  TRUE temporary tablespaces are faster at space
management, they use tempfiles, not datafiles (you cannot back them up, well you
can but it is POINTLESS)....
Review &
      Followup

      Rating: 5
Tom And only
      Tom  September 09, 2002
Reviewer:  khalid
       from Yemen
Thank you very much

September 08, 2002 Steven -- Thanks for the question regarding "How
      to tell if a table has compression enabled?", version 9.2.0

      You Asked
With the new table (data segment) compression feature of 9iR2, I'm
not seeing anything in DBA/ALL/USER_TABLES that indicates if
compression is enabled or not.  There is a COMPRESSION column present
in DBA/ALL/USER_TAB_PARTITIONS that indicates if a partition has
compression enabled.  When I do an export, the compression
attribute shows up when I do SHOW=Y, but it would be better if I
could pick that attribute out of a dictionary table.  Any ideas?

      and we said...
Appears to be an oversight -- we can workaround like this.

I used my getaview script as SYS to suck out the definition of
user_tab_partitions and user_tables.

------------ getaview -------------------------
set heading off
set long 99999999
set feedback off
set linesize 1000
set trimspool on
set verify off
set termout off
set embedded on

column column_name format a1000
column text format a1000

spool &1..sql
prompt create or replace view &1 (
select decode(column_id,1,'',',') || column_name  column_name
  from user_tab_columns
 where table_name = upper('&1')
 order by column_id
/
prompt ) as
select text
  from user_views
 where view_name = upper('&1')
/
prompt /
spool off

set termout on
set heading on
set feedback on
set verify on
-------------------------------------------------

then, looking at user_tab_partitions -- I found that the compressed column was
really:

       decode(bitand(s.spare1, 2048), 2048, 'ENABLED', 'DISABLED'),

and that s was a correlation name for sys.seg$.  I modified the user_tables
create view statment in the following way:

1) changed the view being created to my_user_tables
2) added a column name compressed to the end of the list of columns
3) added the above decode -- unchanged since this query also aliased sys.seg$ as
"S"
4) created the view (as sys or sysdba -- I was actually sysdba which is sys)
5) created a public synonym
6) granted select on it

you can use this view until they correct this oversite.

do NOT just add this to user_tables, that could have serious unintended side
effects elsewhere!

Review &
      Followup

      Rating:
      5
  September 09, 2002
Reviewer:
      A reader

      Rating: 5
Irreplaceable
      service  September 15, 2003
Reviewer:  Miroslav
      Dzakovic

September 08, 2002 Marcin -- Thanks for the question regarding "User
      defined aggregates and intervals", version 9.0.1

      You Asked
Hi!

I found your post on one of the newsgroup in which you described how to write
user defined SUM aggregate for new interval type. You also provided code for
"day_to_second_sum_type".

In my work I need SUM and AVG aggregates for interval type with precision 6 for
days and 6 for seconds - so "interval day(6) to second(6)" is the type I'm
interested in.

I changed your code so that  "total interval day to second" is
"total interval day(6) to second(6)" now. However I got problems with
ODCIAggregateTerminate - returnValue which is the output parameter has to small
precision hence I receive error while executing aggregate.
While iterating everything works fine until invoking terminate function. Is
there any way to workaround this precision limitations??
Could you also give me a hint how to build AVG aggregate?

And one more thing - Let's say I have a table which contains two colums - Name
and birth date;
How to group all the rows on daily or yearly or monthly basis.
How to assign specific group number to each row???

Thank You very much!

Marcin Belczewski
marcin.belczewski@globalintech.pl

      and we said...
Ok, it won't work without using base SQL datatypes -- so thats what we'll do.
We'll just have to use a character string to send and get results is all.  Here
we go -- a SUM and AVG routine for day to second intervals for Oracle9i:

ops$tkyte@ORA920.US.ORACLE.COM> create or replace type day_to_second_sum_type as
object
  2  (
  3     total interval day(9) to second(9),
  4
  5     static function
  6          ODCIAggregateInitialize(sctx IN OUT day_to_second_sum_type )
  7          return number,
  8
  9     member function
 10          ODCIAggregateIterate(self IN OUT day_to_second_sum_type ,
 11                               value IN varchar2 )
 12          return number,
 13
 14     member function
 15          ODCIAggregateTerminate(self IN day_to_second_sum_type,
 16                                 returnValue OUT  varchar2,
 17                                 flags IN number)
 18          return number,
 19
 20     member function
 21          ODCIAggregateMerge(self IN OUT day_to_second_sum_type,
 22                             ctx2 IN day_to_second_sum_type)
 23          return number
 24  );
 25  /

Type created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create or replace type body
day_to_second_sum_type
  2  is
  3
  4  static function ODCIAggregateInitialize(sctx IN OUT day_to_second_sum_type)
  5  return number
  6  is
  7  begin
  8      sctx := day_to_second_sum_type( numtodsinterval( 0, 'SECOND' ) );
  9      return ODCIConst.Success;
 10  end;
 11
 12  member function ODCIAggregateIterate(self IN OUT day_to_second_sum_type,
 13                                       value IN varchar2 )
 14  return number
 15  is
 16      l_value interval day(9) to second(9);
 17  begin
 18          l_value := value;
 19          dbms_output.put_line( l_value );
 20      self.total := self.total + l_value;
 21      return ODCIConst.Success;
 22  end;
 23
 24  member function ODCIAggregateTerminate(self IN day_to_second_sum_type,
 25                                         returnValue OUT varchar2,
 26                                         flags IN number)
 27  return number
 28  is
 29  begin
 30      returnValue := self.total;
 31      return ODCIConst.Success;
 32  end;
 33
 34  member function ODCIAggregateMerge(self IN OUT day_to_second_sum_type,
 35                                     ctx2 IN day_to_second_sum_type)
 36  return number
 37  is
 38  begin
 39      self.total := self.total + ctx2.total;
 40      return ODCIConst.Success;
 41  end;
 42  end;
 43  /

Type body created.

ops$tkyte@ORA920.US.ORACLE.COM> show err
No errors.
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> CREATE or replace
  2  FUNCTION ds_sum(input varchar2 )
  3  RETURN varchar2
  4  PARALLEL_ENABLE AGGREGATE USING day_to_second_sum_type;
  5  /

Function created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> set echo on
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create or replace type day_to_second_avg_type as
object
  2  (
  3     total interval day(9) to second(9),
  4     obs   number,
  5
  6     static function
  7          ODCIAggregateInitialize(sctx IN OUT day_to_second_avg_type )
  8          return number,
  9
 10     member function
 11          ODCIAggregateIterate(self IN OUT day_to_second_avg_type ,
 12                               value IN varchar2 )
 13          return number,
 14
 15     member function
 16          ODCIAggregateTerminate(self IN day_to_second_avg_type,
 17                                 returnValue OUT  varchar2,
 18                                 flags IN number)
 19          return number,
 20
 21     member function
 22          ODCIAggregateMerge(self IN OUT day_to_second_avg_type,
 23                             ctx2 IN day_to_second_avg_type)
 24          return number
 25  );
 26  /

Type created.

ops$tkyte@ORA920.US.ORACLE.COM> show errors
No errors.
ops$tkyte@ORA920.US.ORACLE.COM> pause

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create or replace type body
day_to_second_avg_type
  2  is
  3
  4  static function ODCIAggregateInitialize(sctx IN OUT day_to_second_avg_type)
  5  return number
  6  is
  7  begin
  8      sctx := day_to_second_avg_type( numtodsinterval( 0, 'SECOND' ), 0 );
  9      return ODCIConst.Success;
 10  end;
 11
 12  member function ODCIAggregateIterate(self IN OUT day_to_second_avg_type,
 13                                       value IN varchar2 )
 14  return number
 15  is
 16      l_value interval day(9) to second(9);
 17  begin
 18          l_value := value;
 19      self.total := self.total + l_value;
 20          self.obs   := self.obs + 1;
 21      return ODCIConst.Success;
 22  end;
 23
 24  member function ODCIAggregateTerminate(self IN day_to_second_avg_type,
 25                                         returnValue OUT varchar2,
 26                                         flags IN number)
 27  return number
 28  is
 29  begin
 30          if ( self.obs > 0 )
 31          then
 32          returnValue := self.total/self.obs;
 33          else
 34                  returnValue := NULL;
 35          end if;
 36      return ODCIConst.Success;
 37  end;
 38
 39  member function ODCIAggregateMerge(self IN OUT day_to_second_avg_type,
 40                                     ctx2 IN day_to_second_avg_type)
 41  return number
 42  is
 43  begin
 44      self.total := self.total + ctx2.total;
 45          self.obs   := self.obs + ctx2.obs;
 46      return ODCIConst.Success;
 47  end;
 48  end;
 49  /

Type body created.

ops$tkyte@ORA920.US.ORACLE.COM> show errors
No errors.
ops$tkyte@ORA920.US.ORACLE.COM> pause

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> CREATE or replace
  2  FUNCTION ds_avg(input varchar2 )
  3  RETURN varchar2
  4  PARALLEL_ENABLE AGGREGATE USING day_to_second_avg_type;
  5  /

Function created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create table t ( x interval day(9) to second(9)
);

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into t
  2  select numtodsinterval( rownum*1000, 'hour' )
  3    from all_objects
  4   where rownum < 25;

24 rows created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select to_dsinterval( ds_avg(x) ) avg,
  2         to_dsinterval( ds_sum(x) ) sum
  3    from t;

AVG
---------------------------------------------------------------------------
SUM
---------------------------------------------------------------------------
+000000520 19:59:59.999999999
+000012500 00:00:00.000000000

ops$tkyte@ORA920.US.ORACLE.COM>

Review &
      Followup

      Rating: 5
Works great -
      thanks!  September 09, 2002
Reviewer:  Marcin
      Belczewski  from Poland
You solved my problem - thanks!!

September 09, 2002 Bhavesh -- Thanks for the question regarding
      "Calling Pl sql stored procedure from Servlet", version 1.0.2.0.0

      You Asked
Hi Tom,
    Is it possible to call a PSP (Pl sql stored procedure) from servlet. If yes
can you please provide some details on how to do this.
Thanks in advance.
Regards

Bhavesh

      and we said...
A servlet can use jdbc to connect to a database and run any stored procedure
that connection has the privileges to execute.

So yes, a servlet can easily run a stored procedure using jdbc.  A servlet could
also use the URL classes contained in java to run a URL (hence a PSP) as well
without jdbc.

If you use jdbc, after you run the procedure, you'll call owa.get_page to
retrieve the data the procedure generated.
Review &
      Followup

      Rating: 3
Could you pl
      give me one sample Servlet.  September 09, 2002

Reviewer:  Chandra S.Reddy  from India
Hi Tom,
It's so nice to hear that, PSP can be integrated with Java application.
Below is the URL for my PSP application.

http://chandra:7778/pls/softprojex
Could you please give me sample code for a Servlet, that will call above URL ??

Thanks in Advance.

          Followup:

You'll have to read up on the java URL classes to see how that is done.  You'll
just be running a URL

Or, even better, you can just call PLSQL from Java using JDBC in the normal way.
 If you have my book, you can look at the dbms_output section in the appendix.
There I show you how to get dbms_output output back in java -- it'll be the same
for PLSQL "psp" like procedures except you'll call owa.get_page instead of
dbms_output.

      Rating:
      3
Thanks  September 09, 2002
Reviewer:
      A reader
Thank you.
I'll keep in mind your tips.

September 09, 2002 b -- Thanks for the question regarding "General
      Protection error", version 1.3.2

      You Asked
Sir ,

I encountered the following error while running some reports:

  R25SRV caused a general protection fault in module
  TK23WIN.DLL at  0030:0009

  The O/S is Microsoft Windows 2000 server.

regards
Suresh

      and we said...
Please contact support, although I'm almost 100% positive they will say "well,
run it on an operating system that was developed BEFORE the tool was".

Why do people upgrade operating systems (especially windows, seemed to run
rampant with windows users) but refuse to upgrade the other pieces of software
that go with it.

1.3.2 developer was released YEARS before Windows/2000 the operating system was.
 I would not expect it to function 100%, if at all.  Microsofts many and varied
operating systems:

win 311
win 95
win 98
win me
win 2000
win nt
win xp

are all separate, distinct and heterogenous.  We have software that runs on
each, however -- the software only runs on the operating systems it was designed
for and as it is impossible to design for stuff microsoft hadn't yet written....
it won't work.
Review & Followup

      Rating: 2
General
      protection error  September 09, 2002
Reviewer:
      suresh  from INDIA
Thank for the infomation about Micrsoft Products

September 09, 2002 mathew -- Thanks for the question regarding "some
      details on release2", version 9i

      You Asked
hi,
  1)9i Release 2 claims to reduce table size by compressing it. Will this not
increase the cost of the query as the CPU will need more resources to decompress
it? Is this installed by default or is it a seperate install option?

2)Is it possible to do the following

Table A has primary Key A_pk
Table B has primary key B_pk
both the PK's have the same set of values but are master tables
Table C has one of its column that acts as a foreign key to both the table
Is it possible for a single table to have a column refer to 2 different tables.
Can this be done without creating additional tables?

regards,
mathew

      and we said...
1) see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4635180000986
We've run our TPC/H's with this feature turned on and turned out record results!

Sure, CPU use to interpret the block data goes up a tad but IO waits for disk go
down quite a bit and the RAM required for the buffer cache can go waaaayyy down.

It is a feature of Oracle9i EE -- installed by default with EE.

2) If they both have the same set of values, of what use is having a foreign key
to both??  doesn't seem sensible.  Can you do it?  Yes, does it make sense?  Not
to me:

ops$tkyte@ORA920.US.ORACLE.COM> create table a ( x int primary key );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> create table b ( x int primary key );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create table c ( x int );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> alter table c add constraint c_fk1 foreign
key(x) references a;

Table altered.

ops$tkyte@ORA920.US.ORACLE.COM> alter table c add constraint c_fk2 foreign
key(x) references b;

Table altered.

ops$tkyte@ORA920.US.ORACLE.COM>

Review &
      Followup

      Rating:
      4
  September 10, 2002
Reviewer:
      mathew  from India

September 09, 2002 Paul -- Thanks for the question regarding "SQL
      statement is different in v$sql", version 9.2.0

      You Asked
Tom,

I was under impression that Oracle doesn't change SQL statement while parsing
(with exception of bind variables' names).
However, I see the following difference:

This is what I execute in my instead-of-trigger:

  insert into v_profname
    ("routingListName", "orgName","defaultRoutingList")
  values
    (:new."routingListName",:new."orgName",l_default);

However, in v$sql and, of course, in my tkprof output, I see this:

INSERT into v_profname     ("routingListName", "orgName","defaultRoutingList")
values     (:b1                  ,:b2          ,:
b3)

Why would Oracle convert word "insert" to upper case? Why are there extra spaces
in the "values" clause?

Are there any rules that Oracle follows while preparing a statement for parsing?

pdruker@DEVL01>  show parameter cursor_sharing

NAME                                 TYPE        VALUE
------------------------------------ -----------
---------------------cursor_sharing                       string      EXACT

Thanks

      and we said...
PLSQL must rewrite all sql that is static sql.

Why?

consider:

declare
   x  number := 5;
begin
   update t set a = x;
end;

Well -- X is a bind variable (or is it???) what if the table were "t ( a int, x
int )" -- then X is not a bind variable.  If the table is " t( a int )" then X
is a bind variable.

PLSQL must parse the query, resolve all of the objects therein, figure out what
is a bind variable and what is not and rewrite it.

When it rewrites it, it is free to rewrite however it likes (it'll always
rewrite that statement the same way -- show SHARED SQL is very much promoted
using PLSQL).  As long as it doesn't change the meaning, the case, amount of
white space --etc, is quite simply not relevant.

(:new."routingListName",:new."orgName",l_default);
(:b1                  ,:b2          ,:b3

It is interesting to see that the whitespace just about is the same as the
original length of the names in there -- maybe that has something to do with it
(but it isn't really relevant in the grand scheme of things)
September 09, 2002 Günther -- Thanks for the question regarding
      "binding array parameter in dynamic function call", version 8.1.6 and
8.1.7

      You Asked
Hi TOM,

I spent a lot of time to search your database and after that I decided to ask
you.
Didn't find any helpful hints.
And here goes my question:

I want to bind an array of string to a procedure paramater in a dynamic call.
Is this possible. I found no restrictions in any manuals.

In the following example I tested it in two ways. 1st as a static call.
And this works as it has to work.
But I need it in a dynamic call because of implementing an configurable engine
that call functions which are define in tables....however.

Do you have any Idea? Am I totally wrong or is there any restriction of Oracle?

create or replace procedure bulk_test( a in dbms_sql.varchar2_table) as
begin
    dbms_output.put_line('inside Bulk_Test');
end;
/
show errors

-- static call (of cource this works)

declare
v2t    dbms_sql.varchar2_table;
begin
    for i in 1..10
    loop
       v2t(i) := 'E_'||i;
    end loop;

    bulk_test(v2t);

    dbms_output.put_line('Bulk_Test finished');
end;
/

-- and the result is:
Bulk_Test
Element 1: E_1
Element 2: E_2
Element 3: E_3
Element 4: E_4
Element 5: E_5
Element 6: E_6
Element 7: E_7
Element 8: E_8
Element 9: E_9
Element 10: E_10
Bulk_Test finished

-- and now dynamically (as i would need, but it doesn't work)

declare
c    number;
v2t    dbms_sql.varchar2_table;
text    varchar2(2000);
ret    number;
begin
    text := 'begin bulk_test(:V2T); end;';

    for i in 1..10
    loop
       v2t(i) := 'E_'||i;
    end loop;

    c :=  dbms_sql.open_cursor;
    dbms_sql.parse( c, text , dbms_sql.NATIVE);
    dbms_sql.bind_array   (c, ':V2T', v2t, 1, 10 );
    ret := dbms_sql.execute(c);
    dbms_sql.close_cursor(c);

    dbms_output.put_line('Bulk_Test finished');

exception
    when others then
        dbms_sql.close_cursor(c);
        dbms_output.put_line(SQLERRM);
end;
/

-- and this is the result:

ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of arguments in call to
'BULK_TEST'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored

best regards
and
many thanky for helping
  Günther (Flatleader) Ebenführer
  guenther.ebenfuehrer@infonova.com
  Graz, AUSTRIA

      and we said...
You may achieve that functionality this way:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace type myArrayType as table
of varchar2(4000)
  2  /

Type created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure test_proc( p_data
in myArrayType )
  2  as
  3  begin
  4          for i in 1 .. p_data.count
  5          loop
  6                  dbms_output.put_line( 'p_data(' || i || ') = ' || p_data(i)
);
  7          end loop;
  8  end;
  9  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          l_data myArrayType := myArrayType( 1, 2, 3, 4 );
  3  begin
  4          execute immediate 'begin test_proc( :x ); end;' USING l_data;
  5  end;
  6  /
p_data(1) = 1
p_data(2) = 2
p_data(3) = 3
p_data(4) = 4

PL/SQL procedure successfully completed.

before you ask -- yes, you MUST use a SQL datatype (myArrayType in this
example).  No, you cannot put that type in a PACKAGE, it must be a top level SQL
type

Another approach is to use package globals as your input/output "binds", for
example:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package globals
  2  as
  3          l_stringArray dbms_sql.varchar2_table;
  4  end;
  5  /

Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure test_proc( p_data
in dbms_sql.varchar2_table )
  2  as
  3  begin
  4          for i in 1 .. p_data.count
  5          loop
  6                  dbms_output.put_line( 'p_data(' || i || ') = ' || p_data(i)
);
  7          end loop;
  8  end;
  9  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          l_data dbms_sql.varchar2_table;
  3  begin
  4
  5          for i in 1 .. 4
  6          loop
  7                  l_data(i) := 'E_' || i;
  8          end loop;
  9
 10
 11          globals.l_stringArray := l_data;
 12          execute immediate 'begin test_proc( globals.l_stringArray ); end;';
 13          l_data := globals.l_stringArray;
 14  end;
 15  /
p_data(1) = E_1
p_data(2) = E_2
p_data(3) = E_3
p_data(4) = E_4

PL/SQL procedure successfully completed.

Review &
      Followup

      Rating: 4
automatic type
      conversion - cool !  September 06, 2002
Reviewer:
      Robert  from PA
>> l_data myArrayType := myArrayType( 1, 2, 3, 4 );

Ooh, didn't error out on you...so automatic type conversion for collections
too...cool.

          Followup:

Yes, I like collections more then PLSQL tables in 99% of the cases (just wish I
didn't have to call .extend sometimes!)

      Rating: 5
Simple if you
      know how to do it  September 09, 2002
Reviewer:
      Gunther  from Graz, Austria, Europe
And like most of all answers of you is the solution very simple if you know the
way how to do it :)

But on restiction is in that solution: the function bind_array doesn't work at
all in this case.

so the call
    dbms_sql.parse( c, begin bulk_test(:V2T); end;',
                    dbms_sql.NATIVE)
    dbms_sql.bind_array   (c, ':V2T', v2t, 1, 10 );
has to be replaced by
    dbms_sql.parse( c, begin bulk_test(v2t); end;',
                    dbms_sql.NATIVE)

and wah la.... it works as i need id.

And in combination with additional bind like
    dbms_sql.parse( c, begin bulk_test(v2t, :B1, :B2);
                    end;', dbms_sql.NATIVE)
    dbms_sql.bind_variable   (c, ':B1', B1 );
    dbms_sql.bind_variable   (c, ':B2', B2 );

i can build my generic engine.

many thanks to you

best regards from th other side of the atlantic
Günther

      Rating: 5
Simple if you
      know how to do it - 2nd  September 09, 2002

Reviewer:  Gunther  from Graz, Austria, Europe, World
      :)
Hi Tom,

i am to fast for this world :) so i forgot one question:

is this a 'bug' (i know there are no bugs only features in Oracle) or why do i
have to use a global parameter.
Is the bind_array function not designed for my problem, is it in next version or
.....?

an other thanks in advance
Günther

          Followup:

There is no binding for plsql table types in DBMS_SQL, only for simple scalar
types and for array processing (useful with inserts/updates/deletes).

It is not a bug, rather, it would be an enhancement request on your part.

Bind_array is just not designed to do what you want it to do, it is for array
processing of insert/update/delete statements.

      Rating: 4
Exactly what I
      have been looking for  May 04, 2003
Reviewer:
      James  from Woburn, MA USA
Exactly what I have been looking for.

We have a situation where such a solution is very desirable in constructing
dynamic SQL calls to various functions with varying number of parameters.

September 09, 2002 sreekanth -- Thanks for the question regarding
      "Uploding text file with utl_file", version 8.1.7.3

      You Asked
Tom,

I want to upload text files from webpage using utl_file. So i am doing the
following

procedure copy_page is
    begin
        htp.p('<html>');
        htp.header(3,'Upload a file');
htp.p('<body bgcolor="#CCCCCC">');
htp.formOpen(curl => 'gsi_util.copy_file', cmethod => 'POST',
cenctype => 'multipart/form-data');
htp.p('<table><tr><td>');
htp.p('File: </td><td><INPUT TYPE=file NAME="pfile">');
htp.p('</td></tr></table>');
htp.formSubmit;
htp.formClose;
htp.p('</body>');
htp.p('</html>');
end;

procedure copy_file(pFile long) is
         v_filehandle              utl_file.file_type;
            v_filedir                 VARCHAR2(100) :=
'f:\ORACLE\PRODDB\8.1.7\PLSQL\TEMP';
        vfilename          varchar2(100);
        vLine                long;
    begin
        htp.p('<html>');
        htp.p('<body bgcolor="#CCCCCC">');
        begin
                SELECT
substr(value,1,decode(instr(value,',')-1,-1,length(value),instr(value,',')-1))
                into v_filedir
                FROM SYS.GV_$PARAMETER
                WHERE UPPER(NAME) = 'UTL_FILE_DIR';
            exception
            when others then
                null;
        end;
        vFilename := 'ftptest.txt';
        v_filehandle := UTL_FILE.FOPEN(v_filedir, vFilename, 'w');
        UTL_FILE.PUT_LINE(v_filehandle,pfile);
        UTL_FILE.FCLOSE(v_filehandle);
        htp.p('upload Succesfull');
        htp.p('</body>');
        htp.p('</html>');
    exception
    when others then
        htp.p('upload failed');
        htp.p('</body>');
        htp.p('</html>');
    end;

but when it passes the pfile it is just passing the file name instead of file
content.  so When i create the file it is writing just the filename in it.

Please tell how to get the file content.

thanks
sreekanth.g

      and we said...
Well, the uploaded file is stored in the database, in either a LONG RAW or a
BLOB (you configure that).

If it is in LONG RAW, you won't be writing it out to the file system.

If it is in a blob, you can read out pieces of the blob using DBMS_LOB.READ, and
using UTL_RAW.CAST_TO_VARCHAR2, convert the RAW binary data into varchar2 data
for writing.

In any case, you need to SELECT out the blob data, iterate over it reading a
chunk at a time using dbms_lob and then use utl_file.put to write it out.

It would be similar in concept to this:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:232814159006
but instead of htp.p, you would use utl_file.put

Review &
      Followup

      Rating:
      4
  September 09, 2002
Reviewer:
      gadhiraju, sreekanth  from tampa, fl usa

September 09, 2002 Sankar -- Thanks for the question regarding
      "Questions on Columns in the ORACLE & FIXED_DATE", version 8.1.7

      You Asked
Tom,

(1) Is their any restriction on the number of columns in a table/View? Will
there be any performance problem/selectivity problem/index problem arise because
of increasing the number of columns of already existing table?

(2) If we want to add a new column to a existing table, the new column will be
shown at the end in the SQL *plus session. I know that the table is only a
logical view. But why ORACLE is not allowing to add a new column in between the
existing ones via alter table command in a table? Or what makes ORACLE to limit
this facility from offering it, since physically there will not be any change in
the ORALCE database?

(3) We are using Fixed_Date(via sql plus) facility in ORACLE for one of our
testing. But once the date is set, the Server need to be restarted to bring back
the database original date. Is there any way to change the date again without
restarting the server

Thanks for taking my Questions!
Warm Regards,
Sankar

      and we said...
1) there is a limit of 1,000 columns yes.

Increasing the number of columns in a table might impact performance -- if the
newly added columns are to be populated with data for the existing rows.  You
could end up migrating most or even ALL of the data in your table (migrating is
when we move the row data to a new block -- chain the data across two blocks,
making access slower).

2) physically there most certainly IS a change.  The column goes at the end
cause there are already rows there with data and we aren't going to go back and
rewrite every row and stick a new column in the middle.

If you want to order the columns in a certain way -- use a view, thats a really
good use for them.

3) ops$tkyte@ORA817DEV.US.ORACLE.COM> alter system set fixed_date =
'2001-01-01-01:01:01';

System altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select sysdate from dual;

SYSDATE
---------
01-JAN-01

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter system set fixed_date = NONE;

System altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select sysdate from dual;

SYSDATE
---------
07-DEC-01

will do it.
Review & Followup

      Rating:
      4
  December 11, 2001
Reviewer:
      Sankar  from India
For the first question, if we add say 2 columns, which is NULLABLE, and if
decided that for all existing rows, we will populate it NULL, then is there any
impact in the performance?? Of course, the new columns for the old rows will be
populated by GUI at request...

          Followup:

probably not but it could cause row migration as the new columns are populated
which can decrease performance.

You will not notice any difference at first.  You may over time if rows migrate.

      Rating:
      4
  December 11, 2001
Reviewer:
      Ashutosh  from Australia
Hi Tom,
      Where does Oracle pick SYSDATE from when its first installed.For example,
if we have more than one text box on same machine,can we have seperate sysdates
for each one of them without having to execute any alter system commands.
That is whenever those instance are started they should start with specified
dates.
Thanks for the guidance.

          Followup:

Oracle doesn't "pick" sysdate -- it comes from the operating system.  whatever
the OS clock is - thats our clock.

      Rating: 4
Incrementing
      time  September 09, 2002
Reviewer:  David
       from Nashville
Setting the fixed_date is great for our testing environment, but I noticed that
the time does not increment once you set it.  Is it possible to set the time to
a point in time and have it continue from that point?  In other words, set the
clock back and have it start ticking from that point forward?

Thanks much

          Followup:

Reset the system (OS) clock for that.  That is where we get sysdate from.

September 09, 2002 Dev -- Thanks for the question regarding
      "Temporary Tablespace", version 8.1.7

      You Asked
This is a review related to

I used a table with million records and did the sort by..
It took almost 5 min in both the cases.
anyway We'll change it in our test database and see., I'll get back to you.
Thanks

          Followup:

single user = no big deal

100 users = big deal.

test to SCALE users (the payoff is in concurrency).

      Rating: 4
How it's
      related to multi-user concurrency??  September 09, 2002

Reviewer:  A reader
I didn't get the concurrency part. Please explain me how i the temp tablespace
settings related to concurrency ? We're looking for tuning the batch programs
which runs as a part of nightly batches.. If the program is volume/resource
intensive and takes long times then we run programs in multi-threaded(8-12
threads max..). Sometimes small programs runs at the same time.. Other than that
most of the batch runs as a serial execution.

          Followup:

Lets put it this way.

use true temporary tablespaces for temp. the other way is for backwards
compatibility.

If you want to see the effect, run a large multiuser test someday.  That is
where you will see it.

September 09, 2002 rama -- Thanks for the question regarding
      "Triggers vs Packages !", version 8.1.7

      You Asked
Tom,

1.I joined this firm recently and found the following in practice.
Dont write database triggers - reason they ( ex. DBA) said, they would
considerably slow down the DB performance. So, use Packages, procedures,
functions. Fortunately for me, I have the habit of packaging all the source
code. But, is it really true that triggers slow down the database ?
2.If a senior developer needs to take the role of a backup DBA, what are the
primary things he needs to be interested on?

      and we said...
1) no, triggers will not "slow down" the database.  However, doing things in
packages in my opinion is many times better and can be more performant then
triggers.

Triggers can lead to convuluted logic, lots of unexpected side effects (they
should have been expected but weren't).

Triggers can make debugger harder, things "just happen".

However triggers can and ARE very very useful.  They should not be OUTLAWED,
they should be used judiciously.

For example, if some column has an edit that says "upper case data only".  I
would vote for a trigger.

If some object has a complex business rule that touches a dozen other tables,
does tons of sql itself, causes other triggers to fire -- gets really tricky --
I would vote for a packaged procedure to do the logic from the get go (easier to
understand).

All features in the database, each and every one, has ups and downs -- pros and
cons.  I hate it when features are outlawed because someone used it
inappropriately at some point in time.  Common sense, experience, testing,
trying alternatives -- that is what should be used.  Not a draconian "thou shalt
never use feature X".

2) the pager number of the real DBA?  How to contact the real DBA when they are
not there?

Depends on your site.  Do you want Junior inexperienced person trying to perform
media recovery (i don't, you can do more harm then good if you don't know what
you are doing).

Do you want Junior inexperienced person adding datafiles due to a lack of space?
 Maybe (maybe you really did run out of space).  Maybe not (maybe we ran out of
space cause of a bad statement that needs to be found and fixed -- not given YET
more space to consume).

It really depends on your needs.  I would ask the real DBA what they felt the
senior developer needed to know (and hopefully that real DBA knows to make
"jr_dba" role and grant it selective privs -- just enough to do what they've
been told they can do, like reset a password or something ;)

Review &
      Followup

      Rating: 5
The programmers
      view....  September 07, 2002
Reviewer:  Chuck
      Jolley  from Oklahoma City, OK USA
The DB I am working on right now has many triggers some of which are several
hundred lines long. We have never had any problem with them creating performance
issues.
They are virtually all row level triggers.

This is how we decide between triggers & packaged code.
Triggers are event handlers and procedures & functions are, well, functions. ;)
Which is appropriate for a given unit of business logic should be immediately
apparent to any experienced programmer.
Chuck

          Followup:

Well, I always have to add my 2 cents worth here... ;)  of course....

I agree with your basic premise (common sense must rule) but... as a programmer
and as a guy who is asked to read lots of other peoples code to find bugs -- I
think a trigger that is several hundred lines long violates my rule #1 with
writing code.  That is way way way too long -- too many lines of code -- to be a
single entity.  Me, I would have modularized the trigger into dozens (I like my
code to fit on the screen from top to bottom and be able to see the entire
procedure/function/trigger -- that is what I strive for) of packaged procedures.
 The, the trigger would become about one line long and the procedures would have
munched the data.

      Rating:
      5
  September 07, 2002
Reviewer:
      A reader

      Rating: 5
Maybe a
      practice based on an older version?  September 09, 2002

Reviewer:  Connor  from UK
I remember reading somewhere (newsgroup/Metalink?) that somewhere in the version
7.1/7.2 vintage, the p-code for triggers was not stored, and thus as soon as the
code got aged out, the next usage would effectively recompile the trigger.  I've
seen this used as the justification for triggers always being just a call to a
proc/pack/et al..

Is there/was there any truth to this?

Cheers
Connor

          Followup:

correct -- pre 7.3 triggers were not stored compiled.  They were just text.
they got compiled on every hard parse of them.

That is not why I promote the use of packages in the above comment -- I promote
the use of packages cause I hate subroutines with hundreds of lines of code in
them.  I believe if you have more then 100 lines of code in a single routine,
you've gone too far.  Time to break it down into smaller bit size pieces.

      Rating: 3
Performance and
      Packages  September 09, 2002
Reviewer:
      A reader
Hi Tom,
One of our experienced DBA has given following suggestion.
"going forward pl try to create stored procedures in the packages to improve the
performance and easy maintenance".

Is it True ? Can you prove with the examples.
Thank You
Kalpesh

          Followup:

Absolutely 100% true -- I love that DBA.  Except they should have said "you need
a written documented reason to use a standalone procedure"

Here is the #1 reason:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:286816015990
#2 and not less important is that packages promote good coding methodologies --
more modular, procedure hiding, less polution of the namespace (less object
names out there), grouping of related functionality, and so on.

Packages are the ONLY way to go for any real piece of code.

September 09, 2002 Pavan -- Thanks for the question regarding
      "Execute Immediate for a fetch statement ", version 8.1.7

      You Asked
Hi Tom

I have created a procedure which is a Ref Cursor.
My question is, Once the ref Cursor is created I can simply say
fetch c1 into t_empno, t_ename, t_job, t_deptno;
which would definetly work. But when I am creating a Ref Cursor based on
Unknown columns then how do I fetch the values i.e if I am building my Ref
Cursor dynamically.
create or replace procedure proc1
declare
type lex_cur is ref cursor;
c1 lex_cur;
sql_text varchar(2000);
t_empno number;
t_ename varchar2(20);
t_job varchar2(20);
t_deptno number;
begin
sql_text := 'select e.empno, e.ename, e.job, d.deptno from emp e, dept d where
e.deptno = d.deptno';
open c1 for sql_text;
loop
execute immediate 'fetch c1 into t_empno, t_ename, t_job, t_deptno';
dbms_output.put_line(t_empno||' '||t_ename);
end loop;
end;

Tom, I would be more than happy, if this problem is solved at the earliest.

Thanks
Pavan

      and we said...
You cannot use native dynamic sql by definition then.  You do not know at
compile time the number of bind variables, nor the number or type of output
variables.

I mean -- how the HECK would you know you needed to declare t_empno of type
number?

You must use dbms_sql for this type of processing.  See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:68212348056
for a comprehensive example.

Review &
      Followup

      Rating: 3
Execute
      Immediate for a fetch statement   September 09, 2002

Reviewer:  Pavan Chelvaraj  from Australia

Hi Tom

Thanks for your reply. In my case, I know all the variables and the column names
which would appear in the select statement. But not all the columns appear
always. It depends on the user query. If the user selects only 5 columns then, I
need to fetch the values in only the correct 5 variables I have
e.g. If the user selects empno and ename in the above case. Then I would move
these values into t_empno and t_ename respectively.
I thought we can use EXECUTE IMMEDIATE.

Cheers

Pavan

          Followup:

Nope, if the number of things you fetch into changes -- you must use dbms_sql.

Think about the syntax of

  execute immediate 'query' INTO a, b, c;

A, B, and C are compiled into the code. There is always three of them, no more,
no less.

And

   open l_cursor for 'query';
   fetch l_cursor into a, b, c, d;

A, B, C, and D are compiled into the code -- there is always 4, no more, no
less.

It is DBMS_SQL for you.


Subject | Article | Last Updated (East coast time)
Improving Performance of query on V$SESSION | article only | Sep 10, 2002 07:31 am
To read blob image from remote database over the dblink. | article only | Sep 10, 2002 07:32 am
How to create this sequence | article only | Sep 10, 2002 08:30 am
PLS_INTEGER versus NUMBER versus "dynamic types" | article only | Sep 10, 2002 11:05 am
Invalid Rowid | article only | Sep 10, 2002 12:47 pm
Maxvalue in partitioned table. | article only | Sep 10, 2002 12:57 pm
partition for equal to values | article only | Sep 10, 2002 01:04 pm
Questions about "IN" and "REFERENCE" | article only | Sep 10, 2002 01:10 pm
Parameters to query ! | article only | Sep 10, 2002 01:14 pm
ORA-12570 TNS:packet reader failure | article only | Sep 10, 2002 08:48 pm
Sybase Clustered Index concept in Oracle ? | article only | Sep 11, 2002 08:36 am
How to determine bad sql statement | article only | Sep 11, 2002 10:17 am
Can I do this in single sql query? | article only | Sep 11, 2002 12:00 pm
Script to allow a user to change his default tablespace | article only | Sep 11, 2002 12:12 pm
Long column Tables move to different tablespace | article only | Sep 11, 2002 03:30 pm
Parse CPU to Parse Elapsd has horrible ratio.. but others not too bad? | article only | Sep 11, 2002 03:35 pm
I/O , and other questions on partition elimination at sub partition level | article only | Sep 11, 2002 03:38 pm
Performance of variable substitution in Stored Procs | article only | Sep 12, 2002 08:01 am
Query Performance (count(*) vs count(some column) rehashed yet again) | article only | Sep 12, 2002 08:53 am
Creating Foreign key from one database to another | article only | Sep 12, 2002 03:25 pm
privilege issue | article only | Sep 12, 2002 07:55 pm
Identifying the data file | article only | Sep 14, 2002 01:39 pm
Few Questions | article only | Sep 14, 2002 01:49 pm
Logminer Doubt | article only | Sep 14, 2002 02:04 pm
Solaris direct i/o | article only | Sep 14, 2002 02:09 pm
PL/SQL cursor and update of same table | article only | Sep 14, 2002 03:22 pm
Materialized views vs. Trigger | article only | Sep 14, 2002 04:17 pm
dinamic Order by for cursors | article only | Sep 15, 2002 10:54 am
update | article only | Sep 15, 2002 01:22 pm
Index levels | article only | Sep 15, 2002 01:38 pm
Query -- Need help to formulate | article only | Sep 15, 2002 06:46 pm
sqlserver to oracle | article only | Sep 15, 2002 06:51 pm
LONG datatype | article only | Sep 15, 2002 07:10 pm
1.Indexes 2.Documentation on DB and App Server | article only | Sep 16, 2002 07:18 am
SQL query | article only | Sep 16, 2002 07:39 am
Order by | article only | Sep 16, 2002 07:41 pm
How to export oracle view definition | article only | Sep 16, 2002 07:42 pm
Index Rebuilds | article only | Sep 16, 2002 09:15 pm
Using a cursor variable as a function in a select statement | article only | Sep 16, 2002 09:29 pm
row(s) 1 - 39 of 39
September 10, 2002 Ranju -- Thanks for the question regarding "Improving Performance of query on V$SESSION", version 7.3.4.5.0
You Asked
There are more than 1000 tables in our application and due to
users requirement we have to add trigger on every table to
do journalling for INSERT/UPDATE(for all columns)/DELETE.
During a batch run the main part of the trigger does only the
following which takes about 20% of the total elapsed time:

select 1 from v$session where audsid=userenv('sessionid')
and program like '%programname%'
/
If true we ignore the rest of the trigger.

We do lot of inserts/updates during batch run. Is there any way
to improve this query or is there any better way of doing this
journalling as we want to capture  any DML's done via
sqlplus or forms or any other source or executing
CONNECT statment when already inside sqlplus session.

Another issue is on which SYS tables to put triggers for
journalling whenever GRANT is run or ROLE is changed?
Thanx for u'r help in advance.
and we said...
I would create a package:

create or replace package state_pkg
as
    found_one_in_v$session  boolean default FALSE;
end;
/

create or replace package body state_pkg
as

begin
   for x in ( select *
                from v$session
               where audsid = userenv('sessionid')
                 and program like '%programname%' )
   loop
       found_one_in_v$session = TRUE;
   end loop;
end;
/

That package, when "elaborated" (as soon as you reference the
boolean variable) will run the query once per session,
instead of once per trigger execution.  Its a self
initializing global variable -- they are specific to a session
(I cannot see the value of your package variables, you cannot
see mine)

In your trigger, use the package variable to short circut the
execution.

As for which SYS tables to put triggers on...

o you cannot put triggers on SYS tables.  It doesn't work.

o you can use auditing to capture this information.

o in Oracle8i, you can use DDL triggers to capture even more
  information (but not before)
Review & Followup
Rating: 4
interesting approach July 25, 2001
Reviewer: Olga from Canada
Though this is not a new question, I was juat knid of seaching useful info in
archives, let me say that I found it very useful and interesting. Thank you!

Rating: 2
Roland September 10, 2002
Reviewer: roland daane from Netherlands
Hi Tom,

If you were to rename the application, the program in v$session would show the
renamed name of the executable.

How would you get the module instead?

Followup:
what's a "module"

September 10, 2002 Vijayakumar -- Thanks for the question regarding "To read blob image from remote database over the dblink.", version 8.1.6
You Asked
Hi Tom,
      We have 8i db one on the production server and another on the webserver
(public access for online service).  I have the same table created in both the
database.
I have a procedure to load the all the images form the file system about (1400)
into this table (blob field) in production server through oracle forms user
interface. (working fine)
Once it is loaded and verified, then I have another procedure to transfer this
data to the webserver table (identical table) over the db link (working fine).
The dblink is only oneway (prod -> webserver only).

Now the user wants a form interface to validate the data tranfer (query only).
Wants to scroll through and see all the images on the form one by one.

Since I am not allowed to create public synonym (not allowed due to maintaince
issue in our current setup) for the remote table, what is the best way to
achieve my objective.
I was trying to do with reference cursor and build a form based on reference
cursor, but it is not allowed according to oracle document if it is blob/clob
field.
The suggested workaround is to use DBMS_LOB package.

How can I achieve this using DBMS_LOB package?. Any solution?

Thanks
Vijayakumar Belaguli
and we said...
The easiest way I know to do this is:

create global temporary table T ( b blob ) on commit delete rows;

Now, your program would execute:

   insert into t select blob_column from table@remote_site where ....;

and then it would work on the blob in table T locally.  Much easier then trying
to piecewise fetch it using dbms_lob@remote_site!

When you commit, the blob will disappear.
Review & Followup
Rating: 3
To read blob image from remote database over the dblink.", version 8.1.6 September 10, 2002
Reviewer: Belaguli vijayakumar from Singapore
Thanks for your reply and the solution.
Is there any other way of doing this without having to create any temporary
table?
As mentioned earlier, the form interface is to be used only for querying remote
table having images in blob field, so the question of issueing a commit does not
arise at all.

Followup:
Yes, you can use dbms_lob but it would be heinously hard and the wrong way to do
it.

You have 8i.
You have temporary tables.
It is the correct and proper and best answer.

I don't know what you mean by "the question of issuing a commit does not arise
at all", of course it does.

September 10, 2002 Sun -- Thanks for the question regarding "How to create this sequence ", version 8.1.7
You Asked
Hello Tom,
  I would want to create a sequence like this:
  XXNNNNN where XX is ranged from AA to ZZ, NNNNN is ranged from 00001 to 99999

  and the rules would like this:
  Fisrt sequence of XX starts from 'AA', NNNNN from '00001', next serial would
be 'AA00002'. When it reached the sequence 'AA99999', next sequence would be
'AB00001', ending with 'AB99999'. When it reached 'AB99999', next sequence would
be 'AC00001', ....., etc.

  When it reached 'AZ99999', next sequence would be 'BA00001' ending with
'BA99999', When it reached 'BA99999', next sequence would be 'BB00001',.....,
etc.
and we said...
Well, I can do 00000 to 99999 -- off the top of my head, I couldn't think of a
"good" way to skip the 00000 numbers without resorting to PLSQL functions and
for performance, you would want to skip that.

In any case, you can use this as an idea to get started.  First, I prime a table
with a couple of numbers to test boundary values and get the function going,
then I show how a sequence can be used with it:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( r int );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 2 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 99999 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 99999+1 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 99999+2 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 199999 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 199999+1 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 199999+2 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 999999 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 999999+1 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 999999+2 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 9999999 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 9999999+1 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 9999999+2 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select r,
  2             chr( ascii('A')+ trunc(trunc(r/100000)/26) ) ||
  3             chr( ascii('A')+mod(trunc(r/100000),  26) ) ||
  4             to_char(mod(r,100000), 'fm00000')
  5    from (select r from t )
  6  /

         R CHR(ASCII(
---------- ----------
         1 AA00001
         2 AA00002
     99999 AA99999
    100000 AB00000
    100001 AB00001
    199999 AB99999
    200000 AC00000
    200001 AC00001
    999999 AJ99999
   1000000 AK00000
   1000001 AK00001
   9999999 DV99999
  10000000 DW00000
  10000001 DW00001

14 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create sequence my_seq;
Sequence created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select my_seq.nextval,
  2             chr( ascii('A')+ trunc(trunc(my_seq.nextval/100000)/26) ) ||
  3             chr( ascii('A')+mod(trunc(my_seq.nextval/100000),  26) ) ||
  4             to_char(mod(my_seq.nextval,100000), 'fm00000')
  5    from dual
  6  /

   NEXTVAL CHR(ASCII(
---------- ----------
         1 AA00001

ops$tkyte@ORA817DEV.US.ORACLE.COM> /

   NEXTVAL CHR(ASCII(
---------- ----------
         2 AA00002

ops$tkyte@ORA817DEV.US.ORACLE.COM> /

   NEXTVAL CHR(ASCII(
---------- ----------
         3 AA00003

ops$tkyte@ORA817DEV.US.ORACLE.COM>
September 10, 2002 -- Thanks for the question regarding "PLS_INTEGER versus NUMBER versus "dynamic types"", version 8.1.7
You Asked
It has been suggested to me that I use PL/SQL declarations like
PROCEDURE foo ( p_id IN PLS_INTEGER )...
instead of
PROCEDURE foo (p_id IN NUMBER ) ...
or
PROCEDURE foo (p_id IN mytable.my_id%TYPE )

I've always preferred the last option, since it keeps the code flexible with
respect to the actual data. However, the person recommending PLS_INTEGER is
doing so for performance. Can you comment? I'm reluctant to give up
properly-engineered, lower maintenance code for the sake minuscule performance
differences.

Thanks for the great site.
and we said...
You are right in my opinion.

PLS_INTEGER is only appropriate for performance in very localized code (it
almost ALWAYS gets turned into a number anyway.

The theoretical gains are way outweighed by ease of programming, and using %type
is great.

I might, *might* use pls_integer or binary_integer or some other type in some
very very tight code that had to be tuned to DEATH.

Using my simple test harness:
http://asktom.oracle.com/~tkyte/runstats.html
we can see that simply passing the parameters results in no gain or loss:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure p1( x in
pls_integer )
  2  as
  3  begin
  4      null;
  5  end;
  6  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure p2( x in number )
  2  as
  3  begin
  4      null;
  5  end;
  6  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5
  6      l_p1    pls_integer := 0;
  7      l_p2    number := 0;
  8  begin
  9      insert into run_stats select 'before', stats.* from stats;
 10
 11      l_start := dbms_utility.get_time;
 12      for i in 1 .. 1000000
 13      loop
 14          p1( l_p1 );
 15      end loop;
 16      l_run1 := (dbms_utility.get_time-l_start);
 17      dbms_output.put_line( l_run1 || ' hsecs' );
 18
 19      insert into run_stats select 'after 1', stats.* from stats;
 20      l_start := dbms_utility.get_time;
 21      for i in 1 .. 1000000
 22      loop
 23          p2( l_p2 );
 24      end loop;
 25      l_run2 := (dbms_utility.get_time-l_start);
 26      dbms_output.put_line( l_run2 || ' hsecs' );
 27      dbms_output.put_line
 28      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 29
 30      insert into run_stats select 'after 2', stats.* from stats;
 31  end;
 32  /
724 hsecs
723 hsecs
run 1 ran in 100.14% of the time

PL/SQL procedure successfully completed.

in 1,000,000 calls... and for many general operations

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure p1( x in out
pls_integer )
  2  as
  3  begin
  4      select count(*) into x from dual;
  5  end;
  6  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure p2( x in out
number )
  2  as
  3  begin
  4      select count(*) into x from dual;
  5  end;
  6  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5
  6      l_p1    pls_integer := 0;
  7      l_p2    number := 0;
  8  begin
  9      insert into run_stats select 'before', stats.* from stats;
 10
 11      l_start := dbms_utility.get_time;
 12      for i in 1 .. 100000
 13      loop
 14          p1( l_p1 );
 15      end loop;
 16      l_run1 := (dbms_utility.get_time-l_start);
 17      dbms_output.put_line( l_run1 || ' hsecs' );
 18
 19      insert into run_stats select 'after 1', stats.* from stats;
 20      l_start := dbms_utility.get_time;
 21      for i in 1 .. 100000
 22      loop
 23          p2( l_p2 );
 24      end loop;
 25      l_run2 := (dbms_utility.get_time-l_start);
 26      dbms_output.put_line( l_run2 || ' hsecs' );
 27      dbms_output.put_line
 28      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 29
 30      insert into run_stats select 'after 2', stats.* from stats;
 31  end;
 32  /
2288 hsecs
2266 hsecs
run 1 ran in 100.97% of the time

PL/SQL procedure successfully completed.

You'll see no measurable difference.  Just to be fair -- for somethings, it can
be effective:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure p1( x in out
pls_integer )
  2  as
  3  begin
  4      x := x+1;
  5  end;
  6  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure p2( x in out
number )
  2  as
  3  begin
  4      x := x+1;
  5  end;
  6  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5
  6      l_p1    pls_integer := 0;
  7      l_p2    number := 0;
  8  begin
  9      insert into run_stats select 'before', stats.* from stats;
 10
 11      l_start := dbms_utility.get_time;
 12      for i in 1 .. 100000
 13      loop
 14          p1( l_p1 );
 15      end loop;
 16      l_run1 := (dbms_utility.get_time-l_start);
 17      dbms_output.put_line( l_run1 || ' hsecs' );
 18
 19      insert into run_stats select 'after 1', stats.* from stats;
 20      l_start := dbms_utility.get_time;
 21      for i in 1 .. 100000
 22      loop
 23          p2( l_p2 );
 24      end loop;
 25      l_run2 := (dbms_utility.get_time-l_start);
 26      dbms_output.put_line( l_run2 || ' hsecs' );
 27      dbms_output.put_line
 28      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 29
 30      insert into run_stats select 'after 2', stats.* from stats;
 31  end;
 32  /
93 hsecs
171 hsecs
run 1 ran in 54.39% of the time

PL/SQL procedure successfully completed.

but in most cases, it'll be a wash, and in the case you identify as a
bottleneck (using dbms_profiler for example) you would "tune" to death.
Review & Followup
Rating: 5
Thanks September 10, 2002
Reviewer: Daryl from Carrollton, TX USA
I appreciate your continuing interest in doing things the "right way" (coding,
bind variables, rollback space, etc.). Why is it that so many people have
opinions on "performance" that can't be backed up by reality?

Followup:
Mystique I think.  The old "hey, i know this inside piece of information.  do it
like this and you'll go faster".

If it was 100% about speed, we'd be programming assembler.  Some tightly written
spaghetti coded assembler cannot be beat for raw performance.  It just takes
along time and a really good coder.

There are nuggest of truth in everything -- this PLS_INTEGER for example (which
cannot replace a number actually -- one thing i FORGOT to mention was that the
behaviour of your PROGRAM will change!!!!!!!!

ops$tkyte@ORA920.US.ORACLE.COM> declare
  2          x pls_integer;
  3          y number;
  4  begin
  5          y := 5.232;
  6          x := y;
  7          dbms_output.put_line( y );
  8          dbms_output.put_line( x );
  9  end;
 10  /
5.232
5

PL/SQL procedure successfully completed.)  can be in some unusual cases provably
faster.  (but conversely, I can prove that it is slower as well

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure p1( x in out
pls_integer )
  2  as
  3  begin
  4      x := x+1.0;
  5  end;
  6  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure p2( x in out
number )
  2  as
  3          l_var pls_integer := x;
  4  begin
  5      x := x+1.0;
  6  end;
  7  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5
  6      l_p1    pls_integer := 0;
  7      l_p2    number := 0;
  8  begin
  9      insert into run_stats select 'before', stats.* from stats;
 10
 11      l_start := dbms_utility.get_time;
 12      for i in 1 .. 100000
 13      loop
 14          p1( l_p1 );
 15      end loop;
 16      l_run1 := (dbms_utility.get_time-l_start);
 17      dbms_output.put_line( l_run1 || ' hsecs' );
 18
 19      insert into run_stats select 'after 1', stats.* from stats;
 20      l_start := dbms_utility.get_time;
 21      for i in 1 .. 100000
 22      loop
 23          p2( l_p2 );
 24      end loop;
 25      l_run2 := (dbms_utility.get_time-l_start);
 26      dbms_output.put_line( l_run2 || ' hsecs' );
 27      dbms_output.put_line
 28      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 29
 30      insert into run_stats select 'after 2', stats.* from stats;
 31  end;
 32  /
188 hsecs
158 hsecs
run 1 ran in 118.99% of the time

PL/SQL procedure successfully completed.

depending on what you do!)

You know -- procedures add overhead.  There is overhead in calling a procedure
for a procedure.  So, should we INLINE all of the code -- and make one huge
1,000 line procedure?  Not if you want me to even consider looking at it,
accepting it on my system, testing it....

it is an INTEGER after all, not a NUMBER!

September 10, 2002 anthony -- Thanks for the question regarding "Invalid Rowid", version 8.1.6
You Asked
Hi tom,

Can you explain to me what happened here??

SQL> delete from temp_Tbl;

97741 rows deleted.

SQL> commit;

Commit complete.

SQL> insert into temp_tbl select * from all_objects;

97741 rows created.

SQL> commit;

Commit complete.

SQL> select object_name
  2  from temp_Tbl , x1
  3  where temp_Tbl.rowid(+)=x1.c1;

OBJECT_NAME
------------------------------
CUCY_PAIR
DENO_S
DEFTS
DETL
DIY
DI_PAIR
ED
ED2
EENT
FTY_TBE

10 rows selected.

SQL> truncate table temp_Tbl;

Table truncated.

SQL>  insert into temp_tbl select * from all_objects;

97741 rows created.

SQL> commit;

Commit complete.

SQL> select object_name
  2  from temp_Tbl , x1
  3  where temp_Tbl.rowid(+)=x1.c1;
from temp_Tbl , x1
     *
ERROR at line 2:
ORA-01410: invalid ROWID

TNX.....
NOTNA
and we said...
don't know what X1 is but...

X1 contains some rowid that is not in TEMP_TBL after the second insert.  ROWIDS
are physical addresses -- the data got put in in different places after the
truncate then it did after an insert.

In fact, the DELETE/INSERT could equally easily throw the invalid rowid error as
well.

ROWIDS are physical addresses and only exist for the life of a row.  They can be
reused by some new row later.  you got "lucky" the first time (or depending on
how you look at it, you got UNLUCKY -- you got 10 rows but not the 10 rows that
X1 used to point at).  you got "unluck"/"lucky" the second time.
September 10, 2002 khalid -- Thanks for the question regarding "Maxvalue in partitioned table.", version 8.1.7
You Asked
Dear Tom.
Can i add partition to tables whitch has partition with maxvalue in its
defition.

i tri to do it but i end up with the following error.
ORA-14074: partition bound must collate higher than that of the last partition
Thank you very much.
and we said...
you would split an existing partition (eg: the partition with MAXVALUE for
example) into two.

Since all possible values from negative infinity on up to positive infinity are
covered already -- you just split one of the existing partitions in two.  Search
for

alter table split partition

on this site for exapmles.
Review & Followup
Rating: 5
Thansk Tom September 10, 2002
Reviewer: khalid from Yemen
Thank you very much.

September 10, 2002 Saradha -- Thanks for the question regarding "partition for equal to values", version 8.1.7
You Asked
Tom,

I have a large table of around 300 million records and I would like to partition
the table based on a column that is CHAR data type.
The distinct value for this column is as follows.

Column Name: Country_Code
             ------------
             BD
             CH
             IN
             ML
             NP
             PK
             SA
             SG
             SL
             US

The rows are almost evenly distributed for each country code and
I would like to do partitions for each country code.

As with the RANGE partition I can not specify the equal to value and
with the HASH partition I do not have the control of a specific country data
going into the specific country partition, can you please tell me how to do
this.

Thanks
Saradha
and we said...
Well, in 9i, we have list partitions which allow you to assign a discrete value
to a specific partition.

Until then, you can use

  PARTITION PART_BD VALUES LESS THAN ('BE') ,
  PARTITION PART_CH VALUES LESS THAN ('CI') ,
  PARTITION PART_IN VALUES LESS THAN ('IO') ,
.....

Just "add" a character to the second character.
Review & Followup
Rating: 1
May 16, 2003
Reviewer: A reader

September 10, 2002 Yang -- Thanks for the question regarding "Questions about "IN" and "REFERENCE"", version 8.1.7
You Asked
Question 1:  Please look at the following steps first...
create table temp
(a number(5,0),
 b char(5)
);

insert into temp
values(1,'a');

insert into temp
values(8,'z');

insert into temp
values(5,'c');

insert into temp
values(6,'t');

insert into temp
values(2,'y');

I carry out such statement as:
select * from temp
where a in ('1','2','6','5','8');
I hope the result is:
     A B
---------- -----
     1 a
     2 y
     6 t
     5 c
     8 z
But the actual result isF(i.e. the order of field "A" is in accordance with the
"INSERT" order)
         A B
---------- -----
         1 a
         8 z
         5 c
         6 t
         2 y
Why? What should I do to get the expected result?

Question 2:
If I have a table named "A":
CREATE TABLE "A"(
    a number(5, 0)
    CONSTRAINT A_PK PRIMARY KEY ("a")
);

And I have many other tables, such as:
1.
CREATE TABLE "B"(
    b number(5, 0)
);
ALTER TABLE "B" ADD CONSTRAINT B_FK
    FOREIGN KEY ("b")
    REFERENCES "A"("a")
;
2.
CREATE TABLE "C"(
    c number(5, 0)
);
ALTER TABLE "C" ADD CONSTRAINT C_FK
    FOREIGN KEY ("c")
    REFERENCES "A"("a")
;

.
.
.

n.
CREATE TABLE "N"(
    n number(5, 0)
);
ALTER TABLE "N" ADD CONSTRAINT N_FK
    FOREIGN KEY ("n")
    REFERENCES "A"("a")
;

-------- The question is---------------
How can I know which rows in table "A" is being referenced by other tables?
For example:
If table "A" have the following data:
1,2,3,4,5
And the table "B" have the following data:
3,5
I can say,
The data "3" and "5" of table "A" is being referenced by table "B", but the data
"1","2" and "4" is not being referenced by other table "B".
----------------------------------------
and we said...
THERE IS NO SUCH THING AS "INSERT ORDER".

We put data where ever we feel like it in a HEAP table.
We put data in key order in an index organized table.
We put data where is hashed to in a hash clustered table.

Otherwise, data goes where ever we feel like it.  You can NEVER get data out in
the "order" it went in. That concept is totally alien to relational databases.

The ONLY time you can say anything about the order of data in a result set is
when you have an ORDER BY on the query itself.  Otherwise, just expect data that
meets your criteria and make no assumptions about the order.  You would need a
timestamp or some sequence number in order to order by your data and get it
loosely in "order of insertion"

You can join A to B.

select distinct a.a, decode( b.a, NULL, 'Not referenced', 'Referenced' )
  from a, b
 where a.a = b.a(+)
/

You can use a select ( select )

select a, nvl( select 'Referenced' from b where b.a = a.a ), 'Not referenced' )
  from a
/

You can use minus and interect:

select a from a
minus
select a from b;  -- everything not referenced

select a from a
intersect
select a from b;  -- everything referenced

to name a few.
September 10, 2002 Rama -- Thanks for the question regarding "Parameters to query !", version 8.1.7
You Asked
I have the following situation.

Two packages a and b with procedures/functions in a schema "schema1" as listed
below.

    Package a:
        Function getInfo     : returns a  nested table type  Ex: InfoType
        Function getData    : returns a  nested table type  Ex: DataType

        Note: Both functions take the following parameters:

            1. number : output of b.getId packaged function call
            2. date   : process date
    Package b:

        Function getId        : returns a number

        Note: Parameters:
            1. Varchar2
            2. Date

I need to select some columns from InfoTye and DataType object types from a
different schema "schema2".

This is what I do,

select
    infoTyp.info1,
    infoTyp.info2,
    DataTyp.data1,
    DataTyp.data2
from
    table (Cast( schema1.a.getInfo(b.getId('some string', '09-SEP-2002'),
'09-SEP-2002') as schema1.Infotype)) InfoTyp,
    table (Cast( schema1.a.getData(b.getId('some string', '09-SEP-2002'),
'09-SEP-2002') as schema1.Datatype)) DataTyp
where
    InfoTyp.Id = DataTyp.Id

My questions:

    1. How do I call getId function only once, and make use of its ouput in both
packaged function "a" calls.
       As you can see, I am making a new GetID call whenever I need to pass it
to a packaged function.

    2. How do I parametrise the calls to getInfo and getData in the above
select.
        ( Assuming both take same parameters).

I need to this in a select statement.
and we said...
as an aside.  Always always always use to_date and an explicit date format to
convert a string to a date.  You'll never be sorry you did, never!  You'll only
be sorry some day if you do not.

1) you could:

dbms_application_info.set_client_info( b.getId('some string', '09-SEP-2002') );

and then run the query

select
    infoTyp.info1,
    infoTyp.info2,
    DataTyp.data1,
    DataTyp.data2
from
    table (Cast( schema1.a.getInfo( userenv('client_info'),
             '09-SEP-2002') as schema1.Infotype)) InfoTyp,
    table (Cast( schema1.a.getData(userenv('client_info'),
             '09-SEP-2002') as schema1.Datatype)) DataTyp
where
    InfoTyp.Id = DataTyp.Id

Or you could optimize getId to remember its last inputs and output in three
package global variables so that if called again with the same inputs -- it just
returns the known output....

2) You use bind variables in whatever host language you are using.
September 10, 2002 Venkat -- Thanks for the question regarding "ORA-12570 TNS:packet reader failure", version 8.1.5
You Asked
I created a new database in a client machine in windows-nt.
and the database is created properly.

When i tried to connect database thru sqlplus from the same machine "with"
network cord plugged in .
conn system/manager (with no connect string)
connected...

But when i tried to connect database thru sqlplus from the same machine
"without" network cord plugged in
conn system/manager (with no connect string)
ERROR:
ORA-12570 TNS:packet reader failure

Can u pls tell me what could be the reason.

Thanxs in advance..

Regards,
Venkata Brahmajee
and we said...
Article-ID:         <Note:91210.1>
Circulation:        PUBLISHED (EXTERNAL)
Folder:             network
Topic:              Articles to be Refiled
Title:              ORA-12570 ON CONNECTION ATTEMPT IN SERVER MANAGER WITH NO
                    NETWORK CONNECTION
Document-Type:      BULLETIN
Impact:             LOW
Skill-Level:        NOVICE
Server-Version:     08.01.05.0X
Updated-Date:       09-MAR-2001 12:41:19
Content-Type:       TEXT/PLAIN
Keywords:           NETCONFIG; OSAUTHENT; SVRMGRINFO; WINNT;
Errors:             ORA-12570;  TNS-12631;
Products:           62;
Platforms:          912;

Problem Description:
====================

When your server is not connected to the network and you try to connect in
Server Manager (svrmgrl), the connection fails returning an ORA-12570. If you
check the "sqlnet.log" you will find a TNS-12631 error.

  ORA-12570 / TNS-12570: TNS:packet reader failure
  ORA-12631 / TNS-12631: Username retrieval failed

Problem Explanation:
====================

These errors are associated with remote authentication on NT under NTS (NT
Transport Services). Essentially, the following parameter is set in the
"sqlnet.ora":

  SQLNET.AUTHENTICATION_SERVICES = (NTS)

As a result, if you are logged on as a domain user, and you don't have a
network connection (network cable temporary removed or laptop booted
standalone), you will not be able to connect to the database because the
authentication will try to reach the domain users database on the PDC
(Primary Domain Controller) or BDC (Backup Domain Controller).

Solution Description:
=====================

There a 2 solutions to connect to the database when no network is present:

    1. You can log in as a NT local user.

    2. You can disable NTS in sqlnet.ora by setting the following parameter:

           SQLNET.AUTHENTICATION_SERVICES = (NONE)

Explanation:
============

1. When you log in as a local user, then the local users database is contacted
   and not the remote domain users database.

2. When NTS is disabled, even though you are logged in as a domain user, the
   domain user's database will not be accessed.
Review & Followup
Rating: 4
SQLNET.AUTHENTICATION_SERVICES September 10, 2002
Reviewer: I Singh from Kansas City,MO
My database server and the client are on two different domains.(no trust between
them) I have SQLNET.AUTHENTICATION_SERVICES = (NTS) on the database server. This
means that the client has to be authenticated.

When I have SQLNET.AUTHENTICATION_SERVICES = (NTS) on the client then I get
authentication error. ORA-12638.
This makes sense since the database server domain is not able to authenticate
the user at the client domain.

But when I have SQLNET.AUTHENTICATION_SERVICES = (NONE) on the client then I
dont get this error and it lets me connect to the database.
Why does this work? Is the client overriding the seting on the database server ?
Isnt this a security issue ?

Followup:
If it isn't the right setup for you then yes, (NONE) would be wrong and you
would use NTS.  It is controlled 100% on the server, the default is (NTS), you
have to explicitly turn this off so no -- it is not a security issue unless you
make it so.

Rating: 3
ORA-12570 TNS:packet reader failure September 10, 2002
Reviewer: I Singh from Kansas City, MO
I am not sure if I fully understand the answer. You have mentioned that "It is
controlled 100% on the server" but regardless of the authentication value on the
server the client can make changes on its side and connect ( by making
authentication = NONE) which means that it is the client and not the database
server which is in control.

Also could you please elaborate on "the default is (NTS), you have to explicitly
turn this off...". The default value is NONE.

Thanks in advance.

Followup:
You need to supply more information.  show me your server setup and client setup
and exactly how you are attempting to connect.

Rating: 3
sqlnet.authentication September 10, 2002
Reviewer: I Singh from Kansas City, MO
client sqlnet.ora file (Client is on test.dom domain)
**********************
NAMES.DEFAULT_DOMAIN = world.com
SQLNET.AUTHENTICATION_SERVICES= (NTS)
NAMES.DIRECTORY_PATH= (TNSNAMES, ONAMES, HOSTNAME)
TRACE_LEVEL_CLIENT = 16

server sqlnet.ora  (Server is on world.com domain)
********************
NAMES.DEFAULT_DOMAIN = world.com
SQLNET.AUTHENTICATION_SERVICES= (NTS)
NAMES.DIRECTORY_PATH= (TNSNAMES, ONAMES, HOSTNAME)
TRACE_LEVEL_CLIENT = 16

H:\>sqlplus sys/oracle@test
SQL*Plus: Release 8.1.7.0.0 - Production on Tue Sep 10 15:07:23 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

ERROR:
ORA-12638: Credential retrieval failed

Enter user-name:

It works fine when I change the CLIENT sqlnet.ora to

NAMES.DEFAULT_DOMAIN = world.com
SQLNET.AUTHENTICATION_SERVICES= (NONE)
NAMES.DIRECTORY_PATH= (TNSNAMES, ONAMES, HOSTNAME)
TRACE_LEVEL_CLIENT = 16

There is no trust between domains world.com and test.dom.
My questions are same as before.

Thanks

Followup:
Ok -- but yet you have to give the password.  I would only be concerned
"security wise" if

sqlplus / as sysdba

worked to the remote database.

Rating: 3
Check the client's sqlnet.ora file September 11, 2002
Reviewer: Logan Palanisamy from Santa Clara, CA, USA
I Singh,

Look at the names.default_domain of the client side sqlnet.ora. It says
world.com not test.com as you say.

Looks to me like both the client and the servers are on the same domain
(world.com)

Rating: 4
More explanantion please .. July 14, 2003
Reviewer: kumar from India
Tom,

I got this error while trying to connect to the DB on the same machine i am
working on.
ORA-12638: Credential retrieval failed
Actually my NT account got locked after I logged on to this machine. I disabled
the NTS in sqlnet.ora file and then it got connected. I would like know what
this NTS does.. Does it mean that while I am connecting there is another level
of authentication apart from the one done at the database level ?

Thanks

September 11, 2002 Doug -- Thanks for the question regarding "Sybase Clustered Index concept in Oracle ?", version 8.1.7
You Asked
Tom - there seem to be a few products that have the concept of a clustered index
- which seems to be an index that affects the order in which the rows are stored
in the table (that right?).. The "clustered index" concept doesn't seem to exist
in Oracle.  Would IOT be the closest thing?  And, since that isn't exactly the
same thing, do you have any comments as to why Oracle doesn't view this as a key
feature?
and we said...
Sure it does.

We have IOT's -- which are very very close to the "concept" of the sybase
clustered index -- but doesn't have the upto 110% overhead.

We have b*tree clusters -- which force data with the same key values to be
physically colocated together (and it is generally COLOCATION that is relevant
here, not that the data is in order of a, b, c, d, e, f, g, ....)

We have hash clusters as well.

I know you have my book -- read the chapter on Tables again -- I even
specifically point out the Sybase clustered index and how it compares with IOT's
in that chapter!
Review & Followup
Rating: 4
Yes it is in the book July 08, 2002
Reviewer: Doug from Danbury, CT USA
Yes I did find it in the book and am re-reading the tables chapter.  You mention
that the data being co-located is more significant that it being in order.  On
page 224 you imply that it really is in order, just like a Sybase clustered
index.  When starting in your index clustered tables discussion you mention -
"It is not storing the data sorted - that is the role of the IOT"

From a book entitled "Sybase Architecture and Administration" by John Kirkwood.

"Sybase uses B-tree to index the tables and has two types: clustered and
non-clustered. .... Therefore a Sybase table with a clustered index has the data
records maintained in sequence of the index field"

So basically - the 110% you refer to is because Sybase still uses an index on
the data even though the data is forced to be ordered by the index?  The index
is sparse, which is I guess why it isn't 200%?
Also, the data in an IOT *is* in order isn't it?  Even if there are block splits
etc., won't the structure itself maintain both co-location and sorting order?
a,b,c,d,e etc?

Thanks.

Followup:
On page 224 - i explicity say a couple of times "the data is not stored in
order", "the data is not in order -- that is what an IOT is for".  I cannot find
the implication myself...

The 110% is DOUBLE (200% would be triple).  In the worst degenerate case --
where the primary key is almost the entire row -- the index is no longer
"sparse" and because of the overhead of the index structure itself, the data can
be doubled.

The data in an IOT is ordered -- yes, I say that and it is true.  BUT -- the
data is still not physically ordered on disk (neither in Sybase or an in an
IOT).  That may be considered a matter of semantics -- but the rows can (and
will be) very very far apart from eachother.  An index is a data structure that
is managed a block at a time.  The "second" block in the index may be very very
far from the first.  It gives the ILLUSION of being stored sorted, but on disk
-- the data is just a big jumble of stuff waiting to be put back together.

Rating: 4
IOT and Primary Key Order September 11, 2002
Reviewer: Subhrajyoti Paul from Mumbai, India...
Tom,
   The oracle 8i conecepts manual page 10-40 has a comparison of HEAP table with
IOT. It says, in an IOT, a full-index scan returns all rows in primary key
order. I tried to check it out by doing the following...

ptsdba@ptsmig.world> create table iot_objects(id, oname, owner, status, primary
key(id, owner)) organization index as select rownum*2, object_name, owner,
status from dba_objects;

ptsdba@ptsmig.world>insert into iot_objects
select r, object_name, owner, status from
(select rownum r, object_name, owner, status from dba_objects)
where mod(r,2)=1;

ptsdba@ptsmig.world>commit;

ptsdba@ptsmig.world>select id from iot_objects;

Now this query returned me 34852 rows... The first 61 rows came in order
1
.
.
61
458

But the 62nd row was the value 458 and then there was some more contigous nos
and then again there was some gap...

I go the value 62 in the 14279th row...

Now this does not seem to be in order. I am selcting the first column in the
primary key which is supposed to be most significant in deciding the order.

The Explain Plan is like this...
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=59 Card=1 Bytes=4)
   1    0   INDEX (FAST FULL SCAN) OF 'SYS_IOT_TOP_45935' (UNIQUE) (Co
          st=59 Card=1 Bytes=4)

I am confused... I guess I am doing something wrong...But whatever I do, as per
the manual, the primary key order should be maintained...

I am using Oracle 8.1.7 on sun solaris... I did not include the output of the
select query as it was 34852 lines long...;-)

Followup:
It did a FAST FULL SCAN, not an INDEX FULL SCAN.

They are different.  In a FAST FULL scan, we read the blocks as they exist on
disk -- we do multi-block IO.  We read a mixture of root block, branch
(interior) blocks, and leaf blocks -- we through out all of the root/branch
blocks and just dump the data found in the leaf blocks.  We don't process the
blocks in "sorted order" but the order they are on the disk.

In an index full scan, we start at the root block, read a branch block -- get to
a leaf block and then read each and every leaf block in "sorted order" using
single block IO's (read one leaf block at a time).

Bottom line -- just always remember this -- unless you have an ORDER BY on your
query, the data is coming back to you in SOME ORDER.  Unless you have an ORDER
BY, the data will come back in SOME ORDER.  It will not be sorted, it will not
be the order of insertion, it will be in SOME ORDER and that order can change
from run to run of the query even !!!! different plans result in different
orders!!!

Rating: 5
I did not know they were different...Thanks.... September 11, 2002
Reviewer: Subhrajyoti Paul from Mumbai, India...
I guess... I will have to study your chapter on Index ASAP, otherwise I will
keep on asking this dumb questions...:))

September 11, 2002 Jesse -- Thanks for the question regarding "How to determine bad sql statement", version 8.1.7
You Asked
Hi Tom,

  I need your help to determine bad sql statement. I ran a tkprof for an
application, I have the report. I need some hints as how to determine bad sql
statement and improve the statement.

  For example, I read from somewhere like :

  Good sql statement should have:
  1. low parse count, 1 is the best
  2. low ratio of physical read to rows fetched ?
  3. no full table scan if possible
  4. use index whenever possible

  Do you have some rule of thumb as the above?

  For example, I extract one of the trace of the report which I 'think' is bad,
could you pls help to determine why it is bad and
how can I improve on it, Thks:

SELECT mqport, mqsvce, mqsize, mqtype, COUNT(1) AS cnt
FROM
 ecp030   WHERE mqmvdt > 0     AND mqsta IN ('DMTY', 'RCCN', 'SHRT', 'RMTY')
     AND SUBSTR(mqrpdt, 1, 6) = 200205  GROUP BY mqport, mqsvce, mqsize,
  mqtype

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch       11     22.60    2077.05     129698     204139          0          10
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       13     22.62    2077.05     129698     204139          0          10

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 366  (SEALINER_TEST)   (recursive depth: 1)

Rows     Row Source Operation
-------  ---------------------------------------------------
     10  SORT GROUP BY
     16   TABLE ACCESS BY INDEX ROWID ECP030
 206796    INDEX RANGE SCAN IDX_ECP030_1 (object id 35556)

Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: CHOOSE
     10   SORT (GROUP BY)
     16    TABLE ACCESS (BY INDEX ROWID) OF 'ECP030'
 206796     INDEX (RANGE SCAN) OF 'IDX_ECP030_1' (NON-UNIQUE)

Thks & Rgds,
Jesse
and we said...
Do you have my book "Expert one on one Oracle"

In chapter 10, I spend quite a few pages on how to read a tkprof report, what to
look for, whats good, whats bad and how to figure out why the elapsed time is so
much larger then cpu and so on.  Short of that, see
http://asktom.oracle.com/~tkyte/tkprof.html
GOOD statements are those that perform well.  INDEXES do not infer, imply, mean,
assure "good performance".  In fact, in your case I believe it may well be
BECAUSE you are using an index, that your runtime is so POOR and a full
tablescan would be preferred.

FULL SCANS are not evil, they do not mean slow, indexes are not "pure goodness",
they do not mean fast.

I see you are comparing a string to a number -- bad, use TO_NUMBER on the substr
OR use 'number' on the number (make it a string).  Avoid implicit conversions
ALWAYS

Try this query in sqlplus:

SELECT /*+ FULL(ecp030) */ mqport, mqsvce, mqsize, mqtype, COUNT(*) AS cnt
  FROM ecp030
 WHERE mqmvdt > 0
   AND mqsta IN ('DMTY', 'RCCN', 'SHRT', 'RMTY')
   AND SUBSTR(mqrpdt, 1, 6) = '200205'
 GROUP BY mqport, mqsvce, mqsize, mqtype

and see how that performs.  You might be pleasantly surprised.  Then, in order
to avoid the hint, you will read the Designing and Tuning for Performance guide:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76992/toc.htm
learn all about the cost based optimizer and start using it so you get smarter
query plans.
Review & Followup
Rating: 4
September 11, 2002
Reviewer: Dave from Ohio
Can SUBSTR(mqrpdt, 1, 6) = '200205' use an index in recent versions of Oracle?
If not, then ...

mqrpdt like '200205%'

... might be an improvement.

Followup:
yes, if there is an index on substr(mqrpdt,1,6) an index could be used.

September 11, 2002 Tony -- Thanks for the question regarding "Can I do this in single sql query?", version 8.1.6
You Asked
Hi Tom,
I have a requirement to update post code in a table. The post code is stored in
varchar2(4) as some of the post codes are numeric and some are non-numeric. The
requirement is to prefix the numeric postcodes having < 4 charaters with leading
'0'. When I try this:

select * from test;

POST
----
2121
MXP
879

select * from test
where length(postcode) < 4;

POST
----
MXP
879

update test set postcode = lpad(postcode,4,'0')
   where length(postcode) < 4
   and substr(postcode,1,1) BETWEEN 0 and 9;
and substr(postcode,1,1) BETWEEN 0 and 9
    *
ERROR at line 3:
ORA-01722: invalid number

a) I can do this in pl/sql by handing an exception of invalid number. But can I
do this in sql?
b) Could you please also clarify, how does oracle evaluate the predicates in
update statement, Is it the last predicate first or otherwise?
Thanks
and we said...
ops$tkyte@ORA920.US.ORACLE.COM> select *
  2    from t
  3   where length(post) < 4
  4     and translate( post, '0123456789', '0000000000' ) = rpad( '0',
length(post), '0' )
  5  /

POST
----
879

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> update t
  2     set post = lpad(post,4,'0')
  3   where length(post) < 4
  4     and translate( post, '0123456789', '0000000000' ) = rpad( '0',
length(post), '0' )
  5  /

1 row updated.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select * from t;

POST
----
2121
MXP
0879

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:688839853415
for the proof that the order of evaluation of a predicate in SQL is 100% not
deterministic.  The order of evaluation of a predicate can vary even given the
SAME EXACT query.
Review & Followup
Rating: 2
is it possible September 11, 2002
Reviewer: A reader
update test set postcode = lpad(postcode,4,'0')
   where length(postcode) < 4
   and substr(postcode,1,1) BETWEEN 0 and 9; <-----

don't you think this it self is an error ?
aren't you comparing "string" ( returned by substr )
    with the number ?

  is it an implecit conversion ?

Thanks

Followup:
Yes, I think that:

  and substr(postcode,1,1) BETWEEN 0 and 9; <-----

is an error -- a PROGRAMMING error on behalf of the query author.  It performs
an implicit conversion from the string returned by SUBSTR to a number and it'll
FAIL when the substr returns a non-number.

Rating: 5
Alway useful, of course. September 11, 2002
Reviewer: Chuck Jolley from Oklahoma City, OK USA
What about:
update test set postcode = lpad(postcode,4,'0')
   where length(postcode) < 4
   and substr(postcode,1,1) BETWEEN '0' and '9';

Followup:
Sure, but what happens to

0AB

The translate is a neat way to check if something is a number, the between '0'
and '9' certainly works.

September 11, 2002 Pramod -- Thanks for the question regarding "Script to allow a user to change his default tablespace", version 8.1.7
You Asked
A user without "alter user" privilege can change their own passwords but cannot
change their default tablespace.  How would you allow a user to change his
default tablespace without giving the user "ALTER USER" privilege.  Do you have
a  stored procedure for that?
and we said...
This is an *excellent* use of a stored procedure.

As a privileged user (one that has the ALTER USER privilege granted *directly*
to them, not via a role -- see
http://asktom.oracle.com/~tkyte/Misc/RolesAndProcedures.html
for details) just create a procedure:

create or replace procedure change_default_tablespace( p_new_ts in varchar2 )
as
begin
    execute immediate
       'alter user ' || user || ' default tablespace ' || p_new_ts;
end;
/

and then grant execute on change_default_tablespace to whomever.  You can (and
maybe should) put even more logic in there to make sure they change it to some
tablespaces you approve of (eg: NOT system) or use quotas to limit what they can
write where...
Review & Followup
Rating: 5
Why? September 11, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
Why would you want a user to be able to change the default tablespace?  Sure you
can grant them quota 0 on system etc.., but what about any other tablespaces
that they need access to, but shouldn't be used as a default?  I use locally
managed tablespaces based on size:

object_size < 4M
4M < object_size < 128M
128M < object_size

Now I sure wouldn't want some user setting their default tablespace to the one
dedicated to objects greater than 128M in size.

It just seems like putting too much trust in the wrong hands...

$0.02

- Mark

Followup:
Hence the "You can (and maybe should) put even more logic in there to make
sure...." comment from me ;)

Rating: 5
RE: Hence the... September 11, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
RE: Hence the...

Tom:

I understand your "hence the..." comment.  I guess I was asking the Original
Poster as much as you.  By the time you've messed around creating the algorithm
to accomplish this (and it is likely to change), I just don't see the point in
it.  When I create a user and give them a default tablespace, it is on purpose.
If they feel they need it changed, they can give me their argument and I'll be
reasonable about it.  Anyway, I guess I just don't understand what value a user
changing their default tablespace would have...

Thanks for the quick follow-up!

Going to OracleWorld this year?

- Mark

Followup:
Yes, as a matter of fact, I'll be at OracleWorld this year -- Oracle Magazine is
setting up some sessions and I'll be doing some other stuff.

September 11, 2002 Partha -- Thanks for the question regarding "Long column Tables move to different tablespace", version 8.1.7
You Asked
Hi, Tom

Can you tell me an easy way to move tables from one tablespace to another.These
tables have long datatype columns.

I can use alter table move command to move to different tablespace but if table
has got long datatype columns how should i proceed.

Give me your solution.

Thanks in advance.
and we said...
You can use EXP/IMP to export and import the table (eg:

a) exp tables=t
b) drop table t
c) create table t in new tablespace
d) imp full=y IGNORE=y

(and please make sure you have BACKUPS before doing b)!!!!!)

You can use sqlplus copy
http://asktom.oracle.com/~tkyte/Misc/MoveLongs.html
and then after you drop the source table, you could rename the newly copied
table.
Review & Followup
Rating: 3
Long columns September 03, 2002
Reviewer: A reader

Rating: 1
Moving Data in Long September 11, 2002
Reviewer: Tahir from USA
It seems that creativity of offered solution in regards to migrating data in
long columns from one table to another stops at obvious use of SQLPLUS COPY
command and pointless recommendation of using CLOBs. Give me a break.
What if requirement is to use PL/SQL and table has more than one column then the
only option is to use DBMS_SQL but then one might find self in giving an
actually useful advise.

Followup:
You give me a break, geez.

pointless recommendation of clobs?  Well, you are the first to mention clob on
this page (and guess what, it wouldn't be pointless).

"what if the requirement is to use PLSQL" -- HUH?  I have no idea what the heck
you are rambling on about.  You make absolutey less then ZERO sense.

Get a life.

Rating: 5
RE: creativity September 11, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
RE: creativity...

Perhaps Tahir can provide additional creative techniques?  Or perhaps staying
with a deprecated type (long) is somehow less pointless than moving to a
flexible, efficient, supported, etc... type?  I, for one, would be open to a
legitimate attempt to address this question.  Oh, wait, perhaps a legitimate
discussion was not the point of the post...

- Mark

Rating: 5
No doubt September 11, 2002
Reviewer: Dennis from Missouri, USA
Man ok, so you should try to not point out something that works?  And I didn't
see CLOB mentioned anywhere either.  You know what?  Whenever Tom stops amazing
me (and the world explodes) then I'll be sure to look for the AskTahir website
which will be full of wonderful Oracle7 tips.

Too bad you can't ban people on their manners.  Not that I'm being nice either.

Dennis

Rating: 4
Correction September 13, 2002
Reviewer: Tahir from USA
Gentlemen,

To set record strait I also find Tom’s site very informative and I would stop at
that as word "amazing" is not in my vocabulary.
My comments were intended as a response to other forum and only after hitting
submit button I realized it went to Tom. Now you can understand why they were
off the topic.

Thank you for your sincere input.
Tahir

Rating: 5
December 04, 2003
Reviewer: A reader
Now that is one of the most "amazing" explanation I ever heard!!

September 11, 2002 Doug -- Thanks for the question regarding "Parse CPU to Parse Elapsd has horrible ratio.. but others not too bad?", version 8.1.7
You Asked
Tom - this is from running a statspack report.  It captures activity over a 3
day period.

Instance Efficiency Percentages (Target 100%)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            Buffer Nowait %:  100.00       Redo NoWait %:  100.00
            Buffer  Hit   %:   99.58    In-memory Sort %:   99.93
            Library Hit   %:   99.31        Soft Parse %:   90.44
         Execute to Parse %:   93.29         Latch Hit %:   99.97
Parse CPU to Parse Elapsd %:   13.69     % Non-Parse CPU:  100.00

My question is - what explains the Parse CPU to Parse Elapsd ratio being so
abysmal compared to the rest?  Every hit I have on the Oracle support site and
related subjects on your site and even some other sources indicate that it
ususally goes hand in hand with the Execute to Parse ratio, which in this case
is 93% (not fantastic, but not horrible - certainly not 13%)..

Also the hits indicate to look for bind variable problems.  I found a couple of
statements with bind variable problems that won't be fixed until the next
update.

But.. I'm at a loss as to why that ratio is SO BAD.. 13% while the execute to
parse is in the low 90's..   Can you point me in the right direction?  What am I
looking at here?

Thanks.
and we said...
Execute to parse and Parse CPU to Parse Elapsed cannot be compared in any
way shape or form.

EXECUTE to PARSE (1- parse/execute)

This is a measure of how many times a statement was executed as opposed to being
parsed. In a system where we parse, and then execute the statement, and never
execute it again in the same session, this ratio will be 0 (bad). On a system
where we parse a statment once and then execute it 100 times, we'll get a 99%
ratio.  The closer we get to 100, the more efficient we are.  93.29 is about 1
parse for every 15 executes (well done) on average.

The next number to look at is the Parse CPU to Parse Elapsd. In this case for
every CPU second spent parsing we spent about 7.3 seconds wall clock time
parsing. This means we spent some time waiting for a resource ? if the ratio was
100 percent, it would imply CPU time was equal to elapsed time and we processed
without any waits.  You have some bad waits parsing -- which are typically due
to contention on the shared pool but can be caused by other things as well (eg:
trying to compile or alter some objects in a busy system can really jam things
up and through some abnormally long parse times in there).

I would start with a sql_trace/tkprof (with timed stats on) to see whats going
on.  Bottom line however -- these too numbers are not related really (although
-- if your execute to parse was to drop seriously that would introduce more
contention -- and perhaps make the problem worse.  A good execute to parse does
not imply a good parse cpu to parse elapsed however)....
Review & Followup
Rating: 4
Excellent December 09, 2001
Reviewer: Doug from CT, USA
Very interesting.  I will have to track down what is holding up the CPU now.

Rating: 4
From % to seconds September 11, 2002
Reviewer: Josat from MD USA
Tom

   In answering his questions, you convert couple of %stat into number of
executions and number of seconds, for example,
"93.29 is about 1
parse for every 15 executes (well done) on average" and "for
every CPU second spent parsing we spent about 7.3 seconds wall clock time"

How did you translate the % into these numbers?

Thanks

Followup:
  1* select (1-1/15)*100 from dual
ops$tkyte@ORA920.US.ORACLE.COM> /

(1-1/15)*100
------------
  93.333333

Math?  If someone says the pct of Executes To Parses was 93.29%, you just setup

(1 - execute/parse) = 93.29% => 1-.9329 = execute/parse => .0671 = execute/parse
which means execute to parse is 671 parses per 10,000 executes or about 1/15th

Same sort of logic with CPU

September 11, 2002 -- Thanks for the question regarding "I/O , and other questions on partition elimination at sub partition level", version 8.1.7
You Asked
Tom

How was your vacation .

a.
Suppose say a table has 3 partitions with 5 sub partitions each. The ? is will
will sub partition elimination will occur while doing a query which has the
partition key and the sub partition key  in its where clause.

or will the query do a full range scan of all the subpartitions and their
indexes.

can you give me an example with explain plan and discuss? please.

b.Does I?O stand for input and output( a stupid ?, but still),What  is I/O? what
is contention for diskspace?,what are the factors which contribute to I/O? what
is contention for data blocks? When 10 applications are running on the same
server, why will tthey become slow, what factors will contribute to it? what is
the difference between contention for data blocks and free lists?

Thx
and we said...
a) absolutely it will do sub partition elimination.

In fact, it will do it if you just have the sub partition key in the query --
it'll look into each RANGE partition but only at the sub partitions that could
be candidates.

Here is the example, a simple range partitioned table with two hash sub
partitions.

ops$tkyte@ORA817.US.ORACLE.COM> create table t ( x int, y int )
  2  partition by range (x)
  3  subpartition by hash (y)
  4  ( partition p1 values less than ( 5)
  5          ( subpartition p1_s1 tablespace users, subpartition p1_s2
                                                          tablespace tqm_data),
  6    partition p2 values less than (51)
  7           ( subpartition p2_s1 tablespace users, subpartition p2_s2
                                                          tablespace utils )
  8  )
  9  /

Table created.

ops$tkyte@ORA817.US.ORACLE.COM> insert into t values (  1, 1 );
ops$tkyte@ORA817.US.ORACLE.COM> insert into t values (  1, 2 );
ops$tkyte@ORA817.US.ORACLE.COM> insert into t values ( 50, 1 );
ops$tkyte@ORA817.US.ORACLE.COM> insert into t values ( 50, 2 );

this shows each row went into a separate physical partition, when y = 1, it
went into partition pX_s2, when y = 2 it went into pX_s1:

ops$tkyte@ORA817.US.ORACLE.COM> select * from t subpartition(p1_s1);

         X          Y
---------- ----------
         1          2

ops$tkyte@ORA817.US.ORACLE.COM> select * from t subpartition(p1_s2);

         X          Y
---------- ----------
         1          1

ops$tkyte@ORA817.US.ORACLE.COM> select * from t subpartition(p2_s1);

         X          Y
---------- ----------
        50          2

ops$tkyte@ORA817.US.ORACLE.COM> select * from t subpartition(p2_s2);

         X          Y
---------- ----------
        50          1

so, lets knock out the data where y = 2, take it offline:

ops$tkyte@ORA817.US.ORACLE.COM> alter tablespace users offline;
Tablespace altered.

ops$tkyte@ORA817.US.ORACLE.COM> select * from t where y = 2;
select * from t where y = 2
              *
ERROR at line 1:
ORA-00376: file 7 cannot be read at this time
ORA-01110: data file 7: '/d02/oradata/ora817dev/users.dbf'

ops$tkyte@ORA817.US.ORACLE.COM> select * from t where y = 1;

         X          Y
---------- ----------
         1          1
        50          1

that shows that partition elimination is taking place even when only the sub
partition key is referenced....  We got the y=1's back, the 2's were not touched
or even considered

Now, lets wipe out another partition and see what happens

ops$tkyte@ORA817.US.ORACLE.COM> alter tablespace tqm_data offline;
Tablespace altered.

ops$tkyte@ORA817.US.ORACLE.COM> select * from t where x =  1 and y = 1;
select * from t where x =  1 and y = 1
              *
ERROR at line 1:
ORA-00376: file 22 cannot be read at this time
ORA-01110: data file 22: '/d01/oradata/ora817dev/tqm_data.dbf'

ops$tkyte@ORA817.US.ORACLE.COM> select * from t where x =  1 and y = 2;
select * from t where x =  1 and y = 2
              *
ERROR at line 1:
ORA-00376: file 7 cannot be read at this time
ORA-01110: data file 7: '/d02/oradata/ora817dev/users.dbf'

ops$tkyte@ORA817.US.ORACLE.COM> select * from t where x = 50 and y = 1;

         X          Y
---------- ----------
        50          1

ops$tkyte@ORA817.US.ORACLE.COM> select * from t where x = 50 and y = 2;
select * from t where x = 50 and y = 2
              *
ERROR at line 1:
ORA-00376: file 7 cannot be read at this time
ORA-01110: data file 7: '/d02/oradata/ora817dev/users.dbf'

 and that shows that partition elimination + subpartition elimination works --
there is only ONE partition avaialable for querying and we were able to use
it...

b) I/O stands for input/output.  I/O is a commonly used term for "physical disk
reads" today.  In database terms, it would be safe to assume that I/O is a
physical disk read/write (unless of course someone says "logical I/O", then its
a buffered read/write.

I assume you mean contention for "disk", not diskspace.  Contention is caused by
many people simultaneously trying to use a non-shareable resource.  If you have
10 disks in your system and only one of them is getting any reads/writes -- you
have introduced contention into your system by directing everyone to the same
disk.  You need to spread out the data on that disk onto the other disks so that
IO is spread out evenally.

Factors that contribute to IO -- you run a query.  Everything needs to do some
IO (logical or physical).  an undersized buffer cache can cause you to do more
physical IO then you really need to.  Committing too frequently can cause you to
wait on physical io more then you should.  there are hundreds of causes
(thousands perhaps)

10 applications running on a server might not make it slow.  100 applications
might not.  There are millions of reasons why concurrent executions of an
application might be slow however.  Assume the first line of each application
is:

select * from t where x = 1 for update;

They will run one at a time, each waiting for the other.  They could be trying
to use a scarce resource concurrently (eg: 10 cpu hungry applications on a
single cpu box).

followup to comments one and two below...

The subpartitions are based on an internal hash function.  We take the
subpartition key, apply a hash function to it, and put the data into the
partition it hashed to.

The goal of the hash function is to create an even distribution of data across
the subpartitions.  If I empty out table T and populate it with:

ops$tkyte@ORA817.US.ORACLE.COM> begin
  2          for i in 1..1000
  3          loop
  4                  insert into t values ( 1, i );
  5          end loop;
  6  end;
  7  /

PL/SQL procedure successfully completed.

I find that it distributes the data into the two subpartitions fairly evenly:

ops$tkyte@ORA817.US.ORACLE.COM> select count(y), min(y), max(y), avg(y) from t
subpartition(p1_s1);

  COUNT(Y)     MIN(Y)     MAX(Y)     AVG(Y)
---------- ---------- ---------- ----------
       495          2       1000 500.288889

ops$tkyte@ORA817.US.ORACLE.COM> select count(y), min(y), max(y), avg(y) from t
subpartition(p1_s2);

  COUNT(Y)     MIN(Y)     MAX(Y)     AVG(Y)
---------- ---------- ---------- ----------
       505          1        999 500.706931

It will hash the column even if its a character string using the hash function
as well.  For example, if we create table T with Y varchar2(1) and load it with
this:

ops$tkyte@ORA817.US.ORACLE.COM> begin
  2          for i in 1..26
  3          loop
  4                  insert into t values ( 1, chr(ascii('a')+ mod(i,26)) );
  5          end loop;
  6  end;
  7  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817.US.ORACLE.COM>
ops$tkyte@ORA817.US.ORACLE.COM> select count(y), min(y), max(y) from t
subpartition(p1_s1);

  COUNT(Y) M M
---------- - -
        11 c u

ops$tkyte@ORA817.US.ORACLE.COM> select count(y), min(y), max(y) from t
subpartition(p1_s2);

  COUNT(Y) M M
---------- - -
        15 a z

We can see that once again, it achieved a fairly good distribution putting
almost half of the values into each partition.

This also points out why you want to choose a subpartition key with many
different values.  Suppose Y only took on two values - 'a' and 'z'.  It would be
a very POOR choice for a hash key!  All of the data would hash to the same
subpartition.  A primary key is a very good hash key, something with a wide
range a values is a good hash key.  A column with very few values -- that would
be bad.

followup to comment three

I made the claim:

this shows each row went into a separate physical partition, when y = 1, it
went into partition pX_s2, when y = 2 it went into pX_s1:

ops$tkyte@ORA817.US.ORACLE.COM> select * from t subpartition(p1_s1);

         X          Y
---------- ----------
         1          2

ops$tkyte@ORA817.US.ORACLE.COM> select * from t subpartition(p1_s2);

         X          Y
---------- ----------
         1          1
......

which is proven by the selects that followed.  They selected the rows from each
discrete subpartition to show us what is in there.

In this example EACH row went into a separate physical subpartition as
demonstrated by showing the rows in each subpartition

followup to comments 4 and 5

The hash is based on the entire string -- not just the leading edge.  As Connor
points out -- the get_hash_value is using (currently) the same function as the
internal hash does.  That would be an excellent method of testing the
distribution of data before hand if you like

followup to comment 6

Sure, extending on the example above with the insertion of 'a','b',....'z' into
one of two partitions, we can use dbms_utility.get_hash_value to "forecast"
which partition the data will go into.

Here I call get_hash_value and send it three inputs.  The first is what to hash,
the second is the "base" of the hash table (start at 1 to get values 1, 2, 3, 4,
... ), the third is the size of the hash table (2 in my example -- 2
subpartitions to hash into).

This shows that 'a' goes into subpartition #2 which it did

ops$tkyte@ORA817.US.ORACLE.COM> select dbms_utility.get_hash_value('a',1,2) from
dual;

DBMS_UTILITY.GET_HASH_VALUE('A',1,2)
------------------------------------
                                   2

ops$tkyte@ORA817.US.ORACLE.COM> select dbms_utility.get_hash_value('c',1,2) from
dual;

DBMS_UTILITY.GET_HASH_VALUE('C',1,2)
------------------------------------
                                   1

ops$tkyte@ORA817.US.ORACLE.COM> select dbms_utility.get_hash_value('u',1,2) from
dual;

DBMS_UTILITY.GET_HASH_VALUE('U',1,2)
------------------------------------
                                   1

ops$tkyte@ORA817.US.ORACLE.COM> select dbms_utility.get_hash_value('z',1,2) from
dual;

DBMS_UTILITY.GET_HASH_VALUE('Z',1,2)
------------------------------------
                                   2
Review & Followup
Rating: 4
how is the data getting into sub partitons , by what cirteria August 04, 2001
Reviewer: play from USA
Tom

whenyou are doing
ops$tkyte@ORA817.US.ORACLE.COM> insert into t values (  1, 1 );
ops$tkyte@ORA817.US.ORACLE.COM> insert into t values (  1, 2 );

How are the values going into different sub partitons, why are they not going
into the same partition, what is the criteria for distribution.

what if I give a query which has a where clause like where
y in (1,2)

Rating: 5
DATA distribution in sub partitions August 04, 2001
Reviewer: joe from USA
if I have values 1 to 1000 for y column , how will the data get distributed
among different sub partitions.

what decides that which values should go into which sub partition.

what if y is a varchar2 column , and has values from a to z.
how will the data get distributed in the subpartitions

Rating: 3
how are you so sure about this? August 05, 2001
Reviewer: A reader
"
this shows each row went into a separate physical partition, when y = 1, it
went into partition pX_s2, when y = 2 it went into pX_s1:

"

Rating: 3
interesting followup August 05, 2001
Reviewer: A reader
"
A column with very few values -- that would
be bad.
"

well, what if my sub partition key is a varchar2 columns, and there are a lot of
values,  but most of them start with similar prefixes

like

aert, tgbf, epju, bftc

how will the data be distributed in this case

Rating: 4
Response to "how to see what things hash to" August 06, 2001
Reviewer: Connor from UK
The function DBMS_UTILITY.GET_HASH_VALUE appears to use the same algorithm.  So
if you have 4 subpartitions, you could call this with your candidate values and
a hash size of 4 and see what they hash to.

Rating: 5
can you show us how to test August 06, 2001
Reviewer: Mike from CA
"The hash is based on the entire string -- not just the leading edge.  As Connor
points out -- the get_hash_value is using (currently) the same function as the
internal hash does.  That would be an excellent method of testing the
distribution of data before hand if you like"

can you show us an example of testing the distribution of data before hand

Rating: 5
Great explaination August 08, 2001
Reviewer: Sunil from Bangalore
The explaination was really great. The answers just eased into my brain's
storage partition.

Rating: 4
How do use dbms_utility.get_hash_value on a multi column key September 11, 2002
Reviewer: Rob from Pittsburgh, PA
Tom:

In your example you show how to get the hash bucket for a single value:

ops$tkyte@ORA817.US.ORACLE.COM> select dbms_utility.get_hash_value('a',1,2) from

dual;

Let say that my hash partition key was made up of 3 columns, a varchar2 column,
a number column, and a date column. How could I use dbms_utility.get_hash_value
to see in advance the distribution of data. Would you to_char the number and
date columns and concatenate that to the varchar2 column and pass that
concatenation as the first arg to the get_hash_value function? Could you provide
an example.

Rob

Followup:
I don't know that you can -- I don't know what they do to the hash key to
compute the partition it hashed into.

Rather then guess, I'll just say "don't know and shouldn't matter".  It was
interesting for this example to be able to predict, but in real life, it isn't
really relevant or controllable by you.

Your goal is to get to a hash key that is as unique as you can to get the best
spread.

Rating: 5
Imagine my surprise April 30, 2003
Reviewer: A reader
When reading Metalink note 166118.1, I found at the end:

"See
"
http://asktom.oracle.com/pls/ask/f?p=4950:8:267626::NO::F4950_P8_DISPLAYID,
F4950_P8_CRITERIA:1112305237628,%7Bsubpartition%7D%20and%20%7Belimination%7
D" for an example and discussion of subpartition elimination. "

Of course, their link is not formatted correctly, but, hey...

Rating: 5
Excellent information. Thanks. June 26, 2003
Reviewer: Scott Wesley from Perth, WA

September 12, 2002 Alan -- Thanks for the question regarding "Performance of variable substitution in Stored Procs ", version 8.0.5 & 8.1.7
You Asked
Hello

The following problem has me *very* confused.

If I run the following update statement, it takes no more than 15msecs to
process.

    UPDATE stationapproval sa
       SET sa.recordstatus = 2
       WHERE sa.headerid = 11223
       AND sa.stationid = 1810;

However, if I run the same statement inside a stored procedure (or just as a
procedure), it takes 4 seconds!  If I substitute the variables for hardcoded
values in the procedure, it again takes less than a second.

    declare
        headerId header.id%TYPE := 11223;
        pStationId stationtostation.childstationid%TYPE := 1810;
    begin
        dbms_output.put_line('Started: '||to_char(sysdate, 'HH:MI:SS'));

        UPDATE stationapproval sa
           SET sa.recordstatus = 2
         WHERE sa.headerid = headerId
           AND sa.stationid = pStationId;

        dbms_output.put_line('Ended: '||to_char(sysdate, 'HH:MI:SS'));

    end;

output:

    Started: 11:08:54
    Ended: 11:08:58

Why does the use of variables cause such a decrease in performance?

Cheers

Alan
and we said...
and what is the performance of:

update stationapproval sa
   SET sa.recordstatus = 2
 WHERE sa.headerid = headerid
   AND sa.stationid = 1810;

in sqlplus???

Look at your headerid, you are just saying "where column = column", you have NOT
parameterized that part.

You are comparing an apple to a pizza here.

I recommend everyone use a naming convention for variables to avoid confusion
between a PLSQL variable and a database identifier.

l_variable - for all LOCAL variables (eg: you would use l_headerId)
p_variable - for all PARAMETERS to procedures/functions
g_variable - for all PACKAGE GLOBAL VARIABLES
Review & Followup
Rating: 4
D'oh! September 12, 2002
Reviewer: Alan Tibbetts from London
Its one of those blindingly obvious things again!  I'm more of a Java programmer
than a PL/SQL guy, therefore I was assuming 'headerId' and 'headerid' to be
different.  Time to adopt those naming conventions methinks...

Rating: 3
For Alan September 12, 2002
Reviewer: Paul from UK
inclusion of the the following line in your code might have given you a clue as
to what was going on...

dbms_output.put_line(SQL%ROWCOUNT||' rows updated.');

September 12, 2002 seema -- Thanks for the question regarding "Query Performance (count(*) vs count(some column) rehashed yet again)", version 8.x
You Asked
Hi Tom..

Greetings !
Please make me clear which query runs fast.

1) select count(*) from T;
2) select count(rowid) from T;

Does it really matters, using rowid instead of '*' ?
please clarify if yes.

regards

Seema
and we said...
count(*) is not only more proper, more meaningful, more correct -- it'll be at
least AS if not MORE efficient then count( any column here ).

Use *, I wish I could figure out why people shy away from '*' -- it is the right
thing to use when you wish to count rows.  That is what it does!

count(rowid) means "count the number of non-null rowids".
count(*) just means "count rows, really fast, as fast as you can"
count(1) is just as bad logically and all as count(rowid)

count(*) is correct, proper, better, more meaningful, etc etc etc...

----------------------------------------------------------
select count(*)
from
 big_table

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.24       0.24          0       2102          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.24       0.24          0       2102          0           1

Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 64

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE
1000000   INDEX FAST FULL SCAN BIG_TABLE_PK (object id 30637)

********************************************************************************

select count(rowid)
from
 big_table

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.34       0.34          0       2102          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.34       0.34          0       2102          0           1

Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 64

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE
1000000   INDEX FAST FULL SCAN OBJ#(30637) (object id 30637)
September 12, 2002 Munzer -- Thanks for the question regarding "Creating Foreign key from one database to another", version 8i
You Asked
Tom:

I have designed an oracle database in 8i.  One table has an organization code
that should be a foreign key to antoerh dtabase that lists all organization
codes.

The question is can i create my table by specifying orgcode as a foreign key to
another table in a different database?

Do i have to create a databaselink?  If yes, what happens if the otehr database
or the link is down?

Thank you,

Munzer
and we said...
No, you cannot do RI over a database link.  It would utterly tie the two tables
together -- indicating that they need to be in the SAME database (eg: if one
database is down -- the OTHER cannot be modified.  The parent cannot be modified
if the child is down and the child cannot be modified if the parent is down).

Tables that are so dependent on eachother must be in the same database.
Review & Followup
Rating: 4
i have a situation September 12, 2002
Reviewer: A reader
I have a situation.
In database A,there is

Table1
col1 primarykey
col2...etc

Table2
col1 primarykey
col4..etc

Table3
col1 primarykey
col5...etc

Table1 has foreignkey relation with table2 - col1.
Table3 has foreignkey relation with table2 - col1.

Now Table2 is a redundant table in database A. The same information can be
queried from database B thru a link from A.(I can make a view with the same name
as table2 [with link to B] and just recompile my packages..so that is not much
of work [even column names are same in B]).
But the integrity relationshps already exist in A.
I cant have relationships defined thru links...and i need to get rid of
Table2...is there a way?

Also, while coding using links in procedures..what kind of exceptions do we have
to specifically handle (specially for db links)...is there any special condition
to be handled for links?..or the normal exception handlers are sufficient.?
In someof my procedures i am selecting data from thru a link.Ex..insert into
A.schema.table (select col from remotetable@linktoB).

In another procudure i am inserting/updating data into remote database a
link(another different link to a diff database...not the above condition).

How do i make sure that all exceptions are handled (if any special ones are to
be handled other than the normal ones like when nodatafound, toomanyrows etc
etc..)

Followup:
why do you "need" to get rid of table2?

You should ONLY have exception handlers for those errors you expect and can
"recover" from.  Since you never expect an error and you cannot really recover
from them -- it is best to not have one -- as the only thing that could happen
if you do is that you silently IGNORE the error.

September 12, 2002 Saradha -- Thanks for the question regarding "privilege issue", version 8.1.7
You Asked
Tom,

I have a stored procedure created in my schema and granted
EXECUTE privilege to some other users in the database.
There is one user to whom I did not give the privilege,
is able to execute my procedure because he has EXECUTE ANY PROCEDURE
privilege.Is there any other way I can prevent him executing my procedure.I
definitely can not ask the dba to revoke EXECUTE ANY PROCEDURE privlege from
him.

Thanks
Saradha
and we said...
Add:

  if ( user = 'That Username' )
  then
     raise_application_error( -20001, 'Nah Nah na na Nah, you cannot run me' );
  end if;

to the beginning of the procedure....

Hope the DBA knows that with

o create session
o EXECUTE ANY PROCEDURE

he has given that user the KEYS TO THE KINGDOM (give me those two privs and I'll
kidnap your entire database, doing ANYTHING I like as ANY user I like and you'll
not be able to stop me)....

That little IF will work until this user decides to read your code out, see
whats stopping him, re-write it and put it back over your stuff -- which he can
do with those two privs....
Review & Followup
Rating: 5
Thank you very much September 11, 2002
Reviewer: Saradha Bavanandam

Rating: 2
Please elaborate September 12, 2002
Reviewer: Robert from TN, USA
Tom,

Can you please elaborate on how those 2 privs allow someone to kidnap the
database.
Is the meaning that there are certain powerful procedures owned by SYS, etc.
that could be run to wreak havoc?

Thanks,

Robert

Followup:
yup.  hammer, nail, hitting on the head.

Rating: 5
Not Again! September 12, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
We've been through this before...  See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:862779019442
for more details (and why I'm glad
it didn't get fully published)...

- Mark

September 14, 2002 Chella -- Thanks for the question regarding "Identifying the data file", version 8i
You Asked
I am receiving data from an external source on a daily basis and then I have to
upload the data into Oracle table. I can do this via a Control file but the
problem is the INFILE name is not fixed and hence I am unable to specify INFILE
'filename'. The only way I can identify the filename is by looking at the date
in the Windows Explorer. Is there a way to achieve what I want to do ? TIA
and we said...
You can use the DATA= command line parameter instead of INFILE= and if the file
follows some naming convention, you could use the command:

for %f in ( *.* ) do sqlldr userid=user/pw control=control.ctl data=%f

to load it (where *.* of course is a wildcard that id's your specific file).

followup to comment one

Sorry it was not clear.

Instead of

LOAD DATA
INFILE 'c:\bsp\stmt.txt'
APPEND
INTO TABLE tblMailboxData
....

You will have

LOAD DATA
APPEND
INTO TABLE tblMailboxData
....

(no infile statment).  Instead, you will use DATA= on the command line to
sqlldr:

sqlldr userid=x/y control=c.ctl DATA=c:\bsp\stmt.txt

Now, if your filename is always CC*.001 and you want to load ALL of the CC*.001
files in a directory, the Windows command:

for %f in ( CC*.001 ) do sqlldr userid=user/pw control=control.ctl data=%f

will do that for you.
Review & Followup
Rating: 3
Identifying Data file July 10, 2001
Reviewer: Chella from London, United Kingdom
The answer was helpful however, I am performing the upload using a control file
which looks like;

LOAD DATA
INFILE 'c:\bsp\stmt.txt'
APPEND
INTO TABLE tblMailboxData
(
RecID           SEQUENCE(MAX,1),        -- To create a unique row id. Is this
the correct syntax ?
CustSortCode    POSITION(1:6),
CustAcNumber    POSITION(7:14),
TransCode       POSITION(16:17),
RemSortCode     POSITION(18:23)
)

My data file name starts with 'cc', file extension is '001'. How do I replace
the DATA= with INFILE ? Could you give me the correct syntax or point me to some
materials ? Ta

Rating: 4
Identifying Data file September 13, 2002
Reviewer: Oliver DommackThoms from Germany
Good Example, it even helps on those 'batch' Jobs where you don't know what
files you get maybe via ftp.

I'm having an problem that's connected:
My files are deleted after insertion. In order to trace back where the data came
from, I need the filename stored in the db as well!

Currently I'm woring with the .ctl as a variable in a Shell Skript which
replaces the filename for the INFILE statement and as a CONSTANT for the
db-input. That does solve my problem but is quite unaesthetic. ;-))
Is there a System/SQL Loader/Oracle variable where I can get the filename from
if the Skript would instead simply execute a static .ctl with the DATA=
parameter????

Followup:
The shell replacement is the way to accomplish it.  write the control file based
on the filename and run it.  Unaesthetic or not, its the way to get it done.

September 14, 2002 Sagi -- Thanks for the question regarding "Few Questions", version 8i
You Asked
Hi Tom !

I have got a few doubts and hope you would not mind in answering them.

1) Assuming that there was some major problem and i lost all my backup
(Datafiles, Archive Logs etc)taken to disks/tapes. Once the backup are taken on
tape the archive log files are deleted physically from the file system.

My point is I have lost my tapes (could be overwritten,broken,fire etc) but
still my database is intact. In order to start the new backups i wanted to reset
the logs so that i can start the backups freshly. I issue:

SHUTDOWN IMMEDIATE
STARTUP MOUNT
RECOVER DATABASE UNTIL CANCEL
ALTER DATABASE OPEN RESETLOGS

At this point my archive logs get reset and now i start making backups again
(Datafile, ARchive Logs etc).

Considering the above senario, would this be the correct suggestion, if not or
if there is any other solution can you provide it.

2) I have created the dictionary file dump using DBMS_LOGMNR_D.Build. After this
I have created new tables and droped them (jsut for testing). Now if I run the
Log Miner and issue the query

  SELECT SQL_UNDO from V$LOGMNR_CONTENTS
  WHERE SQL_UNDO like '%<tbl>%

Note: <tbl> is the new table that has been created and droped after creating the
dictionary dump.

I still see to my surprise that the the name the object name i.e my new table
name.

I think we create the dump in order to see the object's name instead of their
id.Right? If so in this case i have created the table after the dictionary dump.
So how come still i can see the object name. Is this information taken from the
archive log file i use in logminer? If yes then what is the necessaty of
creating the dictionary file.

3) I have two machines. A and B. I have oracle (8i) installed on machine A. I
have a lot of disk space on B. So I have mapped (for convient let us say the map
drive is Z:) and now want to create a new tablespace. But the datafile should be
on 'Z' drive. Is this  possible?

Using Windows-NT/2000 environment.

4) Can you give the difference between Standby DB and Dataguard (DG). (In Oracle
8i). I am confussed. I know that DG is on top of Standby DB. But what is the
advantage.

5) begin
   for I to Infinite
   loop
      dbms_output.put_line ('Thanx tom');
   end loop ;
   end ;

Thanx a lot.

Regards,
Sagi
and we said...
1) you need not do resetlogs, you just need to backup is all.  You don't need
the "old" stuff that you lost, once you backup the "new stuff"

2)  did you run this "test" more then once, if so, you are seeing old sql-undo
perhaps?

3) No, No, NO -- network drives are totally, wholly and very unreliable.  If
there is one thing a database will NOT tolerate is unreliable disks.

4) Dataguard adds many features such as

o data guard monitor (to watch the environment)
o OEM tools (to manage it)
o graceful switchover and switchback (reverse roles)

and in 9i

o more and better ways to keep the system in sync.  Instead of ARCH doing the
work (meaning you are always a redo behind), LGWR does it in real or near real
time ensuring 0% loss of data.

o 9iR2 data guard adds a logical standby which can be open for reporting
continously while being maintained with updates from the production system as
well.

5)

ERROR at line 1:
ORA-20000: ORU-10027: buffer overflow, limit of 1000000 bytes
ORA-06512: at "SYS.DBMS_OUTPUT", line 91
ORA-06512: at "SYS.DBMS_OUTPUT", line 58
ORA-06512: at line 4

;)
Review & Followup
Rating: 4
September 12, 2002
Reviewer: Sagi
Thanx for your time tom.

The 5th Question was ment to say what me and others feel for you.

Since it has your name in the DBMS_OUTPUT.PUT_LINE it would not give error.
Kidding. Just thought to thank you in a different way for the work you are
doing.

Regards,

Rating: 4
just curious to know September 12, 2002
Reviewer: mathew from India
hi,
  Your answer to question 3 was that a network drive should never be used for a
data file. Is it possible to have a datafile over the network? and if a reliable
network drive was avaliable what risk is still involved?

thanks,
mathew

Followup:
We support various SAN's and NAS arrays (storage area networks, network attached
storage).

But a simple windows share -- no.  A unix nfs mounted file system -- no.

Rating: 4
September 12, 2002
Reviewer: Sagi from India
Thanx tom.

Rating: 5
just curious to know... September 12, 2002
Reviewer: Senthil from Boston, MA
Tom-

for your reply #3 and followup to Mr.Mathew, is it not possible to create
tablespace on shared resources(like windows share or nfs mounted) or not
recommended. i understand your comment on reliability of using shared device.

thanks much.

Followup:
Anything is "possible".

Supported -- no.  Suggested -- no.  Bad idea -- most likely.  Sure way to slow
down your database -- absolutely.  (temp is somewhat IO intensive, why use the
sloooowwwweessst devices for that?)

Rating: 5
that clears...thanks September 12, 2002
Reviewer: Senthil

Rating: 3
September 13, 2002
Reviewer: Sagi from India
For Question #3. Can you explain a bit more.

Windows network drive can not be used makes some meaning. But why cant we use
unix NFS?

How do we achive this then?

Would be helpful to many of us if you could give an example as to how we can
achive this.

Also can you give some link for SANs?

Thanx in advance.

Regards,

Followup:
You cannot use Unix NFS for the same reasons as a Windows share -- they are
effectively the same technology at the end of the day.

You DO NOT USE NFS mounted disks for the database.  You just don't.  Not unless
they are part of a certified configuration like a Netapp fileserver.  Good old
fashioned NFS, that'll not work.

see
http://otn.oracle.com/deploy/availability/htdocs/oscp.html
for a list of technology vendors we work with.

I would google

SAN "storage area network"

to find more info on that.

Rating: 5
Question #5 - finally got you September 13, 2002
Reviewer: John from ma
My first thought was 'for I to ' would fail

After a thorough test

   for I to Infinite
         *
ERROR at line 2:
ORA-06550: line 2, column 10:
PLS-00103: Encountered the symbol "TO" when expecting one of the following:
in
The symbol "in was inserted before "TO" to continue.

I prove my case, o great one!!!

:)

September 14, 2002 Venkat -- Thanks for the question regarding "Logminer Doubt", version 8.1.7
You Asked
Hi Tom,
When I was going through Logminer, I got some doubts, could
you please clarify me.

It has given me both Undo and Redo SQL's. when I perform
delete operation, I could able to see a insert.

When I did a insert record, logminer was showing me delete
of that record with all the columns in the where class.

Ex : insert into dept values (60,'XYZ','ABC');

Redo:
=====
insert into dept values (60,'XYZ','ABC');

undo:
=====
delete from dept where deptno=10 and deptname='XYZ' and
location='ABC' and rowid='AAAHSnAAIAAAAAeAAD';

But what I expected is just a rowid in the where condition.
In books I have read Insert will generates least amount of redo,
but I don't see any difference here. Then how it is
really working inside? Is it just showing like that to us like this
& internally it stores rowid?

Regards,
Venkat
and we said...
The delete is being "cautious".  What if -- you inserted that 60, 'XYZ', 'ABC'
value and someone came along and modified it?

A rowid delete would blindly delete their modified data - not the data you
inserted, the data they updated.

So, logminer used the rowid to conclusively find your row and then used the
column values to make sure the row hadn't been modified.

I don't get the point about "redo" here.  You must understand that in order to
REDO the insert -- we have to store the "after" image of the row?  The values of
the row must be in REDO in order to REDO the insert.
Review & Followup
Rating: 2
Error September 13, 2002
Reviewer: Vikas Khanna from INDIA
I was working on Logminer and testing it out. When I tried to create the
dictionary file by specifying
EXEC DBMS_LOGMNR_D.BUILD('ORCLDICT.ORA','D:\ORANT81\DATABASE');

and utl_file_dir = 'D:\ORANT81\DATABASE' in the inot.ora file but it gives me an
error message

BEGIN DBMS_LOGMNR_D.BUILD('ORCLDICT.ORA','D:\ORANT81\DATABASE'); END;

*
ERROR at line 1:
ORA-06532: Subscript outside of limit
ORA-06512: at "SYS.DBMS_LOGMNR_D", line 793
ORA-06512: at line 1

What could be the cause of this problem. Moreover the file exists there at the
following path.

Please suggest what needs to be done.

Followup:
search for

"Subscript outside of limit"

on this site.

September 14, 2002 jian -- Thanks for the question regarding "Solaris direct i/o", version 8.1.7
You Asked
Tom,

We are running Oracle database (mostly 8.1.7) on Solaris 2.6 and 2.8. We are
interested use of Solaris UFS direct i/o feature. You mentioned at NYOUG meeting
that you have been using the feature.

Do you see a significant performance benefit? Do you see or encounter any issue
using the feature? Is there any paper on the benchmark running Oracle on Solaris
using direct i/o.

Many thanks,

Jian
and we said...
I mount my UFS's like this:

/dev/vx/dsk/rootdg/vol01   /dev/vx/rdsk/rootdg/vol01  /d01  ufs 3   yes
forcedirectio
/dev/vx/dsk/rootdg/vol03   /dev/vx/rdsk/rootdg/vol03  /log1  ufs        3   yes
 forcedirectio
/dev/vx/dsk/rootdg/vol02   /dev/vx/rdsk/rootdg/vol02  /d02  ufs 3   yes
forcedirectio
/dev/vx/dsk/rootdg/vol07   /dev/vx/rdsk/rootdg/vol07  /d03  ufs 3   yes
forcedirectio
/dev/vx/dsk/rootdg/vol05   /dev/vx/rdsk/rootdg/vol05  /log2  ufs 3   yes
forcedirectio
/dev/vx/dsk/rootdg/vol06   /dev/vx/rdsk/rootdg/vol06  /arch1  ufs       3   yes
 forcedirectio
/dev/dsk/c0t0d0s7       /dev/rdsk/c0t0d0s7      /arch2   ufs    3   yes
forcedirectio

(foredirectio is on the same line)

This is an extract from support note <Note:156419.1>

...
File System or Raw Device?
==========================
The next choice is whether to use raw disk or file systems for the Oracle data.
Many database and system administrators shy away from using raw disk because of
its perceived additional complexity. However, raw disk offers the highest
performance when used with a tuned SGA, as raw disk has the shortest kernel code
path. If the use of file systems is preferred, then UFS mounted globally with
the
forcedirectio option offers the next-best performance. The forcedirectio
option impacts any other application whose data is collocated on the same file
system, as none of the data will be cached in the UNIX page cache.
Using multiple file systems not only allows flexibility in the use of directio,
but may also speed up fail-over time, because the Sun Cluster 3.0 HA
infrastructure can recover underlying kernel structures for file systems in
parallel. The only drawback to this approach might be that of ongoing space
management.
.......................

So, forcedirectio is almost as good as raw (i saw a measurable, significant
decrease in log related waits -- made writing much faster)

I see no documented issues with regards to this feature anywhere, only
recommendations to use it.
Review & Followup
Rating: 4
forcedirectio equivalent on windows NT ?? June 06, 2002
Reviewer: raj
Hi Tom,
It was nice meeting you at NYOUG conference yesterday.
Is there any equivalent of forcedirectio on windows NT? If so, what is it
called.
Thanks again.

Followup:
There is no need on windows -- we bypass the buffer cache already:

Extract from note:
Article-ID:         <Note:114421.1>

3.0  Advantages and Drawbacks of Raw Partitions Compared to File Systems
------------------------------------------------------------------------

3.1  Advantages
---------------
  a)  The absence of a filesystem eliminates all storage overhead that
      comes along with a filesystem.
  b)  No fragmentation on filesystem-level:  disk fragmentation is typical
      for filesystems (NTFS & FAT).
  c)  On most platforms I/O against a file system (cache) creates a rather
      large bottleneck.
  (*) Oracle on NT bypasses the filesystem buffer cache completely by
      using the FILE_FLAG_NO_BUFFERING option on opening files with
      CreateFile().  Oracle also uses the FILE_FLAG_WRITE_THROUGH option.
      So there is no real caching difference between raw I/O and NTFS I/O.

Because of this implementation (b & c), the performance of Oracle on NT is
rather small (5% to 10%) and is mainly due to the absence of fragmentation (b).

3.2 Drawbacks
-------------
  a)  Manageability & security:  when there is no filesystem, we lose all the
      manipulation possibilities offered by a filesystem:  no space allocation
      control, no good security, difficult to maintain.
  b)  Only 1 file per Raw Partition is possible.

Rating: 5
how about backup June 06, 2002
Reviewer: Fan from NJ, USA
How is the performance of calling unix utility inside Oracle? like:
alter tablespace system begin backup;
!cp '/.../system01.dbf' 'backup_dir'
alter tablespace system end backup;

Followup:
That would be within SQLPlus -- that'll be fine EXCEPT for the fact you have no
real error handling capabilities (did that file really get copied?  you'll have
no real idea)

I think a script might be better here.  (well, RMAN actually would be better)

Rating: 3
solaris direct i/o September 13, 2002
Reviewer: paul from orlando, fl
hi tom. this article spoke about direct i/o with UFS filesystems. however, we
have veritas filesystems on solaris 8. we do *not* have veritas quick i/o.

so i am wondering whether oracle can use direct i/o with veritas filesystems? i
read somewhere that there was an api that allows programs to set their i/o into
direct i/o mode, and thought oracle might use that method.

the article also doesn't say how oracle works with filesystems (ufs and veritas)
on solaris if direct i/o is not used. i assume it uses the virtual memory i/o
capability.

Followup:
Well, the question really is "can veritas do directio".  Oracle has nothing to
do with it really.  Not sure as I do not admin veritas myself (not the SA type)

September 14, 2002 Al -- Thanks for the question regarding "PL/SQL cursor and update of same table", version 8.0.5
You Asked
I want to pass through a table with a cursor and update a field on almost every
row in that table with sophisticated logic.  I've read that if I have an open
cursor 'for update' and I attempt a second update (following a commit) it will
fail with an ORA-01002.  How can I avoid this error and at the same time avoid
overflow of my rollback segment (the table to be updated is quite large)?
and we said...
You NEED (eg: must, have to, really need to) size your rollback to be big enough
for the jobs you perform on your system.

Rollback is not a precious resource to be conserved.  It is a crucial piece of
the database.

You know -- a 36gig SCSI drive costs about $200-$250 USD.  In the time you spend
worrying about this, you probably have spent more money then that.  Point being:
 size rollback PROPERLY for what you need to do and the system will take care of
itself.

Me, I never commit in a cursor for loop -- I've written long chapters in books
describing why

o this is bad (data integrity goes OUT THE WINDOW)
o it is not necessary
o it runs *slower* then not committing

Sorry, you are not going to get the answer from me that you want because the
only way to do this correctly is to size your system for what you do.

Else, you'll spend tons of time writing complex logic so that you can pick off
slices of the table and update them at a time.  The money spent on your time
will far exceed that of driving to a store and picking up a new disk.
Review & Followup
Rating: 1
Disk is cheap? September 11, 2002
Reviewer: CJ from Albany, NY USA
You obviously do not have to deal with EMC disk administrators who hoard disk
like it was gold, complain that their cabinets are full, and it costs $$$$$ to
buy another cabinet to hold more disk.

Followup:
Compare your salary and time to the cost of hardware (and yes, yes I do -- all
of the time, constantly, continously).

Rating: 4
Big RBS... September 11, 2002
Reviewer: Robert C from PA
Tom, I don't have your book with me now, but did you show how to get RBS info to
find a big RBS ? if not, would you please show me here ?

Also I tried "set autotrace traceonly explain" on UPDATE statement, unlike
SELECT, the UPDATE was carried out, I had to rollback. This is not the correct
way to analyze the UPDATE, is it ? how would you do it and avoid the execution ?
(well other than ...where 1 = 0)

Thanks!

Followup:
see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:233214228495
or just realize that RBS should not wrap during the period of time you expect
your longest running query to execute.  Say that is 20 minutes, you can just
monitor v$rollstat during 30 minutes of busy time and see if they do wrap. If
they do -- too small, make them bigger (and how much bigger should be easy to
figure out given the number of times they wrap in 30minutes and their current
size)

the traceonly explain only skips the FETCH with SELECTS, for all other
statements, away it goes (it executes the statements).

You'll have to use the "old fashioned way" which is to EXPLAIN PLAN the
statement if you just want the query plan to be displayed for the update.

Rating: 4
Rollback segments not extending September 12, 2002
Reviewer: Sree from MA USA
Tom, In a production database during a batch job I observered that the rollback
segments are not automatically extending even though there is freespace availabe
in the tablespace. Instead they are wrapping around lot of times (~40wraps) and
often jobs fail with ORA-01555 error.
I have 4 rollback segments of 400M each (initial 10M, next 10M, extents 40,
optimal 400M) in 2GB RBS tablespace. They were extending well when the size was
20M.
I wish to understand:
what is stopping them from extending beyond 400M?
What does 40 wraps indicate to me in resizing the rollbacks?

Thank you.

Followup:
RBS ONLY extends due to an insert/update or delete causing them to.

A SELECT will never cause it to extend.  A select reads it, it does not extend
it.

This is one of the major advantages in 9i with UNDO tablespaces.  You tell us
how LONG to retain undo, and we'll grow to hold that much.

Nothing is stopping them from extending, it is just that you are not doing
anything to make them extend.  What you need to do is permanently allocate them
LARGER (set minextents higher)

Rating: 4
ORA-01555 : Another Possibility ? September 14, 2002
Reviewer: Robert C from PA
Tom, just reviewed (again) the ORA-01555 part in your book...you gave 3 reasons:
#1 RBS too small
#2 Fetch across commit
#3 Block cleanout

In Case#1, the reader - the report guy got hit with ora-01555 because of the
writer's actions.

In Case#2, the writer shot himself in the foot...

What I am wondering is :
Other than reason#2, if you are the ONLY user modifying data on the system, is
it at all possible for you to get ORA-01555, due to your OWN action, say in a
very tiny RBS ?

Thanks

Followup:
theoretically -- yes, cause the background processes are always there and they
can be doing transactions as well -- so, if you take long enough, #3 can happen
due to smon commiting lots, snpN running jobs and so on.

If you don't commit and you are the only one there -- it would take a very long
time tho (so practically, I don't think so)

September 14, 2002 juan -- Thanks for the question regarding "Materialized views vs. Trigger", version 9.2
You Asked
Hi Tom.

Some question please.

How consistent are the materialized views?

For example

Table Aaa
Materialized View mmm, a materialized views with an ON COMMIT UPDATE.

Every time I update and commit Aaa, mmm updates.

1) The update to the materialized view when I change table Aaa and commit, is
saved in the redo log?
2) There is a flag that clearly identifies that the materialized view is not up
to date with the table, if there some problem updating?

I don´t have clear the difference between materialized views and trigger.

3) I suppose materialized views are more optimal than triggers that could do the
same through an update, why is more optimal?
4) When is not advisable to use a mateiralized views and use instead a trigger
to update a summary table.

Thank you Tom.
and we said...
1) of course

2) example please

(a trigger is a piece of code that is executed when you insert/update/delete
that you write and does whatever you want.  A materialized view, well, it is not
a trigger at all -- it is a materialized view?)

3) umm, amount of code you have to write (none vs lots).

It'll also serialize lots less.  Try this:  take the EMP table.  Now, create a
materialized view that maintains DEPTNO, COUNT(*).  Try that with a trigger.

The MV will serialize by DEPTNO when people commit.

Now, try it with a trigger -- the trigger will udpate your "mv" after the first
update to an emp row (locking it).  Anyone else that tries to update an emp in
that same dept will block right then and wait for you to commit.

MV's are more "concurrent"

4) I would use an MV unless I found I could not (and you'll probably find that a
trigger cannot at that point as well!!!)
Review & Followup
Rating: 5
September 16, 2002
Reviewer: A reader
Thank you Tom

Rating: 5
September 16, 2002
Reviewer: J Reyes
2) There is a flag that clearly identifies that the materialized view is not up
to date with the table, if there some problem updating?

I suppose there is only one way
For example

if  Table Tab1 is in tablespace tbs1
the materialized view is in tbs2
You update table 1 and tablespace 2 is offline.

or the remote database where is the MV is not accesible.

or if the MV is not commit on update, and in a procedure
I need to be sure that this MV is updated before I run a process.

Please

September 15, 2002 Alvaro -- Thanks for the question regarding "dinamic Order by for cursors", version 7.3.4.0.0
You Asked
Hi Tom,

Actually I have two questions ...

1. I Want to order a cursor dinamically , but not using dinamic sql.

e.g  in developer I want to have :

Procedure Order_proc(P_order in char) Is

 Cursor My_cursor
 Select ename,deptno,hiredate,salary,mgr
 From   emp
 Order  By p_order  -- I need this clause for ordering my query

-- Depending on p_order I could order for all fields ...
Begin
  ...
End;

Is there any way to do it ???

2.  I have to design a program that must take a text like a letter and replace
some "text variables" for their real values.

The text would be like this :

Mr &Name

 We are sending you our phone numbers (&phone1,&phone2) and the address where
you can visit us &address ...

Now , every &something represent a variable and I have to search its value in
the database. But, I don´t want to write a procedure for each one, I want to
write only one procedure that must be able to figure out every value.

Every variable has parameters that I need to issue a query for getting them (I
know the variable's parameters).

I've tried using dbms_sql ...

I have a table where I store the variable name (&Name), the query needed (text),
 and the parameters (binds) needed for getting it from the database.

The problem is defining every parameters and binds using dbms_sql

If one variable has the query (&employe_name):

'select ename from emp where deptno = :dep and mgr = :manager'

I would have to define both binds :dep and :manager with two code lines ...

Could I have only one instruction for defining all parameters in my queries????

for x in (select param_name,param_source from param_query
          where  var_name='&employee_name') Loop
dbms ...
dbms_sql.bind_variable(source_cursor,x.param_name,param_source);
dbms ...
End loop

instead of have ...
dbms ...
dbms_sql.bind_variable(source_cursor,':dep',v_dept);
dbms_sql.bind_variable(source_cursor,':mgr',v_manager);
dbms

I don´t know if I've been clear enough ...

I feel I want to have something dinamically, but this is already "dinamic"

Thanks for your help
and we said...
1) well, dynamic sql would be best of course in this case (the optimizer would
be able to come up with a unique, best plan for everything).  You can use DECODE
however care must be taken to convert everything to a string.  You need to do
this cause the output of a decode with a string, date and number will be a
string.  Therefore, you need to use a date format that is sortable as a string
and a number trick that lets them sort.  Here is an example:

ops$tkyte@ORA8I.WORLD> create table t ( x number, y varchar2(5), z date );

Table created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> insert into t values ( 1, 'aaa', sysdate );

1 row created.

ops$tkyte@ORA8I.WORLD> insert into t values ( 2, 'aaa', sysdate );

1 row created.

ops$tkyte@ORA8I.WORLD> insert into t values ( 202.234, 'aaa', sysdate );

1 row created.

ops$tkyte@ORA8I.WORLD> insert into t values ( 301.999, 'aaa', sysdate );

1 row created.

ops$tkyte@ORA8I.WORLD> insert into t values ( 301.998, 'aaa', sysdate );

1 row created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> insert into t values ( -1, 'bbb', sysdate+5 );

1 row created.

ops$tkyte@ORA8I.WORLD> insert into t values ( -2, 'bbb', sysdate+5 );

1 row created.

ops$tkyte@ORA8I.WORLD> insert into t values ( -202.234, 'bbb', sysdate );

1 row created.

ops$tkyte@ORA8I.WORLD> insert into t values ( -301.999, 'bbb', sysdate );

1 row created.

ops$tkyte@ORA8I.WORLD> insert into t values ( -301.998, 'bbb', sysdate );

1 row created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> insert into t values ( 0, 'ccc', sysdate-5 );

1 row created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> variable order_col varchar2(25)
ops$tkyte@ORA8I.WORLD> exec :order_col := 'x'

PL/SQL procedure successfully completed.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> select *
  2   from t
  3  order by decode( upper(:order_col),
  4           'X', decode(sign(x),
  5                    -1,chr(0) || translate( to_char(abs(x),
'000000000999.999'), '0123456789', '9876543210'),
  6                    to_char(x, '000000000999.999' ) ),
  7           'Y', y,
  8           'Z', to_char(z,'yyyymmddhh24miss') )
  9  /

         X Y     Z
---------- ----- ---------
  -301.999 bbb   19-AUG-01
  -301.998 bbb   19-AUG-01
  -202.234 bbb   19-AUG-01
        -2 bbb   24-AUG-01
        -1 bbb   24-AUG-01
         0 ccc   14-AUG-01
         1 aaa   19-AUG-01
         2 aaa   19-AUG-01
   202.234 aaa   19-AUG-01
   301.998 aaa   19-AUG-01
   301.999 aaa   19-AUG-01

11 rows selected.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> exec :order_col := 'y'

PL/SQL procedure successfully completed.

ops$tkyte@ORA8I.WORLD> /

         X Y     Z
---------- ----- ---------
         1 aaa   19-AUG-01
         2 aaa   19-AUG-01
   202.234 aaa   19-AUG-01
   301.999 aaa   19-AUG-01
   301.998 aaa   19-AUG-01
        -1 bbb   24-AUG-01
        -2 bbb   24-AUG-01
  -202.234 bbb   19-AUG-01
  -301.999 bbb   19-AUG-01
  -301.998 bbb   19-AUG-01
         0 ccc   14-AUG-01

11 rows selected.

ops$tkyte@ORA8I.WORLD> exec :order_col := 'z'

PL/SQL procedure successfully completed.

ops$tkyte@ORA8I.WORLD> /

         X Y     Z
---------- ----- ---------
         0 ccc   14-AUG-01
         1 aaa   19-AUG-01
         2 aaa   19-AUG-01
   202.234 aaa   19-AUG-01
   301.999 aaa   19-AUG-01
  -202.234 bbb   19-AUG-01
  -301.998 bbb   19-AUG-01
  -301.999 bbb   19-AUG-01
   301.998 aaa   19-AUG-01
        -1 bbb   24-AUG-01
        -2 bbb   24-AUG-01

11 rows selected.

the numbers are the trickiest -- you have to pick a format that works with your
numbers.  Here I went out of the way to support negative numbers and if you have
decimals, you'll have to play around with the number of digits you use.

As for the second question -- I'm not 100% sure where you are going with that
but yes, you can definitely code:

for x in (select param_name,param_source from param_query
          where  var_name='&employee_name') Loop
   dbms_sql.bind_variable(source_cursor,x.param_name,param_source);
End loop

to bind in many values for a single query in a loop.  I do that myself, it works
fine.
Review & Followup
Rating: 3
Its very useful August 19, 2001
Reviewer: AnithaGirish from Dallas, Texas
Ive tested this its very useful and simple

Rating: 5
how about ASC/DESC September 14, 2002
Reviewer: Clayton from USA
Very helpful! Could the ASC/DESC qualifier be set dynamically like the ORDER BY
column was?

Followup:
well, it is much harder if you use this decode trick.

You would have to come up with a way to collate the string in either ASC or DESC
order. Sure, you could do it but it gets quite hard.

I would be using dynamic sql at that point personally.

September 15, 2002 Charlie -- Thanks for the question regarding "update", version 8.1.7
You Asked
Hi Tom,

Please help me on the following question:

SQL> create table tt(c_id number, type char(1), p_id number, valid char(1));

Table created.

SQL> insert into tt(c_id, type, p_id) values(1, 'S', null);

1 row created.

SQL> insert into tt(c_id, type, p_id) values(2, 'R', 1);

1 row created.

SQL> insert into tt(c_id, type, p_id) values(3, 'R', 1);

1 row created.

SQL> insert into tt(c_id, type, p_id) values(4, 'R', 1);

1 row created.

SQL> insert into tt(c_id, type, p_id) values(5, 'S', null);

1 row created.

SQL> insert into tt(c_id, type, p_id) values(6, 'S', null);

1 row created.

SQL> insert into tt(c_id, type, p_id) values(7, 'R', 6);

1 row created.

SQL> insert into tt(c_id, type, p_id) values(8, 'R', 6);

1 row created.

SQL> select * from tt;

      C_ID T       P_ID V
---------- - ---------- -
         1 S
         2 R          1
         3 R          1
         4 R          1
         5 S
         6 S
         7 R          6
         8 R          6

8 rows selected.

SQL> column type format a4
SQL> /

      C_ID TYPE       P_ID V
---------- ---- ---------- -
         1 S
         2 R             1
         3 R             1
         4 R             1
         5 S
         6 S
         7 R             6
         8 R             6

8 rows selected.

SQL> column valid format a5
SQL> /

      C_ID TYPE       P_ID VALID
---------- ---- ---------- -----
         1 S
         2 R             1
         3 R             1
         4 R             1
         5 S
         6 S
         7 R             6
         8 R             6

8 rows selected.

=======================================

Basically, type 'S' doesn't have p_id and for each row of type 'R', it check its
PREVIOUS row recursively
until it finds FIRST ROW of type 'S', grab that c_id and put it into p_id field.
 Here is what I want to do:
If I mark any row of type 'S' as invalid (set column valid = 'N'), say row of
c_id = 6, then all of rows of type 'R'
where p_id is equal to the c_id of invalid row of type 'S', its p_id will be
updated/shuffled to c_id
of previous row of type 'S' as follows:

      C_ID TYPE       P_ID VALID
---------- ---- ---------- -----
         1 S
         2 R             1
         3 R             1
         4 R             1
         5 S
         6 S                N
         7 R             5
         8 R             5

How can I do it in a single SQL update statement?  Is analytical function useful
here?

Thanks,
and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from tt;

      C_ID T       P_ID V
---------- - ---------- -
         1 S
         2 R          1
         3 R          1
         4 R          1
         5 S
         6 S
         7 R          6
         8 R          6

8 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> update tt
  2     set valid = decode(type,'S','N',valid),
  3         p_id = decode( type,'S',p_id,(select max(c_id)
  4                                         from tt t2
  5                                        where type = 'S'
  6                                          and valid is NULL
  7                                          and c_id < tt.p_id) )
  8   where c_id = 6
  9      or p_id = 6
 10  /

3 rows updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from tt;

      C_ID T       P_ID V
---------- - ---------- -
         1 S
         2 R          1
         3 R          1
         4 R          1
         5 S
         6 S            N
         7 R          5
         8 R          5

8 rows selected.

might be useful.
Review & Followup
Rating: 4
thanks! September 15, 2002
Reviewer: A reader

September 15, 2002 khalid -- Thanks for the question regarding "Index levels", version 8.1.7
You Asked
Hi Tom
1. How many levels are there in index for table containing N number of rows?
2. Is there formula to calculate the number of levels?
3. If the leaf blocks contain the index to the rows in the table.
What does the branches and root blocks contain?

Thank you very much.
and we said...
1) i don't know -- N isn't the only input into the function here - there are
tons of variables.  3 to 4 levels is normal.

2) no

3) navigational information telling us to go "left" or "right" in the tree to
find the proper leaf node. (hence the name "branch" blocks, they tell us where
to branch to)
Review & Followup
Rating: 3
Thanks Tom September 15, 2002
Reviewer: khalid from Yemen
Thank you very much.

September 15, 2002 -- Thanks for the question regarding "Query -- Need help to formulate", version 901
You Asked
I have 2 tables

A) graph

NodeId not null primary key number,
sal number,
ParentId null number

ParentId  references NodeId

B)Data

DataId number primary key
NodeId number

NodeId references Graph table's NodeId column

Graph table contains the hierarchy information.

I need to do the following :

Select the dataid from the data table& its associated nodeid from the graph
table along with the parentId of the nodeid i.e

Data
Dataid   Nodeid
100       333

Graph
NodeID   ParentID      SAL
222        111
333        555
555        666
666

My output should be :the dataid I pass in and the hierarchy of the nodeid it
belongs to

100        333
           555
           666

SELECT dataid ,  graph.nodeId   FROM data  RIGHT JOIN graph
  ON graph.NodeId=data.NodeId AND data.dataid IN  (333)
start with graph.nodeid in
(select graph.nodeid from graph join data on data.NodeId=graph.NodeId where
dataId in (333))
CONNECT BY graph.nodeid= prior data.nodeid

But this takes huge time as the in list increases ; is there a better way to do
this

Thanks
and we said...
Your query doesn't appear to work at all.  as coded with 333 -- no data.  change
333 to 100 (which is a value for dataid whereas 333 is not) and it goes into an
infinite loop.

Does this result set work for you -- you'll get a single row with the entire
hierarchy in it:

ops$tkyte@ORA920.US.ORACLE.COM> select data.*,
  2        substr(
  3            (select max( to_char(level,'fm0000') ||
                                 sys_connect_by_path(nodeid,'/'))
  4               from graph
  5                  start with nodeid = data.nodeid
  6            connect by prior parentid = nodeid ), 6 ) hierarchy
  7   from data
  8  /

    DATAID     NODEID HIERARCHY
---------- ---------- ---------------
       100        333 333/555/666

ops$tkyte@ORA920.US.ORACLE.COM>

just add "where dataid in ( ..... )" to the outer query.
Review & Followup
Rating: 3
sys_connect_by_path September 14, 2002
Reviewer: A reader from Bangalore
Tom,
 Can you please explain what the function, sys_connect_by_path does and its
purpose.

regards

Followup:
it gives the path back up the tree -- it is the hierarchy back to the root.  By
keeping just the "max level" one, we get the entire hirearchy:

ops$tkyte@ORA920.US.ORACLE.COM> select lpad('*', 2*level, '*' ) || ename
empName,
  2         dname,
  3             job,
  4         sys_connect_by_path( ename, '/' ) cbp
  5    from scott.emp emp, scott.dept dept
  6   where emp.deptno = dept.deptno
  7   start with mgr is null
  8   connect by prior empno = mgr
  9   order siblings by dname
 10  /

EMPNAME              DNAME          JOB       CBP
-------------------- -------------- --------- -------------------------
**KING               ACCOUNTING     PRESIDENT /KING
****CLARK            ACCOUNTING     MANAGER   /KING/CLARK
******MILLER         ACCOUNTING     CLERK     /KING/CLARK/MILLER
****JONES            RESEARCH       MANAGER   /KING/JONES
******SCOTT          RESEARCH       ANALYST   /KING/JONES/SCOTT
********ADAMS        RESEARCH       CLERK     /KING/JONES/SCOTT/ADAMS
******FORD           RESEARCH       ANALYST   /KING/JONES/FORD
********SMITH        RESEARCH       CLERK     /KING/JONES/FORD/SMITH
****BLAKE            SALES          MANAGER   /KING/BLAKE
******ALLEN          SALES          SALESMAN  /KING/BLAKE/ALLEN
******WARD           SALES          SALESMAN  /KING/BLAKE/WARD
******JAMES          SALES          CLERK     /KING/BLAKE/JAMES
******TURNER         SALES          SALESMAN  /KING/BLAKE/TURNER
******MARTIN         SALES          SALESMAN  /KING/BLAKE/MARTIN

14 rows selected.

See, that tells us:

********ADAMS        RESEARCH       CLERK     /KING/JONES/SCOTT/ADAMS

to get to adams, we started at KING, to JONES, to SCOTT to ADAMS....

Rating: 4
September 15, 2002
Reviewer: A reader
Tom
Thanks a lot
Please explain the usage of substr, max, level in the stmt.Why do we need it?

substr((select max( to_char(level,'fm0000') ||

Followup:
I wanted to keep the max level only -- it has the entire hierarchy.  All of the
other levels in there (1, 2, 3, .. n-1 where n = max level) have the partial
hierarchy.

So, I just made the level a fixed with field that "sorts" nicely with to_char()
-- added the entire hirearchy after it.  We keep the "max" one (level = 3 in
this example) and then for display purposes -- we strip it off (we aren't
interested in the level, just the hierarchy for the max level)

September 15, 2002 sreekanth -- Thanks for the question regarding "sqlserver to oracle", version 8.1.7.3
You Asked
Hi tom,

I tried to sumbit this but when submitted it gave page cannot displayed error.
So if you already recevied this request please ignore this.

I would like to ask two questions.

1.  we run oracle projects and financial and we also run hr system on
sqlserver. lot of times we get the data from sqlserver to oracle using dblink.
i write the query

select "PrgDocNo" CHECK_NO from "payreg"@dblink

this works fine.

but if write like this
select PrgDocNo CHECK_NO from "payreg"@dblink
then it gives an error ERROR at line 1:
ORA-00904: invalid column name

so basically i need type exact column names in double quotes which are case
sensitive.

is there a way to query the sqlserver as i query oracle database i mean not case

sensive and not using double quotes.

2.we have web application(iReceivables) which is developed using mod_plsql.  i
wrote custom online report which pulls data from receivables and projects.  This

is a complex report written pl/sql and this package executed when submit the
request and using htp.p i stream the data to the browser. user use this to get
year to date data also.  When they want year to date data then it is taking 5 to

7 min.  But for some customers , they are getting disconneted after 3.5 min.
this is becasue i think they have some kind rule in their proxy server to
disconnect if there is no activity for  3.5 min.

is there a way to make the connection statfull so that it will not get
disconnected.

thanks
sreekanth.g
and we said...
1) that would be a question for askBill.microsoft.com (but I don't think that
have one).

You could create a view

create view payreg
as
select "PrgDocNo" prgdocno, ......
  from "payreg"@dblink
/

and then just

select prgdocno from payreg;

in the applications/sqlplus/whatever.

2) Well, I don't know about you but I wouldn't want my browser to hang for 5-10
minutes for anything.  What about this, instead of running the report when they
hit submit, you just collect their inputs and EMAIL ADDRESS.  Then, you use
dbms_job to schedule your procedure to run in the background right after they
submit.  Your procedure would dump the results into a simple "report" table and
when done would email them a link (with utl_smtp) they can click on to see the
report.  That way, they could even keep old reports for a while if they like.

The get what appears to be really good performance, you get to control the
number of concurrently running reports (via job_queue_processes) and everyone is
happy?
Review & Followup
Rating: 5
September 15, 2002
Reviewer: gadhiraju sreekanth from tampa, fl usa
thanks for your advice.

Regarding my second question i thought of doing the way you suggested but my
concern about safety.  if i send a link in email then any body can copy and
paste that link and access the report.  But i can popup a login screen then
validate the login and show the report.  is there any other way securing the
link without prompting login screen.

thanks
sreekanth.g

Followup:
If you use a cookie as your "i'm already logged in" (eg: when they log in, you
set an encrypted cookie in their browser session and that is what gets them
access to the other pages) then this is not an issue -- they'll already be
logged in and the login window WON'T popup (anymore then it would for any other
screen in your system).

The same would be true if you use BASIC authentication (the kind you get when
you don't store the password with the DAD)

September 15, 2002 Saradha -- Thanks for the question regarding "LONG datatype", version 8.1.7
You Asked
Tom,

We have few tables that have columns defined as LONG and LONG RAW data type. We
are currently planning to upgrade to 9i and can you
please tell me if these data types are still suppored in 9i or we
compulsary have to convert these into BLOB or BFILE datatypes.

Thanks
Saradha
and we said...
Yes these types are still there,  Yes you should convert them anyway.
Especially in 9i -- you can use alter table t modify (x clob) to convert the
data easily and applications that were written to access longs should work
unchanged against them now (in 9i).

Food for thought, in 9i you can use LIKE, substr, instr, upper, initcap, etc etc
etc against a CLOB!!!  Try that with a long.
September 16, 2002 Sridevi -- Thanks for the question regarding "1.Indexes 2.Documentation on DB and App Server", version 9i - Release 2
You Asked
Hi Tom,

1.How can we decide what type of indexes to add on which columns in a table?
(Other than the index created by the primary key). In a still under development
database, where real time statistics are not available for any table, how can we
decide what additional indexes to add?

2. Where can I get extensive documentation on the net for 9iDB and 9iAS -
Release 2 - Enterprise Edition?

Thanks in Advance.

P.S:-We need more of you here. Please be available for questions for a longer
duration.
and we said...
1) In a development database, you had BETTER BE LOADING THE TABLES UP to the
size and scale of production -- so your statement about "real time stats" is
wrong, unless you are planning on coding against a fraction of th data.

If you code against a fraction of the data, be prepared to rewrite your
application in the production environment and have a very bad couple of months
while you do so.  Get some extra sleep now so when you are putting in 100+ hour
work weeks when this goes "production" you'll feel better.

You need to test to scale, otherwise you are guessing about everything -- your
algorithms, your queries, your physical data model, the whole thing.

2)
http://otn.oracle.com/
under "documentation"
Review & Followup
Rating: 4
More information needed September 16, 2002
Reviewer: Suresh from NY
Could you please point to some documentation or any forum where Load Balancing
and Web caching in 9iAS has been discussed thoroughly. As always thanks a lot
for your help in advance.

Followup:
http://otn.oracle.com/
under "documentation"
under "discussion forums"

September 16, 2002 Bhavesh -- Thanks for the question regarding "SQL
      query", version 8.1.7

      You Asked
Hi Tom,
    Hope you must be doing fine. Thanks for all your help in past. This time I
have a typical question for you. Following is my reference table structure.
Key1 Varchar2(32)
Key2 Varchar2(32)
Txt_data Varchar2(128)

Here I have one row as follows :
KEY1 = LOCAL_INVENTORY,
KEY2 = STATUS,
TXT_DATA ='16', '17', 18', 'RJ', 'RM'

OK now I have asset master table containing following fields :
Type code Varchar2(8)
Sub Type Varchar2(2)
Status   Varchar2(2)

The type and sub type code constitutes the primary key. Say we have following
data in this table:
TYPE_CODE   SUB_TYPE STATUS
---------------- ---- ----
32113700         01   RJ
32113700         01   RJ

Now when I query the following in SQL :
SQL> select count(*) from asset where type_code='32113700'
  2  and sub_type ='01' and status_asset NOT in
  3  (select txt_data from refrence_mast where key1='LOCAL_INVENTORY' and
cod_key2 = 'STATUS');
I get following output :

 COUNT(*)
---------
        2

Whereas I believe it should be ZERO because I have two assets in status 'RJ'
which should get filtered off ?

Can you please tell me where I am wrong in the above query.
Thanking you in advance.
Regards

Bhavesh

      and we said...
Think about it.

You are querying

  select count(*) from t where column not in ( '1, 2, 3' );

Now, is that different from

  select count(*) from t where column not in ( 1, 2, 3 );

Yes -- most definitely.  The first one counts rows where "column <> '1, 2, 3'"
-- where column is not equal to the single string '1, 2, 3'.

The second one counts rows where "column <> 1 and column <> 2 and colum <> 3" --
it compares column to three values 1, 2, and 3

There is no way you would expect the STRING returned by (select txt_data from
refrence_mast where key1='LOCAL_INVENTORY' and cod_key2 = 'STATUS') to be
interpreted as some unknown number of items -- it would make it impossible to
use IN and NOT IN with things that had "commas" in them for example.

Your data model is wrong.  This table:

Key1 Varchar2(32)
Key2 Varchar2(32)
Txt_data Varchar2(128)

Needs to be

Key1 Varchar2(32)
Key2 Varchar2(32)
Key3_Txt_data Varchar2(2)

and you need to put into it:

KEY1 = LOCAL_INVENTORY,
KEY2 = STATUS,
KEY3_TXT_DATA ='16'

KEY1 = LOCAL_INVENTORY,
KEY2 = STATUS,
KEY3_TXT_DATA ='17'
....
KEY1 = LOCAL_INVENTORY,
KEY2 = STATUS,
KEY3_TXT_DATA ='RM'

alternatively, search this site for str2table and use that technique (but your
data model is *wrong*, you'll really be wanting to change it)

Review &
      Followup

      Rating: 5
Just an
      observation  September 16, 2002
Reviewer:
      Flanker  from Kalamazoo, MI
Great input, Tom - as always.

I couldn't help but notice that in the "Asset Master" table, both rows are
identical and the person asking the question says that the "type_code" and
"sub_type" columns constitute the primary key. Not sure how that's possible.

      Rating:
      5
Wonderful  September 16, 2002
Reviewer:
      Bhavesh
Just superb. Thanks a million. I got it where I was going wrong. I will
definitely try to get the data model changed at once.
For Flankner... Sorry I made a mistake over here, but still Tom was able to
provide the the accurate answer. In fact I've two tables namely type and asset.
The type master has got type and sub type as primary key. Asset master table has
got one asset code as primary key and type and sub type are referenced as
foreign key. I've given the structure of asset table in the example but forgot
to put the asset code.

September 16, 2002 Sanjai -- Thanks for the question regarding
      "Order by", version 8.1.7

      You Asked
Hi Tom,

On executing this sql query we get this output.

scott@TEST.WORLD> select empno, comm
  2  from emp
  3  order by comm desc;

     EMPNO       COMM
---------- ----------
      7369
      7566
      7782
      7698
      7788
      7839
      7900
      7934
      7902
      7876
      7654       1400
      7521        500
      7499        300
      7844          0

14 rows selected.

I was wondering if there is a way to get the null values in the end and the desc
comm. values in the start of the output.

I need this output.

     EMPNO       COMM
---------- ----------
      7654       1400
      7521        500
      7499        300
      7844          0
      7369
      7566
      7782
      7698
      7788
      7839
      7900
      7934
      7902
      7876

Thanks a lot for your help.

      and we said...
scott@ORA817DEV.US.ORACLE.COM> select empno, comm from emp order by comm desc
NULLS LAST;

     EMPNO       COMM
---------- ----------
      7654       1400
      7521        500
      7499        300
      7844          0
      7369
      7566
      7900
      7934
      7902
      7876
      7698
      7782
      7788
      7839

14 rows selected.

Review &
      Followup

      Rating:
      3
  September 16, 2002
Reviewer:  Sagi
       from India
Thanx tom for teaching one more new thing.

I did not knew that oracle had NULLS LAST

Regards,

      Rating: 5
order
      by  September 16, 2002
Reviewer:  sanjai

awesome tom. Thanks again.

      Rating: 3
order
      by  September 16, 2002
Reviewer:  reader

Tom,

In version 7.x NULLS LAST does not seem to work. Is there a way to get this
output in 7.x

Thanks

          Followup:

It is new with 8i, you'll have to

order by nvl( comm, -9999999999999999999 ) DESC

instead.

      Rating:
      3
  September 16, 2002
Reviewer:
      sanjai
Hi Tom,

I also tired and figured out that NULLS LAST does not seem to work in Oracle 7.x
version. Was just curious to know if  there a way to get this output in 7.x

Thanks a lot.

      Rating: 5
Equivalent of
      NULLS LAST in 7.x  September 16, 2002
Reviewer:
      Logan Palanisamy  from Santa Clara, CA USA
Try the following.

SQL> l
  1* select empno, comm from emp order by nvl(comm, -1) desc
SQL> /

     EMPNO       COMM
---------- ----------
      7654       1400
      7521        500
      7499        300
      7844          0
      7369
      7566
      7900
      7934
      7902
      7876
      7698
      7782
      7788
      7839

14 rows selected.

SQL>

September 16, 2002 Ingersol -- Thanks for the question regarding
      "How to export oracle view definition", version Oracle 7, Release
7.3.2.3.0

      You Asked
Hi Tom,

I need to take the backup of some tables and views.
I tried using export (exp) to backup the views, it returned with
error "EXP-00011: STI.ING_DATE_VW; does not exist".
Here STI - is the user name, ING_DATE_VW - is the view name.
This view is created by sti user, I could able to describe this
view as login as sti user, no problem in that.

Can you please tell me, is it possible to export views?

Thanks,
Ingersol

      and we said...
It is not possible to export just views (or just procedures or
just triggers...)

You can either export:

o the full database
o a schema (all things owned by a user)
o a set of tables and all things that tables needs (indexes,
  triggers, constraints, etc)

You can choose to not export certain things (rows=n and such).
But there is no option to export just a view definition.

Your best bet is to export the schema with the views using
"owner=SCHEMA_NAME rows=n".  This will give you more then what
you want but its probably better.  What you'll get is a
"structural" export.  You'll get all of the DDL you need to
recreate that schema (views, procedures, triggers, table DDL,
constraints, etc).  You can review this afterwards with a "imp
.... full=y show=y" -- that will just show you all of the CREATE
statements in there.

Then export the tables with rows=y to get their data.

Review &
      Followup

      Rating:
      4
  September 13, 2002
Reviewer:
      A reader
How can I capture the 'create statements' in a file? I tried to re-direct it and
it did not work (on unix). And indexfile=y gives only the tables/indexes. Thanks

          Followup:

show=y log=filename

or redirect stdout and stderr to a file from just show=y

      Rating:
      5
  September 13, 2002
Reviewer:
      A reader
OK. I am getting the create statements in the log file. But how can I create a
create_view.sql out of this. There are line breaks and I am not able to run this
without editing. Is there anyway to create a view script using exp imp (other
than writing awk script to format this....?)

          Followup:

you might be happier with "strings expdat.dmp > xxx.dmp"

and then vi xxx.dmp and search for CREATE VIEW

      Rating:
      3
  September 16, 2002
Reviewer:
      A reader
More problems!

There are " and " characters all over the place. Also there is no ; or / at the
end of each statement. (I have more than 500 views, so it it tough to insert
them..). This script can't be run directly.... Do you happen to have a script
which can help me get the view DDL from the dictionary? Thank you.

          Followup:

search this site for

getaview

September 16, 2002 Sanjoy -- Thanks for the question regarding
      "Index Rebuilds", version 8.1.7.4

      You Asked
a)Reading through your site I see you feels index rebuilds are a waste of time.
But, doing index rebuilds doesn't it benefit you in terms of storage taken for
index(deallocation of storage).  Shouldn't this speed up your backup's and etc
..

b)Also, don't you get performance benefits because you don't scan the empty
blocks after index rebuilds.

c)I noticed sometimes even after rebuiling a index the b-level doesn't go
down what is the reason for this.

d)When would you consider a time to rebuild a index.

Thanks
Sanjoy

      and we said...
a) I can show you cases where an index rebuild results in a larger index. HMMMM.

That -- and I firmly believe indexes are like people.  You can put it on a diet,
it looks great for a while, but it'll just grow back to where it was in short
time in most cases.  Therefore, you did alot of work to gain some space for a
short period of time that will ultimately go right back to the index (eg: you
did nothing)

b) truly empty blocks are removed from the index structure -- besides, with a
height of 4 to get to a leaf node -- how many do you think you will hit?

c) why do you think it would?

d) bitmap indexes are the most likely candidates.  b*trees -- almost never.

September 16, 2002 Bill -- Thanks for the question regarding "Using
      a cursor variable as a function in a select statement ", version 8.1.7

      You Asked
Hi Tom,

I am using a table to store metadata, for example I want to sum a field if it
contains a particular value however I may want to do a count if it is contains
another value.  I have provided a basic example of what I want to do.

My metadata table is the following

SVRMGR> select * from bill;
FIELD
----------------------------------------------------------------
SUM
COUNT
2 rows selected.

My procedure querys the bill table to identify the function and then applies the
function to the appropriate field

CREATE OR REPLACE PROCEDURE  TEST
AS

        v_field VARCHAR2(64);
    v_var number;
    str1 VARCHAR2(200);
    v_alog VARCHAR2(200);
    ins1 VARCHAR2(200);

CURSOR c_DetailsCursor
IS
        SELECT field from bill;

BEGIN
OPEN c_DetailsCursor;
LOOP

FETCH c_DetailsCursor INTO v_field;

EXIT WHEN c_DetailsCursor%NOTFOUND;

str1 := 'select :v_field1||(VALUE) from customer';
execute immediate str1 into v_var using v_field;

ins1 := 'insert into summary values (:v_var1)';
execute immediate ins1 using v_var;

END LOOP;

CLOSE c_DetailsCursor;

END;
/

The procedure compiles OK however when I try to execute I get the following

SVRMGR> begin test; end;
     2> /
ORA-00904: invalid column name
ORA-06512: at "REPUSER.TEST", line 23
ORA-06512: at line 1

I tried several other methods but all were unsuccessful.  All assistance would
be greatly appreciated.

Regards

Bill

      and we said...
I ran your test with:

drop table bill;
create table bill ( field varchar2(25) );
insert into bill values ( 'sum' );
insert into bill values ( 'count' );

drop table summary;
create table summary ( data varchar2(30) );

drop table customer;
create table customer( value int );
insert into customer values (1);

So, I would have to guess that your table CUSTOMER does not actually have a
column named value.  Also, I would guess that you really mean to code:

str1 := 'select ' || v_field1 || '(value) from customer';

NOT

str1 := 'select :v_field1||(VALUE) from customer';

-- that second query would (when field = SUM) be the same as:

   select 'SUM' || (value) from customer

in sqlplus, not

   select sum(value) from customer

which is what I think you want.

cursor for loops RULE:

  for x in ( select * from bill )
  loop

       use x.field here.....

forget about explicit cursors as you coded.  Your procedure should be:

CREATE OR REPLACE PROCEDURE  TEST
AS
BEGIN
    for x in ( select * from bill )
    loop
        execute immediate
       'insert into summary
        select ' || x.field || '(value)
          from customer';
    end loop;
end;
/

Smaller = better, explicit cursors -- hate em, implicit cursor -- rock.

Review &
      Followup

      Rating: 5
Great
      work  September 17, 2002
Reviewer:  Bill
      Thuillier  from Cork, Ireland
Thank you Tom for taking the time to answer my query. Your site is a goldmine.


Subject | Article | Last Updated (East coast time)
IOT - order of columns in PK | article only | Sep 17, 2002 07:46 am
Using IN | article only | Sep 17, 2002 07:57 am
append during import -- dealing with structural changes during import. | article only | Sep 17, 2002 08:04 pm
Primary Key created does not reflect in DBA_INDEXES | article only | Sep 17, 2002 08:13 pm
Reusing Datafile | article only | Sep 17, 2002 08:30 pm
initial extents | article only | Sep 18, 2002 07:23 am
How to monitor record count during an execution of a procedure | article only | Sep 18, 2002 07:23 am
To get a Single Row | article only | Sep 18, 2002 07:43 am
AQ subscriber | article only | Sep 18, 2002 07:59 am
xml problem | article only | Sep 18, 2002 08:36 am
Check Constraint | article only | Sep 18, 2002 02:45 pm
How to import data from more than one (.csv ) [Excel] files into the related tables ! | article only | Sep 18, 2002 02:59 pm
Import data | article only | Sep 20, 2002 07:41 am
Import & Export -- at the tablespace level | article only | Sep 20, 2002 07:48 am
Running SQL against Table of Records | article only | Sep 20, 2002 08:12 am
Dynamic SQL giving error. | article only | Sep 20, 2002 08:36 am
before & after triggers and jre version | article only | Sep 20, 2002 08:37 am
DBA Job irrelavent ? | article only | Sep 20, 2002 02:22 pm
update contention and queues | article only | Sep 20, 2002 02:39 pm
counting business days. | article only | Sep 20, 2002 06:21 pm
A Few Questions | article only | Sep 20, 2002 06:56 pm
upgrade | article only | Sep 20, 2002 07:10 pm
Oracle Financial Implementation | article only | Sep 21, 2002 11:07 am
Allow queries without granting access to table | article only | Sep 21, 2002 02:16 pm
online tablespace backup | article only | Sep 21, 2002 04:55 pm
SQL Query | article only | Sep 22, 2002 09:54 am
SQL questions involving combinations | article only | Sep 22, 2002 10:02 am
Can a direct export by consistent? | article only | Sep 22, 2002 10:29 am
Oracle Management server | article only | Sep 22, 2002 10:40 am
Dynamically Creating Next Month as Column in Table | article only | Sep 23, 2002 07:22 am
rebuild indexes | article only | Sep 23, 2002 07:40 am
Object Type | article only | Sep 23, 2002 07:45 am
Oracle InterMedia Question | article only | Sep 23, 2002 07:47 am
varrays, nested tables, and pl/sql records, and pl/sql tables, and ref cursor | article only | Sep 23, 2002 01:24 pm
row(s) 1 - 34 of 34
September 17, 2002 Jan -- Thanks for the question regarding "IOT - order of columns in PK", version 8.1.7
You Asked
I have a look up IOT table with compress=2 with fallowing columns:

CATEGORY   VARCHAR2(10)  pk
CODE       VARCHAR2(10)  pk
LANG       VARCHAR2(3)   pk
CODE_DESC  VARCHAR2(100)

In our Environment, we will have only one language. What is better, if the order

of PK is:

a) LANG,CATEGORY,CODE or
b) CATEGORY,LANG,CODE  ?

Solution a) is more compressible, but probably Index Scanning does more
expensive job
here because all records has the same value in LANG column. Is it true?

2)the next question, if I have IOT with e.g. 5 columns and only 2 of them are PK

(col1,col2), how are the rows ordered in IOT?

It is by col1,col2,col3,col4,col5? It means that is the order of columns in
CREATE TABLE statment important?

Thanks, Jan
and we said...
Do you have my book "Expert one on one Oracle" -- in there I detail and show
that the order of the columns in the index should be based on

a) how the queries are written
b) that putting the columns in reserve order of selectivity is no faster, no
slower then in order of selectivity.

The index range scans will be no faster, no slower -- but since the index is
compressed better with lang on the leading edge -- they might be a bit faster
due to less logical IO's.

Put the columns in the order they best should be...

2) they are ordered by col1, col2 -- period.  col1 and col2 are unique, they
determine the location in the index structure of the data.
Review & Followup
Rating: 4
Re September 16, 2002
Reviewer: Jan from SG
Thanks Tom,

I have your book, it is really good.
So the order of the selectivity is not important.

And 2-cnd question - if IOT is the index itself, how are the rest of the data
which are not in PK ordered? Randomly? Or it depends on the order of columns?

Thanks, Jan

Followup:
As for the 2cnd question -- you are confusing me.

Ok, so you have an IOT:

create table t ( x int, y int, a date, b int, primary key (x,y) ) organization
index;

The IOT is sorted by X,Y (the primary key) and since X,Y are also unique --
there is only one occurence of A,B per X,Y so there is no need to sort by A,B --
eg:

order by x, y, a, b

gives the same result as

order by x, y

The remaining columns are not "sorted" -- there is only one occurence per key
hence there is only one row to "sort".

September 17, 2002 -- Thanks for the question regarding "Using IN", version 8.1.6
You Asked
Hi,

I have a store procedure that takes a list of customer ID (number) separate by
coma (,) and update customer table.

Here is a modified version of my store procedure:

    PROCEDURE update_customer(id_list IN varchar2) IS
    BEGIN
        UPDATE customer
        SET modified_date = SYSDATE
        WHERE customer_id IN (id_list);
    END update_customer;

Where: id_list is a list of customer ID in format '1,2,3,4,5'
    and customer_id data type is NUMBER.

I got a "ORA-01722: invalid number" error when I execute the above store
procedure.  I believe it bombs b/c id_list is a varchar2.  Is there a better way
to do this?
and we said...
well, there are many ways to do this -- since this is an update, I might be
tempted to do this:

ops$tkyte@ORA920.US.ORACLE.COM> create table emp as select e.*, to_date(null)
modified_date from scott.emp e;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create or replace procedure update_customer(
p_id_list in varchar2 )
  2  is
  3      type list is table of number index by binary_integer;
  4      l_id_list long default p_id_list || ',';
  5      l_list    list;
  6  begin
  7      while( l_id_list is not NULL )
  8      loop
  9          l_list(l_list.count+1) := substr( l_id_list, 1,
instr(l_id_list,',')-1 );
 10          l_id_list := substr( l_id_list, instr(l_id_list,',')+1 );
 11      end loop;
 12
 13      forall i in 1 .. l_list.count
 14          update emp
 15             set modified_date = sysdate
 16           where empno = l_list(i);
 17  end;
 18  /

Procedure created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> exec update_customer( null );

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> exec update_customer( '7369, 7499,7521  , 7566'
)

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select empno, modified_date from emp;

     EMPNO MODIFIED_
---------- ---------
      7369 14-SEP-02
      7499 14-SEP-02
      7521 14-SEP-02
      7566 14-SEP-02
      7654
      7698
      7782
      7788
      7839
      7844
      7876
      7900
      7902
      7934

14 rows selected.

As a very straightforward way.  Otherwise, I can use some of the techniques
listed on:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:110612348061
....
Review & Followup
Rating: 4
IN with list September 16, 2002
Reviewer: HP from Seattle, WA
Thanks Tom.  I currently calls Java Store Procedure to split/stringtokenize the
input list; however, I like your way better b/c it saves a call.

In your opinion, does the below query have a better performance than splitting
the list and loop through it? why?

UPDATE customer SET modified_date = SYSDATE
WHERE customer_id IN (&id_list);

Followup:
That would be using substitution variables meaning the query would be unique
each time meaning it would be hard parsed meaning I HATE IT as a solution.

Rating: 4
What about Designing September 17, 2002
Reviewer: Tejas from India
Dear Sir,

I always wonder that whether it is a good designing approach to save the comma
separated values (specially when the field is candidate for the foreign key) or
not ?

Please let us know us views.

Thanks

Followup:
I think it is a terrible idea in general to store a comma separate list of
values.  I would not store:

ID          VALUES
-------     --------------
1           1,2,3,4
2           3,4,5

I would store

ID          VALUES
-------     --------------
1           1
1           2
1           3
1           4
2           3
2           4
2           5

in general.

September 17, 2002 Hemalatha -- Thanks for the question regarding "append during import -- dealing with structural changes during import.", version version 8.1.5
You Asked
1. when I import the data, Can I append that into the existing
data in the database.  Assume both changes in the structure of
data, No changes in the structure of data.

2.  When I import the data, if there is change in structure of
data, the data wont be imported.  If i want to import with
changes, what should i do.
and we said...
The answer to 1) is absolutely yes.  Here is an example showing
that at work:

ops$tkyte@8i> drop table t;
Table dropped.

ops$tkyte@8i> create table t ( x int );
Table created.

ops$tkyte@8i> insert into t values ( 1 );
1 row created.

ops$tkyte@8i> commit;
Commit complete.

ops$tkyte@8i> host exp userid=/ tables=t

Export: Release 8.1.5.0.0 - Production on Tue Jun 13 07:09:23
2000
(c) Copyright 1999 Oracle Corporation.  All rights reserved.
Connected to: Oracle8i Enterprise Edition Release 8.1.5.0.0 -
Production
With the Partitioning and Java options
PL/SQL Release 8.1.5.0.0 - Production
Export done in US7ASCII character set and US7ASCII NCHAR
character set

About to export specified tables via Conventional Path ...
. . exporting table                              T          1
rows exported
Export terminated successfully without warnings.

ops$tkyte@8i> host imp userid=/ full=y ignore=y

Import: Release 8.1.5.0.0 - Production on Tue Jun 13 07:09:25
2000

(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.5.0.0 -
Production
With the Partitioning and Java options
PL/SQL Release 8.1.5.0.0 - Production

Export file created by EXPORT:V08.01.05 via conventional path
import done in US7ASCII character set and US7ASCII NCHAR
character set
. importing OPS$TKYTE's objects into OPS$TKYTE
. . importing table                            "T"          1
rows imported
Import terminated successfully without warnings.

ops$tkyte@8i>
ops$tkyte@8i> select * from t;

         X
----------
         1
         1

So, that shows that the table was appended to.  As for 2, the
answer is also yes (but we might need help).  There are 3 cases
to consider

- you've ADDED a column (no work on your part needed, we'll put
a NULL in it)

- you've DROPPED a column (some work on your part)

- you've changed the datatype of a column.

Here is an example of each of the three.  For the added_a_column
case, we need do nothing.  For the dropped and modified columns,
we will import into a view with an instead of trigger that we
use to perform whatever data remapping we need.  In our case, we
will just ignore the dropped column and we will convert a NUMBER
into a DATE.

ops$tkyte@8i> create table added_a_column ( x int );
Table created.

ops$tkyte@8i> create table dropped_a_column ( x int, y int );
Table created.

ops$tkyte@8i> create table modified_a_column( x int, y int );
Table created.

ops$tkyte@8i> insert into added_a_column values ( 1 );
1 row created.

ops$tkyte@8i> insert into dropped_a_column values ( 1, 1 );
1 row created.

ops$tkyte@8i> insert into modified_a_column values ( 1, 1 );
1 row created.

ops$tkyte@8i> commit;
Commit complete.

ops$tkyte@8i> host exp userid=/
'tables=(added_a_column,dropped_a_column,modified_a_column)'

Export: Release 8.1.5.0.0 - Production on Tue Jun 13 07:23:49
2000

(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.5.0.0 -
Production
With the Partitioning and Java options
PL/SQL Release 8.1.5.0.0 - Production
Export done in US7ASCII character set and US7ASCII NCHAR
character set

About to export specified tables via Conventional Path ...
. . exporting table      ADDED_A_COLUMN          1 rows exported
. . exporting table    DROPPED_A_COLUMN          1 rows exported
. . exporting table   MODIFIED_A_COLUMN          1 rows exported
Export terminated successfully without warnings.

So, that constitutes our test case. We have exported the three
tables "as is".  Now, lets modify them:

ops$tkyte@8i> alter table added_a_column add ( y int );
Table altered.

ops$tkyte@8i> alter table dropped_a_column drop column y;
Table altered.

ops$tkyte@8i> delete from modified_a_column;
1 row deleted.

ops$tkyte@8i> alter table modified_a_column modify y date;
Table altered.

Now, if we attempt to import, we will find that
"added_a_column" works OK but the rest fail

ops$tkyte@8i> host imp userid=/ full=y ignore=y

Import: Release 8.1.5.0.0 - Production on Tue Jun 13 07:23:49
2000

(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.5.0.0 -
Production
With the Partitioning and Java options
PL/SQL Release 8.1.5.0.0 - Production

Export file created by EXPORT:V08.01.05 via conventional path
import done in US7ASCII character set and US7ASCII NCHAR
character set
. importing OPS$TKYTE's objects into OPS$TKYTE
. . importing table    "ADDED_A_COLUMN"          1 rows imported
. . importing table  "DROPPED_A_COLUMN"
IMP-00058: ORACLE error 904 encountered
ORA-00904: invalid column name
. . importing table "MODIFIED_A_COLUMN"
IMP-00058: ORACLE error 932 encountered
ORA-00932: inconsistent datatypes
Import terminated successfully with warnings.

Ok, so now we have to put into the database views that look
like the original tables.  To accomplish this we will:

o rename the tables for a minute
o create a view that selects constants out of the right type --
eg: "select 1" for a number, "select sysdate" for a date,
"select rpad( '*', 30, '*' )" for a varchar2(30) and so on
o create an instead of trigger that "does the right thing" for
us, performing any data conversion/mapping we need

ops$tkyte@8i> rename modified_a_column to
modified_a_column_TEMP;
Table renamed.

ops$tkyte@8i> create or replace view modified_a_column
  2  as
  3  select 1 x, 1 y from modified_a_column_TEMP;
View created.

ops$tkyte@8i> create or replace trigger modified_a_column_IOI
  2  instead of insert on modified_a_column
  3  begin
  4          insert into modified_a_column_TEMP
  5          ( x, y )
  6          values
  7          ( :new.x, sysdate+:new.y );
  8  end;
  9  /
Trigger created.

Here we are converting the number that was stored in Y into an
offset from sysdate (why -- cause that was my "data mapping
algorithm").  You would perform whatever conversion from string
to date, date to number, number to string etc -- you wanted to
here.

Now we take care of the dropped column:

ops$tkyte@8i> rename dropped_a_column  to dropped_a_column_TEMP;
Table renamed.

ops$tkyte@8i> create or replace view dropped_a_column
  2  as
  3  select 1 x, 1 y from dropped_a_column_TEMP;
View created.

ops$tkyte@8i>
ops$tkyte@8i> create or replace trigger dropped_a_column_IOI
  2  instead of insert on dropped_a_column
  3  begin
  4          insert into dropped_a_column_TEMP
  5          ( x )
  6          values
  7          ( :new.x );
  8  end;
  9  /

Trigger created.

Here we are just getting rid of :new.y.  We do not do anything
with it -- just ignore it.  It needs to be in the view so imp
has something to insert into but we discard it in the trigger

Now we are ready to import again:

ops$tkyte@8i> host imp userid=/ full=y ignore=y

Import: Release 8.1.5.0.0 - Production on Tue Jun 13 07:23:50
2000

(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.5.0.0 -
Production
With the Partitioning and Java options
PL/SQL Release 8.1.5.0.0 - Production

Export file created by EXPORT:V08.01.05 via conventional path
import done in US7ASCII character set and US7ASCII NCHAR
character set
. importing OPS$TKYTE's objects into OPS$TKYTE
. . importing table    "ADDED_A_COLUMN"          1 rows imported
. . importing table  "DROPPED_A_COLUMN"          1 rows imported
. . importing table "MODIFIED_A_COLUMN"          1 rows imported
Import terminated successfully without warnings.

Now the import runs cleanly...  We need to go into the
database and drop our views and rename our tables now:

ops$tkyte@8i> drop view modified_a_column;
View dropped.

ops$tkyte@8i> drop view dropped_a_column;
View dropped.

ops$tkyte@8i> rename dropped_a_column_TEMP to dropped_a_column;
Table renamed.

ops$tkyte@8i> rename modified_a_column_TEMP to
modified_a_column;
Table renamed.

When we look at the data, we expect to see:

o 3 rows in added_a_column from the original insert and 2
imports
o 2 rows in dropped_a_column from the original insert and 1
import that worked on it
o 1 row in modified_a_column since we had to empty that table
prior to altering the column type.

ops$tkyte@8i> select * from added_a_column;

         X          Y
---------- ----------
         1
         1
         1

ops$tkyte@8i> select * from dropped_a_column;

         X
----------
         1
         1

ops$tkyte@8i> select * from modified_a_column;

         X Y
---------- ---------
         1 14-JUN-00

ops$tkyte@8i>
Review & Followup
Rating: 5
May 02, 2001
Reviewer: Pichaimani from San Jose, CA, USA
Hi Tom,
   Thanks a lot for all these wonderful case scenarios and solutions. I've
really started enjoying working in Oracle.

Rating: 5
November 20, 2001
Reviewer: Helena Markova from Bratislava, Slovakia

Rating: 4
partitioning August 19, 2002
Reviewer: Anna from California
Tom, do you have any trick like this for cases when export was done from
partitioned table and target server does not have partitioning enabled?

Followup:
I don't have any instances without partitiong but I did do this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> CREATE TABLE t
  2  (
  3    x int primary key,
  4    collection_year int
  5  )
  6  PARTITION BY RANGE (COLLECTION_YEAR) (
  7    PARTITION PART_95 VALUES LESS THAN (1996) ,
  8    PARTITION PART_96 VALUES LESS THAN (1997) ,
  9    PARTITION PART_97 VALUES LESS THAN (1998) ,
 10    PARTITION PART_98 VALUES LESS THAN (1999) ,
 11    PARTITION PART_99 VALUES LESS THAN (2000) ,
 12    PARTITION PART_00 VALUES LESS THAN (2001) ,
 13    PARTITION PART_01 VALUES LESS THAN (MAXVALUE)
 14  )
 15  ;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t
  2  select rownum, 1995+mod(rownum,7) from all_objects;

23620 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM> !exp userid=/ tables=t

Export: Release 8.1.7.4.0 - Production on Tue Aug 20 07:29:03 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.7.4.0 - Production
With the Partitioning option
JServer Release 8.1.7.4.0 - Production
Export done in WE8ISO8859P1 character set and WE8ISO8859P1 NCHAR character set

About to export specified tables via Conventional Path ...
. . exporting table                              T
. . exporting partition                        PART_95       3374 rows exported
. . exporting partition                        PART_96       3375 rows exported
. . exporting partition                        PART_97       3375 rows exported
. . exporting partition                        PART_98       3374 rows exported
. . exporting partition                        PART_99       3374 rows exported
. . exporting partition                        PART_00       3374 rows exported
. . exporting partition                        PART_01       3374 rows exported
Export terminated successfully without warnings.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int, collection_year int
);

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> !imp userid=/ tables=t ignore=y

Import: Release 8.1.7.4.0 - Production on Tue Aug 20 07:29:29 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.7.4.0 - Production
With the Partitioning option
JServer Release 8.1.7.4.0 - Production

Export file created by EXPORT:V08.01.07 via conventional path
import done in WE8ISO8859P1 character set and WE8ISO8859P1 NCHAR character set
. importing OPS$TKYTE's objects into OPS$TKYTE
. . importing partition                  "T":"PART_95"       3374 rows imported
. . importing partition                  "T":"PART_96"       3375 rows imported
. . importing partition                  "T":"PART_97"       3375 rows imported
. . importing partition                  "T":"PART_98"       3374 rows imported
. . importing partition                  "T":"PART_99"       3374 rows imported
. . importing partition                  "T":"PART_00"       3374 rows imported
. . importing partition                  "T":"PART_01"       3374 rows imported
Import terminated successfully without warnings.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from t;

  COUNT(*)
----------
     23620

Have you tried using ignore=y on a pre-created table?

Rating: 4
This would be a lifesaver if I could get it to work! September 17, 2002
Reviewer: Janine Sisk from Mont Vernon, NH USA
The column I am trying to drop is a LOB column, and it's just not working for
me.  I've followed the steps - renamed the table, created the view (matching
each dummy value selected to the datatype of its column), created the trigger,
and imp'd just that file. Everything looks fine, but it always fails with
ORA-00932: inconsistent datatypes, and no rows are inserted into the table.

I'm guessing this has something to do with the way imp handles LOB data.  Very
frustrating!

Followup:
Yes, as you can imagine -- LOBS and LONGS are "special" and take some very
special handling.

You'll need to imp into a table -- rename the table and then CREATE NEW_TABLE as
select from this table to perform any sort of conversions.

September 17, 2002 -- Thanks for the question regarding "Primary Key created does not reflect in DBA_INDEXES", version 8.1.7.2.1
You Asked
Hi Tom,

We would like to know more about the DBA_INDEXES and how it works internally.

Our problem here is we have a Primary Key created for a table but does not have
corresponding information reflected in dba_indexes.

SQL> SELECT * FROM dba_constraints WHERE table_name = 'STD_SEM_HIST' and
constraint_type in ('P','U');

OWNER CONSTRAINT_NAME C TABLE_NAME
------------------------------ ------------------------------ -
------------------------------
SEARCH_CONDITION
--------------------------------------------------------------------------------
R_OWNER R_CONSTRAINT_NAME DELETE_RU STATUS
DEFERRABLE
------------------------------ ------------------------------ ---------
-------- --------------
DEFERRED VALIDATED GENERATED BAD RELY LAST_CHANGE
--------- ------------- -------------- --- ---- -----------
IISDBM US_SMH_PK_AYSMMN P STD_SEM_HIST
ENABLED
NOT DEFERRABLE
IMMEDIATE VALIDATED USER NAME 28-JUN-1999
IISDBM UI_SMH_UK_AYSMMNCC U STD_SEM_HIST
ENABLED
NOT DEFERRABLE
IMMEDIATE VALIDATED USER NAME 28-JUN-1999

SQL> select * from dba_indexes
where index_name in (SELECT constraint_name FROM dba_constraints WHERE
table_name = 'STD_SEM_HIST' and constraint_type in ('P','U'));

no rows selected

We tried to insert a duplicate record and we get the following error (which
shows that the Primary Key is taking effect) :

SQL> insert into std_sem_hist
2 (CRSE_C,SEM_ACAD_Y,SEM_C,SEM_DEG_C,SEM_FAC_C,SEM_SEX_C,SEM_STS_C,STD_MATRIC_N)
3 values
4 ('BDG4', '1998/1999', '1', '101', '37', 'F', 'L', '954460M10')
5 ;
insert into std_sem_hist
*
ERROR at line 1:
ORA-00001: unique constraint (IISDBM.US_SMH_PK_AYSMMN) violated

Can you tell us why it's behaving this way ?

PS: We logged a TAR with the Oracle support, unfortunately they are not able to
give us a valid explanation. But they did tell us what we already know...
and we said...
You are making the false assumption that a primary key will ALWAYS create an
index -- it will not.

A primary key will use an existing indexe (be it unique or non-unique) to
enforce itself.

Consider the following example:

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;
Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( a int, b int, c int );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx on t(a,b);
Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t add constraint t_pk primary
key(a);
Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select constraint_name, constraint_type from
user_constraints;

CONSTRAINT_NAME                C
------------------------------ -
T_PK                           P

ops$tkyte@ORA817DEV.US.ORACLE.COM> select index_name, uniqueness from
user_indexes;

INDEX_NAME                     UNIQUENES
------------------------------ ---------
T_IDX                          NONUNIQUE

there, we have a primary key constraint and a single NON-UNIQUE index (that is
not even on the primary key alone!!).  In this case the primary key "kidnapped"
the existing index and will utilize it to enforce unqiueness.

Now consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;
Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( a int, b int, c int );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t add constraint t_pk primary
key(a);
Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx on t(a,b);
Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select constraint_name, constraint_type from
user_constraints;

CONSTRAINT_NAME                C
------------------------------ -
T_PK                           P

ops$tkyte@ORA817DEV.US.ORACLE.COM> select index_name, uniqueness from
user_indexes;

INDEX_NAME                     UNIQUENES
------------------------------ ---------
T_IDX                          NONUNIQUE
T_PK                           UNIQUE

ops$tkyte@ORA817DEV.US.ORACLE.COM

Here we do have a unique index created.  The add constriant came BEFORE the
create index -- hence there was no suitable index existing and the primary key
created on for us.  Notice that it created a unique index by default -- this is
not always the case as shown below:

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( a int, b int, c int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t add constraint t_pk primary
key(a) deferrable;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx on t(a,b);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select constraint_name, constraint_type from
user_constraints;

CONSTRAINT_NAME                C
------------------------------ -
T_PK                           P

ops$tkyte@ORA817DEV.US.ORACLE.COM> select index_name, uniqueness from
user_indexes;

INDEX_NAME                     UNIQUENES
------------------------------ ---------
T_IDX                          NONUNIQUE
T_PK                           NONUNIQUE

Here, by using a deferrable constraint, we can see the primary key actually
created a NON-UNIQUE index for us.
Review & Followup
Rating: 5
Woudn't it be beneficial to create a unique index for a primary key -avoid confusions March 14, 2002
Reviewer: Ravi from USA
Hi Tom,
It was very useful. But, for administration/maintenance point-of-view, wouldn't
it be beneficial (ofcourse, that's not the way it is, but..) to have a unique
index automatically when we create a primary key?? This is instead of
"kidnapping" (your word, I like it) an existing index (especially even when it
is a composite one) and making it as a part of unique??
Thanks!
Ravi.

Followup:
No, no it wouldn't.

First of all -- the index NEED NOT BE UNIQUE, we have and support deferrable
constraints.

Secondly, there are cases where having the index by non-unique is EXTREMELY
advantageous (data warehouse where i must disable/enable the constraint
sometimes.  If I use a non-unique index, we can get rid of the constraint
WITHOUT losing the index and put the constraint BACK without having to rebuild
it).

There is nothing, but nothing that says "primary keys MUST build a unique
constraint".  As long as an index exists with the primary key columns in any
order on the leading edge of the index -- that is more then good enough.

Rating: 5
March 14, 2002
Reviewer: A reader
What i understood so far was PRIMARY KEY enforce uniqueness by creating UNIQUE
index on column , But i am now wrong.

 Can you explain how PRIMARY KEY maintain uniqueness WITHOUT unique index ?

Followup:
The constraint is enforced procedurally by the server as always -- the index is
used to speed access to the data that needs to be checked.  The index is a
performance thing with constraints, thats all.  Whether it is unique or not --
it is a performance thing.

Rating: 4
Index disappears when pramary key is disabled March 21, 2002
Reviewer: Ravi Kumar from Chicago, IL
Hi Tom,

There is a primary key on a table with two columns. When I disable the
primarykey and run queries, the index is not used. So, how to use that index
even when I disable the primary key.

Thanks,
Ravi.

Followup:
use a non-unique index.  It is NOT that the index isn't used -- it is that the
(unique) index is DROPPED when you disable the constraint.

This demonstrates one way to achieve that, just create the constraint as
DEFERRABLE:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t add constraint t_pk primary
key(x);

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select index_name, uniqueness from
user_indexes;

INDEX_NAME                     UNIQUENES
------------------------------ ---------
T_PK                           UNIQUE

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where x = 1;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   INDEX (UNIQUE SCAN) OF 'T_PK' (UNIQUE)

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t disable constraint t_pk;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where x = 1;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'T'

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t drop constraint t_pk;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t add constraint t_pk primary
key(x) DEFERRABLE;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select index_name, uniqueness from
user_indexes;

INDEX_NAME                     UNIQUENES
------------------------------ ---------
T_PK                           NONUNIQUE

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where x = 1;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   INDEX (RANGE SCAN) OF 'T_PK' (NON-UNIQUE)

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t disable constraint t_pk;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where x = 1;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   INDEX (RANGE SCAN) OF 'T_PK' (NON-UNIQUE)

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off
ops$tkyte@ORA817DEV.US.ORACLE.COM>

Rating: 5
"Primary Key created does not reflect in DBA_INDEXES", version 8.1.7.2.1 July 12, 2002
Reviewer: asake from London, UK
Extremely useful as always.

I have a problem in a DataW/H with deferrable constraints using Oracle 8.1.5.0 .
After a dataload we ended up with duplicate rows and nulls being inserted in
tables with composite primary keys. The tables all have the primary key as
deferrable novalidate.

I understand that with deferrable constraints, Oracle creates a NON-UNIQUE INDEX
which is what we have on these tables, the issue I have is why we can insert
duplicates and nulls in columns defined as not null during table creation and
are columns in the primary key.

Is this standard behaviour?

Thanks

Followup:
how did you do the load?
what is the status of the constraint in dba_constraints?

Rating: 4
July 12, 2002
Reviewer: A reader
Hi Tom,

The data was loaded using Powermart. The status of all constraints in
dba_constraints is enabled.

Asake

Followup:
I believe that powermart is loading the bad data -- it should be scrubbing it
for you.

They are using some of our data warehousing features that allow you to enable
constraints -- but not validate them -- letting the database "trust" you did the
right thing.  Powermart -- isn't doing the right thing.

consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t add constraint t_pk primary
key(x) deferrable;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t disable constraint t_pk;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t enable novalidate constraint
t_pk ;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

         X
----------
         1
         1

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1 );
insert into t values ( 1 )
*
ERROR at line 1:
ORA-00001: unique constraint (OPS$TKYTE.T_PK) violated

ops$tkyte@ORA817DEV.US.ORACLE.COM> select status from user_constraints where
constraint_name = 'T_PK';

STATUS
--------
ENABLED

ops$tkyte@ORA817DEV.US.ORACLE.COM>

Powermart must be doing the same thing (enabling the constraint with NOVALIDATE)
-- but they didn't scrub the data first.

Time to give them a buzz....

Rating: 4
July 15, 2002
Reviewer: asake
Hi Tom,

Thanks for the update.
Enabling a constraint "novalidate", should not allow new data that violates the
constraint to be entered. If the constraint is also deferred, this means the
data is only checked when the transaction commits. I don't understand how
Powermart comes into play as Oracle is menat to enforce the integrity of the
data. I have done a straight insertion into my test table and I can insert
duplicates and nulls without using Powermart.

Kindly clarify: table created originally with composite primary key and primary
key set as deferrable novalidate allows the insertion of nulls and duplicates?

Thanks

Followup:
Give me a test case including the CREATE TABLE.  What you describe now is
different then before.  Before you said "after a load..." and I showed how that
can happen

Prove to me with a cut and paste from sqlplus that what you say is correct.
show the query that proves the constraint is there and working.  Use my
print_table routine like this:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1035431863958
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec print_table( 'select * from
user_constraints where constraint_name = ''T_PK'' ' )
OWNER                         : OPS$TKYTE
CONSTRAINT_NAME               : T_PK
CONSTRAINT_TYPE               : P
TABLE_NAME                    : T_OLD
SEARCH_CONDITION              :
R_OWNER                       :
R_CONSTRAINT_NAME             :
DELETE_RULE                   :
STATUS                        : ENABLED
DEFERRABLE                    : DEFERRABLE
DEFERRED                      : IMMEDIATE
VALIDATED                     : NOT VALIDATED
GENERATED                     : USER NAME
BAD                           :
RELY                          :
LAST_CHANGE                   : 12-jul-2002 17:33:59
-----------------

PL/SQL procedure successfully completed.

so we can read it.  show us the primary key -- tell us the columns -- insert
some duplicate data, commit and show us the outcome.

show me like I show you in short.

Rating: 5
Excellent insight into Primary Key constraint use of indexes August 01, 2002
Reviewer: Dave Anderson from Rhode Island, USA
This was solid info into how PK constraints use indexes.

Rating: 4
How to determine what index is used to enforce constraint. September 17, 2002
Reviewer: David Fitzwater from Sunny, Portland, OR
On version 8.06, is there a view or method which will determine what index is
enforcing the primary or alternate key uniqueness?

I use the technique a lot (i.e non unique index for enforcing constraint).  It's
really handy when other data is accessed via the primary key, but there must be
other indexes (precluding iot).  It would be handy to quickly access where this
technique is used in those cases where one is considering some maintenance or
changes to the underlying index.

Followup:
In 9i -- there is a column in user_constraints that gives you this but not
before.

Before that, see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1360203050892
for something that will help you

September 17, 2002 deepa -- Thanks for the question regarding "Reusing Datafile", version 8.1.7
You Asked
HI tom,

If I do this:
drop tablespace tablespace_name including contents.
How can I reuse that datafile?

Also another one.
I added a second datafile to a tablespace.
Then,idid,

alter datafile '          .ora' offline drop;

i deleted manually the datafile also.
when i restarted the server, it asked for the second datafile.
how to remove the datafile from the tablespace.

thanks!
and we said...
Yes, you can reuse it, that is exactly what reuse is for.

You messed up with the second one -- totally.  Read
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:515222144417
September 18, 2002 mathew -- Thanks for the question regarding "initial extents", version any version
You Asked
hi,
  Is it possible to redefine/alter the initial size of an object after it has
been created?
When one truncates a table does the table come back to the initial *min extent
size?

regards,
mathew
and we said...
You can deallocate unused space but it is not really normal.

In 8i and later, you would be using locally managed tablespaces and things like
this won't even be a consideration at all.

It depends on how you use truncate, it can either retain or release the extra
storage.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t
  2  tablespace system
  3  storage ( initial 8k pctincrease 0 next 8k minextents 5 )
  4  as
  5  select * from all_objects where 1=0;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from user_extents where
segment_name = 'T';

  COUNT(*)
----------
         5

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert /*+ append */ into t select * from
all_objects;

23613 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from user_extents where
segment_name = 'T';

  COUNT(*)
----------
       316

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> truncate table t reuse storage;

Table truncated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from user_extents where
segment_name = 'T';

  COUNT(*)
----------
       316

ops$tkyte@ORA817DEV.US.ORACLE.COM> truncate table t drop storage;

Table truncated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from user_extents where
segment_name = 'T';

  COUNT(*)
----------
         5
Review & Followup
Rating: 3
September 18, 2002
Reviewer: mathew from India
hi,
  thanks for the example as it made things very clear. But why do you say that
using LMT sizing wont be a consideration? If a table has 20 initial extents
allocated by mistake how can I deallocate it and why shouldn't I be worried
about it if I were using LMT?
thanks.

Followup:
don't use storage parameters

initial
next
pctincrease

every again and never worry about it.

Rating: 5
Initial Extents November 02, 2003
Reviewer: dinesh from Australia
Excellent!!

September 18, 2002 Jesse -- Thanks for the question regarding "How to monitor record count during an execution of a procedure", version 8.1.7
You Asked
Tom,
  We used to use dbms_output.put_line to output messages to debug a procedure.
But the messages only appears after the execution completes. We need to monitor
a record count as to how many records have been processed during the execution
of a procedure, do you have any method to do that instead of using the
dbms_output? Thks.
Rgds,
Jesse
and we said...
see

dbms_application_info

it is in the supplied packages guide with all of the other dbms_ and utl_
packages

if you have my book -- see my section on it, goes over exactly how to use
set_session_longops to do exactly this.
Review & Followup
Rating: 5
want to now name of your book September 18, 2002
Reviewer: satish c jagtap from INDIA
Hello Mr. Tom

My self saitsh from India. I alway get good and usefull
information for your site.

My query is very simple I want to now you always tell see may book I want to
know the title of your book so I can
buy for me.

regards
s c jagtap

Followup:
goto
http://asktom.oracle.com/
and click on my face.  It is right there.

Rating: 5
want to now name of your book September 18, 2002
Reviewer: satish c jagtap from INDIA
Hello Mr. Tom

My self satish from India. I alway get good and usefull
information for your site.

My query is very simple I want to now you always tell see may book I want to
know the title of your book so I can
buy for me.

regards
s c jagtap

Rating: 5
Re: want to now name of your book September 18, 2002
Reviewer: A reader
The book Title is Expert One on One Oracle
from Wrox Press.

The author is our great Tom Kyte.

info of the book is on the home page of this web site also.
http://asktom.oracle.com/

September 18, 2002 Balaji -- Thanks for the question regarding "To get a Single Row", version 8.1.7
You Asked
Hi Tom,
    Lucky to get a chance to ask a question.

1) We have a table called ME_TXN_ACC_ENTIRES and the data is as follows

Ac_Number       Batch_no           Txn_Amount
---------       --------           ----------
2700490018      970887             0.25
2700490018      970887             -35.06
2700490018      970887             -2536.11
2700490019      970887              2536.11
2700490019      970887             2000
2700490020      970887             100

2700490022      970888             1.25
2700490023      970888             -135.06
2700490025      970888             -536.11
2700490025      970888              536.11
2700490026      970888             5000
2700490026      970888             1000

With the above data i need to select to an AC_NUMBER with the
min(abs(txn_amount).

For Batch No
970887   => 2700490018 is the AC_NUMBER
970888   => 2700490022 is the AC_NUMBER

To Achieve this i am using the following sql statement.

select z.* from
(select a.ac_number
from me_txn_acc_entries
where a.batch_no = '970887'
order by abs(a.txn_amt)
) z
where rownum < 2

I have created a function with the above statement and call this as a part of
the query to get the AC_NUMBER.

I am sure there should be some other method to retrieve the AC_NUMBER without
passing the BATCH_NO and doing it in one Single SQL Statment.

Can u help me on this regard.

2) I have a table A, with a column whose value should be autoincrement, to
achieve this i created a sequence and use it during the Insert as follows

Insert into A(id, Name, Dept)
select sq_id.nextval, d_name,d_dept
from dept;

when i run the above sql, it takes lot of time to Insert the rows from dept.
From v$sqlarea i found  statement

update seq$ .....

which appears everytime when it inserts rows and it takes lot of time to
complete and proceed with the next statement (meaning the i can see the update
seq$ for some time even though you refresh the sql which sees the sql statement
which is been currently processing for the sid and sql hash value).

Please let me know, how to solve this problem.

Thanks and Regards
M.Balaji
and we said...
1) analytics rock:

ops$tkyte@ORA920.LOCALHOST> select distinct batch_no,
  2                  first_value(ac_number) over
  3                                  (partition by batch_no
  4                                   order by abs(txn_amount) ) ac_num,
  5                  first_value(txn_amount) over
  6                                  (partition by batch_no
  7                                   order by abs(txn_amount) ) ac_num
  8    from t
  9  /

  BATCH_NO     AC_NUM     AC_NUM
---------- ---------- ----------
    970887 2700490018        .25
    970888 2700490022       1.25

ops$tkyte@ORA920.LOCALHOST>

but there are other ways as well

ops$tkyte@ORA920.LOCALHOST> select batch_no,
  2         substr(
  3            min( to_char( abs( txn_amount ), 'fm0000009.99' ) || ac_number ),
 4            11 ) ac_num
  5    from t
  6   group by batch_no
  7  /

  BATCH_NO AC_NUM
---------- -----------------------------------------
    970887 2700490018
    970888 2700490022

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> select batch_no,
  2         ac_number
  3    from t
  4   where abs(txn_amount) = ( select min(abs(txn_amount))
  5                               from t t2
  6                              where t2.batch_no = t.batch_no)
  7  /

  BATCH_NO  AC_NUMBER
---------- ----------
    970887 2700490018
    970888 2700490022

ops$tkyte@ORA920.LOCALHOST>

2) create sequence sq_id cache 1000;

or alter sequence sq_id cache 1000;
Review & Followup
Rating: 5
Exactly the type of query construct am working on! September 18, 2002
Reviewer: Robert from Memphis, USA
I have been waiting to send you a question regarding my query, but this one is
the exact logic as mine. I am going to immediately go to SQL*Plus and apply your
answer to my query.

Thanks!

Rating: 5
Excellent September 18, 2002
Reviewer: Balaji.M from Singapore
Hi Tom,

This is what i wanted and i am going to explore more on analytical functions.

No one can come near you.

Thanks and Regards
M.Balaji

Rating: 5
To get a single row, version 8.1.7 July 30, 2003
Reviewer: Derek Adachi from Seattle, WA USA
I had a dilemma - to retrieve unique rows, which included a time stamp.  The
time stamp would prevent expected output from using the DISTINCT clause and all
attempts to use DENSE_RANK() failed to just return the first instance and drop
the duplicates.

Seeing the example using FIRST_VALUE() gave me the idea to try using the
partition and ordering criteria I was using in my call to the ranking function.
It worked wonderfully.  My query ended up as:
select distinct host, substr(descs,instr(descs,':')+1,length(descs)) Message,
FIRST_VALUE(time_stamp) OVER (PARTITION BY host, descs ORDER BY descs ASC) time
from mastlog_view WHERE EXISTS (SELECT DISTINCT name from inv_machine) AND
error_id='26101' AND (to_date(to_char(SysDate, 'DD/MON/YYYY')) -
to_date(to_char(time_stamp,'DD/MON/YYYY'))) <= 1 ORDER BY host, time ASC

September 18, 2002 She -- Thanks for the question regarding "AQ subscriber", version 8.1.7
You Asked
Hi Tom,
Thanks for taking this question.

I have a queue called msg_queue_admin

I have created it using :
execute dbms_aqadm.create_queue_table (    Queue_table          =>
'cmis_static.msg_table_admin', Multiple_consumers => TRUE, Queue_payload_type =>
'object_type' );

execute dbms_aqadm.create_queue (    Queue_table          =>
'cmis_static.msg_table_admin', Queue_name  => 'cmis_static.msg_queue_admin' )

Object type looks like this :
create or replace type object_type  as object (
 object_name            varchar2(50),
 implementation_type    varchar2(80)
 );

Now I want to add a subsriber to this queue and I want to do the following :

dbms_aqadm.add_subscriber(
queue_name => 'cmis_static.msg_queue_admin'  ,
subscriber => 'customer',
rule       => 'object_type.object_name = ''customer''');

Basically I want to add 2 subscribers - one for customer and the other for
product .
Can I do this ?

Thanks a lot
and we said...
I asked Derrick Lawson, our resident AQ expert, for assistance on this and he
says:

Yes you can do this.  The trick is that the rule parameter must use the
following qualifier syntax for object type in the queue table, in this case :

declare
 subscriber sys.aq$_agent;
begin
--
subscriber := sys.aq$_agent( ... );
--
dbms_aqadm.add_subscriber(
queue_name => 'cmis_static.msg_queue_admin',
subscriber => subscriber,
rule       => 'tab.user_data.object_name = ''customer''');
--
end;
/

I have attached a script which shows a test case.  I hope this helps.

create user thinker identified by thinker;
grant aq_administrator_role         to THINKER;
grant execute on dbms_aq            to THINKER;
grant execute on dbms_aqadm         to THINKER;

exec dbms_aqadm.grant_system_privilege('ENQUEUE_ANY', 'THINKER');
exec dbms_aqadm.grant_system_privilege('DEQUEUE_ANY', 'THINKER');
exec dbms_aqadm.grant_system_privilege('MANAGE_ANY',  'THINKER');

connect thinker/thinker;

prompt ... create the object type
create or replace type foo as object (
object_name            varchar2(50),
implementation_type    varchar2(80))
/

prompt ... build the initial queue table, the queue, and start the queue
begin
  dbms_aqadm.create_queue_table( 'msg_test_qt',             -- queue_table
                                 'foo',                     --
queue_payload_type
                                  null,                     -- storage_clause
                                  null,                     -- sort_list
                                  true,                     --
multiple_consumers
                                  null,                     -- Message Grouping
                                  null,                     -- comment
                                  true,                     -- auto_commit
                                  0,                        -- primary_instance
                                  0,                        --
secondary_instance
                                 '8.1' );                   -- compatible

  dbms_aqadm.create_queue( 'msg_test_queue',          -- queue_name
                           'msg_test_qt',             -- queue_table
                            DBMS_AQADM.NORMAL_QUEUE,  -- queue_type
                            null,                     -- max_retries
                            0,                        -- retry_delay
                            0,                        -- retention_time
                            false,                    -- dependency_tracking
                            null,                     -- comment
                            true );                   -- auto_commit

  dbms_aqadm.start_queue( 'msg_test_queue',           -- queue_name
                           true,                      -- enqueue
                           true );                    -- dequeue
end;
/

prompt ... build the first propagation queue table, the queue, and start the
queue
begin
  dbms_aqadm.create_queue_table( 'msg_prop_qt1',            -- queue_table
                                 'foo',                     --
queue_payload_type
                                  null,                     -- storage_clause
                                  null,                     -- sort_list
                                  true,                     --
multiple_consumers
                                  null,                     -- Message Grouping
                                  null,                     -- comment
                                  true,                     -- auto_commit
                                  0,                        -- primary_instance
                                  0,                        --
secondary_instance
                                 '8.1' );                   -- compatible

  dbms_aqadm.create_queue( 'msg_prop_queue1',         -- queue_name
                           'msg_prop_qt1',            -- queue_table
                            DBMS_AQADM.NORMAL_QUEUE,  -- queue_type
                            null,                     -- max_retries
                            0,                        -- retry_delay
                            0,                        -- retention_time
                            false,                    -- dependency_tracking
                            null,                     -- comment
                            true );                   -- auto_commit

  dbms_aqadm.start_queue( 'msg_prop_queue1',          -- queue_name
                           true,                      -- enqueue
                           true );                    -- dequeue
end;
/

prompt ... build the second propagation queue table, the queue, and start the
queue
begin
  dbms_aqadm.create_queue_table( 'msg_prop_qt2',            -- queue_table
                                 'foo',                     --
queue_payload_type
                                  null,                     -- storage_clause
                                  null,                     -- sort_list
                                  true,                     --
multiple_consumers
                                  null,                     -- Message Grouping
                                  null,                     -- comment
                                  true,                     -- auto_commit
                                  0,                        -- primary_instance
                                  0,                        --
secondary_instance
                                 '8.1' );                   -- compatible

  dbms_aqadm.create_queue( 'msg_prop_queue2',         -- queue_name
                           'msg_prop_qt2',            -- queue_table
                            DBMS_AQADM.NORMAL_QUEUE,  -- queue_type
                            null,                     -- max_retries
                            0,                        -- retry_delay
                            0,                        -- retention_time
                            false,                    -- dependency_tracking
                            null,                     -- comment
                            true );                   -- auto_commit

  dbms_aqadm.start_queue( 'msg_prop_queue2',          -- queue_name
                           true,                      -- enqueue
                           true );                    -- dequeue
end;
/

prompt ... build the first subscriber
declare
  subscriber sys.aq$_agent;
begin
--
subscriber := sys.aq$_agent('THINKER', 'msg_prop_queue1', null);
--
dbms_aqadm.add_subscriber(
queue_name => 'msg_test_queue',
subscriber => subscriber,
rule       => 'tab.user_data.object_name = ''FIRST''');
--
end;
/

prompt ... build the second subscriber
declare
  subscriber sys.aq$_agent;
begin
--
subscriber := sys.aq$_agent('THINKER', 'msg_prop_queue2', null);
--
dbms_aqadm.add_subscriber(
queue_name => 'msg_test_queue',
subscriber => subscriber,
rule       => 'tab.user_data.object_name = ''SECOND''');
--
end;
/

prompt ... schedule the propagation
begin
dbms_aqadm.schedule_propagation(queue_name => 'msg_test_queue');
end;
/

prompt ... build an enqueue procedure
create or replace procedure enqueue_test_message(
  p_name in varchar2,
  p_type in varchar2)
as
  l_e_opts_t   dbms_aq.enqueue_options_t;
  l_m_prop_t   dbms_aq.message_properties_t;
  l_rec        dbms_aq.aq$_recipient_list_t;
  l_msgid      raw( 16 );
  l_foo        Foo;
begin
  l_foo := Foo(p_name,p_type);
  dbms_aq.enqueue('msg_test_queue', l_e_opts_t, l_m_prop_t, l_foo, l_msgid);
exception
  when others then
    raise_application_error(-20001,'Unable to enqueue message '||sqlerrm);
end enqueue_test_message;

prompt ... enqueue a few messages to test
begin
  enqueue_test_message(p_name =>'FIRST',p_type =>'FIRST_TYPE');
  enqueue_test_message(p_name =>'SECOND',p_type =>'SECOND_TYPE');
end;
/
commit;

prompt ... see if they propagated
select * from msg_prop_qt1;
select * from msg_prop_qt2;

prompt ... there is a message in each propagation table so the rules worked
Review & Followup
Rating: 5
Thanks April 10, 2002
Reviewer: She from US
The example was really good. Explained AQ propagation really well. I need to try
this out now !

Rating: 5
September 18, 2002
Reviewer: Sasa from Belgrade, Yugoslavia
Hi Tom,

We have a replication/synchronyzation  system with AQ which works perfectly.It's
defined as multiconsumer queue.But currently number of message in the queue
table increased and it could be a problem in the future.
I have read in Oracle doc that only way in Oracle 8.1.6 to remove message in
multiconsumer queue is to enable Queue monitor.(queue monitor process spawning
every minute)
We have Oracle 8.1.7 and I am wondering is the same princip applicable to 8.1.7
?
If is it applicable then how can we change frequency of Queue monitor process?

Thanks,

Sasa

Followup:
The qmn process is still responsible for that.  the period used by qmn is
"internalized" just like smon and pmon timers.  You do not control it.

September 18, 2002 mathew -- Thanks for the question regarding "xml problem", version 9i r2
You Asked
Hi,
I created a table with a definition as:
create table xml_tab
(
docid number,
dtd clob,
xmldoc clob,
valid number
);

A record has been inserted as docid = 1,
dtd as
-----------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT DEPTNO (#PCDATA)>
<!ELEMENT EMPNO (#PCDATA)>
<!ELEMENT ENAME (#PCDATA)>
<!ELEMENT HIREDATE (#PCDATA)>
<!ELEMENT ROW (EMPNO, ENAME, HIREDATE, DEPTNO)>
<!ATTLIST ROW
num CDATA #REQUIRED
>
<!ELEMENT ROWSET (ROW)>
-----------------------------------------------
and a xmldoc as
-----------------------------------------------
<?xml version = '1.0'?>
<ROWSET>
<ROW num="1">
<EMPNO>7369</EMPNO>
<ENAME>SMITH</ENAME>
<HIREDATE>12/17/1980 0:0:0</HIREDATE>
<DEPTNO>20</DEPTNO>
</ROW>
</ROWSET>
-----------------------------------------------
I need to validate XML documents stored in a XMLDOC column with a
corresponding DTD stored in a DTD column.

I tried and got errors as follows:
---------------------------------------------------------------------------------
----
SQL> declare
2 l_p XMLParser.Parser;
3 l_xmldom XMLDOM.DOMDocument;
4 l_dtd clob;
5 l_root varchar2(100) := 'ROWSET';
6 l_raw RAW(8);
7 l_ValidMode Boolean;
8 begin
9 for rec1 in (select * from xml_tab where docid = 1)
10 loop
11 --l_xmldom := xmlDom.NewDOMDocument;
12 l_p := xmlParser.NewParser;
13 xmlParser.setValidationMode(l_p, true);
14 xmlParser.parseDTDClob(l_p, rec1.dtd, l_root);
15 xmlParser.parseClob(l_p, rec1.xmlDoc);
16 l_ValidMode := xmlParser.getValidationMode(l_p);
17 if l_ValidMode = true then
18 dbms_output.put_line('True');
19 else
20 dbms_output.put_line('False');
21 end if;
22 end loop;
23 end;
24 /
declare
*
ERROR at line 1:
ORA-20100: Error occurred while parsing: Element 'ROWSET' used but
not declared.
ORA-06512: at "SYS.XMLPARSER", line 22
ORA-06512: at "SYS.XMLPARSER", line 103
ORA-06512: at line 15
---------------------------------------------------------------------------------
----

The table would contain a set of XML docs and corresponding DTDs
against which they need to be validated.
Please advice me if I am wrong anywhere or what is a proper
sequence/APIs for doing so.
and we said...
I asked Sean Dillon, our local XML technologist, to take a look at this and
here's what he had to say:
----------------------------

Hi!  Taking a look at your code and the error you received, I can make a couple
of assumptions and give you some advice.  First of all, 'XMLPARSER' is a PLSQL
package that typically belongs to SYS.  You're getting the ORA-06512 for one of
a few reasons:

PROBLEM: The XDK is not installed in your database.
RESOLUTION:  Go to
http://otn.oracle.com/tech/xdk/content.html
and download the latest production XML Developer's Kits.
 Load them into your database according to the included download instructions.

PROBLEM: The XDK is loaded, but your user has no privileges to execute those
packages.  I'm pretty sure the installation instructions for the XDK tell you
how to create public synonyms for the XDK and grant access to a user or PUBLIC,
but if not you can write some SQL that generates the appropriate statements (I'd
lean toward finding a script in the installation instructions to do this, if
possible):
RESOLUTION:

SQL> select 'GRANT EXECUTE ON ' || object_name || ' TO PUBLIC;' runme
  2    from all_objects
  3   where object_name like 'XML%'
  4     and object_type = 'PACKAGE'
  5  /

RUNME
----------------------------------------------------------
GRANT EXECUTE ON XMLATTRCOVER TO PUBLIC;
GRANT EXECUTE ON XMLCHARDATACOVER TO PUBLIC;
GRANT EXECUTE ON XMLDOCUMENTCOVER TO PUBLIC;
GRANT EXECUTE ON XMLDOM TO PUBLIC;
GRANT EXECUTE ON XMLDOMIMPLCOVER TO PUBLIC;
GRANT EXECUTE ON XMLDTDCOVER TO PUBLIC;
GRANT EXECUTE ON XMLELEMENTCOVER TO PUBLIC;
GRANT EXECUTE ON XMLENTITYCOVER TO PUBLIC;
GRANT EXECUTE ON XMLNNMCOVER TO PUBLIC;
GRANT EXECUTE ON XMLNODECOVER TO PUBLIC;
GRANT EXECUTE ON XMLNODELISTCOVER TO PUBLIC;
GRANT EXECUTE ON XMLNOTATIONCOVER TO PUBLIC;
GRANT EXECUTE ON XMLPARSER TO PUBLIC;
GRANT EXECUTE ON XMLPARSERCOVER TO PUBLIC;
GRANT EXECUTE ON XMLPICOVER TO PUBLIC;
GRANT EXECUTE ON XMLTEXTCOVER TO PUBLIC;

16 rows selected.

SQL> select 'CREATE PUBLIC SYNONYM ' || object_name || ' FOR ' ||
  2         object_name || ';' runme
  3    from all_objects
  4   where object_name like 'XML%'
  5     and object_type = 'PACKAGE'
  6  /

RUNME
----------------------------------------------------------
CREATE PUBLIC SYNONYM XMLATTRCOVER FOR XMLATTRCOVER;
CREATE PUBLIC SYNONYM XMLCHARDATACOVER FOR XMLCHARDATACOVER;
CREATE PUBLIC SYNONYM XMLDOCUMENTCOVER FOR XMLDOCUMENTCOVER;
CREATE PUBLIC SYNONYM XMLDOM FOR XMLDOM;
CREATE PUBLIC SYNONYM XMLDOMIMPLCOVER FOR XMLDOMIMPLCOVER;
CREATE PUBLIC SYNONYM XMLDTDCOVER FOR XMLDTDCOVER;
CREATE PUBLIC SYNONYM XMLELEMENTCOVER FOR XMLELEMENTCOVER;
CREATE PUBLIC SYNONYM XMLENTITYCOVER FOR XMLENTITYCOVER;
CREATE PUBLIC SYNONYM XMLNNMCOVER FOR XMLNNMCOVER;
CREATE PUBLIC SYNONYM XMLNODECOVER FOR XMLNODECOVER;
CREATE PUBLIC SYNONYM XMLNODELISTCOVER FOR XMLNODELISTCOVER;
CREATE PUBLIC SYNONYM XMLNOTATIONCOVER FOR XMLNOTATIONCOVER;
CREATE PUBLIC SYNONYM XMLPARSER FOR XMLPARSER;
CREATE PUBLIC SYNONYM XMLPARSERCOVER FOR XMLPARSERCOVER;
CREATE PUBLIC SYNONYM XMLPICOVER FOR XMLPICOVER;
CREATE PUBLIC SYNONYM XMLTEXTCOVER FOR XMLTEXTCOVER;

16 rows selected.

This, of course, needs to be run by SYS or the owner of the XML% packages.  Now,
your user should have visibility and execution privileges on the XML% packages.
Now on to your code.

I used pretty much what you had, but you'll see a couple of minor changes below.
 I'll comment in the code so you can see the highlights:

SQL> create table xml_tab (
  2    docid  number,
  3    dtd    clob,
  4    xmldoc clob,
  5    valid  number
  6  )
  7  /

Table created.

SQL> insert into xml_tab (docid,dtd,xmldoc)
  2  values (1,
  3  '<?xml version="1.0" encoding="UTF-8"?>
  4  <!ELEMENT DEPTNO (#PCDATA)>
  5  <!ELEMENT EMPNO (#PCDATA)>
  6  <!ELEMENT ENAME (#PCDATA)>
  7  <!ELEMENT HIREDATE (#PCDATA)>
  8  <!ELEMENT ROW (EMPNO, ENAME, HIREDATE, DEPTNO)>
  9  <!ATTLIST ROW
 10  num CDATA #REQUIRED
 11  >
 12  <!ELEMENT ROWSET (ROW)>',
 13  '<?xml version = "1.0" encoding="UTF-8"?>
 14  <ROWSET>
 15  <ROW num="1">
 16  <EMPNO>7369</EMPNO>
 17  <ENAME>SMITH</ENAME>
 18  <HIREDATE>12/17/1980 0:0:0</HIREDATE>
 19  <DEPTNO>20</DEPTNO>
 20  </ROW>
 21  </ROWSET>')
 22  /

1 row created.

SQL> declare
  2    l_p         xmlparser.parser;
  3    l_domdoc    xmldom.domdocumenttype;
  4    l_root      varchar2(100) := 'ROWSET';
  5
  6  -- YOU DON'T NEED THESE AT ALL
  7  --  l_xmldom       xmldom.domdocument;
  8  --  l_raw       raw(8);
  9  --  l_dtd       clob;
 10  --  l_validmode boolean;
 11
 12  begin
 13    for rec1 in (select * from xml_tab where docid = 1)
 14    loop
 15      l_p := xmlparser.newparser;
 16      xmlparser.setvalidationmode(l_p, true);
 17      xmlparser.parsedtdclob(l_p,rec1.dtd, l_root);
 18
 19      -- this gets the "DOMDOC" after parsing the dtd
 20      l_domdoc := xmlparser.getdoctype(l_p);
 21
 22      -- this actually sets the DTD ("DOMDOC") to be used by the
 23      -- validator
 24      xmlparser.setdoctype(l_p, l_domdoc);
 25
 26      if xmlparser.getvalidationmode(l_p) then
 27        dbms_output.put_line('TRUE');
 28      else
 29        dbms_output.put_line('FALSE');
 30      end if;
 31
 32      -- now the parse will attempt to validate vs the
 33      -- dtd
 34      xmlparser.parseclob(l_p, rec1.xmldoc);
 35      if xmlparser.getvalidationmode(l_p) then
 36        dbms_output.put_line('TRUE');
 37      else
 38        dbms_output.put_line('FALSE');
 39      end if;
 40    end loop;
 41  end;
 42  /
TRUE
TRUE

PL/SQL procedure successfully completed.

Hope that helps!

_smd_

_____________________________________________________________________
Sean Dillon - Principal Technologist, Oracle Corporation
Author "Beginning Oracle Programming"
http://www.amazon.com/exec/obidos/ASIN/186100690X
Review & Followup
Rating: 5
Thanks! September 18, 2002
Reviewer: EJ from nj usa
Great response. This site is the best.

Rating: 5
Link is wrong... September 19, 2002
Reviewer: A reader
The correct link is
http://otn.oracle.com/tech/xml/content.html

September 18, 2002
      deepa -- Thanks for the question regarding
      "Check Constraint", version 8.1.7

      You Asked
Hai tom,
this is the first time i am posting in your forum.

When i create a table with  a Column of datatype Date,
I was not able to create a check constraint on it.
(ie) to check whether the entry is less than a particular Date.

Is there any restriction that the checks should be varchar or number and not
date.

Is there any way for date check.

Regards,

Deepa

      and we said...
Well, examples of your problem are ALWAYS appreciated -- I have no problems with
dates and check constraints:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t
  2  ( x date
  3    check ( x < to_date( '01-jan-2005', 'dd-mon-yyyy' ))
  4  );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

Maybe you tried to compare a date to a string (which is alot like comparing a
School bus to a house)

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t
  2  ( x date
  3    check ( x < '01-jan-2005' )
  4  );
  check ( x < '01-jan-2005' )
              *
ERROR at line 3:
ORA-02436: date or system variable wrongly specified in CHECK constraint

ops$tkyte@ORA817DEV.US.ORACLE.COM>

you need to TO_DATE and use an EXPLICIT FORMAT!!!! like I did

Or, maybe you tried to use SYSDATE in the check constraint, which cannot be
done:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t
  2  ( x date
  3    check ( x < sysdate )
  4  );
  check ( x < sysdate )
              *
ERROR at line 3:
ORA-02436: date or system variable wrongly specified in CHECK constraint

But, I'm just guessing -- use to_date on a string with a format and all will be
well....
Review
      & Followup

      Rating:
      3
  September 18, 2002
Reviewer:  Sagi
       from India
Hi Tom!

Thanx for teaching one more trick.

I wanted to have a check constraint that checks that its equal to sysdate.

I know we have DEFAULT clause. But then it works only if we dont specify any
input not as check clause.

I tried

create table t1 (x date check (x = to_date(sysdate,'DD-MOM-YYYY')) )

But gives error.

ERROR at line 1:
ORA-02436: date or system variable wrongly specified in CHECK constraint

How do we acheive this.

Thanx in advance.

Regards,
Sagi

          Followup:

you'll be using a trigger then

begin
   :new.x := sysdate;
end;

before INSERT on the table for each row.

September 18, 2002 OP -- Thanks for the question regarding "How to
      import data from more than one (.csv ) [Excel] files into the related
      tables !", version 8.1.5

      You Asked
Dear Tom,

Hi !

Thanks for the previous answers.They were as perfect as
always.

Qust I have got around 50 .csv files (Excel files) containing
some data which needs to be imported into the related tables
in the database.

I have a script which I am running from the command propmt
(Windows NT) ,which makes use of sqlldr command.I am creating
a .ctl file.For every table I am creating a single .ctl file.
The sample of script is as follows

[---c1.ctl---]

LOAD DATA
INFILE  MT_srvc_ctgry_rel.csv
INTO TABLE MT_srvc_ctgry_rel
append
fields terminated by ","
( SRVC_CTGRY_CD,
 SRVC_CD,
 DEL_F,
 DEL_DT DATE 'YYYY-MM-DD',
 UPDT_USER_ID,
 CRT_DT DATE 'YYYY-MM-DD',
 UPDT_DT DATE 'YYYY-MM-DD')

I am making use of following command to execute the script

c:\> SQLLDR user1/user1@dbmatch c1.ctl c1.log

In this script MT_srvc_ctgry_rel is the Table name which is
empty and getting data from MT_srvc_ctgry_rel.csv file.
Now I am creating such script for every Table because
the csv file and ofcourse COLUMNS in every table are different.

Consider I have T1,T2,T3 and T4 tables and
C1.csv,c2.csv.c3.csv.c4.csv files containing data.
Can you suggest me a method through which the data in all
these 4 tables can be inserted at one go through there
respective .csv files.

Is there any Keyword which can be used to mark all the
COLUMNS in the table instead of writing every column in
the script in the parenthesis and also is it possible to
mention more than one file (.csv) name in the script.

Kindly answer !

Thanks & Regards
OP Lalwani

      and we said...
There is no keyword to tell it "all columns" as the order of the
columns is extremely important (it would be bad to have column A
get loaded with B's data and vice-versa).

We can use SQLPlus to generate a CTL file.  I do it for UNLOAD
for example (to unload all data).  See

http://osi.oracle.com/~tkyte/flat/index.html
it has scripts for NT you can modify.  In this fashion, you can
write a script that will generate a CTL file for a given table.
Since the datafile name can be specified on the SQLLDR command
line -- it should not put this in the control file so the
control file is more generic.  In this fashion, if you have T1,
T2 to load and you want to load d1.dat, d2.dat into T1 and
d3.dat and d4.dat into T2, you would issue commands:

  genctl t1 user/password > t1.ctl
  sqlldr user/password t1.ctl t1.log data=d1.dat
  sqlldr user/password t1.ctl t1.log data=d2.dat

  genctl t2 user/pasword > t2.ctl
  sqlldr user/password t2.ctl t2.log data=d3.dat
  sqlldr user/password t2.ctl t2.log data=d4.dat

that will be easier for you to script in any case and shows how
to use the same ctl file for many input files.

Review &
      Followup

      Rating:
      5
  September 18, 2002
Reviewer:  moiz
       from OH USA
I am using Oracle SQLLoader to upload my data from CSV files into a single
table. My current CTL file is as follows:

LOAD DATA
INFILE 'trace_metals100.csv'
INFILE 'trace_metals101.csv'
APPEND
INTO TABLE TRACE_METALS
FIELDS TERMINATED BY ','
(STATION_NO, METAL_NAME, METAL_WT)

Just for my record I want to upload the data file name also along with the data
into a fourth column. How should I do it using SQLLoader??
Regards,
Moiz

          Followup:

you cannot.  the current filename just isn't "available"

      Rating:
      5
  September 18, 2002
Reviewer:  Moiz
       from OH USA
Thanks Tom for ur answer.

Actually, I have a situation where in I have to load over 100 identical data
files thro' ONE control file into ONE table. But I want to keep an identifier
column which will tell me that a particular record was loaded from that
particular data file. It is not necessary to store the data file name. Any
identifier (number or character) will be OK.

Any suggestions??

          Followup:

You will have to write the control file on the "fly" (eg; via a script) and use
the CONSTANT or EXPRESSION features of SQLLDR to do this.

using constant, your script would write a control file that loads the constant
filename into a field.  Using expression, you would use a function like
userenv('sessionid') to load a unique number into the field.

if you did not want to have to write the control file on the fly, you could
always:

create table t ( UNIQUE_batch_ID number default userenv( 'sessionid' ),
                 ..... other fields .....

and just load the other fields, unique batch id will have the session id of the
session that loaded the file.  then load each file with a separate sqlldr
session

September 20, 2002 SUBRAMANIYAM -- Thanks for the question regarding
      "Import data", version 8.1.6

      You Asked
Hi Tom,

          I am having two DB servers.
Server1  ( Oracle 8.1.5) contains two tablespaces as follows.

TABLESPACE_NAME    Datafile                   Used SIZE
--------------     --------                   ---------
Server1_lv_ts      D:\data\server_lv.ora         1300 MB
Server1_lv_i_ts    D:\data\server_lv_i.ora       1900 MB

Server2 (Oracle 8.1.6) contains two tablespaces as follows.

TABLESPACE_NAME    Datafile                 Free SIZE
--------------     --------                    ---------
Server2_hv_ts      /data/server_hv.ora          1400 MB
Server2_hv_i_ts    /data/server_hv_i.ora        2000 MB

In server2 I have the same tablespace name but no free space.
In server2 there is no other free spaces also.

I need to import data from server1 to server2.
Now I have server1 export dump file only.
How shall I import? Can you help me.

Thanks in Advance.

With regards
shanmugam

      and we said...
I would:

imp userid=u/p full=y indexfile=temp.sql

that will create a file ONLY (it will not import any data).  In
this file will be all of the create index/create table
statements.  I would edit this file to uncomment the statements
for the objects I wanted to precreate (the tables).  I would
then edit the SQL to change the tablespace names from the names
of server1 to the names of server2.  I would run that DDL and
then import the data using IGNORE=Y to ignore object creation
errors.  IMP will then just load into the tables where ever you
have them created.

As an efficiency thing, you might chope the above file into 2
files -- one of tables and one of indexes.  YOu would run the
table creates before the imp, imp with "ignore=y indexes=n" and
then run the index create script AFTER the import.  It'll go
faster that way (and you can even split the create index script
into many create index scripts and run them in parallel to speed
things up a bit as well).

It'll take a bit of setup on your part but as there is no way to
map multiple tablespaces to a new set of tablespaces using
imp....
Review
      & Followup

      Rating: 4
Can I edit
      export dump file?  March 26, 2002
Reviewer:
      AR  from New York.
Tom, I know that this is the preferred method. However it is really painful if
you have many tables to migrate to different tablespaces. 'Alter table move
tablespace' is such a useful command for this, but if you have LONG/LONG RAW
datatypes you cant use it. I tried a shortcut: I edited the export dump file
using a sed script and replaced the tablespace name on the ddl on the header
area and imported. The data looks OK. Would you recommend this method? What
would be your recommendation for migrating tables with LONG datatype to another
tablespace? Can I convert the datatype to LOB, use alter table move and then
convert back to LONG? What I am trying to do is to move tables (according to
extent sizing) to various locally managed tablespaces with uniform extent size
in a SAP application. What would you suggest. Thanks a lot for your time.

          Followup:

NO NO NO.

dmp files are binary.  SED will mess them up, vi will mess them up, any editor
will mess them up.

I would suggest export/import of INDIVIDUAL tables.  If you do this:

o revoke unlimited tablespace from the user.
o quota them 0k on the original tablespace.
o make their default tablespace the place you want the object to go to and quota

  unlimited on that.

o exp table, drop table, import table.  table will be in new tablespace now.

      Rating: 5
Thanks
      Tom  March 26, 2002
Reviewer:  AR  from
      NYC
WOW! Thanks for the instantaneos reply. I did exactly as you suggested. I wrote
a ksh script to automate it. It worked great (imported 530 tables to various
tablespaces).

Just out of curiosity.... I realize that Oracle doesn't support editing the
export dump files. But doesn't it work sometimes? When I ran the import with the
edited dump file, the import terminated successfully. I tried this on more than
10 individual dumpfiles and everytime the import terminated successfully. The
data also looked OK. Can I assume its OK if the import doesnt give an error? Or
can there be a silent data corruption? This is only a curiosity question for
understanding how it works... I swear I dont want to try any unsupported tricks
on my database.

Thanks again Tom,
Rgds,
AR

          Followup:

NO -- it should never work.  There are binary bits and bytes in there.  Most any
editor will totally corrupt the file, just not safe at all.

      Rating: 3
Question
      Regarding Import  August 26, 2002
Reviewer:
      Riaz Shahid  from PRAL, Islamabad, Pakistan
Hello Tom!

Thank you from all of developers and DBAs comunity being helping us and giving
efficient ways to resolve problems.

Here's my problem ragarding import.

I have a table named gstt05 having (say) the following columns:

st_reg_no   char(13)   P.K.
tax_period  char(6)    P.K.
total_tax   number(14,4)

with (say) the follwoig data:

1112223334441  052002   150000
0019901567844  062002   200000

I have an updated dmp file (for the above table) with (say) the following data:

1112223334441 052002    170000

I import data but it gives me error like "Unique Integrity Constraint
Violated-row rejected" due to primary key data already present in the table. How
can i use import to update value in the gstt05 table.
The data (after import) should look like:

1112223334441 052002    170000
0019901567844  062002   200000

Regards

Riaz

          Followup:

You'll have to IMP that data into another schema and then

update ( select a.total_tax old_tax, b.total_tax new_tax
           from gstt05 a, another_schema.gstt05 b
          where a.st_reg_no = b.st_reg_no
            and a.tax_period = b.tax_period )
   set old_tax = new_tax;

      Rating: 3
Still More
      Problems....  August 27, 2002
Reviewer:  Riaz
      Shahid  from PRAL, Islamabad, Pakistan
Hello !

Thanks for your quick reply.

Sory i didn't told you "complete" problem.

The dmp file may also contain new records. What we will do in that case.

What should i do in order to:
(1) insert new record in gstt05 table, if it doesn't have that record.
(2) update gstt05 table, if it already has that record.

Regards

Riaz

          Followup:

read

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:5146520944672

      Rating: 4
import and
      internal/sys/system  September 17, 2002
Reviewer:
      Ram  from India
Hello Tom,

oracle 8.1.7 on sun solaris.
I created a new instance (for testing) and was halfway thru the import (from
prod) when the import terminated with an error.(IMP00009 and IMP00018). i was
using system/xxx.

then i tried again with options show = y,rows=n to check whether it is able to
read till the end of the export dump.

but surprisingly, i get an invalid userid/password error!
the same uid/pwd worked the first time. same situation with sys user also.

this begs the question:
1) how did the passwords change ? did the import, import the sys/system data
which had a different password ? if not then how ?

2) this is even more surprisingly ( to me at least).
connect internal still worked. so i tried the import with internal and i got the
IMP-00013-only a DBA can import a file exported by another DBA error.

i was under the impression that internal is *almost* like sys with sysdba privs.
so why this error ?

pls throw some light on this ?

thanks.

          Followup:

The full imp contained alter user statements to set the SYS and SYSTEM passwords
to what they where on the source database -- one of the very first things it did
was alter user sys identified by .... and alter user system identified by.....

connect internal doesn't require a password.

so, now you know what your passwords are.

      Rating: 4
dont quite
      understand ?  September 17, 2002
Reviewer:
      Ram  from India
ok. thats fine for the first question. thats was fast!
What about the second question ?
why did the import fail when using 'internal' ?
actually, the import did prompt me for internal's password (this is not needed
but then why did it prompt) and then the import failed with a IMP-00013.
internal does have DBA role doesnt it ? so why this cryptic error message ?

i think now i know one of the 'best' times to reach you in this site :-)

          Followup:

Internal is "special", it is just "special".

there is no "user" internal in the DBA_* tables -- hence the lookup to see if
internal has DBA "failed"

      Rating: 4
import order
      and database links  September 19, 2002
Reviewer:
      Ram  from india
2 more questions on import.

1) there doesnt seem to be any order in the way data is being imported. the log
shows:
importing a's objects in a
importing b's objects in b
importing c's objects in c
importing a's objects in a
importing b's objects in b

the first step itself should have taken care of all of a's objects isnt it ?

2) How are database links imported?
eg:
source db A has a db link to B. this would have been created using a service
name resolution.
now, while importing a full db dump of A into a new db C, the database link
would not get imported because the service name will not be resolved isnt it
?(because the service name has not been created in C). i am not sure but i
believe that i am thinking correctly because i see an 'error looking up remote
object xyz@abc' and a 'tns could not resolve service name' in the log.

by the way, i just read the rbs extending thread. GREAT!

          Followup:

1) there is order,

Tables come first (for all users in order)
then RI (for all users)
then synonyms (for all users)
and so on.

Each object type is exported by user in turn.

2) the link gets imported, it just won't work if the service name isn't there.

September 20, 2002 Ashwin -- Thanks for the question regarding
      "Import & Export -- at the tablespace level", version Oracle 7.3

      You Asked
How Do I Export just one particular tablespace among diffrent
tablespaces from my server to another machine?

      and we said...
In 7.3, you cannot export a tablespace.  You can export

o everything
o a user (schema)
o a table

You would need to generate a list of tables in that tablespace
to export (select table_name from dba_tables where
tablespace_name = 'THAT_TABLESPACE') and then

exp ... tables=(t1,t2,t3,...) ...

Review &
      Followup

      Rating: 4
Import and
      Export Tablespace (Oracle version 7.3)  December 02, 2001

Reviewer:  Samson  from Hong Kong
For Oracle8i, is it possible?

I found that there are options 'TRANSPORT_TABLESPACE=' and 'TABLESPACES=' for
export command in Oracle8i

However, I got the following error messages after I tried:
----------------------------------------------------------
C:\>exp file=users.dmp transport_tablespace=y tablespaces=users

Export: Release 8.1.6.0.0 - Production on Sun Dec 2 14:51:05 2001
(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Username: sys
Password:

Connected to: Oracle8i Enterprise Edition Release 8.1.6.0.0 - Production
With the Partitioning option
JServer Release 8.1.6.0.0 - Production
EXP-00044: must be connected 'AS SYSDBA' to do Point-in-time Recovery or
Transportable Tablespace import
EXP-00000: Export terminated unsuccessfully
----------------------------------------------------------

How to resolve it?

          Followup:

As the message says -- you need to be connected "as sysdba"

exp userid="""sys/manager as sysdba""" transport_tablespace=y
                                                 tablespaces=(tts_ex1,tts_ex2)

This is a feature of Oracle8i and up only.  It does allow you to "export" an
entire tablespace as it simply allows you to detach the datafiles from the
source database.  It does not actually EXPORT the data, it lets you take a copy
of the datafiles for a given set of tablespaces.

You cannot transport tablespaces from earlier releases.

      Rating:
      4
  September 19, 2002
Reviewer:  Mike
       from TX
exp userid="""sys/manager as sysdba""" transport_tablespace=y

tablespaces=(tts_ex1,tts_ex2)

Tom,
How could I get away without the sys password? I have tried and could not work
out.

Thanks

          Followup:

Well, I use OS authenticated accounts myself so mine is really

 / as sysdba

that would be my first and foremost recommendation.

$ exp transport_tablespace=y tablespaces=users

Export: Release 8.1.7.4.0 - Production on Fri Sep 20 07:45:47 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Username: sys as sysdba
Password:

seems to work

September 20, 2002 Igor -- Thanks for the question regarding
      "Running SQL against Table of Records", version 8.1.5

      You Asked
Tom,

Let say I have defined types:

TYPE testREC IS RECORD (gauge_id integer,
            gauge_name varchar2(30));

TYPE arrayTAB IS TABLE OF testREC
    INDEX BY BINARY_INTEGER;

and I have them populated:

declare out_rec arrayTAB;
begin
FOR nJ IN 1..3 loop
out_rec(nJ).gauge_id := nJ;
out_rec(nJ).gauge_name := 'nJ = ' || to_char(nJ);
end loop;
.....................
................

Is it possible to use something like:

SELECT * FROM THE (SELECT CAST(out_tec as arrayTAB) from dual...

in order to get the value of 'gauge_name' in out_rec where
gauge_id has specific value (say '2) ?

TIA

Igor Neyman

      and we said...
Yes and No.

Not using PLSQL types of RECORDS and PLSQL tables.

Yes using Object Types (like a record) and Nested Table Types
(like a plsql table).

the types must be defined at the SQL level for SQL to operate on
them.

Here is an example:

Instead of a PLSQL record:

ops$tkyte@DEV816> create or replace type myRecordType
  2  as object
  3  ( x int,
  4    y date,
  5    z varchar2(25)
  6  )
  7  /

Type created.

Instead of a PLSQL table:

ops$tkyte@DEV816> create or replace type myTableType
  2  as table of myRecordType;
  3  /

Type created.

ops$tkyte@DEV816> create or replace function my_function
                         return myTableType
  2  as
  3      some_data myTableType := myTabletype();
  4  begin
  5      for i in 1 .. 10 loop
  6          some_data.extend;
  7          some_data(i) :=
  8              myRecordType( i, sysdate+i, 'record ' || i );
  9      end loop;
 10  --shows we can select from VARIABLE
 11      for x in
 12      ( select *
 13          from TABLE ( cast( some_data as mytableType )  )
 14         order by z desc
 15      )
 16      loop
 17          dbms_output.put( x.x );
 18          dbms_output.put( ' ' || x.y );
 19          dbms_output.put_line( ' ' || x.z );
 20      end loop;
 21
 22      return some_data;
 23  end;
 24  /

Function created.

or select from "F(x)" where F is a plsql function returning a
variable

ops$tkyte@DEV816> select *
  2    from the ( select cast( my_function() as mytableType )
  3                 from dual );

         X Y         Z
---------- --------- -------------------------
         1 16-SEP-00 record 1
         2 17-SEP-00 record 2
         3 18-SEP-00 record 3
         4 19-SEP-00 record 4
         5 20-SEP-00 record 5
         6 21-SEP-00 record 6
         7 22-SEP-00 record 7
         8 23-SEP-00 record 8
         9 24-SEP-00 record 9
        10 25-SEP-00 record 10

10 rows selected.

and here is the dbms_output output

ops$tkyte@DEV816> exec null;
9 24-SEP-00 record 9
8 23-SEP-00 record 8
7 22-SEP-00 record 7
6 21-SEP-00 record 6
5 20-SEP-00 record 5
4 19-SEP-00 record 4
3 18-SEP-00 record 3
2 17-SEP-00 record 2
10 25-SEP-00 record 10
1 16-SEP-00 record 1

PL/SQL procedure successfully completed.

ops$tkyte@DEV816>
ops$tkyte@DEV816> select * from TABLE ( cast( my_function() as
mytableType ) )
  2  /

         X Y         Z
---------- --------- -------------------------
         1 16-SEP-00 record 1
         2 17-SEP-00 record 2
         3 18-SEP-00 record 3
         4 19-SEP-00 record 4
         5 20-SEP-00 record 5
         6 21-SEP-00 record 6
         7 22-SEP-00 record 7
         8 23-SEP-00 record 8
         9 24-SEP-00 record 9
        10 25-SEP-00 record 10

10 rows selected.

ops$tkyte@DEV816>
ops$tkyte@DEV816> exec null;
9 24-SEP-00 record 9
8 23-SEP-00 record 8
7 22-SEP-00 record 7
6 21-SEP-00 record 6
5 20-SEP-00 record 5
4 19-SEP-00 record 4
3 18-SEP-00 record 3
2 17-SEP-00 record 2
10 25-SEP-00 record 10
1 16-SEP-00 record 1

PL/SQL procedure successfully completed.
Review &
      Followup

      Rating: 4
question about
      table type  September 20, 2002
Reviewer:
      mike
I've used "select from table_type" in my program and I follow exactly your
syntax.  I can compile it but when I run it, the following error returns.  Can
you tell me why?

ORA-00600: internal server code, arguments:[15419], [severe error during PL/SQL
execution]
ORA-06544: PL/SQL:  internal error
ORA-06553: PLS-801: internal error
create or replace package body pk_test is

procedure sp_test
is
     type myTableType is table of char(2);
     l_data    myTableType := myTabletype();

     ls_item_desc char(20);
begin
     dbms_output.enable(50000);

     l_data.extend;
     l_data(1) := '01';

     select item_desc
       into ls_item_desc
       from item
      where item_code in
        ( select * from
           table(cast(l_data as mytableType )));

     dbms_output.put_line('item desc: ' || ls_item_desc);

end sp_test;

end pk_test;

          Followup:

don't know what version you are using but it is probably related to this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure sp_test
  2  is
  3       type myTableType is table of char(2);
  4       l_data    myTableType := myTabletype();
  5
  6       ls_item_desc char(20);
  7  begin
  8       dbms_output.enable(50000);
  9
 10       l_data.extend;
 11       l_data(1) := '01';
 12
 13       select dummy item_desc
 14         into ls_item_desc
 15         from dual
 16        where dummy in
 17          ( select * from
 18             table(cast(l_data as mytableType )));
 19
 20       dbms_output.put_line('item desc: ' || ls_item_desc);
 21
 22  end sp_test;
 23  /

Warning: Procedure created with compilation errors.

ops$tkyte@ORA817DEV.US.ORACLE.COM> show err
Errors for PROCEDURE SP_TEST:

LINE/COL ERROR
-------- -----------------------------------------------------------------
13/6     PL/SQL: SQL Statement ignored
18/18    PLS-00642: local collection types not allowed in SQL statements

the types must be defined at the SQL level for SQL to operate on them.

create or replace type myTableType as table of char(2)
/

create or replace procedure sp_test ......

create the type OUTSIDE the procedure/package.

September 20, 2002 Prince -- Thanks for the question regarding
      "Dynamic SQL giving error.", version 8.1.7.2

      You Asked
Tom,

I have the following. When I execute the sql stmt, I get the following errors. I
tried many different ways, but I couldn't figure out the root cause.

SQL> create table t1 (x number(10)) ;

SQL> declare
  2    dynstr varchar2(1000) ;
  3    maxrec number(10) default 0;
  4    tablename varchar2(30) default 'T1';
  5  begin
  6    dbms_output.put_line('Val = ' || to_char(maxrec)) ;
  7    dynstr := 'insert into t1 (x) select max(x) + 1000 from t1 returning x
into :b1';
  8    execute immediate dynstr RETURNING INTO maxrec;
  9  end ;
 10  /
Val = 0
declare
*
ERROR at line 1:
ORA-00933: SQL command not properly ended
ORA-06512: at line 8

SQL> declare
  2    dynstr varchar2(1000) ;
  3    maxrec number(10) default 0;
  4    tablename varchar2(30) default 'T1';
  5  begin
  6    dbms_output.put_line('Val = ' || to_char(maxrec)) ;
  7    dynstr := 'insert into t1 (x) select max(x) + 1000 from t1';
  8    execute immediate dynstr RETURNING INTO maxrec;
  9  end ;
 10  /
Val = 0
declare
*
ERROR at line 1:
ORA-01006: bind variable does not exist
ORA-06512: at line 8

If I run the same wiyhout the select clause, its working fine

declare
  dynstr varchar2(1000) ;
  maxrec number(10) default 0;
  tablename varchar2(30) default 'T1';
begin
  dbms_output.put_line('Val = ' || to_char(maxrec)) ;
  dynstr := 'insert into t1 (x) values( 1000) returning x into :b1';
  execute immediate dynstr RETURNING INTO maxrec;
end ;
/

Val = 0

PL/SQL procedure successfully completed.

Ques 2:
~~~~~~~
Though not related to the above one,
Is there any difference (say in the space usage, logging etc), if a table is
defined with all the null columns at the end?

Thanking you...

      and we said...
q1

'begin insert into ... returning x into :b1; end;'

you need to use a plsql block.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          x number;
  3  begin
  4          execute immediate
  5          'begin
  6             insert into t values ( 1 ) returning x into :b1;
  7           end;' using OUT x;
  8  end;
  9  /

PL/SQL procedure successfully completed.

I just pray you aren't using this technique as a "sequence" -- that would be
terrible, horrible, a really truly bad idea.

q2) yes, trailing null columns in a table take zero bytes.  columns in the
interior (that have a non-null column following them) will consume 1 byte of
storage

Review &
      Followup

      Rating: 4
Thanks
      tom  March 21, 2002
Reviewer:  Prince

Tom, you guessed it correct. I know, its a real bad idea, but one of the tables
(I myself don't understand the morale behind this) needs to have a sequence kind
of number per part of the primary key column.

PK is ClientID, Record#

And the application need to have the Record# incremented per ClientID and the
increment is done in autonomous transaction. I have no idea, how to achive this
thru sequence.

regarding Q2:
~~~~~~~~~~~~~
What does the one byte indicates? Just the presence of a null column?

          Followup:

the one byte just indicates the NULL column is there, yes.

      Rating: 2
Still I get the
      same error, If select is used   March 21, 2002

Reviewer:  Prince.
Tom,

Still I get the same error, if I use "insert into tab SELECT dada FROM TAB".
What am I doing wrong here? It always works, if there is "VALUES" caluse instead
of "SELECT"

declare
          x number;
begin
          execute immediate
          'begin
             insert into t select max(x) + 100 from t returning x into :b1;
           end;' using OUT x;
    dbms_output.put_line('Val ' || to_char(x));
end;
/

Thanks,

          Followup:

Yes, of course -- I should have noticed that.  Sorry.

insert into select ....

in general will insert N rows where N = 0, 1, or more.  You cannot use RETURNING
with that, it might not return anything, it might return many things.

Since it is such a bad idea, just do this:

create sequence client_seq;

insert into t values ( client_seq.nextval ) return x into plsql_variable;

(use static sql, use a sequence).

else, you are going to run three statements:

o insert into t values (-1);
o select max(x) + 100 into something from t;
o update t set x = something where x = -1;

that way, you prevent two people from assigning the same "sequence" to two
different records.  Else, you would have to have "retry logic" if you get a
dup_val_on_index...

      Rating: 3
But, how come
      "delete" can do almost the same?  March 21, 2002

Reviewer:  Prince
Thank You Tom, for the response.

But the issue I have with the sequence (in this particulare case) is, I want to
have values like,

ClientID  Record# (kindof sequnce, but not exactly)
--------  -------
100         101
100         103
100         104
100         105

200         100
200         102
200         103
200         105

Note, the record# is unique only within the ClientID. We have sequences in many
other places.

Also, If the insert returns more than one row, shouldn't it just give error
(same as "single-row subquery returns more than one row) or if it returns "no
rows" then the return variables to be null.

Isn't the delete also, same. The delete could return 0,1,..N row. But why the
delete is successful?

Ex,

SQL> select * from t2 ;

         X          Y
---------- ----------
         1        100
         3        300

SQL> declare
  2    dynstr varchar2(1000) ;
  3    inp number(10) default 4;
  4    ot number(10);
  5    tablename varchar2(30) default 'T2';
  6  begin
  7      dynstr := 'delete from ' || tablename || ' where x < :b1 returning y
into :b2';
  8      EXECUTE IMMEDIATE dynstr USING inp, OUT ot;
  9      dbms_output.put_line('Deleted '||sql%rowcount||
 10                      ' row(s). Value: '||nvl(to_char(ot),'NULL'));
 11  end;
 12  /
Deleted 2 row(s). Value: 300

SQL> declare
  2    dynstr varchar2(1000) ;
  3    inp number(10) default 1;
  4    ot number(10);
  5    tablename varchar2(30) default 'T2';
  6  begin
  7      dynstr := 'delete from ' || tablename || ' where x < :b1 returning y
into :b2';
  8      EXECUTE IMMEDIATE dynstr USING inp, OUT ot;
  9      dbms_output.put_line('Deleted '||sql%rowcount||
 10                      ' row(s). Value: '||nvl(to_char(ot),'NULL'));
 11  end;
 12  /
Deleted 0 row(s). Value: NULL

Could, you please explain the anomaly?

Thanks again.

          Followup:

Well, you won't with your approach (autonomous transactions)... You'll get:

101
103
105
107

all it takes is ONE rollback and you'll stand a chance of losing one (session 1
gets 101 and inserts it.  Session 2 gets 102 and inserts it.  session 1
rollsback the insert.  101 is "gonzo")

might as well just use sequences and give them ascending but apparently random
numbers.

With returning -- no it shouldn't (cause it doesn't).  In general, insert into
select will NOT work with returning, hence insert into select is not SUPPORTED
with returning.

I cannot explain the ambiguity -- only that it is documented in the SQL
reference that this is so (that returning is supported with values only)

      Rating: 3
Any
      workarounds...?  September 20, 2002
Reviewer:
      Adrian Billington  from UK
Tom

Your last post says ...
"
In general, insert into
select will NOT work with returning, hence insert into select is not SUPPORTED
with returning.
"

This is quite frustrating, as I can...:

a) delete from <table> returning <column> bulk collect into <collection>;

b) update <table> set <column> = <value> returning <column> bulk collect into
<collection>;

...but I can't...:

c) insert into <table> select * from <other table> returning <column> bulk
collect into <collection>;

Forgive me, but I just can't see the difference and why this is an exception.

Do you know of any sensible workarounds?

Regards

Adrian

          Followup:

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          type numList is table of number index by binary_integer;
  3          l_x  numList;
  4  begin
  5          select rownum bulk collect into l_x from all_users;
  6          forall i in 1 .. l_x.count
  7             insert into t values ( l_x(i) );
  8  end;
  9  /

PL/SQL procedure successfully completed.

would be one way.

September 20, 2002 sushant -- Thanks for the question regarding
      "before & after triggers and jre version", version 8.1.5

      You Asked
   what is actual difference between before and after triggers
   is there anything substantial  which  can be acheived by a before
  trigger which cannot be done by after triggers or viceversa

  how do i know which jre version is running in my database(is there a command
to
find out)

normally if i want to delete a row from the primary key table
and a corresponding row should be deleted from the foreign key table
we use the on delete cascade option while creating the foreign key table
  but suppose if we dont use the on delete cascade option while creating the
table then how do we acheive it along with a normal delete statement
on a row from the primary key table

      and we said...
For the before/after trigger:

Well, a before trigger can prevent the work from ever taking place -- an AFTER
trigger will have allowed the work to happen.  So, if you were using a statement
trigger to do additional privilege checking -- it would be infinitely more
performant in a BEFORE trigger, thereby preventing the work from ever happening
(less wasted cycles processing a statement that will be rolled back which itself
is expensive).

A before row level trigger can modify the :new values, an after row trigger
cannot.

For the on delete cascade:

If you did not have the on delete cascade, you will have to explicitly delete
from the child tables PRIOR to deleting from the parent table.

and lastly on the jre version, this information is available in the system
properties:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace and resolve java source
named "ver"
  2  as
  3  public class util
  4  {
  5      public static void prop()
  6      {
  7          System.getProperties().list(System.out);
  8       }
  9      public static String ver()
 10      {
 11          return System.getProperty("java.version", "unknown");
 12      }
 13  }
 14  /

Java created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure jdk_props as
language java name 'util.prop()';
  2  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function jdk_ver return
varchar2 as language java name
  2  'util.ver() return java.lang.String';
  3  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2      dbms_java.grant_permission( user,
  3                                 'java.util.PropertyPermission',
  4                                 '*',
  5                                 'read,write' );
  6      commit;
  7  end;
  8  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set serveroutput on size 1000000
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_java.set_output(1000000)

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec jdk_props
-- listing properties --
java.specification.name=Java Platform API Specification
java.version=1.2.1
oracle.aurora.mts.INIT=oracle.aurora.mts.rdbms.INIT
user.timezone=EST
java.specification.version=1.2
java.vm.vendor=Oracle Corporation
java.vm.specification.version=1.0
user.home=
java.naming.factory.initial=oracle.aurora.namespace.InitialContex...
os.arch=sparc
java.vendor.url=
http://www.oracle.com/java/
file.encoding.pkg=sun.io
user.region=US
java.home=/export/home/ora817/javavm/
java.class.path=
line.separator=
java.io.tmpdir=/var/tmp/
jdbc.drivers=oracle.jdbc.driver.OracleDriver
os.name=Solaris
java.vendor=Oracle Corporation
oracle.jserver.version=8.1.7
oracle.server.version=8.1.7
java.library.path=/export/home/ora817/lib
java.vm.specification.vendor=Sun Microsystems Inc.
oracle.aurora.namespace.INIT=oracle.aurora.namespace.rdbms.INIT
oracle.aurora.rdbms.SID=ora817dev
file.encoding=ISO8859_1
oracle.aurora.mts.session.INIT=oracle.aurora.mts.session.rdbms.INIT
java.specification.vendor=Sun Microsystems Inc.
oracle.aurora.vm.environment.name=rdbms
user.name=
user.language=en
java.vm.name=JServer VM
java.vm.specification.name=Java Virtual Machine Specification
java.class.version=46.0
oracle.aurora.rdbms.oracle_home=/export/home/ora817/
sun.boot.library.path=/export/home/ora817/lib:/export/home/...
sqlj.runtime=sqlj.framework.ide.aurora.rdbms.Oracl...
java.naming.factory.url.pkgs=oracle.aurora.ejb.jndi:com.sun.jndi.u...
java.protocol.handler.pkgs=oracle.aurora.rdbms.url
os.version=5.7
java.vm.version=1.2.1
java.compiler=
path.separator=:
file.separator=/
user.dir=

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select jdk_ver from dual;

JDK_VER
--------------
1.2.1

Review &
      Followup

      Rating: 5
Very
      Informative post.  September 20, 2002
Reviewer:
      Yogeeraj  from Mauritius
Hi,

Is the JRE version in the database related to the version of JRE we use in our
JDeveloper?

will there be any issues if i use latest version of JDeveloper for application
development with our Oracle 8.1.7.4 database?

Thank you for a reply.

Best Regards
Yogeeraj

          Followup:

No, the jre in the database is the jre that was in widespread use when the
database was released.

the jre in jdev is the jre that was in widespread use when jdev was released.

If you are planning on putting the java IN the database (eg: java stored
procedures), then you might find there are features available in java as seen by
jdeveloper that are not available in the jre in the database.

September 20, 2002 anthony -- Thanks for the question regarding "DBA
      Job irrelavent ?", version 9i

      You Asked
Tom,

1. Has DBA job irrelavent as Oracle claims that Oracle9i is self-tuning?

2. Where are the areas( like dictinary cache ..etc ) self-tunned in Oracle9i?
Can you explain in detail?

Thanks
Tony

      and we said...
1) hah, yeah, sure.

It is easier to manage UNDO issues in 9i since the DBA doesn't have to figure
out how many RBS's to have or how big they should be allocated.  The UNDO
tablespace makes that easier.  However, how many times do you configure rollback
segments in a database (answer = not often)....  Ok, so the setup of the
database is easier but you still have to back it up, monitor it, fix it if it
stops, tune things and so on.

The news of the death of the DBA is somewhat premature.  The database is more
self managing (undo tblspace, system managed extents, oracle managed files, db
cache advisor, etc etc etc) but there is still alot to manage (for everything we
made easier -- we added new features.  You have iFS, you have ultrasearch, you
have xmldb, you have .... it takes me 8 hours to rattle off the 9iR1 new
features and another 4 hours for 9iR2 -- its a fairly large product)

2) don't know what you mean by "where".  If you mean "what" -- I would suggest
you look at the 9i New Features guide (otn.oracle.com -> documentation -> 9i DB
-> NEW)
Review & Followup

      Rating: 4
DBA for a 9i
      database  September 20, 2002
Reviewer:  A
      reader
Tom,

However, it is not every new features of 9i are used by a customer. Frankly, may
be a small fraction of the new features are immediately used by a customer. For
example, does a customer uses XMLDB?

The tunning and configuration parts are two of main jobs of a DBA. Therefore,
there is some truth about reducing importance of an Oracle9i or later version
DBA in my opinoin.

A reader

          Followup:

Do you manage a 9i database?

      Rating:
      4
  September 20, 2002
Reviewer:  A
      reader  from SoCal
I don't know how the DBA role will ever "lessen" or "diminish".  Even with all
the new features and automation of tasks etc, it still seems that very very few
truly understand the Oracle Architecture and the implications of accepting
automated or manual parameters/configurations.

      Rating:
      4
  September 20, 2002
Reviewer:  Dave
       from Ohio
Maybe it would be more accurate to say that if an Enterprise upgraded a large
number of 7, 8 or 8i to 9i, and did not make use of new features such as iFS or
XML or materialized views, then you might reduce the number of DBA's required to
manage them.

      Rating: 5
Marketing
        September 20, 2002
Reviewer:  Brian
       from SF, CA, USA
Part of Oracle's marketing has always been to say that Oracle is easy.  If you
don't understant Oracle you may believe that.

Seven years ago Oracle was saying their new version was amazingly easy, and they
gave the impression DBA's would have almost nothing to do.

The actual fact is Oracle is one of the most complicated pieces of software
around.  Oracle keeps adding more and more features with each release.  They can
SAY whatever they want, and some people will believe it, but the fact is that
Oracle isn't getting less complex -- it's getting more complex.

DBA's are the Oracle experts, and even they find it difficult to keep up.  There
will always be a need for Oracle experts, this is the DBA's job, and while some
aspects of the job change somewhat over time, Oracle just keeps adding options.
Somebody has to know about these options.  With the new options making Oracle
even more complex, the need for a DBA increases.

Don't believe the sales hype.

      Rating:
      5
  September 20, 2002
Reviewer:  Bill
       from LA
its amazing to see what goes on in environments that do not have DBAs.  I
recently was called to look on an environment for a multi-million dollar company
that had 1 Production database running on Oracle, with NO backup, NO archiving,
NO strategy, all files running on ONE Disk, no mirroed logs/controlfiles...
amazing what folks will do so they don't have to pay for a DBA.
they might as well be running M$$ql.  8)

      Rating:
      4
Paradigms...  September 21, 2002
Reviewer:
      Sven Bleckwedel  from Santos, SP Brazil
Some of these questions rely on cost, ever.  Some "managers" (without enough
knowledge about anything) think that is better to "cut" some "not necessary
costs".  Without any strong "Do you know about the consequences about this
decision ?" and showing what happens when wrong decisions can make, they believe
that they are making correct resolutions...

Every person that know more than this type of "manager" have the responsability
to show the reality, when possible.  This is our job...

September 20, 2002 daniel -- Thanks for the question regarding
      "update contention and queues", version 8.1.7

      You Asked
Hi Tom,

I've run into the following problem repeatedly when working with OLTP apps...I
was wondering what your solution is.

Given the table

SQL> create table t (x integer, y integer, constraint primary key (x));

and a large number of concurrent users try to execute

SQL> update t set y = y + :y_delta where x = :x;

for a small number of distinct values for ":x" (let's say 50 TPS), I inevitably
end up with contention on the row-level locks, which slows down response times
and consumes excessive CPU.

Assuming updates to t.y don't have to be "real time", I typically try to
eliminate the contention by queuing the updates in another table:

SQL> create table t_queue (x integer, y_delta integer);

SQL> insert into t_queue (x, y) values (:x, :y_delta);

I guess my first question should be, how would you solve the update contention
problem?

But given the queued-update approach, how do I write the stored procedure to
process rows in t_queue and apply changes to t? I've seen code that does this:

create or replace procedure process_t_queue
is
begin
    for cur in (
        select x, sum(y_delta) total
          from t_queue
         group by x)
    loop
        update t
           set y = y + cur.total
         where x = cur.x;
    end loop;

    delete t_queue;
end;

but that's susceptable to phantoms (i.e., concurrent inserts into t_queue
between the update and delete). My usual solution has been to add a date column
to avoid deleting too many rows:

SQL> alter table t_queue add created date default sysdate not null;

create or replace procedure process_t_queue
is
    -- select records 2 seconds old
    v_created date := sysdate - 2/24/60/60;
begin
    for cur in (
        select x, sum(y_delta) total
          from t_queue
         where created < v_created
         group by x)
    loop
        update t
           set y = y + cur.total
         where x = cur.x;
    end loop;

    delete t_queue where created < v_created;
end;

The flip side of this approach is updating a field in the queue table (marking
the rows about to be processed), then selecting and deleting only the updated
records... basically the same idea.

In some cases, you can make the cursor FOR UPDATE, which usually works fine. But
when you need an ORDER BY or GROUP BY in the cursor, it's no help.

I've also played around with serializable transactions, but can't avoid cannot
serialize access errors on the delete. (It seems like the completely wrong
application for serializable since this transaction could run for several
seconds.)

I have considered using a FOR UPDATE cursor anyway and emulating the GROUP BY in
PL/SQL (not pretty), or using AQ. Having never used AQ I can't make an informed
decision about it, but from skimming the manual I get the sense it's overkill
for what should be a very simple problem (even though RDBMS's typically aren't
geared towards queue-style processing).

What approach would you use? Would you even use the queue table in the first
place to avoid the contention?

      and we said...
wow, I've never run across such an issue in an OLTP app before, very unique.
Not typical but ... My answer is rather simple however -- AQ is the answer
(advanced queues).  You have this software, it is in your database, it is
designed to handle things like this exactly.

I don't see it as being "overkill" neccesarily, apps queue a message, plsql
dequeues a message and does the update.  Rather simple actually.

In 9iR2, a rather interesting use of flashback query could be put to the test:

ops$tkyte@ORA920.US.ORACLE.COM> create table t ( x number primary key, y number
);
Table created.

ops$tkyte@ORA920.US.ORACLE.COM> exec gen_data( 'T', 500 );
PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> drop table t2;
Table dropped.

ops$tkyte@ORA920.US.ORACLE.COM> create table t2
  2  as
  3  select x, dbms_random.value( 0, 1 ) y from t
  4  /
Table created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into t2
  2  select x, dbms_random.value( 0, 1 ) from t
  3  /
500 rows created.

ops$tkyte@ORA920.US.ORACLE.COM> commit;
Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM> exec dbms_lock.sleep(6*60)
PL/SQL procedure successfully completed.

gotta sleep a bit to avoid
ERROR at line 2:
ORA-01466: unable to read data - table definition has changed
due to me dropping and recreating objects on the fly here....  Would not be
needed "normally"

ops$tkyte@ORA920.US.ORACLE.COM> commit;
Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM> variable n number
ops$tkyte@ORA920.US.ORACLE.COM> exec :n :=
dbms_flashback.get_system_change_number;
PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> print n

         N
----------
   1652852

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> update ( select x, y, (select sum(t2.y) from t2
as of scn :n where t2.x = t.x ) sum_y
  2             from t
  3            where x in ( select x from t2 as of scn :n )
  4         )
  5     set y = y+sum_y;

500 rows updated.

Now, I broke the update of T and delete of T2 into two pieces so that I could
simulate the adding of rows during the UPDATE/DELETE processing -- you would put
them together into one procedure of course...

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> declare
  2      PRAGMA AUTONOMOUS_TRANSACTION;
  3  begin
  4      insert into t2
  5      select x, dbms_random.value( 0, 1 ) from t;
  6      commit;
  7  end;
  8  /

PL/SQL procedure successfully completed.

there is the guy putting data into the table whilst we are updating/deleting
it....

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> delete from t2
  2   where rowid in ( select rowid from t2 as of scn :n );

1000 rows deleted.

Our delete only sees the 1000 T2 rows we saw in our update -- the flashback
does that for us...

ops$tkyte@ORA920.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select count(*) from t2;

  COUNT(*)
----------
       500

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> spool off

and we are ready to process the new rows now..

Review &
      Followup

      Rating: 5
guess it's time
      to upgrade to 9i  September 20, 2002
Reviewer:
      D  from NY
(I've encountered the update contention problem a few times, particularly with
aggregate counts. For example, assume my.yahoo.com records the number of failed
login attempts per hour and it gets several hundred per sec. Instead of updating
the counter column on each attempt, you could enqueue the failures and update
periodically using dbms_job. Other examples: a reader poll on the home page of
cnn.com, counting product views at amazon.com. Really only an issue when the TPS
gets huge. (When it gets really huge you're sometimes better off processing
outside oracle (e.g., using webserver logs or local flatfiles) but it's always
nice to be able to do it all in oracle.)

Regarding flashback, is there a way (supported of course) to access the same
functionality in 8.1.7 (i.e., reading the scn and using it sql queries)?

Also, can you write update statements like the one in your example in PL/SQL in
9i? That works great for me as long as I don't put it in a BEGIN..END block. Is
there a common workaround for SQL that won't parse in PL/SQL?

(This sqlplus session is 8.1.6)

"afiedt.buf" 6L, 123C written
  1  UPDATE
  2   (SELECT y, (SELECT sum(y) FROM t2 WHERE t2.x=t1.x) n
  3      FROM t1
  4     WHERE x IN (SELECT x FROM t2))
  5* SET y = y + n
SQL> /

2 rows updated.

Elapsed: 00:00:00.01
SQL> commit;
SQL> ed
Wrote file afiedt.buf

"afiedt.buf" 8L, 135C written
  1  BEGIN
  2  UPDATE
  3   (SELECT y, (SELECT sum(y) FROM t2 WHERE t2.x=t1.x) n
  4      FROM t1
  5     WHERE x IN (SELECT x FROM t2))
  6  SET y = y + n;
  7* END;
SQL> /
 (SELECT y, (SELECT sum(y) FROM t2 WHERE t2.x=t1.x) n
             *
ERROR at line 3:
ORA-06550: line 3, column 14:
PLS-00103: Encountered the symbol "SELECT" when expecting one of the following:
( - + mod not null others <an identifier>
<a double-quoted delimited-identifier> <a bind variable> avg
count current exists max min prior sql stddev sum variance
execute forall time timestamp interval date
<a string literal with character set specification>
<a number> <a single-quoted SQL string>
ORA-06550: line 3, column 53:
PLS-00103: Encountered the symbol "N" when expecting one of the following:
; return returning and or

Elapsed: 00:00:00.18
SQL>

          Followup:

Well, in 817, you would do row at a time processing most likely

  for x in (select t2.*, rowid rid from t2 for update)
  loop
      update t ...
      delete from t2 where rowid = x.rid;
  end loop;

In 8i, to use some of the "new" features of sql "select (select)" being one of
them -- you need to use dynamic sql to bypass the PLSQL sql parser.  In 9i and
beyond, this will not be the case anymore.

"afiedt.buf" 8L, 135C written
  1  BEGIN
     execute immediate
  2  'UPDATE
  3   (SELECT y, (SELECT sum(y) FROM t2 WHERE t2.x=t1.x) n
  4      FROM t1
  5     WHERE x IN (SELECT x FROM t2))
  6  SET y = y + n';
  7* END;
SQL> /

September 20, 2002 RAGHAVENDRA -- Thanks for the question regarding
      "counting business days.", version

      You Asked
HOW TO GET THE COUNT OF DAYS BETWEEN ANY
GIVEN TWO DATES EXCLUDING SUNDAYS AND SATURDAYS BY USING ONLY
SQL?

      and we said...
something like:

ops$tkyte@8i> @test 29-feb-2000 01-feb-2000
ops$tkyte@8i> set echo on
ops$tkyte@8i>
ops$tkyte@8i> select count(*)
  2    from ( select rownum-1 rnum
  3             from all_objects
  4            where rownum <= to_date('&1') - to_date('&2')+1 )
  5   where to_char( to_date('&2')+rnum-1, 'DY' ) not in (
'SAT', 'SUN' )
  6  /
old   4:           where rownum <= to_date('&1') -
to_date('&2')+1 )
new   4:           where rownum <= to_date('29-feb-2000') -
to_date('01-feb-2000')+1 )
old   5:  where to_char( to_date('&2')+rnum-1, 'DY' ) not in (
'SAT', 'SUN' )
new   5:  where to_char( to_date('01-feb-2000')+rnum-1, 'DY' )
not in ( 'SAT', 'SUN' )

  COUNT(*)
----------
        21

will do it.  Make sure the table in the inline view (all_objects
in my example) has at least as many rows as you plan on having
dates span (eg: it needs at least 366 rows to do 1 year, 3660 to
do 10 and so on).  all_objects, typically with thousands of
rows, is a good generic candidate -- you can always build your
own very compact table as well to make this faster.

A logical extension would be to add 'holidays' and other
non-business days. We would use a table with dates to exclude.
For example:

ops$tkyte@8i> create table exclude_dates ( no_work date primary
key ) organization index;

Table created.

ops$tkyte@8i> insert into exclude_dates values ( '14-FEB-2000'
);

1 row created.

ops$tkyte@8i>
ops$tkyte@8i>
ops$tkyte@8i> select count(*)
  2    from ( select rownum-1 rnum
  3             from all_objects
  4            where rownum <= to_date('&1') - to_date('&2')+1 )
  5   where to_char( to_date('&2')+rnum-1, 'DY' ) not in (
'SAT', 'SUN' )
  6     and not exists ( select null from exclude_dates where
no_work =
  7     trunc(to_date('&2')+rnum-1) )
  8  /
old   4:           where rownum <= to_date('&1') -
to_date('&2')+1 )
new   4:           where rownum <= to_date('29-feb-2000') -
to_date('01-feb-2000')+1 )
old   5:  where to_char( to_date('&2')+rnum-1, 'DY' ) not in (
'SAT', 'SUN' )
new   5:  where to_char( to_date('01-feb-2000')+rnum-1, 'DY' )
not in ( 'SAT', 'SUN' )
old   7:    trunc(to_date('&2')+rnum-1) )
new   7:    trunc(to_date('01-feb-2000')+rnum-1) )

  COUNT(*)
----------
        20

Review &
      Followup

      Rating: 2
This code
      does'nt work!  May 24, 2002
Reviewer:  Matthew
      Murphy  from London England
Test this code for the dates '24-MAY-02' to '09-JUN-02'
what is the result - it should be 10 ( 11 if you want to include the 24th of MAY
)

          Followup:

right -- we fixed that in another question once, missed it here.  should be:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select rnum , to_date('&2')+rnum
  2    from ( select rownum-1 rnum
  3             from all_objects
  4            where rownum <= to_date('&1') - to_date('&2')+1 )
  5   where to_char( to_date('&2')+rnum, 'DY' ) not in ( 'SAT', 'SUN' )
  6     and not exists ( select null from exclude_dates where no_work =
trunc(to_date('&2')+rnum-1) )
  7  /
old   1: select rnum , to_date('&2')+rnum
new   1: select rnum , to_date('24-may-02')+rnum
old   4:           where rownum <= to_date('&1') - to_date('&2')+1 )
new   4:           where rownum <= to_date('09-jun-02') - to_date('24-may-02')+1
)
old   5:  where to_char( to_date('&2')+rnum, 'DY' ) not in ( 'SAT', 'SUN' )
new   5:  where to_char( to_date('24-may-02')+rnum, 'DY' ) not in ( 'SAT', 'SUN'
)
old   6:    and not exists ( select null from exclude_dates where no_work =
trunc(to_date('&2')+rnum-1) )
new   6:    and not exists ( select null from exclude_dates where no_work =
trunc(to_date('24-may-02')+rnum-1) )

      RNUM TO_DATE('
---------- ---------
         0 24-MAY-02
         3 27-MAY-02
         4 28-MAY-02
         5 29-MAY-02
         6 30-MAY-02
         7 31-MAY-02
        10 03-JUN-02
        11 04-JUN-02
        12 05-JUN-02
        13 06-JUN-02
        14 07-JUN-02

11 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*)
  2    from ( select rownum-1 rnum
  3             from all_objects
  4            where rownum <= to_date('&1') - to_date('&2')+1 )
  5   where to_char( to_date('&2')+rnum, 'DY' ) not in ( 'SAT', 'SUN' )
  6     and not exists ( select null from exclude_dates where no_work =
trunc(to_date('&2')+rnum-1) )
  7  /
old   4:           where rownum <= to_date('&1') - to_date('&2')+1 )
new   4:           where rownum <= to_date('09-jun-02') - to_date('24-may-02')+1
)
old   5:  where to_char( to_date('&2')+rnum, 'DY' ) not in ( 'SAT', 'SUN' )
new   5:  where to_char( to_date('24-may-02')+rnum, 'DY' ) not in ( 'SAT', 'SUN'
)
old   6:    and not exists ( select null from exclude_dates where no_work =
trunc(to_date('&2')+rnum-1) )
new   6:    and not exists ( select null from exclude_dates where no_work =
trunc(to_date('24-may-02')+rnum-1) )

  COUNT(*)
----------
        11

(skip the second -1 in the outer predicate)

      Rating:
      4
Great  September 19, 2002
Reviewer:
      Chris
Same problem here.
But if I had table t_HISTORIES with 2 HISTORY_DATE :
09-JUL-2002 (first row)
23-SEP-2002 (second row)
how can I pass this 2 dates into this code in a single query ?

          Followup:

where I have

 to_date('&1')

you'll have

  (select min(dt) from t_histories)

where I have

 to_date('&2')

you'll have

  (select max(dt) from t_histories)

      Rating: 4
missing right
      parenthesis   September 19, 2002
Reviewer:
      Chris
I change the parameter :

to_date('&1') --> (select min(HISTORY_DATE) from t_histories)

to_date('&2') --> (select max(HISTORY_DATE) from t_histories)

I will got this code :

select count(*)
    from ( select rownum-1 rnum
             from all_objects
            where rownum <=  (select min(HISTORY_DATE) from
t_FRAUD_CASE_HISTORIES) -(select max(HISTORY_DATE) from
t_FRAUD_CASE_HISTORIES)+1)
   where to_char((select max(HISTORY_DATE) from t_FRAUD_CASE_HISTORIES) +rnum-1,
'DY' ) not in ( 'SAT', 'SUN' )

I run it but it said :
where rownum <=  (select blablaE)  - (select blablaI)+1)
                                   *
ERROR at line 4:
ORA-00907: missing right parenthesis

          Followup:

use

(select min(history_date)-max(history_date)+1 from t_fraud_case_histories)

for that "complex" one.

      Rating: 4
Select many
      date rows  September 20, 2002
Reviewer:
      Chris
It works now. With this edited line I got the number = 58
select count(*)
    from ( select rownum-1 rnum
             from all_objects
            where rownum <= ( select max(HISTORY_DATE) - min(HISTORY_DATE) +1
from t_FRAUD_CASE_HISTORIES))
where to_char((select
min(HISTORY_DATE) from t_FRAUD_CASE_HISTORIES) +rnum-1, 'DY' ) not in ( 'SAT',
'SUN' )

      Rating:
      4
continue  September 20, 2002
Reviewer:
      Chris
Sorry I got trouble with posting many lines at once.
But that use min and max function. What if I want to query this 2 dates from
many rows in log transaction table:
select date1 from t_hist where state = open and id=1select date1 from t_hist
where state = closed and id=1

          Followup:

 ( select date1 - date2 + 1
     from (select date date1 from t_hist where state=open ... ),
          (select date date2 from t_hist where state=closed .... )
 )

      Rating:
      4
cont2  September 20, 2002
Reviewer:
      A reader
Pff sorry mistype. I mean :
select date1 from t_hist where state = open and id=1
select date2 from t_hist where state = closed and id=1

      Rating:
      3
  September 20, 2002
Reviewer:  A
      reader  from NY, USA
Tom,
I do the test as:
 insert into exclude_dates values ( '04-JUL-02');

Then save sql of select rnum , to_date('&2')+rnum
     from ( select rownum-1 rnum
               from all_objects
              where rownum <= to_date('&1') - to_date('&2')+1 )
     where to_char( to_date('&2')+rnum, 'DY' ) not in ( 'SAT', 'SUN' )
       and not exists ( select null from exclude_dates where no_work =
trunc(to_date('&2')+rnum-1) );

in test.sql

then do:

SQL> @test 05-jul-02 28-jun-02
old   1: select rnum , to_date('&2')+rnum
new   1: select rnum , to_date('28-jun-02')+rnum
old   4:               where rownum <= to_date('&1') - to_date('&2')+1 )
new   4:               where rownum <= to_date('05-jul-02') -
to_date('28-jun-02')+1 )
old   5:      where to_char( to_date('&2')+rnum, 'DY' ) not in ( 'SAT', 'SUN' )
new   5:      where to_char( to_date('28-jun-02')+rnum, 'DY' ) not in ( 'SAT',
'SUN' )
old   7: trunc(to_date('&2')+rnum-1) )
new   7: trunc(to_date('28-jun-02')+rnum-1) )

      RNUM TO_DATE('
---------- ---------
         0 28-JUN-02
         3 01-JUL-02
         4 02-JUL-02
         5 03-JUL-02
         6 04-JUL-02

Why 04-JUL-02 still shows up?

          Followup:

same problem as the other one pointed out -- I shouldn't have the rnum-1 in the
outer query at all:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select rnum, dt
  2    from ( select rownum-1 rnum, to_date('&2')+rownum-1 dt
  3             from all_objects
  4            where rownum <= to_date('&1') - to_date('&2')+1 )
  5   where to_char( to_date('&2')+rnum, 'DY' ) not in ( 'SAT', 'SUN' )
  6     and not exists ( select null from exclude_dates where no_work =
trunc(to_date('&2')+rnum) )
  7  /
old   2:   from ( select rownum-1 rnum, to_date('&2')+rownum-1 dt
new   2:   from ( select rownum-1 rnum, to_date('28-jun-02')+rownum-1 dt
old   4:           where rownum <= to_date('&1') - to_date('&2')+1 )
new   4:           where rownum <= to_date('05-jul-02') - to_date('28-jun-02')+1
)
old   5:  where to_char( to_date('&2')+rnum, 'DY' ) not in ( 'SAT', 'SUN' )
new   5:  where to_char( to_date('28-jun-02')+rnum, 'DY' ) not in ( 'SAT', 'SUN'
)
old   6:    and not exists ( select null from exclude_dates where no_work =
trunc(to_date('&2')+rnum) )
new   6:    and not exists ( select null from exclude_dates where no_work =
trunc(to_date('28-jun-02')+rnum) )

      RNUM DT
---------- ---------
         0 28-JUN-02
         3 01-JUL-02
         4 02-JUL-02
         5 03-JUL-02
         7 05-JUL-02

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*)
  2    from ( select rownum-1 rnum
  3             from all_objects
  4            where rownum <= to_date('&1') - to_date('&2')+1 )
  5   where to_char( to_date('&2')+rnum, 'DY' ) not in ( 'SAT', 'SUN' )
  6     and not exists ( select null from exclude_dates where no_work =
trunc(to_date('&2')+rnum) )
  7  /
old   4:           where rownum <= to_date('&1') - to_date('&2')+1 )
new   4:           where rownum <= to_date('05-jul-02') - to_date('28-jun-02')+1
)
old   5:  where to_char( to_date('&2')+rnum, 'DY' ) not in ( 'SAT', 'SUN' )
new   5:  where to_char( to_date('28-jun-02')+rnum, 'DY' ) not in ( 'SAT', 'SUN'
)
old   6:    and not exists ( select null from exclude_dates where no_work =
trunc(to_date('&2')+rnum) )
new   6:    and not exists ( select null from exclude_dates where no_work =
trunc(to_date('28-jun-02')+rnum) )

  COUNT(*)
----------
         5

ops$tkyte@ORA817DEV.US.ORACLE.COM>

      Rating: 4
WHAU GREAT
      !!!  September 22, 2002
Reviewer:  CHRIS

THE QUERY IS PERFECT NOW !
YOU'RE JUST GREAT !! THX TOM !!!!

September 20, 2002 Sagi -- Thanks for the question regarding "A Few
      Questions", version 8x

      You Asked
Hi Tom !

As usual need your help for the below questions:

1. Can you give me a URL that talks about mod_plsql with examples in depth.

2. I am aware of installing STATSPACK. But the problem is its output i.e.
Report. Can you either explain it (if possible) or give us the URL where it
explains the report in detail.

3. Can you give me an Example as of how we can send SMS when the db is being
shutdown or started.

When we Install 8.1.7 we get the following services (Windows NT/2000 platform):

a) OracleOraHome81Agent
b) OracleOraHome81ClientCache
c) OracleOraHome81CMAdmin
d) OracleOraHome81CMan
e) OracleOraHome81DataGather
f) OracleOraHome81PagingServer

If I am not wrong then c&d are used for Connection Pooling etc by CMAN
(Connection Manager). Right?

4. What are the other services. How do we use them and what for?

5. Is OracleOraHome81Agent used for HS (Hetrogenous Service)? If no then can you
explain this.

6. We use
SQLNET.AUTHENTICATION_SERVICES=
Value is NONE, NTS. What are the other values available. Can you explain about
this parameter. Does this palay a major role from security point of view.

Thanx in advance. Have a nice weekend.

REgards,
Sagi.

      and we said...
1)
http://technet.oracle.com/docs/products/ias/doc_library/90200doc_otn/web.902/a90855/toc.htm
2) well, if you search for it on www.oracle.com/oramag, you find a couple.  If
you have my book "Expert one on one" I spend more then a couple of pages on
reading a report (and tkprof and trace files and so on)

3) nope, but -- iAS does wireless stuff and many times just running a "url" can
do that (eg: I can send a page by hitting a URL and utl_http can run urls for
me).

OracleService<SID>
------------------
Created for the database instance SID.

Oracle<ORACLE_HOME>TNSListener
------------------------------
Listens for and accepts incoming connection requests from client applications.
Automatically starts when the WINNT computer restarts.

Oracle<ORACLE_HOME>NAMES
------------------------
Used for distributed Naming Service developed for Oracle Environments

Oracle<ORACLE_HOME>CMan & Oracle<ORACLE_HOME>CMAdmin
----------------------------------------------------
Provides connection concentration, Net8 Access Control and Multi-Protocol
Support

OracleEXTPRC
------------
Used for external Naming methods (NDS and NIS)

Oracle<ORACLE_HOME>Agent
------------------------
Listens for and responds to job and event requests sent from the Oracle
Enterprise Manager Console

Oracle<ORACLE_HOME>ContextService
---------------------------------
Context Server processes started on an NT server are normally terminated by
WIndows NT when the user logs off. THis service can be used to start server
processes that survive logouts,

Oracle<ORACLE_HOME>ExtprocAgent
-------------------------------
Enables external procedure callouts from PL/SQL to the database to be
implemented with multiple, light-weight threads instead of a single dedicated
process.

OracleWebAssistant
----------------
Enables information frorm database queries to be published to a Web page at
specified time intervals.

OracleMTSService0
----------------
Provides the COM communication interface between Microsoft Transaction Server
(and its MS DTC component) and the Oracle8i database.

Oracle<ORACLE_HOME>ClientCache
------------------------------
Enables caching on the client machine of service addresses resolved via Oracle
Names.

Oracle<ORACLE_HOME>DataGatherer
-------------------------------
Collects data and statistics from the database for Oracle Enterprise Manager.

Oracle<ORACLE_HOME>HTTPServer
-----------------------------
HTTP entry point for appliations.

Oracle<ORACLE_HOME>PagingServer
-------------------------------
Service used by Oracle Enterprise Manager to send pages to DBAs when certain
user defined events occur.

6) tis all documented for you convienently in the net8 admin guide:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/network.817/a76933/params.htm#436585
(yes, it would play in security)
....

Review &
      Followup

      Rating:
      4
  September 23, 2002
Reviewer:  Sagi
       from India
Thanx tom.

Regards,
Sagi

September 20, 2002 ashraf -- Thanks for the question regarding
      "upgrade", version 8.1.7

      You Asked
1- i need to ask you a simple question what is the best way to upgrade from ver
8i 1.7 to oracle 9

2 - i know that the next version of microsoft which .net is not support to java
so what is for oracle database and internal java procedure

best reg.

      and we said...
1) it would be to read the migration/upgrade guide:

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/nav/docindex.htm#index-MIG
2) I hadn't heard that.  I thought .nyet was supposed to be "open to any and all
languages, regardless of sex, race, color or creed".

I guess if they outlaw Java, you would have to choose:

a) to "wang" yourself (wang was a popular computer system about a decade or more
ago.  they locked you into a hardware platform -- a suite of software (they made
the spreadsheet, word processor, OS, et. al.).  All from a single vendor.  sound
familar?

b) Or, to go open systems and use standards when making purchasing decisions
instead of products.  Buy things that MANY vendors make (eg: j2ee runtime
engines for example, sql database engines).  Make as one of your purchasing
criteria the following litmus test "similar or even better -- the same --
products are available from at least 3 commericially viable companies"

It is a crucial choice.  Think back 5 years and ask your IT guys "whay did you
field really important systems on".  I'll bet it wasn't windows.

Go back 10 years -- ask same question -- Early adopters might say unix but most
won't (it'll be wang or VMS or something like that).

Go back 15 years -- ask same question.... (i was using a 3278 green screen
terminal to program PL/I against SQL/DS on an IBM/VM CMS system, submitting
batch jobs to TSO on MVS running something called "db2" back then)

and so on.

So, fast forward 5 years -- as MS says "where do you to go today?", I'd ask
"where do you want to be then?"

Actually, with .net, there is this thing called web services that should allow
for some degree of interoperability unless they warp that into something that
makes us not able to talk.
Review & Followup

      Rating: 5
I hope that
        September 21, 2002
Reviewer:  ashraf
       from kuwait
if you send me your email i can send you one technical mail forward for me i
need your comment if you have a time

September 21, 2002 Mokhles -- Thanks for the question regarding
      "Oracle Financial Implementation", version 8.0.5

      You Asked
Please, I would like to know the roles of the DBA during the Oracle Financial
Implemntation ?
Regards

      and we said...
Oracle Database Administrator:  This person will be primarily responsible for
the RDBMS software and operation.  This includes, but is not limited to, such
duties as

*    database startup and shutdown,
*    database resource monitoring (tablespace usage and fragmentation, I/O
        loads, rollback segments, space planning etc.),
*    tuning the Oracle RDBMS (database initialization parameters, redo
        logfiles, archiving etc.),
*    RDBMS security,
*    maintenance of separate development/test/production regions,
*    monitor and administrate the movement of Oracle software between
        environments,
*    periodic maintenance and disposal of 'log' and 'out' files generated by
        the applications,
*    periodic maintenance of 'dynamic' tables such as interface tables and
        concurrent request tables,
*    upgrade of all Oracle RDBMS and Oracle Applications software,
*    coordinate many of the activities of the NT System Administrator and
        Oracle Application Object Library Administrator

The Oracle DBAs can be the most important person(s) in the support network.
They will work closely with the System Administrator, LAN System Administration
Group, and Oracle Application Object Library Administrator to keep the system up
and running efficiently.  A well rounded DBA will have (in order of importance)
extensive Oracle RDBMS experience, a great deal of System experience (specific
platform experience a plus, but not too important), and preferably, some
exposure to the Oracle Applications as well.  In this way, the Oracle DBA can
'bridge" all levels from the operating system to the end user.

Review &
      Followup

      Rating:
      4
  September 20, 2002
Reviewer:
      A reader
You have given a very forthright answer. But I think the questioner wanted to
find out if there is anything more to it for working as an "Oracle Apps DBA".
You know that everywhere people insist on couple of years experience on Oracle
Applications while recruiting DBAs for Oracle ERP projects. Why is that? If one
is good at the Oracle D/B and associated technologies, will he not be able to
support an Oracle ERP application without any problem? What is your openion?

          Followup:

*    periodic maintenance and disposal of 'log' and 'out' files generated by
        the applications,
*    periodic maintenance of 'dynamic' tables such as interface tables and
        concurrent request tables,
*    upgrade of all Oracle RDBMS and Oracle Applications software,
*    coordinate many of the activities of the NT System Administrator and
        Oracle Application Object Library Administrator

all require somewhat in depth knowledge of APPS -- especially point 3, "upgrade
apps"

      Rating:
      4
  September 20, 2002
Reviewer:
      A reader
Agreed, but how is Oracle applications different from any other application
developed based on Oracle technologies? Does the Oracle applications product
give any different interfaces to the DBA (like SAP does)? Could you please give
more explanation. (This might be a non-issue for you actually, but I am curious
to know why do people think Oracle Applications DBA role is different then the
standard DBA role). Thanks.

          Followup:

They are wanting you to have hands on, in depth knowledge in the configuration,
installation and workings of the appplications.

how does concurrent manager work/run.
how do I find out the real user behind the apps user in the database.
stuff "specific" to apps.

      Rating: 5
distinction
      between apps dba and dba  September 20, 2002

Reviewer:  Steve Montgomerie  from Orlando

My experience is with PS but do you find that Oracle Apps DBA's are strictly DB
centric or are thet expected to have carnal knowledge of the application as
well?

          Followup:

they have carnal knowledge of the apps they support.

      Rating:
      4
  February 01, 2003
Reviewer:  Sean
       from Atlanta
In reality (in general), most of the APPS DBA do not know much about Oracle
Database. They only know about the apps part such as how apply patches, manager
concurrent jobs, and upgrade the apps.

      Rating: 4
DBA and
      AppsDBA  September 10, 2003
Reviewer:  Shouvik
      Basu  from India
I have worked as both and can confidently say working as a core DBA is much more
intellectually rewarding if you are a programmer/techie by heart.

AppsDBA is a production support person

(1) There is no Designing for performance, tables are already designed. No
application of tuning skills. The codes are already there.
(2) Rarely Apps databases are configured on RAC, Dataguard. No advanced
replication, etc.
(3) Only recently Apps is in 9i Database (11.5.9 is in 9i), while 10g Database
is on ground. So you are always one version back.

The only thing they have in common is Backup and Recovery.

Apps is yet to catchup in its usage of the database (most of the apps tables
donot have primary keys, let alone foreign keys, I donot know if this is a
consious design decision, but it makes no sense to me).

When people look for Apps DBA they really look for Apps DBA because there are
special areas where the E-Business suite pinches which only a veteran knows
(like critical patches to apply).

Being a 8i and 9i OCP, I personally would like to go back to core DBA jobs from
AppsDBA role, which I am playing now.

      Rating:
      4
Interesting  September 29, 2003
Reviewer:
      Robert  from Memphis, USA

September 21, 2002 Kevin -- Thanks for the question regarding "Allow
      queries without granting access to table", version 9i

      You Asked
I need to be able to support an unusual type of response to a standard table
query: the result of the query needs to be something along the lines of "you
don't have sufficient permissions to return any table data from this query, but
if you did, 10 records would have been returned".

The query will likely be run against a remote server, and the owner of the
server would probably rather not create/manage accounts for all these people
that are essentially being denied access, but it's early enough in the process
that I have a lot of design latitude.

I'd ultimately like to make it flexible enough that if we implemented FGAC with
DBMS_RLS, we could accomplish the same type of functionality on rows that aren't
included in the user's resultset.

      and we said...
Unusual to say the least.

FGAC won't do this (it would just prevent them from seeing the records, you
would never know how many records you didn't "see").

The only thing that pops into mind would be a stored procedure that returns a
ref cursor.  The user would run the procedure, the procedure would open the
query and if the end user isn't allow to have it, they would just fetch and
throw out the data -- counting rows.  The stored procedure would return the
number of rows it fetched.  If the user was allowed to see it, the procedure
would return the ref cursor and would NOT fetch from it at all.
September 21, 2002 Anil -- Thanks for the question regarding "online
      tablespace backup", version 8.1.7

      You Asked
Hi Tom,

   1. When we make the online backup of the tablespace,Oracle stores the before
image data of the tablespace in RedoLog.Is it True?. If ,Yes! then why it is
required?

    2. In 8.1.7 can we create tablespaces of multiple block size ?

Regards,
Anil.

      and we said...
1) no, it is false.  redo -- only redo -- goes into redo.  read also

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:271815712711
2) no, that is a 9i new feature.  Not available prior to that.
September 22, 2002 Ram -- Thanks for the question regarding "SQL
      Query", version 8.1.7

      You Asked
Tom,

    I have table

  Name            Datatype
Code              Varchar2(2) PK
Name              varchar2(50)

  The result i am looking is like search....ie i will pass code and name as
arguments..if both code and name are null then i want all the rows to be
displayed and if code is passed then the query should return only rows for that
code and same for name.
  Can this result be achieved by a single SQL query??if not what is the
alternate way to do???

Thanks.

      and we said...
You should be able to use the following concept in this case, since there is
only one column in question.

It'll EITHER full scan the table if "id is null" or index range scan the table
if "id is not null".  We can see this by running the two queries I'll union all
together in isolation (so as to see the logical io's performed by each) and we
can verify that when done together in a union all -- we really only ran one of
the queries:

ops$tkyte@ORA817DEV.US.ORACLE.COM> /*
DOC>drop table t;
DOC>
DOC>create table t as select * from all_objects;
DOC>alter table t add constraint t_pk primary key(object_id);
DOC>*/
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> variable id number
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where :id is not null AND
object_id = :id;

no rows selected

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   FILTER
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'T'
   3    2       INDEX (UNIQUE SCAN) OF 'T_PK' (UNIQUE)

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

that query did no work in the db really, we saw ":ID IS NOT NULL" and didn't
even run it

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where :id is null;

23619 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   FILTER
   2    1     TABLE ACCESS (FULL) OF 'T'

Statistics
----------------------------------------------------------
          0  recursive calls
         12  db block gets
       1881  consistent gets
          0  physical reads
          0  redo size
    2786196  bytes sent via SQL*Net to client
     175139  bytes received via SQL*Net from client
       1576  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      23619  rows processed

and that part pulled all of the rows back for us with 1881 logical ios (lios)

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where :id is not null AND
object_id = :id
  2  UNION ALL
  3  select * from t where :id is null;

23619 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   UNION-ALL
   2    1     FILTER
   3    2       TABLE ACCESS (BY INDEX ROWID) OF 'T'
   4    3         INDEX (UNIQUE SCAN) OF 'T_PK' (UNIQUE)
   5    1     FILTER
   6    5       TABLE ACCESS (FULL) OF 'T'

Statistics
----------------------------------------------------------
          0  recursive calls
         12  db block gets
       1881  consistent gets
          0  physical reads
          0  redo size
    2786196  bytes sent via SQL*Net to client
     175139  bytes received via SQL*Net from client
       1576  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      23619  rows processed

put the two together and there you go -- it is as if we did the full scan
ONLY.  Now, plug in an actual value:

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec :id := 45798

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where :id is not null AND
object_id = :id;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   FILTER
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'T'
   3    2       INDEX (UNIQUE SCAN) OF 'T_PK' (UNIQUE)

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

this time, that query ran -- did an index read or two and then a table access
to get the data..

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where :id is null;

no rows selected

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   FILTER
   2    1     TABLE ACCESS (FULL) OF 'T'

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

and the full scan did NOTHING this time...

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where :id is not null AND
object_id = :id
  2  UNION ALL
  3  select * from t where :id is null;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   UNION-ALL
   2    1     FILTER
   3    2       TABLE ACCESS (BY INDEX ROWID) OF 'T'
   4    3         INDEX (UNIQUE SCAN) OF 'T_PK' (UNIQUE)
   5    1     FILTER
   6    5       TABLE ACCESS (FULL) OF 'T'

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

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

run the same query as before, but with the bind variable NOT NULL and we do
the index plan, not the full table scan

Review &
      Followup

      Rating: 3
sorry for
      confusion  September 21, 2002
Reviewer:  Ram
       from US
Tom,

  I got what you are saying..but consider
for example..if table contain

AB    Test
CD
RT    Normal
UT    Random

My question is
  If i pass arguments for code and name all the 4 rows should be displayed,if i
pass code as say 'AB' then first row to be displayed and if i pass 'Random' or
'R' then 4th row should be displayed ...
  So how to achieve this in single sql

          Followup:

You do not want a single query then.  You want three queries.  You will want to
use a stored procedure that returns a ref cursor that opens a specific query
given the inputs.

Anything else will tend to be "non optimal" as far as execution plans go.

Your first query (no inputs, all NULLS) wants a full scan

Your second query (input code) wants to use an index on code

Your third query (input some text) wants to use an index on data

      Rating: 3
Will this
      Work  September 21, 2002
Reviewer:
      A reader
Tom ,
     Will this work.

select * from t where
decode(:cd,null,'1',code) = nvl(:cd,'1') and
decode(:n,null ,'1',name) = nvl(:n,'1')

          Followup:

I said there are ways to write the query -- however, they REALLY want 3 queries
(as all queries are not created equal).

That query would full scan (eg: be good when the binds are both NULL)

when you pass in a value for code -- you DON'T WANT A FULL SCAN, you want a
unique index probe.

when you pass in a value for data -- you DON'T WANT A FULL SCAN, you want an
index range scan.

So, whether or not that works isn't the point, they don't want to use it at all.

September 22, 2002 Dan -- Thanks for the question regarding "SQL
      questions involving combinations", version 8.1.7.1

      You Asked
Dear Tom...

I have two questions that are somewhat related to each other in concept:

1) Consider the following table which represents a state history per each person
identified by PERSON_ID:

SQL> CREATE TABLE status_history
  2         (person_id     NUMBER,
  3          status_code   VARCHAR2(1),
  4          status_date   DATE)
  5  /

Table created.

SQL> INSERT INTO status_history
  2  SELECT 1, 'A', TO_DATE('01-JAN-2002', 'DD-MON-YYYY') FROM DUAL
  3  UNION SELECT 1, 'B', TO_DATE('02-JAN-2002', 'DD-MON-YYYY') FROM DUAL
  4  UNION SELECT 1, 'C', TO_DATE('03-JAN-2002', 'DD-MON-YYYY') FROM DUAL
  5  UNION SELECT 2, 'A', TO_DATE('03-JAN-2002', 'DD-MON-YYYY') FROM DUAL
  6  UNION SELECT 2, 'C', TO_DATE('05-JAN-2002', 'DD-MON-YYYY') FROM DUAL
  7  UNION SELECT 2, 'B', TO_DATE('06-JAN-2002', 'DD-MON-YYYY') FROM DUAL
  8  UNION SELECT 3, 'B', TO_DATE('02-JAN-2002', 'DD-MON-YYYY') FROM DUAL
  9  UNION SELECT 3, 'A', TO_DATE('07-JAN-2002', 'DD-MON-YYYY') FROM DUAL
 10  UNION SELECT 3, 'C', TO_DATE('08-JAN-2002', 'DD-MON-YYYY') FROM DUAL
 11  UNION SELECT 4, 'B', TO_DATE('01-JAN-2002', 'DD-MON-YYYY') FROM DUAL
 12  UNION SELECT 4, 'A', TO_DATE('05-JAN-2002', 'DD-MON-YYYY') FROM DUAL
 13  UNION SELECT 4, 'C', TO_DATE('08-JAN-2002', 'DD-MON-YYYY') FROM DUAL
 13  /

12 rows created.

SQL> SELECT * FROM status_history
  2  ORDER BY person_id, status_date
  3  /

PERSON_ID S STATUS_DA
--------- - ---------
        1 A 01-JAN-02
        1 B 02-JAN-02
        1 C 03-JAN-02
        2 A 03-JAN-02
        2 C 05-JAN-02
        2 B 06-JAN-02
        3 B 02-JAN-02
        3 A 07-JAN-02
        3 C 08-JAN-02
        4 B 01-JAN-02
        4 A 05-JAN-02
        4 C 08-JAN-02

12 rows selected.

My question is: how does one determine all possible sets of state changes? In
other words, the desirable result is something like the following:

COMBO   STATUS_CODE
-----   -----------
    1             A
    1             B
    1             C

    2             A
    2             C
    2             B

    3             B
    3             A
    3             C

2) Consider the following table which holds attributes for things:

SQL> CREATE TABLE thing_attributes
  2         (thing_id         NUMBER,
  3          attribute_code   VARCHAR2(1))
  4  /

Table created.

SQL> INSERT INTO thing_attributes
  2  SELECT 1, 'A' FROM DUAL
  3  UNION SELECT 1, 'B' FROM DUAL
  4  UNION SELECT 2, 'B' FROM DUAL
  5  UNION SELECT 2, 'C' FROM DUAL
  6  UNION SELECT 2, 'D' FROM DUAL
  7  UNION SELECT 3, 'A' FROM DUAL
  8  UNION SELECT 3, 'D' FROM DUAL
  9  UNION SELECT 4, 'D' FROM DUAL
 10  /

8 rows created.

SQL> SELECT * FROM thing_attributes
  2  /

 THING_ID A
--------- -
        1 A
        1 B
        2 B
        2 C
        2 D
        3 A
        3 D
        4 D

8 rows selected.

Each thing can have at least one and no more than four of the attributes 'A',
'B', 'C', or 'D'.

Now consider the following table which contain things belonging to persons:

SQL> CREATE TABLE belongings
  2         (person_id   NUMBER,
  3          thing_id    NUMBER)
  4  /

Table created.

SQL> INSERT INTO belongings
  2  SELECT 100, 1 FROM DUAL
  3  UNION SELECT 100, 2 FROM DUAL
  4  UNION SELECT 100, 3 FROM DUAL
  5  UNION SELECT 100, 4 FROM DUAL
  6  UNION SELECT 200, 1 FROM DUAL
  7  UNION SELECT 200, 2 FROM DUAL
  8  UNION SELECT 200, 4 FROM DUAL
  9  /

7 rows created.

SQL> SELECT * FROM belongings
  2  /

PERSON_ID  THING_ID
--------- ---------
      100         1
      100         2
      100         3
      100         4
      200         1
      200         2
      200         4

7 rows selected.

Now the question is: how does one determine all the possible combinations of
attributes that a person's collection of "things" can have, choosing only one at
a time? In other words, for person 200, the ideal answer would be something like
this:

COMBO THING_ID ATTRIBUTE_CODE
----- -------- --------------
    1        1              A
    1        2              B
    1        4              D

    2        1              B
    2        2              B
    2        4              D

    3        1              A
    3        2              C
    3        4              D

    4        1              B
    4        2              C
    4        4              D

    5        1              A
    5        2              D
    5        4              D

    6        1              B
    6        2              D
    6        4              D

To clarify, since there is only one attribute for thing 4, and there are three
for thing 2 and two for thing 1, the total number of combinations should be 6.

Can either of these thing be accomplished using pure Oracle SQL, or does this
sort of thing require PL/SQL or Java?

Many thanks in advance,

dan kefford

      and we said...
1) analytics can be VERY useful here:

ops$tkyte@ORA920.US.ORACLE.COM> select distinct rtrim(state_change,'-')
  2    from (
  3  select person_id,
  4         max(decode(rn,1,status_code,null)) ||'-'||
  5         max(decode(rn,2,status_code,null)) ||'-'||
  6         max(decode(rn,3,status_code,null)) ||'-'||
  7         max(decode(rn,4,status_code,null)) ||'-'||
  8         max(decode(rn,5,status_code,null)) ||'-'||
  9         max(decode(rn,6,status_code,null)) ||'-'||
 10         max(decode(rn,7,status_code,null)) ||'-'||
 11         max(decode(rn,8,status_code,null))  state_change
 12    from ( select person_id, status_code,
 13                  row_number() over ( partition by person_id
 14                                      order by status_date ) rn
 15             from status_history
 16         )
 17   group by person_id
 18         )
 19  /

RTRIM(STATE_CHA
---------------
A-B-C
A-C-B
B-A-C

ops$tkyte@ORA920.US.ORACLE.COM>

for example (doesn't make sense to pull along person_id, we could -- but it
isn't meaningful at all)

2) that will, i believe (it hurts my head to think of doing this in SQL),
require a little bit of procedural processing.

this'll be one of those rare cases where you want to say "forget SQL for a
minute, lets just program it"
Review &
      Followup

      Rating:
      4
excellent  September 22, 2002
Reviewer:
      A reader
excellent codes.
possible problem is: if times of status change is over 8, you have to modify
this sql.

          Followup:

Yes, i neglected to point that out -- the number of status changes must be
finite and known.

      Rating: 5
Always
      something usefull  September 22, 2002
Reviewer:
      Chuck Jolley  from Oklahoma City, OK USA
row_number() !?

I guess we now have a full set:
row_number()
dense_rank()
and rank()

Very helpfull!
Thanks,
Chuck

September 22, 2002 alan -- Thanks for the question regarding "Can a
      direct export by consistent?", version 8.1.7.3

      You Asked
Hi Tom,

When doing a direct export can we also specify the consistent=y parameter?  I
haven't seen anything that says no but I also haven't seen anything that says
yes.

And, if it is possible, then how does Oracle do it given that the sql is not
being evaluated?

Thanks,

Alan

      and we said...
The sql is being evaulated.  You might want to re-read that chapter in the
Utilities guide.

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76955/ch01.htm#32400
that picture shows it best.  we do 99% of the same steps.  Yes, you can do
consistent=y

September 22, 2002 -- Thanks for the question regarding "Oracle
      Management server", version 9.0.1

      You Asked
Hi Tom,
Just needed to know if the Oracle Management server is inclusive with the Oracle
Database Enterprise version for 9.0.1.
I tried searching this on web but nowhere does oracle give a clear indication,
that if u hv brought this pack then Orqcle management server is inclusive but in
case you need additional tunning packs then you have to purchase them separatly.

Thanks

Nihal

      and we said...
OMS is part of base OEM (part of the infrastructure).

For Oracle9i Release 1, if you goto:

http://asktom.oracle.com/~tkyte/README_rdbms.htm#546820
I've posted the readme that comes with the database (in $ORACLE_HOME/rdbms/doc).
 That has the feature/options for 9iR1.  For 9iR2, you would look to:

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96531/ch5_edit.htm#73594
but OMS isn't listed there as it is just part of the "workings" of OEM -- sort
of like how we don't like PMON ;)  It is just part of the workings.

Review &
      Followup

      Rating: 5
Having problems
      in connecting to OMS  February 07, 2003
Reviewer:
      Riaz Shahid  from PRAL, Islamabd, Pakistan
Thanks for ur answers abt OMS. Me facing following problem while connecting to
OMS:

C:\>oemctrl status oms
OEMCTRL for Windows NT: Version 2.2.0.0.0
Copyright (c) 1998, 2000, Oracle Corporation.  All rights reserved.
oracle.sysman.emSDK.client.omsClient.SecureCommException: EMSDK-1100 : Unable to
 establish a secure communication channel.
        at
oracle.sysman.emSDK.client.omsClient.Client.initCryptoContext(Client.java:1715)
        at oracle.sysman.emSDK.client.omsClient.Client.doLogon(Client.java:1234)

        at
oracle.sysman.emSDK.client.omsClient.Client.silentLogin(Client.java:776)
        at oracle.sysman.vxa.VxaOMSStatus.main(VxaOMSStatus.java:125)

C:\>oemctrl stop oms
OEMCTRL for Windows NT: Version 2.2.0.0.0
Copyright (c) 1998, 2000, Oracle Corporation.  All rights reserved.
Stopping the Oracle Management Server...
VXA-1005 : Unable to establish a secure communication channel.
VXA-2012 : Failed to log in

I am using Oracle 8.1.7 on Windows 2000. How can i solve that ???

Regards

September 23, 2002 Laura -- Thanks for the question regarding
      "Dynamically Creating Next Month as Column in Table", version 8.1.7

      You Asked
I want to dynamically create the next month.  I do not want to copy and paste
the last line and change the month to the next month manually.  For example, the
next line would be SUM((DECODE(YEAR_MONTH,'200206',SUM_SAMPLES,NULL)))
SAMPLES_MADE_200206.  I am not asking for you to do it.  What I am asking is
where I can find a good example to learn from.  I was reading the questions on
www.orafaq.org and I must admit I am getting confused.  I am assuming that I
would want to use a cursor.  If my question does not belong here, I apologize.
Any assistance would be greatly appreciated.  I do not have much experience in
cursors.

INSERT INTO LJC_TABLE
SELECT ACM_MSTRACCOUNTKEY,
       ACM_MEDPROVNO,
       ACM_EXTPHYSRXID,
       SMPL_ID,
       SMPL_DESC_TEXT,
       SUM((DECODE(YEAR_MONTH,'200001',SUM_SAMPLES,NULL))) SAMPLES_MADE_200001,
       SUM((DECODE(YEAR_MONTH,'200002',SUM_SAMPLES,NULL))) SAMPLES_MADE_200002,
       SUM((DECODE(YEAR_MONTH,'200003',SUM_SAMPLES,NULL))) SAMPLES_MADE_200003,
       SUM((DECODE(YEAR_MONTH,'200004',SUM_SAMPLES,NULL))) SAMPLES_MADE_200004,
       SUM((DECODE(YEAR_MONTH,'200005',SUM_SAMPLES,NULL))) SAMPLES_MADE_200005,
       SUM((DECODE(YEAR_MONTH,'200006',SUM_SAMPLES,NULL))) SAMPLES_MADE_200006,
       SUM((DECODE(YEAR_MONTH,'200007',SUM_SAMPLES,NULL))) SAMPLES_MADE_200007,
       SUM((DECODE(YEAR_MONTH,'200008',SUM_SAMPLES,NULL))) SAMPLES_MADE_200008,
       SUM((DECODE(YEAR_MONTH,'200009',SUM_SAMPLES,NULL))) SAMPLES_MADE_200009,
       SUM((DECODE(YEAR_MONTH,'200010',SUM_SAMPLES,NULL))) SAMPLES_MADE_200010,
       SUM((DECODE(YEAR_MONTH,'200011',SUM_SAMPLES,NULL))) SAMPLES_MADE_200011,
       SUM((DECODE(YEAR_MONTH,'200012',SUM_SAMPLES,NULL))) SAMPLES_MADE_200012,
       SUM((DECODE(YEAR_MONTH,'200101',SUM_SAMPLES,NULL))) SAMPLES_MADE_200101,
       SUM((DECODE(YEAR_MONTH,'200102',SUM_SAMPLES,NULL))) SAMPLES_MADE_200102,
       SUM((DECODE(YEAR_MONTH,'200103',SUM_SAMPLES,NULL))) SAMPLES_MADE_200103,
       SUM((DECODE(YEAR_MONTH,'200104',SUM_SAMPLES,NULL))) SAMPLES_MADE_200104,
       SUM((DECODE(YEAR_MONTH,'200105',SUM_SAMPLES,NULL))) SAMPLES_MADE_200105,
       SUM((DECODE(YEAR_MONTH,'200106',SUM_SAMPLES,NULL))) SAMPLES_MADE_200106,
       SUM((DECODE(YEAR_MONTH,'200107',SUM_SAMPLES,NULL))) SAMPLES_MADE_200107,
       SUM((DECODE(YEAR_MONTH,'200108',SUM_SAMPLES,NULL))) SAMPLES_MADE_200108,
       SUM((DECODE(YEAR_MONTH,'200109',SUM_SAMPLES,NULL))) SAMPLES_MADE_200109,
       SUM((DECODE(YEAR_MONTH,'200110',SUM_SAMPLES,NULL))) SAMPLES_MADE_200110,
       SUM((DECODE(YEAR_MONTH,'200111',SUM_SAMPLES,NULL))) SAMPLES_MADE_200111,
       SUM((DECODE(YEAR_MONTH,'200112',SUM_SAMPLES,NULL))) SAMPLES_MADE_200112,
       SUM((DECODE(YEAR_MONTH,'200201',SUM_SAMPLES,NULL))) SAMPLES_MADE_200201,
       SUM((DECODE(YEAR_MONTH,'200202',SUM_SAMPLES,NULL))) SAMPLES_MADE_200202,
       SUM((DECODE(YEAR_MONTH,'200203',SUM_SAMPLES,NULL))) SAMPLES_MADE_200203,
       SUM((DECODE(YEAR_MONTH,'200204',SUM_SAMPLES,NULL))) SAMPLES_MADE_200204,
       SUM((DECODE(YEAR_MONTH,'200205',SUM_SAMPLES,NULL))) SAMPLES_MADE_200205
FROM LJC_SAMPLES_PREPIVOT
WHERE SMPL_ID=1900000520
GROUP BY ACM_MSTRACCOUNTKEY, ACM_MEDPROVNO, ACM_EXTPHYSRXID, SMPL_ID,
SMPL_DESC_TEXT
ORDER BY ACM_MSTRACCOUNTKEY, ACM_MEDPROVNO, ACM_EXTPHYSRXID, SMPL_ID,
SMPL_DESC_TEXT

      and we said...
You need to dynamically generate this insert then -- it could look like this:

create or replace procedure do_insert( p_start_month in date )
as
    l_stmt     long;
    l_month_to date default add_months( trunc(sysdate,'month'), -1 );
    l_curr_month date default p_start_month;
begin
    l_stmt := 'insert into ljc_taable
               select CM_MSTRACCOUNTKEY, ACM_MEDPROVNO,
                      ACM_EXTPHYSRXID, SMPL_ID, SMPL_DESC_TEXT,';

    for i in 0 .. months_between( l_month_to, p_start_month )
    loop
        l_stmt := l_stmt ||
                   'sum((decode(year_month,''' ||
                   to_char(l_curr_month, 'yyyymm' ) ||
                   ''', sum_samples,0))) samples_made_' ||
                   to_char(l_curr_month,'yyyymm') || ',';
        l_curr_month := add_months(l_curr_month,1);
    end loop;

    l_stmt := rtrim(l_stmt,',') || ' FROM LJC_SAMPLES_PREPIVOT
    WHERE SMPL_ID=1900000520
    GROUP BY ACM_MSTRACCOUNTKEY, ACM_MEDPROVNO, ACM_EXTPHYSRXID, SMPL_ID,
    SMPL_DESC_TEXT
    ORDER BY ACM_MSTRACCOUNTKEY, ACM_MEDPROVNO, ACM_EXTPHYSRXID, SMPL_ID,
    SMPL_DESC_TEXT';

    execute immediate l_stmt;
end;
/

exec do_insert( to_date( '200001','yyyymm' ) );

given that it is july right now -- that'll create an insert upto 200206

Review &
      Followup

      Rating: 5
Dynamically
      Creating Next Month as Column in Table  July 10, 2002

Reviewer:  Laura Cerniglia  from Parsippany, NJ USA

Thank you very much for educating me.  This is the type of example that I have
been looking for.

      Rating:
      5
  July 12, 2002
Reviewer:  Laura
      Cerniglia  from Parsippany, NJ USA
I am new to this web site and I am not sure if I should place my follow up
question here.

I have been working with the code that you provided.  It compiles sucessfully.
However, when I run it I get the following message:

13:18:21 SQL> exec do_insert( to_date( '200001','yyyymm' ) );
BEGIN do_insert( to_date( '200001','yyyymm' ) ); END;

*
ERROR at line 1:
ORA-00904: invalid column name
ORA-06512: at "ORASOPS.DO_INSERT", line 25
ORA-06512: at line 1

I have been trying to debug it and I am getting nowhere.  Please help.  I am
very frustrated with myself.  Could it be an issue with the privileges set to
the logon that I am using?

          Followup:

Well, does the table you are inserting into HAVE those columns?

I guess we need to backup a moment -- the procedure above will create a sql
statement to insert into an existing table, which always has an existing set of
columns.

Does this procedure also need to dynamically "add" a column on the fly -- for
the most recent month?

      Rating:
      5
  July 12, 2002
Reviewer:  Laura
      Cerniglia  from Parsippany, NJ USA
Does this procedure also need to dynamically "add" a column on the fly -- for
the most recent month?

Yes.  This code is part of a larger script.  In the script I drop the table and
recreate it following your example. Then I want to insert into it following your
example.

CREATE OR REPLACE PROCEDURE DO_INSERT1 (P_START_MONTH  )
AS
    L_STMT         LONG;
    L_MONTH_TO     DATE DEFAULT ADD_MONTHS( TRUNC(SYSDATE,'MONTH'), -2 );
    L_CURR_MONTH   DATE DEFAULT TO_DATE(P_START_MONTH,'YYYYMM');

BEGIN
    L_STMT := 'CREATE TABLE LJC_TABLE (
               ACM_MSTRACCOUNTKEY          NUMBER(10),
               ACM_MEDPROVNO               VARCHAR2(14),
               ACM_EXTPHYSRXID             NUMBER(10),
               SMPL_ID                     NUMBER(16),
               SMPL_DESC_TEXT              VARCHAR2(80),' || CHR(10);

    FOR I IN 0 .. MONTHS_BETWEEN( L_MONTH_TO, L_CURR_MONTH )
    LOOP
        L_STMT := L_STMT ||
                   'SAMPLES_MADE_''' ||
                   TO_CHAR(L_CURR_MONTH, 'YYYYMM' ) ||
                   '''NUMBER(5),' || CHR(10);

        L_CURR_MONTH := ADD_MONTHS(L_CURR_MONTH,1);
    END LOOP;

    L_STMT := RTRIM(L_STMT,',') || ' )';

    EXECUTE IMMEDIATE L_STMT;
END;

create or replace procedure do_insert( p_start_month in date )
as
    l_stmt     long;
    l_month_to date default add_months( trunc(sysdate,'month'), -1 );
    l_curr_month date default p_start_month;
begin
    l_stmt := 'insert into ljc_table
               select CM_MSTRACCOUNTKEY, ACM_MEDPROVNO,
                      ACM_EXTPHYSRXID, SMPL_ID, SMPL_DESC_TEXT,';

    for i in 0 .. months_between( l_month_to, p_start_month )
    loop
        l_stmt := l_stmt ||
                   'sum((decode(year_month,''' ||
                   to_char(l_curr_month, 'yyyymm' ) ||
                   ''', sum_samples,0))) samples_made_' ||
                   to_char(l_curr_month,'yyyymm') || ',';
        l_curr_month := add_months(l_curr_month,1);
    end loop;

    l_stmt := rtrim(l_stmt,',') || ' FROM LJC_SAMPLES_PREPIVOT
    WHERE SMPL_ID=1900000520
    GROUP BY ACM_MSTRACCOUNTKEY, ACM_MEDPROVNO, ACM_EXTPHYSRXID, SMPL_ID,
    SMPL_DESC_TEXT
    ORDER BY ACM_MSTRACCOUNTKEY, ACM_MEDPROVNO, ACM_EXTPHYSRXID, SMPL_ID,
    SMPL_DESC_TEXT';

    execute immediate l_stmt;
end;

Something tells me that what you told me works for the second part (the
inserting) but that for the creation of the table I need to do something
different.

          Followup:

QED:

create or replace procedure make_my_table_please( p_start_month in date )
as
    l_stmt     long;
    l_month_to date default add_months( trunc(sysdate,'month'), -1 );
    l_curr_month date default p_start_month;
begin
    execute immediate 'drop table ljc_table';

    l_stmt := 'create table ljc_table AS
               select CM_MSTRACCOUNTKEY, ACM_MEDPROVNO,
                      ACM_EXTPHYSRXID, SMPL_ID, SMPL_DESC_TEXT,';

    for i in 0 .. months_between( l_month_to, p_start_month )
    loop
        l_stmt := l_stmt ||
                   'sum((decode(year_month,''' ||
                   to_char(l_curr_month, 'yyyymm' ) ||
                   ''', sum_samples,0))) samples_made_' ||
                   to_char(l_curr_month,'yyyymm') || ',';
        l_curr_month := add_months(l_curr_month,1);
    end loop;

    l_stmt := rtrim(l_stmt,',') || ' FROM LJC_SAMPLES_PREPIVOT
    WHERE SMPL_ID=1900000520
    GROUP BY ACM_MSTRACCOUNTKEY, ACM_MEDPROVNO, ACM_EXTPHYSRXID, SMPL_ID,
    SMPL_DESC_TEXT
    ORDER BY ACM_MSTRACCOUNTKEY, ACM_MEDPROVNO, ACM_EXTPHYSRXID, SMPL_ID,
    SMPL_DESC_TEXT';

    execute immediate l_stmt;
end;

the procedure should therefore create the table after dropping it...  The
paranoid (or maybe better described as those that don't want to get a phone call
in the middle of the night from someone asking "hey, where is my table") might
use this instead:

create or replace procedure do_insert( p_start_month in date )
as
    l_stmt     long;
    l_month_to date default add_months( trunc(sysdate,'month'), -1 );
    l_curr_month date default p_start_month;
begin
    l_stmt := 'create table ljc_table_temp AS
               select CM_MSTRACCOUNTKEY, ACM_MEDPROVNO,
                      ACM_EXTPHYSRXID, SMPL_ID, SMPL_DESC_TEXT,';

    for i in 0 .. months_between( l_month_to, p_start_month )
    loop
        l_stmt := l_stmt ||
                   'sum((decode(year_month,''' ||
                   to_char(l_curr_month, 'yyyymm' ) ||
                   ''', sum_samples,0))) samples_made_' ||
                   to_char(l_curr_month,'yyyymm') || ',';
        l_curr_month := add_months(l_curr_month,1);
    end loop;

    l_stmt := rtrim(l_stmt,',') || ' FROM LJC_SAMPLES_PREPIVOT
    WHERE SMPL_ID=1900000520
    GROUP BY ACM_MSTRACCOUNTKEY, ACM_MEDPROVNO, ACM_EXTPHYSRXID, SMPL_ID,
    SMPL_DESC_TEXT
    ORDER BY ACM_MSTRACCOUNTKEY, ACM_MEDPROVNO, ACM_EXTPHYSRXID, SMPL_ID,
    SMPL_DESC_TEXT';

    execute immediate 'rename ljc_table to ljc_table_' ||
                              to_char(sysdate,'YYYYMMDDHH24MISS');
    execute immediate 'rename ljc_table_temp to ljc_table';
end;

don't drop the table and then fill it -- create a temp table, after that
succeeds, rename (hide) the current table and rename the temp table to be
current.  That way, if something goes horribly wrong, at least you have the old
one to revert to quickly.  You can drop that later if and when you feel like it.

      Rating: 5
Dynamically
      Creating Next Month as Column in Table  July 17, 2002

Reviewer:  Laura Cerniglia  from Parsippany, NJ USA

I now have it working.

Thank you so very much for your help!!!  You are an Oracle Guardian Angel.

I have also learned that it helps if your logon is set up right.  This is what
my DBA wrote to me:
"One of the problems  with Execute Immediate is you can use commands like create
table only if the user is granted that privilege directly rather than through a
role, which you already had. I gave create table privilege to you directly,
which should take care of the problem. "

      Rating:
      4
  August 14, 2002
Reviewer:
      A reader
Tom:
Refering to the original question and your response:

I did a similar crosstab query sometime back. And I was faced with the same
problem of dynamically generating the timeperiod for the insert. I created a
function which returns the corresponding time period (current month, prev month,
etc....), but I found that the query became sooo slow when using the function.
The reason was the recursive SQL performed for the function. Eventhough the
function was very simple, it was called millions of times which probably
contributed to the slowdown (am I correct?). I worked around that using macro
substitution in SQLPlus. But as soon as I saw your function, I adopted it.

It was little unexpected for me to have the query slow down somuch due to the
usage of function. Can you please comment on that? Thanks again your beautiful
solution!

          Followup:

if the function was in plsql and it took 1/100 of a second to run (pretty good
for plsql) and you called it a million tims that is 10,000 seconds.  If it ran
in 1/1000 of a second, that is still 1,000 seconds (over 16 minutes).

PLSQL called from SQL adds some overhead.  In a simple query -- you don't see
it.  Do anything complex 1,000,000 times and it can add up!

      Rating:
      5
  September 22, 2002
Reviewer:  Laura
      Cerniglia  from NJ USA
Now my users want me to do the same thing at a day level instead of a month
level.

Following your example, I have the below:

CREATE OR REPLACE PROCEDURE LJC_FAR_TEST
AS
    STMT1            LONG;
    P_START_DAY      DATE DEFAULT TO_DATE( '20020101','YYYYMMDD');
    L_DAY_TO         DATE DEFAULT ADD_MONTHS( TRUNC(SYSDATE,'DAY'), 0 );
    L_CURR_DAY1      DATE DEFAULT P_START_DAY;
---
BEGIN
---
    STMT1 := 'CREATE TABLE LJC_FAR_SUM_TABLE_AT_TERRLEVEL (
                  TRM_NAME              VARCHAR2(15),
                  CBU                   VARCHAR2(15),
                  ABM                   VARCHAR2(15),
                  RABM                  VARCHAR2(15),
                  DABM                  VARCHAR2(15),
                  DESCRIPTION           VARCHAR2(80),';
      FOR I IN 0 .. MONTHS_BETWEEN( L_DAY_TO, P_START_DAY )
      LOOP
          STMT1 := STMT1 ||
                     'DATE_' ||
                     TO_CHAR(L_CURR_DAY1, 'YYYYMMDD' ) ||
                     '   NUMBER(5),';
          L_CURR_DAY1 := ADD_MONTHS(L_CURR_DAY1,1);
      END LOOP;
    STMT1 := RTRIM(STMT1,',') || ' )';
---
    EXECUTE IMMEDIATE STMT1;
---
     COMMIT;
---
END;

Basically, I want to create date fields like the following:
DATE_20020801                                                  NUMBER(5)
DATE_20020802                                                  NUMBER(5)
DATE_20020803                                                  NUMBER(5)
DATE_20020804                                                  NUMBER(5)
DATE_20020805                                                  NUMBER(5)
DATE_20020806                                                  NUMBER(5)
DATE_20020807                                                  NUMBER(5)
DATE_20020808                                                  NUMBER(5)
DATE_20020809                                                  NUMBER(5)
DATE_20020810                                                  NUMBER(5)
DATE_20020811                                                  NUMBER(5)
DATE_20020812                                                  NUMBER(5)
DATE_20020813                                                  NUMBER(5)
DATE_20020814                                                  NUMBER(5)
DATE_20020815                                                  NUMBER(5)
DATE_20020816                                                  NUMBER(5)
DATE_20020817                                                  NUMBER(5)
DATE_20020818                                                  NUMBER(5)
DATE_20020819                                                  NUMBER(5)
DATE_20020820                                                  NUMBER(5)
DATE_20020821                                                  NUMBER(5)
DATE_20020822                                                  NUMBER(5)
DATE_20020823                                                  NUMBER(5)
DATE_20020824                                                  NUMBER(5)
DATE_20020825                                                  NUMBER(5)
DATE_20020826                                                  NUMBER(5)
DATE_20020827                                                  NUMBER(5)
DATE_20020828                                                  NUMBER(5)
DATE_20020829                                                  NUMBER(5)
DATE_20020830                                                  NUMBER(5)
DATE_20020831                                                  NUMBER(5)
DATE_20020901                                                  NUMBER(5)
DATE_20020902                                                  NUMBER(5)
DATE_20020903                                                  NUMBER(5)
DATE_20020904                                                  NUMBER(5)
DATE_20020905                                                  NUMBER(5)
DATE_20020906                                                  NUMBER(5)
DATE_20020907                                                  NUMBER(5)
DATE_20020908                                                  NUMBER(5)
DATE_20020909                                                  NUMBER(5)
DATE_20020910                                                  NUMBER(5)
DATE_20020911                                                  NUMBER(5)
DATE_20020912                                                  NUMBER(5)
DATE_20020913                                                  NUMBER(5)
DATE_20020914                                                  NUMBER(5)
DATE_20020915                                                  NUMBER(5)
DATE_20020916                                                  NUMBER(5)
DATE_20020917                                                  NUMBER(5)
DATE_20020918                                                  NUMBER(5)
DATE_20020919                                                  NUMBER(5)
DATE_20020920                                                  NUMBER(5)
DATE_20020921                                                  NUMBER(5)
DATE_20020922                                                  NUMBER(5)

My code above obviously does not do it at the day level.  I tried looking for
functions to use.  I found the NEXT_DAY and LAST_DAY functions.  Would you use
these?  I am afraid that I am making this more difficult then it needs to be.

          Followup:

     FOR I IN 0 .. (l_day_to-p_start_day-1)
     LOOP
          STMT1 := STMT1 ||
                     'DATE_' ||
                     TO_CHAR( p_start_day+i, 'YYYYMMDD' ) ||
                     '   NUMBER(5),';
     END LOOP;
     ....

(the commit you have is superflorous, DDL commits for you)

      Rating:
      5
  October 09, 2002
Reviewer:  Laura
      Cerniglia  from Parsippany, NJ USA
Thanks!!!

September 23, 2002 trinh -- Thanks for the question regarding
      "rebuild indexes", version 817

      You Asked
Do we loss the index, if the online re-build fails?
thanks

      and we said...
No, you do not. All DDL is done transactionally, no loss of data will occur.
Review &
      Followup

      Rating: 5
what does "done
      transactionally" mean?  September 22, 2002

Reviewer:  A reader

          Followup:

In a transaction -- DDL is done like this:

  begin
      do the DDL
      commit;
  exception
      when others then
           ROLLBACK;
           raise;
  end;

the DDL is done transactionally, with the ability to rollback the work done in
the event of failure.

      Rating: 4
Index with
      nologging option  September 23, 2002
Reviewer:
      hrishab  from India
Dear Tom,

    My question is suppose if i create an index with nologging option and if
there is failure, can the index be recovered from redo log files. As per my
knowledge the nologging doesn't log the redo data for only direct path inserts
which writes the data directly to the data files.

Your views on this shall be highly appreciated.

Regards,
hrishab

          Followup:

If the index is in the middle of being created, and there is a failure, then NO
the index cannot be recovered since it never exists.

If you create an index with nologging and later (after the index was created)
there is a failure and you lose one of the datafiles associated with this index,
AND you didn't backup right after creating the index, then you will lose that
index as we cannot recreate it.

September 23, 2002 Reji -- Thanks for the question regarding "Object
      Type ", version 8.1.6

      You Asked
Tom:
    I have a qn related to Object type in Oracle8i.

    I have a type "Address" as object and I am using that
    type in a table.  Now, If I want to create a
statment/prepared statement in JDBC to insert some data to this
table, How do I do this?  Any samples available?

-Thx
-Reji

      and we said...
Jpublisher is what you are looking for.

<Quote>
JPublisher connects to a database and retrieves descriptions of
the SQL object types or PL/SQL packages that you specify on the
command line or from an input file. By default, JPublisher
connects to the database by using the JDBC OCI driver, which
requires an Oracle client installation, including Net8 and
required support files. If you do not have an Oracle client
installation, JPublisher can use the Oracle Thin JDBC driver.

JPublisher generates a Java class for each database object type
it translates. The Java class includes code required to read
objects from and write objects to the database. When you deploy
the generated JPublisher classes, your JDBC driver installation
includes all the necessary runtime files. If you create wrapper
methods, you additionally must have the SQLJ runtime libraries.

JPublisher also generates a class for each PL/SQL package it
translates. The class includes code to invoke the package
methods on the server. IN arguments for the methods are
transmitted from the client to the server, and OUT arguments and
results are returned from the server to the client.

</quote>

See

http://technet.oracle.com/doc/oracle8i_816/java.816/a81357/toc.htm
...

Review &
      Followup

      Rating: 4
Thanks but
      having problem with JPub  September 23, 2002

Reviewer:  Pratap Kumar Mishra  from Calcutta, India

Hi Tom,
   YOur reply is to the point as usual. But while trying to execute jpub I am
having a problem. When I try to give the command
jpub -sql=address_objtype:AddressObjType -user=scott/tiger
It gives an error
Exception in thread "main" java.lang.NoClassDefFoundError: oracle/jpub/Main

Which means that it is not getting the oracle/jpub/Main class. My classpath
includes the classes12.zip also. Do I need to anything more. Thanks in advance.

                Pratap

          Followup:

jpub is just a script, edit it and make sure the class paths it is setting up
look reasonable.

September 23, 2002 Rukshan -- Thanks for the question regarding
      "Oracle InterMedia Question", version 8.1.7.3

      You Asked
Tom,

We use Oracle Intermedia to index xml documents (metadata which relates to
products) stored in a CLOB column in the database. I have been asked to look
into the following possibilities:

1) When a customer searches eg: Videoconference as one word and when the text in
the xml document is written as Video Conference, currently no hits are returned.
How can we over come this with Intermedia Text search?.

2) Our product codes in the database (again indexed via Intermedia Text) is
stored in the format: "ABC-999.A". The .A is only relevant internally. How do
you suggest we implement the search functionality so that if the customer types
in "ABC" or "ABC 999" or "ABC999" or "999" in the search screen still finds the
product code "ABC-999.A"?.

Any suggestions or solutions to above would be greatly appreciated.

Thanks & Regards

Rukshan

      and we said...
1) you can use a thesaraus:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/inter.817/a77063/cthespkg.htm#2821
to add "synonyms".

2) Well, ABC-999.A would be stored as three pieces really (as the - and . are
"joiners" and factored out.  "A" being a commmon word would probably be in the
stoplist.

So, a search for ABC, should find that row.  999 too.  ABC 999 should hit it.
But ABC999 -- maybe what you want to do for this (and #1) is to have your search
string routine when given:

ABC 999    (or Video Conference)

would search for:

({ABC} and {999}) or {ABC999}
({Video} and {Conference}) or {VideoConference}

Review &
      Followup

      Rating: 5
indexed via
      intermedia text  September 22, 2002
Reviewer:
      michael j  from USA
can we use intermedia only for a column,table or index?

i thought, to have this, we need to enable the context server at server
side.long time back when i tried using that, the server was too slow.

if we can enable this for a table or column, it will be useful.. how can i do
this in oracle 9i?

          Followup:

interMedia (the text indexing stuff) always and forever worked on a column in a
table (or a function).

See the Oracle Text documentation for "how".

      Rating:
      4
Joiners  September 23, 2002
Reviewer:
      Rukshan Soza  from London
Tom,

When you say - and . can be joiners you mean define it as printjoins?

Rgds, Rukshan

          Followup:

yup

September 23, 2002 play -- Thanks for the question regarding
      "varrays, nested tables, and pl/sql records, and pl/sql tables, and ref
      cursor", version 8.1.7

      You Asked
Tom

what is the difference between a varray and a nested table, can you give me a
very very simple example , and explain the difference
when should I use  varrays and when should I use nested tables

the same with pl/sql record and pl/sql table
say I hvae a curser select ename,deptno from emp;
I can declare and use a pl/sql record or pl/sql table to hold these values, can
you tell me when should I choose to use pl/sql record and when should I use
pl/sql table.

lastly, can you give me a simple example for ref cursor

what is the difference between a regular explicit cursor and a ref cursor

      and we said...
a varray is fixed in size (its N elements long and no longer)
a nested table is inifinite in size potentially

a varray stores data in a fixed order, element(1) of a varray is well known and
will be the same time after time
a nested table does not assure the order will be preserved, the value of
element(1) may change over time (all of the elements will be there, but in
different orders)

a varray is stored as a blob with the table
a nested table is stored in a separate physical database table as a parent child
table.

In PLSQL code (when I do not plan on creating a database column of that type) --
I use nested tables exclusively.  I cannot think of a good reason to constrain
my array size artificially when I don't have to.  In PLSQL code, there is very
little difference between a varray and nested table type (the fixed order issue
isn't an issue with PLSQL nested tables, they are just like varrays -- its only
when you SAVE the nested table to a database table that the order might be
different when you read it back in)

In SQL (using these in create tables) -- well, there I don't really have any
advice.  I've used a varray everynow and then but stick to relation constructs
pretty much.  If I've a need of a nested table construct, I set up a parent
child relationship.

I cannot compare a record to a table -- they are orthogonal to eachother.  You
can have a table of records...  A record is used to hold a row, you can use a
table of records to hold many rows at the same time.  They are somewhat apples
and oranges.

simple ref cursor, sure:

scott@ORA8I.WORLD> variable x refcursor
scott@ORA8I.WORLD> begin open :x for select * from dept; end;
  2  /

PL/SQL procedure successfully completed.

scott@ORA8I.WORLD> print x

    DEPTNO DNAME          LOC
---------- -------------- -------------
        10 ACCOUNTING     NEW YORK
        20 RESEARCH       DALLAS
        30 SALES          CHICAGO
        40 OPERATIONS     BOSTON

The 2 major differences between an static explicit cursor and a ref cursor are:

o the ref cursor is parsed each time it is opened in plsql.  The static cursor
will be parsed ONCE per session typically (unless it gets flushed out)

o the ref cursor can point to a different query time it is opened (hence the
need to parse each time, its not static).  the static query cannot.

Review &
      Followup

      Rating: 2
How to print
      the ref cursor returned from a multi-row Function.  August
      06, 2001
Reviewer:  Sandy  from OH, USA
That was a good explanation. Tom if you could also demonstrate "How to print the
ref cursor returned from a multi-row Oracle Function", assuming the function
returns a ref-cursor type. I tried to print the output of "mce_output_qry_func"
as given below.

SQLPLUS> variable x refcursor
SQLPLUS> begin  x :=  mce_output_qry_func('CALENDAR',660,922,1); end;

It did'nt work. Would be of great help if you could get me that.

      Rating: 4
good
      explanation !  August 09, 2001
Reviewer:
      A reader

      Rating: 4
Number of limit
      on PL/SQL table  September 23, 2002
Reviewer:
      A reader  from usa
Hi, Tom,

We got a problem on number of limit on Pl/SQL table.

Suppose we use DBMS_SQL package dynamically bind variables, decribe variable and
get column values from any query. And further suppose we declare a pl/sql table
like:
type vc_arr is table of varchar2(2000) index by
                                       binary_integer;
g_cvalue  vc_arr;
use g_cvalue to hold values of some of the columns,
if the query returns more than millions of records, do we have a limit number of
records for g_cvalue variable to hold these many of records? If it does, what is
the work around?

Thanks

          Followup:

You would NEVER want to fetch millions of rows.

You should fetch maybe 100 at a time, process them, fetch the next 100.

The APIs support this (array fetching of N records at a time), look at the
define_array procedure.


Subject | Article | Last Updated (East coast time)
SQL help | article only | Sep 24, 2002 06:52 am
resizing the INITIAL extent of an object | article only | Sep 24, 2002 07:27 am
Global tablenames and dynamic html tables | article only | Sep 24, 2002 05:43 pm
9i Oracle Text searching multiple tables | article only | Sep 24, 2002 06:06 pm
DB upgarde/migration, UNDO_RETENTION setting | article only | Sep 24, 2002 06:23 pm
the status of spatial index. | article only | Sep 25, 2002 07:08 am
Init.ora and Database Instance | article only | Sep 25, 2002 08:17 am
Java vs Pro*C | article only | Sep 25, 2002 09:01 am
Query in Java Stored Procedures are slow? | article only | Sep 25, 2002 09:15 am
SQL Query Help | article only | Sep 25, 2002 09:25 am
Segments | article only | Sep 25, 2002 10:47 am
Uniform Size on LMT | article only | Sep 25, 2002 11:00 am
special character in query | article only | Sep 25, 2002 11:02 am
How to handle ORA-01422 exact fetch returns more .... | article only | Sep 25, 2002 12:28 pm
Maximum procedures in a package | article only | Sep 25, 2002 02:32 pm
SQL Help | article only | Sep 25, 2002 02:39 pm
How can I execute an external procedure | article only | Sep 25, 2002 02:42 pm
Why no constraint PK violation | article only | Sep 25, 2002 02:45 pm
Locally managed tablespace and PCTINCREASE storage parameter | article only | Sep 25, 2002 04:21 pm
any alternative for trigger | article only | Sep 26, 2002 07:26 am
Result set | article only | Sep 26, 2002 08:26 am
Does CBO considers HWM when calculating cost for a full table scan ? | article only | Sep 26, 2002 11:30 am
About Free space(TEMP & Other Tablepsaces) | article only | Sep 26, 2002 11:53 am
Using Rownum to limit a range of rows (Nth to Mth) returned from a Query | article only | Sep 26, 2002 04:42 pm
Monitoring Database | article only | Sep 26, 2002 06:29 pm
Allocation Error | article only | Sep 27, 2002 07:38 am
owa.vc_arr's mean and document ? | article only | Sep 27, 2002 08:36 am
General Development Advice | article only | Sep 27, 2002 03:22 pm
Parsing Strings and Creating Rows In Tables | article only | Sep 27, 2002 03:26 pm
PLS-00327: Table is not in SQL scope here | article only | Sep 27, 2002 03:30 pm
sqlplus hangs and takes for ever to do this | article only | Sep 27, 2002 04:49 pm
getting rows m through n | article only | Sep 27, 2002 06:47 pm
Obtaing Start and end Dates | article only | Sep 28, 2002 07:16 am
Strange situation.. | article only | Sep 28, 2002 07:44 am
Why include for table for all indexes for all indexed columns | article only | Sep 28, 2002 08:18 am
A question about SQL select | article only | Sep 28, 2002 08:25 am
how to change freelist groups for a table without recreating it | article only | Sep 28, 2002 08:47 am
SQL plus session not getting updated on saving changes in editor. | article only | Sep 28, 2002 10:59 am
Preventing the truncation of a table | article only | Sep 28, 2002 12:49 pm
Pivot Table like Interface | article only | Sep 28, 2002 02:35 pm
Connect by and outer join | article only | Sep 28, 2002 07:24 pm
FGAC | article only | Sep 28, 2002 07:41 pm
count | article only | Sep 29, 2002 08:05 am
How to delete duplicated check constraints ? | article only | Sep 29, 2002 01:05 pm
Rollback Segment | article only | Sep 29, 2002 03:13 pm
what is the correct answer? | article only | Sep 29, 2002 08:27 pm
Use of xpath, support for namespaces | article only | Sep 30, 2002 07:26 am
How to use NVL for this case? | article only | Sep 30, 2002 06:44 pm
row(s) 1 - 48 of 48
September 24, 2002 -- Thanks for the question regarding "SQL help", version 9.0.1
You Asked
Hi Tom,

I have two tables with same columns in different databases. They have columns
below:
Id, col_a, col_b, col_c...

Need to copy couple of rows from remote database from time to time while
updating maxima id to another local table for every copying.

Seems insert doesn't support returning max(id) like this. I tried following
statement and got error:

declare
curr_id number;
max_id number;
begin
select curr_id into :curr_id from table_c;
insert into table_a select * from table_b@remote where id>:max_id returning
max(id) into :max_id;
update table_c set curr_id=:max_id where name='MAX_ID';
end;

ORA-00933: SQL command not properly ended

How can I get max_id without re-query the remote or local database again after
every copying? Is it possible to copy rows from remote database and update
max(id) at the same time?

Thanks.
John
and we said...
Ok, that is totally different syntax then before.

INSERT does not supporting RETURNING when there is more then one row (as there
would be with insert into select)

You would not have to query the remote database, just the local, after the
insert -- and as long as ID has an index on it - this'll be immediate (we read
the last index block and that is pretty much it).

So, it would look like:

begin
   insert into table_a
   select *
     from table_B@remote
    where id > (select curr_id from table_c where name = 'MAX_ID' );

   update table_c
      set curr_id = (select max(id) from table_a )
    where name = 'MAX_ID';
end;
September 24, 2002 Girish -- Thanks for the question regarding "resizing the INITIAL extent of an object", version
You Asked
Hi Tom,
I have one problem which is quite serious
and needs to solved immediately.

I have around 50 tables in a production
database with initial extents as 3mb.
But these are actually small tables which occupy less
than 20k.
How can i reduce the initial
extents of these tables.

The above requirement is quite simple
and can be handled by exporting,
dropping the table , recreate the table
with lesser initial ext and
importing back with IGNORE ERRORS
option.

But what is complicating the requirement
is that these tables have data which
are being referred(have foreign key
constraints) by child tables
so dropping the table and recreating will
not be the best of solutions.

Do you have any ideas on how to handle this??

Pls let me know

Looking forward for your help

Thanks,
Girish V
and we said...

You mention no versions so i'll have to guess.  I'll guess
Oracle8i, release 8.1 first and then 8.0 and before later.  In
Oracle8i I can do something like:

ops$tkyte@8i> create table p
  2  ( x int,
  3    constraint p_pk primary key(x)
  4  )
  5  tablespace users
  6  storage ( initial 5m )
  7  pctfree 20
  8  /
Table created.

ops$tkyte@8i> create table c ( y int references p );
Table created.

ops$tkyte@8i> insert into p values ( 1 );
1 row created.

ops$tkyte@8i> insert into c values ( 1 );
1 row created.

ops$tkyte@8i> insert into c values ( 1 );
1 row created.

ops$tkyte@8i> select initial_extent, pct_free
  2    from user_tables
  3   where table_name = 'P';

INITIAL_EXTENT   PCT_FREE
-------------- ----------
       5242880         20

ops$tkyte@8i> alter table p
  2  move tablespace users
  3  storage ( initial 5k )
  4  pctfree 0;
Table altered.

REM must rebuild indexes after a MOVE
ops$tkyte@8i> alter index p_pk rebuild;
Index altered.

ops$tkyte@8i> select initial_extent, pct_free
  2    from user_tables
  3   where table_name = 'P';

INITIAL_EXTENT   PCT_FREE
-------------- ----------
         16384          0

If you are in 8.0 or before, you have to do what you describe
above with exp/imp or more simply for such small tables:

- use the script below "cons.sql" to generate alter statements
that will rebuild the
  referential integrity constraints.

- RENAME the table   "rename T to T_HOLD"

- create table T tablespace X storage ( ... )  as select * from
T_HOLD.  Alter table T add constraint .... primary key....

- drop table T_HOLD CASCADE constraints

- use the statements you generated in step 1 to rebuild the
foreign keys.

==================== cons.sql
================================================
column fkey format a80 word_wrapped
select
'alter table "' || child_tname || '"' || chr(10) ||
'add constraint "' || child_cons_name || '"' || chr(10) ||
'foreign key ( ' || child_columns || ' ) ' || chr(10) ||
'references "' || parent_tname || '" ( ' || parent_columns ||
');' fkey
from
( select a.table_name child_tname, a.constraint_name
child_cons_name,
         b.r_constraint_name parent_cons_name,
         max(decode(position, 1,
'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 2,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 3,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 4,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 5,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 6,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 7,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 8,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 9,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,10,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,11,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,12,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,13,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,14,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,15,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,16,',
'||'"'||substr(column_name,1,30)||'"',NULL))
            child_columns
    from user_cons_columns a, user_constraints b
   where a.constraint_name = b.constraint_name
     and b.constraint_type = 'R'
   group by a.table_name, a.constraint_name, b.r_constraint_name
) child,
( select a.constraint_name parent_cons_name, a.table_name
parent_tname,
         max(decode(position, 1,
'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 2,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 3,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 4,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 5,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 6,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 7,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 8,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position, 9,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,10,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,11,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,12,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,13,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,14,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,15,',
'||'"'||substr(column_name,1,30)||'"',NULL)) ||
         max(decode(position,16,',
'||'"'||substr(column_name,1,30)||'"',NULL))
            parent_columns
    from user_cons_columns a, user_constraints b
   where a.constraint_name = b.constraint_name
     and b.constraint_type in ( 'P', 'U' )
   group by a.table_name, a.constraint_name ) parent
where child.parent_cons_name = parent.parent_cons_name
  and parent.parent_tname = upper('&1')
/
========================= eof
=======================================================

when run like:

ops$tkyte@8i> @cons p
old  53:   and parent.parent_tname = upper('&1')
new  53:   and parent.parent_tname = upper('p')

FKEY
-----------------------------------------------------------------
---------------
alter table "C"
add constraint "SYS_C0020322"
foreign key ( "Y" )
references "P" ( "X");

it generates the foreign key constraints for all children OF it.
 You would spool this output to be rerun after rebuilding the
table.
Review & Followup
Rating: 5
And in 9.2.0? September 23, 2002
Reviewer: Juan Carlos Reyes from Bolivia
Nice script,

I ask to know if maybe there is some other way to rezise the initial extent.
I tried to resize, but I could´nt but as you have excellent ideas I try asking
you?
I can create and recreate a table, but The problem is that there are several
tables, that has too big initial size.

Thanks to your suggestions I created the tablespaces like this.

CREATE TABLESPACE TBL_USERS   DATAFILE 'E:\oraXXX\datafiles\DFL_USER_XXX'
       SIZE 100M
       REUSE AUTOEXTEND ON NEXT  640k MAXSIZE UNLIMITED
       SEGMENT SPACE MANAGEMENT AUTO
       EXTENT MANAGEMENT LOCAL  UNIFORM SIZE 64K AUTOALLOCATE  ;

Is someway to reduce the initial extent without recreating the table?

Followup:
If the table is in a DICTIONARY managed tablespace and the space has NEVER been
used, the following works:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int )
  2  tablespace system
  3  storage ( initial 5m );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select bytes from user_extents where
segment_name = 'T';

     BYTES
----------
   5242880

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t deallocate unused keep 100k;

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select bytes from user_extents where
segment_name = 'T';

     BYTES
----------
    114688

Rating: 5
Excellent :) September 24, 2002
Reviewer: A reader
Thank you

September 24, 2002 -- Thanks for the question regarding "Global tablenames and dynamic html tables", version 9i
You Asked
I have a package that utilizes oracle tables to display information on the web.
I am using reference cursors to populate the html tables. which allows me to
specify the Oracle tablename as a global variable

example:
PROCEDURE DISPLAY_data
IS
-- Decalare and Populate the cursor************************

stmt VARCHAR2 (2000);
type DisplayCursorType is ref cursor;
Display_cursor DisplayCursorType;

--***********Begin The HTML*********************************
Begin
stmt:='SELECT column1, column2, column3 from '||Table_name||' order by column1';
     htp.print ('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01
Transitional//EN">');
     htp.print ('<html><head><title> DPG - IAP Information</title>');
     htp.print ('<link rel=stylesheet type="text/css"
href="'||servername||''||cssfile||'"></head>');
     htp.print ('<body bgcolor="#ffffff" topmargin="0" bottommargin="0"
leftmargin="0" rightmargin="0">');
     htp.print ('<table width="100%" border="1" cellpadding="4" cellspacing="2"
cols="3">');
     htp.print ('<tr height="35"><td colspan="3"
class="bigtitlestart"><b>'||InstName||' - Information</b><br>');
     htp.print ('<font size="1"><i>For more detailed site information, click on
a site number.</i></font><br><br>');
     htp.print( '<input type=''image''
src="'||servername||''||graphicspath||'return_home_butt.jpg"

onClick=''document.location="document_viewer_'||InstAbbr||'.FRONTPAGE_document"''
width="141" height="25">
         <input type=''image''
src="'||servername||''||graphicspath||'generate_report_butt.jpg" height="25"
width="141"></td></tr>');
     htp.print ('<tr height="22">');
     htp.print ('<td nowrap class="subtitle">column1</td>');
     htp.print ('<td nowrap class="subtitle">column2</td>');
     htp.print ('<td nowrap class="subtitle">column3</td>');
     htp.print ('</tr>');

-- Loop through the recordset and poulate the table**********
OPEN Display_cursor for stmt;
     LOOP
          FETCH Display_cursor INTO var1, var2, var3;
         EXIT WHEN Display_cursor%notfound;

         htp.print ('<tr><td class="plain" align="center" valign="center">');
         htp.anchor ('document_VIEWER_'||InstAbbr||'.document_detail?id=' ||
var1, var1);
         HTP.print ('</td>');
         HTP.print ('<td class="plain" valign="center">' ||var2 || '</td>');
         HTP.print ('<td class="plain"><p align="justify">' ||var3 ||
'</p></td>');
         htp.print ('</tr>');
    END LOOP;
    CLOSE Display_cursor;
--***********************************************************

    htp.print ('</table></body></html>');

END DISPLAY_data;
what I am interested in doing is making the package truely transparent. Is there
a way of specifying an Oracle table name as a global variable and populating the
html column name headings dynamicly as well as populate the table with the
respective data? This way when you build an application for different projects
you would not have to rebuild your code each time, but rather just specify a new
tablename.

Any suggestions and examples of code on this topic would be greatly appreciated.

Thanks in advance,

Dave
and we said...
You'll need to use DBMS_SQL to do this.

With DBMS_SQL, you can process queries that have

o an unknown number of columns
o are of any datatype
o have column names you want to print.

So, change the code to use dbms_sql and you'll be going in the right direction.

for examples see:

o my book "Expert one on one Oracle" if you have, chapter on dynamic sql
o search for dump_csv on this site
o read the code for OWA_UTIL out of the database (use the DBA_SOURCE or
ALL_SOURCE tables for the package body of owa_util).  Check out the cellsprint
routine.  It does 99% of what you want already.
September 24, 2002 Mike -- Thanks for the question regarding "9i Oracle Text searching multiple tables", version 9.2.0
You Asked
Hi Tom,

I currently have a simple search page that incorporates 9i Oracle Text by using
the Context index on a CLOB column.  What I'd like to know is if its possible to
use a single query with CONTAINS that searches a CLOB column in Table A as well
as multiple VARCHAR columns in Table B without having to modify my query to join
the two tables, etc.

thanks,
Mike A.
and we said...
Well, thats what I do here on asktom.

I index the question + answer (table T1), and the reviews (table T2) and the
followups to the reviews (T3).

I accomplish this by indexing a function, not the table itself (requires a
rebuild of the index, not the app).

see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:440419921146
for an example.
September 24, 2002 tammy -- Thanks for the question regarding "DB upgarde/migration, UNDO_RETENTION setting", version 8.1.7.3.0
You Asked
Hi Tom: thank you for your previous replies. they are very helpful. we are going
to migrate 4 terabytes DW to oracle 9.2.0.1.0 from 8.1.7.3.0. What is best
method to upgrade or migrate and take less time during maintenance window? in
the init.ora file,we have UNDO_RETENTION parameter, what is the best setting for
4 terabytes DW? I was told to set to UNDO_RETENTION=21600 (6 hours) at least?
and we said...
The best method will be to test it -- really.  Its a function of the amount of
plsql you have and other objects like views, tables and such, much more so then
the amount of RAW data you have (we don't rewrite the entire instance).

You'll need to test anyway, else you will have downtime, 100% sure you will
have unexpected, unplanned downtime.

I know you cannot test 4tb, you'll need to test some reasonable subset of that
(and vary the subset sizes to see what effect the amount of raw data might have
-- do a 4gig upgrade and a 40gig and a 400 gig just to test).

As for the undo retention period, that'll depend.  Do you run queries at the
same time you are modifying (conventional path insert/update/delete)?  If so,
you'll want the undo retention period to be longer then your longest running
query.

If you do not do that (eg: you load, then you query query query, then you load,
then you query query query), the retention period can be quite small indeed.
September 25, 2002 Yuemin -- Thanks for the question regarding "the status of spatial index.", version 9.0.1
You Asked
after creating spatial index fail:
create index LLZOOM_SIDX
*
ERROR at line 1:
ORA-29855: error occurred in the execution of ODCIINDEXCREATE routine
ORA-13249: internal error in Spatial index: [mdidxrbd]
ORA-13249: Error in Spatial index: index build failed
ORA-13249: Error in spatial index: [mdrcrtxfergm]
ORA-13364: Layer Dimensionality does not match geometry dimensions
ORA-06512: at "MDSYS.SDO_INDEX_METHOD_9I", line 7
ORA-06512: at line 1

Then I found status of the index is valid in user_indexes table.
Is this index good or bad?
and we said...
I asked Clarke Colombo -- the expert in our spatial technologies to look at this
and he says:

Tom,

This error occurs when the dimensionality of one of the records in the table
being indexed does not match the dimensionality  defined in the
user_sdo_geom_metadata table. For example, if the geometry column is defined
as having three dimensions then all records in the table must have a value
for each of the three dimensions.

When checking the status of a domain index the user must query three columns
(status, domidx_status and domidx_opstatus) from the user_indexes view. All
three columns must be valid in order for the index to be valid. In the case
of this user, when the  index was being created one of the records was found
to be bad. The dimensionality of one of the records did not match the
metadata and the error was thrown. When an error like this occurs the index
status will be listed as valid but the domidx_opstatus will be listed as
failed. Since the domain indexing operation failed the index is considered
invalid.

To correct the problem, the user needs to drop the existing index, correct
the data problem and recreate the index.

Hope this helps,

Clarke
Review & Followup
Rating: 4
spatial index's status September 25, 2002
Reviewer: A reader
very useful answer. thanks a lot.

September 25, 2002 Jeff -- Thanks for the question regarding "Init.ora and Database Instance", version 8.1.6
You Asked
Tom,
I was reading one of your responses to a question posed to you
about init.ora and how to set up utl_file_dir there.
I got interested because I am new to this and I am already
confronted with writing a stored proc with utl_file package.
My question is if for example my database name is entrance and
the instance is ent. How can I set up my utl_file_dir in
init.ora
in this database instance?
I know that init.ora is in $ORACLE_HOME/dbs.
I have looked at your examples and step-by-step method so far
and none has answered this question for me.
I just keep getting invalid_path error message.
Your help is greatly appreciated.
Thank you!
Jeff
and we said...
You would

$ vi $ORACLE_HOME/dbs/init$ORACLE_SID.ora

and add a line like:

utl_file_dir = /tmp

After restarting, you can verify that UTL_FILE_DIR is setting
correctly via:

SVRMGRL> show parameter utl_file

And then you should be able to use '/tmp' as a valid path in an
fopen call.
Review & Followup
Rating: 2
Reviewr September 25, 2002
Reviewer: priyaramnan from Chennai,India
greetings,

In our organization we linux server in which oracle ver 8 is installed.
now in the init.ora prameter file i have given as
utl_file_dir = /home/priya
from my machine i have mapped to the linux home directory as "L:".My
machine operating system is win 95

from my machine i have created a procedure to write into a text file in the
L:\priya.The procedure is given below

Create or replace PROCEDURE write_file IS
 l_file       UTL_FILE.FILE_TYPE;
BEGIN
 l_file := utl_file.fopen('l:\priya', 'test.txt', 'w');
 UTL_FILE.PUT_LINE(l_file, 'this is a test');
 IF UTL_FILE.IS_OPEN(l_file) THEN
   UTL_FILE.FCLOSE(l_file);
 END IF;
END;

Now when i execute this i get an error as

ERROR at line 1:
ORA-06510: PL/SQL: unhandled user-defined exception
ORA-06512: at "SYS.UTL_FILE", line 98
ORA-06512: at "SYS.UTL_FILE", line 157
ORA-06512: at "DEPOSITS.WRITE_FILE", line 4
ORA-06512: at line 1

i even tried
utl_file_dir = *

i get the same error

how to proceed further??.(i checked for the case sensitivity)

i checked the v$parameter table but the value is null for the
utl_file_dir=/home/priya.
I don't understand why??
becoz once i changed the parameter file i even restarted the instance.
i am in badly need of the answer.

Thanxs in advance.

Followup:
server is running on linux.

you are using windoze file names.

think about it.  Server is running on linux.  plsql runs in the server.  server
sees /home/priya - NOT L:\priya.

use the directory name the SERVER where the code is running would see.

If utl_file_dir is not set (you need NULL) then you messed up your init.ora file
in some way.  Look for the LAST utl_file_dir entry -- that is the one that
counts -- in the init.ora file.

Rating: 5
Reviewer September 26, 2002
Reviewer: priyaramnan2k2 from Chennai,India
Thanxs Mr.Tom it worked  well.

September 25, 2002 Vinnie -- Thanks for the question regarding "Java vs Pro*C ", version 8.1.7
You Asked
Tom,

If YOU were trying to load data into the dB what would be the fastest front end
to get this accomplished....JAVA or PRO*C or perhaps something other?
and we said...
as fast as I could type I would answer:

SQLLDR

and in 9i -- EXTERNAL TABLES + direct path insert (insert /*+ APPEND */)

I would never dream of writer a loader (pretty boring stuff).  But, will C be
faster then Java, well -- I'm pretty much 110% convinced that I could best any
piece of java code out there with C, yes (and if I used OCI, I'd really best it
as I could write to the direct path api's)
September 25, 2002 anthony -- Thanks for the question regarding "Query in Java Stored Procedures are slow?", version 8.1.7
You Asked
Hi Tom,

Whenever Java stored procedures are executed, queries & DMLs inside the Java
procedures are dynamically compiled and hard parsed. But this is not the case in
PL/SQL stored procedures as they are pre-compiled. Hence, the performance of
Java stored procedure is slower than its PL/SQL counterpart even without
considering run time byte-code conversion by JVM. Do you agree with this
argument? If not, Why?
and we said...
They are only hard parsed if they've never ever been parsed before!  Else Java
will cause a soft parse like any other language.

I do not agree with the argument (although, if you are PROCESSING DATA, your
premise is correct).  Do not forget -- PLSQL itself is generally "byte code run
by a VM".  Plsql has been running in a VM since day one itself (and in 9i, we
have native compilation of PLSQL and in 8iR3 we have native compilation of java
-- so neither *needs* to run as byte code, both can run as native OS binary
code)

PLSQL does a soft or hard parse the first time a query is executed.  Subsequent
executes of the same procedure will generally (giving the cursor was allowed to
stay cached) reuse the already opened cursor.  If you have open_cursors set too
low, we'll flush these cursors out however and you'll be soft parsing that query
again.

Java lets it upto the programmer.  Do they

  prepare
  bind
  execute
  close

or do they code

  if first_time
     prepare
     first_time = false

  bind
  execute

So, it is upto the java programmer.

I will put forth that if the goal is to process data -- to do lots of SQL
selects/other dml -- then the choice between PLSQL as a stored procedure
language and Java should be clear -- PLSQL is the *only* logical choice.  Less
code to do the same work (although SQLJ can get you close).  PLSQL also has no
impedance mismatch in datatypes.  PLSQL datatypes are SQL datatypes.  In Java,
there is always a mapping from one to the other (overhead).

If your goal is to ftp a blob to another system, then the choice between PLSQL
as a stored procedure lanaguage and Java should be clear.  Java is the only
logical choice.
September 25, 2002 Venkat -- Thanks for the question regarding "SQL Query Help", version 8.1.7
You Asked
Hi Tom,

Could you please help me how this can be done in SQL.

I have 2 tables.

dept
----
SQL>select * from dept;
deptno  dname loc1 loc2 loc3
------- ----- ---- ---- ----
10       X     LA1  LA2  LA3
20       Y     LB1  LB2  LB3
30       Z     LC1  LC2  LC3
40       A     LD1  LD2  LD3

emp
---
SQL>select * from emp;

empno locations  position
----- ---------  --------
10     AA          1
20     AB          1
10     AC          3
10     AD          2
20     AE          3
30     AF          2
40     AG          1
30     AH          1

Now I want to update dept tables based on the following condition,

For each matching deptno from emp to dept, take the locations value
from emp and update the dept table's column. The column may be
loc1, loc2 or loc3 based on position in emp table.

After updating dept should look like this,

SQL>select * from dept;
deptno  dname loc1 loc2 loc3
------- ----- ---- ---- ----
10       X     AA   AD  AC
20       Y     AB   LB2 AE
30       Z     AH  AF  LC3
40       A     AG  LD2  LD3

Thanks,
Venkat
and we said...
I'll assume you really meant DEPTNO where you have EMPNO in the EMP table, else
there is no way to correlate the data:

ops$tkyte@ORA920.US.ORACLE.COM> select * from dept;

    DEPTNO D LOC LOC LOC
---------- - --- --- ---
        10 x la1 la2 la3
        20 y lb1 lb2 lb3
        30 z lc1 lc2 lc3
        40 a ld1 ld2 ld3

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> update dept
  2     set ( loc1, loc2, loc3 ) =
  3         ( select nvl(max(decode(position,1,locations,null)),dept.loc1),
  4                  nvl(max(decode(position,2,locations,null)),dept.loc2),
  5                  nvl(max(decode(position,3,locations,null)),dept.loc3)
  6             from emp
  7            where emp.deptno = dept.deptno )
  8   where deptno in ( select deptno from emp )
  9  /

4 rows updated.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select * from dept
  2  /

    DEPTNO D LOC LOC LOC
---------- - --- --- ---
        10 x AA  AD  AC
        20 y AB  lb2 AE
        30 z AH  AF  lc3
        40 a AG  ld2 ld3

ops$tkyte@ORA920.US.ORACLE.COM>
Review & Followup
Rating: 5
SQL Query Help September 25, 2002
Reviewer: Venkat from Hyderabad
Hi Tom,

  It was really terrific. It was very very useful to me.

Thank you very much,
Venkat

September 25, 2002 Steve -- Thanks for the question regarding "Segments", version 8.1.7
You Asked
Hi Tom

I need to find out through sql which database segments are attracting the
greatest amount of physical I/O. Are there any problems with this in 8i?  How
accurate can I expect the results to be?

Cheers

Steve
and we said...
Pretty funny -- the timing and all.

On my home page right now is a link to a "smartdba" contest.  You should go
there and see the question.  You might get a chuckle.

In 8i, and before, we track IO by file.  We can show you the files, we can
identify the segments in that file but that is about it.  9iR2 does have segment
level monitoring of IO.

So, if you have lots of segments in a file, you won't be able to pinpoint it.
If you have a hot file that has but one segment -- you'll be in luck.
Review & Followup
Rating: 1
The contest marks down heavily for this... September 18, 2002
Reviewer: Robert from Memphis, USA
Tom,

See rule 8 of the contest.
They "mark down heavily" for answers that depend on these statistics
("dedicating a tablespace to a specific segment").
Isn't there another way?

I thought I remember seeing a v$ table that had a row for every block in the
buffer cache (?).

Thanks,

Robert.

Followup:
well, they say:

8. Answers that depend on storage configurations that are unusual in Oracle
(such as dedicating a tablespace to a specific segment) will be heavily marked
down.

but my answer (which is a PARTIAL answer) is at the file level here.  It is but
one part of the answer that can help you find that segment (it can help you
reduce the set of segments to a very small finite one rapidly can it not?).
Also, remember the question

...
Write him a report explaining the problems of doing this under Oracle8i and how
accurate you would expect your results to be.
......

It is not easy nor really 100% possible to do this under 8i.

the view you talk about is v$bh and might be useful (but remember, it is mostly
for counting LIO's not physical ones)

Rating: 5
Hey Tom September 18, 2002
Reviewer: A reader from Edinburgh
I hope you're not going to carry on this thread and give any assistance out. It
would kind of defeat the point of the challenge, no?

Doug

Followup:
I'm done....

Rating: 4
September 19, 2002
Reviewer: Steve from UK
Hey - guess I'll wait for the competition result then!!

Rating: 3
"smartdba" contest MAILER-DAEMON@ yahoo.com September 24, 2002
Reviewer: A reader
Hi Tom a question I had sent several time the email from yahoo, and this returns
an error after trying several hours.

What should I do, retry, or the contest is over now.
As the date of sending is important how I´m going to tell them which is the
first time I sent the email?

Thank you

Message from  yahoo.com.
Unable to deliver message to the following address(es).

<smartdba@dbazine.com>:
Sorry, I wasn't able to establish an SMTP connection. (#4.4.1)
I'm not going to try again; this message has been in the queue too long.

--- Original message follows.

Return-Path: <cachitok@yahoo.com>
Message-ID: <20020923203057.56217.qmail@web14008.mail.yahoo.com>
Received: from [65.173.58.2] by web14008.mail.yahoo.com via HTTP; Mon, 23 Sep
2002 13:30:57 PDT
Date: Mon, 23 Sep 2002 13:30:57 -0700 (PDT)
From: Juan Carlos Reyes Pacheco <cachitok@yahoo.com>
Subject: smart dba challenge , Please consider the Fwd: failure delivery
To: smartdba@dbazine.com
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii

Followup:
They said they had email issues over the weekend, fixed yesterday.  Give it
another go.

Rating: 3
September 25, 2002
Reviewer: A reader
Thank you Tom
But in the rules if there were two answer with the same punctuation, the date of
submission will decide,
Do you know what is going to happen about this?

Followup:
Here is the response:

...
The odds of having identical answers submitted are very small. We have not
received any identical answers to date, so the mail server problem will not
impact the award process. Please keep those answers coming.
.....

So, so far, it has not been an issue (the timing) and I agree that the odds of
having a plug identical answer are very very small indeed.

Rating: 5
September 25, 2002
Reviewer: A reader
Thank you

September 25, 2002 Joe -- Thanks for the question regarding "Uniform Size on LMT", version 8.1.6
You Asked
Tom,

I have a LMT tablespace defined as follows:

CREATE TABLESPACE ABC_IDX
    DATAFILE '/ora101/oradata/ABC/ap_idx01.dbf' SIZE 10240064K,
             '/ora101/oradata/ABC/ap_idx02.dbf' SIZE 4096064K,
           '/ora310/oradata/ABC/ap_idx03.dbf' SIZE 1024064K autoextend on
maxsize  3000M
extent management local uniform size 64M
/
I have about 35 indexes created at this point. It looks like the 64M I chose for
the uniform extent size was too big. I believe there is a lot of wasted space.
Is it possible to use the deallocate_unused_clause on the alter index? If so,
how can I tell how much space was actually used?
Or, is it better to do an EXPORT, rebuild the tablespace and IMPORT the data.

Thanks for your help.

Joe
and we said...
Uniform is uniform.

The extents will ALL always be 64m in this tablespace.

You could:

o create new tablespace with smaller uniform OR system managed extent sizes (for
when you don't KNOW the ultimate size or just don't "care")

o alter index INDEX_NAME rebuild tablespace New_Tablespace <online if you want>

o drop this tablespace
Review & Followup
Rating: 4
Follow up Question September 25, 2002
Reviewer: Joe from Bridgewater,NJ
Tom,
If I do an export for the user, drop and recreate the user, drop and recreate
the LMT with a uniform size of lets say 5M and import the data which was
exported for this user, how will this affect the number of extents? (ie if an
index acually used 10M of the 64M, will the new index use 2 extents?

Thanks,

Joe

Followup:
No, do not do that, you'll end up with NO CHANGE in storage!!!

Do it the way I suggest, alter index rebuild.  It'll be

a) faster
b) easier
c) using less space (your goal)
d) safer (you KNOW it'll work without losing a single thing.  with
export/import, too easy to lose a grant or something along the way)

Consider this example:

ops$tkyte@ORA920.US.ORACLE.COM> create tablespace test
  2  datafile size 5184k
  3  extent management local
  4  uniform size 1m
  5  /

Tablespace created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create table t ( x int ) tablespace users;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> create index t_idx on t(x) tablespace test;

Index created.

ops$tkyte@ORA920.US.ORACLE.COM> select segment_name, extent_id, bytes from
user_extents;

SEGMENT_NAME                    EXTENT_ID      BYTES
------------------------------ ---------- ----------
T                                       0      65536
T_IDX                                   0    1048576

so, t_idx is 1 extent, 1 meg...

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> host exp userid=/ tables=t

Export: Release 9.2.0.1.0 - Production on Wed Sep 25 10:06:09 2002

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

Connected to: Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production
Export done in WE8ISO8859P1 character set and AL16UTF16 NCHAR character set

About to export specified tables via Conventional Path ...
. . exporting table                              T          0 rows exported
Export terminated successfully without warnings.

ops$tkyte@ORA920.US.ORACLE.COM> host imp userid=/ full=y show=y

Import: Release 9.2.0.1.0 - Production on Wed Sep 25 10:06:09 2002

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

Connected to: Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production

Export file created by EXPORT:V09.02.00 via conventional path
import done in WE8ISO8859P1 character set and AL16UTF16 NCHAR character set
. importing OPS$TKYTE's objects into OPS$TKYTE
 "CREATE TABLE "T" ("X" NUMBER(*,0))  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRA"
 "NS 255 STORAGE(INITIAL 65536 FREELISTS 1 FREELIST GROUPS 1) TABLESPACE "USE"
 "RS" LOGGING NOCOMPRESS"
. . skipping table "T"

 "CREATE INDEX "T_IDX" ON "T" ("X" )  PCTFREE 10 INITRANS 2 MAXTRANS 255 STOR"
 "AGE(INITIAL 1048576 FREELISTS 1 FREELIST GROUPS 1) TABLESPACE "TEST"
LOGGIN"
 "G"
Import terminated successfully without warnings.

Import is telling us that the initial extent will AGAIN be 1m - the current
initial extent!!!

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> drop table t;

Table dropped.

ops$tkyte@ORA920.US.ORACLE.COM> drop tablespace test;

Tablespace dropped.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create tablespace test
  2  datafile size 5184k
  3  extent management local
  4  uniform size 512k
  5  /

Tablespace created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> host imp userid=/ full=y

Import: Release 9.2.0.1.0 - Production on Wed Sep 25 10:06:09 2002

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

Connected to: Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production

Export file created by EXPORT:V09.02.00 via conventional path
import done in WE8ISO8859P1 character set and AL16UTF16 NCHAR character set
. importing OPS$TKYTE's objects into OPS$TKYTE
. . importing table                            "T"          0 rows imported
Import terminated successfully without warnings.

ops$tkyte@ORA920.US.ORACLE.COM> select segment_name, extent_id, bytes from
user_extents;

SEGMENT_NAME                    EXTENT_ID      BYTES
------------------------------ ---------- ----------
T                                       0      65536
T_IDX                                   0     524288
T_IDX                                   1     524288

See the index is in two extents to get 1m of space

So, just do it the way I suggest -- you can write a very very very easy query
against user_indexes to generate the alter index rebuild statements!!!

Rating: 5
A hacked up solution September 25, 2002
Reviewer: Connor McDonald from UK
sys@bcon> create tablespace blah
  2  datafile '/ora02/bcon/bcondemo01.dbf.blah' size 10m reuse
  3  extent management local uniform size 2m;

Tablespace created.

sys@bcon> create table t1 ( x number ) tablespace blah;

Table created.

sys@bcon> exec dbms_space_admin.tablespace_migrate_from_local('BLAH');

PL/SQL procedure successfully completed.

sys@bcon> alter tablespace blah minimum extent 128k;

Tablespace altered.

sys@bcon> alter tablespace blah default storage ( initial 128k next 128k);

Tablespace altered.

sys@bcon> create table t2 ( x number ) tablespace blah
  2  storage ( initial 128k);

Table created.

sys@bcon> exec dbms_space_admin.tablespace_migrate_to_local('BLAH');

PL/SQL procedure successfully completed.

sys@bcon> select segment_name, bytes
  2  from user_segments
  3  where segment_name in ('T1','T2');

SEGMENT_NAME                                     BYTES
---------------------------------------- -------------
T1                                             2097152
T2                                              131072

So you've got a smaller extent in an LMT...admittedly its no longer uniform :-)

Followup:
oh yuck.

alter index rebuild -> new tablespace.  (please!)

Rating: 4
uniform size vs autoallocate (system managed) September 25, 2002
Reviewer: A reader
hi tom

In oracle 9i tablespace management is by default autoallocate, wouldnt this lead
to fragmentation?

Shall we stick to the traditional strategy creating several sized tablespace
(such as small medium big) but using uniform size or we should use the new
default oracle bahaviour?

Followup:
No, fragmentation occurs in a tablespace when you have lots of wacky sized
extents -- eg:

5 blocks allocated
50 blocks allocated
15 blocks allocated
35 blocks allocated
150 blocks allocated
10 blocks allocated

and start DROPPING/truncating objects leaving "holes" of weird sizes.

With an LMT using system managed extents -- you'll find the extents are one of a
COUPLE of sizes - not an infinite number of sizes -- and each size can be used
by any other object (cause the NEXT extent is not a fixed number -- it is some
number we decide for you AT the point in time we need it)

It will not fragment.

(i prefer uniform when you KNOW the sizes of the objects.  If you don't know or
don't care to know, system allocated is just fine)

Rating: 5
Uniform LMT September 25, 2002
Reviewer: Joe from Bridgewater, NJ
Tom,

Thanks for the visual. I will be rebuilding the indexes.

Joe

September 25, 2002 Kalpesh -- Thanks for the question regarding "special character in query", version 8.1.5
You Asked
Hi Tom,
I want to find all the symbols (varchar2(25))from one of my tables
which are having '_' character or '_' at position 3.
eg. KJ_W

Could you tell me How to do that ?
Thank You
Kalpesh
and we said...
select * from t where substr( x, 3, 1 ) = '_';

or

select * from t where x like '__\_%' escape '\';

will do that. (that is underscore underscore backslash underscore)
Review & Followup
Rating: 3
finding tables with "__" ( double underscore) September 25, 2002
Reviewer: Ram from CT, USA
Hi Tom,

assume that there are couple of tables in my database with 'double underscore'
like A__B, XYZ_A, etc

I am trying to use your solution, but not getting exact thing for what I was
looking for.

SQL> select table_name from user_tables where substr(table_name,-2,2) ='__' ;

I've tried couple of other optins too, but not getting the right list.

Couldyou pl give me the solution?

Regards

Followup:
that would be substr( table_name, 2, 2 )

whats the -2 for in your example?

to find the ones with double understands

where table_name like '%\_\_%' escape '\';

September 25, 2002 ismail -- Thanks for the question regarding "How to handle ORA-01422 exact fetch returns more ....", version 2000
You Asked
Hi Tom

I'm trying to execute SQL statment but the result is :

 ORA-01422       exact fetch returns more than requested number of rows

 Cause:       More rows were returned from an exact fetch than specified.
  Action:       Rewrite the query to return fewer rows or specify more rows in
the exact fetch.

  So How can I handle this proplem ?

Thank you
and we said...
If you EXPECT the query to return more then one row, you would code:

   for x in ( select * from t where ... )
   loop
      -- process the X record here
   end loop;

If you expect the query to return AT LEAST one record and AT MOST one record,
you would code:

   begin
       select * into ....
         from t where ....

       process....
   exception
       when NO_DATA_FOUND then
          error handling code when no record is found
       when TOO_MANY_ROWS then
          error handling code when too many records are found
   end;

If you just want the FIRST record

declare
    c1 cursor for select * from t where ...
begin
    open c1;
    fetch c1 into ..
    if ( c1%notfound ) then
        error handling for no record found
    end if;
    close c1;
end;
Review & Followup
Rating: 2
September 24, 2002
Reviewer: MW from Germany
Hi, Tom

I have a following Problem.

I have a table and it's primary key value get from the trigger ( refer
Sequence). It was working file. All of a sudden, when I try to insert values,
then I got this Error.

ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at "myusr.TRG_LNKPCGRP_PCGRPNO", line 5
ORA-04088: error during execution of trigger 'myusr.TRG_LNKPCGRP_PCGRPNO'

But this fetch value is in trigger is as

Select SEQ_LNKPCGRP_PCGRPNO.NEXTVAL INTO iCounter FROM Dual;

So I am realy in confusing oin this. Could you please help me on this.

Thank you in advance.

Followup:
check to see if someone added a row to dual:

ops$tkyte@ORA920.US.ORACLE.COM> @connect "/ as sysdba"
sys@ORA920.US.ORACLE.COM> insert into dual values ( 'y' );

1 row created.

sys@ORA920.US.ORACLE.COM> select * from dual;

D
-
X

sys@ORA920.US.ORACLE.COM> select count(*) from dual;

  COUNT(*)
----------
         2

sys@ORA920.US.ORACLE.COM>

dual is magic, make sure to COUNT(*) it, not just select * from it

sys@ORA920.US.ORACLE.COM> declare x number; begin select my_seq.nextval into x
from dual; end;
  2  /
declare x number; begin select my_seq.nextval into x from dual; end;
*
ERROR at line 1:
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 1

will happen when dual has more then 1 row

Rating: 5
September 25, 2002
Reviewer: MW from Germany
Thank you very much. this solved the problem.

Followup:
Now, you need to find out who put that row into dual and soundly and thoroughly
beat them about the head with a wet noodle..  Doh.

Rating: 5
SELECT * and SELECT COUNT(*) from Dual are Inconsistent? September 25, 2002
Reviewer: Mac from Bloomington, IL USA
Tom,

So someone can insert a second row into DUAL, and when they SELECT * from that
table, they only get 1 record back? If so, why? What kind of "magic" is this
which goes against established rules?

Why does COUNT(*) from DUAL always return the correct number?

Does this mean SELECT column_name FROM DUAL will always be untrustworthy?

(Since I do not have sysdba privilege, I can not check this for myself.)

I am very grateful for your excellent site. If only more teachers in the world
had your logic, patience and approach...

Thank you!

Followup:
DUAL is special, DUAL is magic.

DUAL is also part of the Oracle data dictionary (the SYS.* tables).

As is true with ALL SYS OWNED TABLES -- the moment you insert/update/delete them
directly is the moment your database becomes UNSUPPORTED and UNSUPPORTABLE.
Don't ascribe any normal behavior to anything related to SYS, it is special as
well.

I suggest not even LOGGING IN as sys ever if you can - always use SYSOPER and
only for startup/shutdown (as thats all it can do)

Rating: 5
DUAL September 25, 2002
Reviewer: Paul from UK
I once got 4 rows in DUAL after repeatedly doing a full System Import!

But if Oracle have this magic thing (lets call it SQL*Magic for consistency),
why don't they use it to prevent anyone adding another row to DUAL?

To be serious, if there was a unique index on DUAL, it would have stopped my
Import problem - I guess this isn't done for performance reasons.

Just musing.

Paul

Followup:
SYS owned objects are never (have never) been exported.

DUAL is owned by SYS (or at least it should be)

Only if DUAL was owned by some user OTHER THEN sys would this, could this have
occurred.

Hence, I do not see how this could have happened with EXP/IMP.

Now, if you run sql.bsq more then once, this could happen (our bootstrap, that
which creates the data dictionary -- the SYS "real" data dictionary).  But, if
you run sql.bsq more then once -- that might do it (but that would be a truly
bad thing in any case, I would just trash that database and start over, there
would be other bad side effects)

Rating: 5
True but ... September 25, 2002
Reviewer: Paul from UK
... this was a long, long time ago when (if I recall correctly) DUAL was owned
by SYSTEM rather than SYS.

I think it was V6 (could've been V5!).

Somebody out there must remember - Tom, you can't be that young if you
programmed at school on punch cards ;-)

Paul

Followup:
but it has been going on 10 years since I've fired up a v6 database!  could be
and I just don't remember.

I'm sure someone out there has one running and could take a look see for us....

September 25, 2002 E -- Thanks for the question regarding "Maximum procedures in a package", version 8.1.7.2
You Asked
Hi Tom,
  Thanks for your valuable reply which you had given earlier. I need an
emergency reply for the following query since my project is going for live
within couple of days.
 For the whole project we had two packages .Each packages has 100 to 125
procedures. Aproximate line of code of the procedures lies between 25 to 2000
lines.
 Could you please tell what is the optimised number of procedures in a package.
One of my friend is telling we should have only 10procedures in a package.Is it
correct.
 We want to increase the performance of our site.The whole site is depends on
the procedures only.
 Could you also please tell how to find the size of the package.explain briefly
about the columns in user_object_size.

Expecting your reply.
Thanks Tom,
E.Sholan
and we said...
2000 LINES in a procedure?  Thats not a procedure -- thats an entire subsystem!

Does that procedure fit on a screen?  I mean -- when you pull it up in an
editor, can you see the TOP and BOTTOM and everything in between on a single
monitor?  If not, its toooo long.  break it up (modularize the code!)

There is no such thing as an optimized number of procedures in a package.  I've
seen everything from one to over a hundred.  I would not want a package to go
over 2 to 3,000 lines personally (i think it is getting "too big" and needs to
be broken down at that point.  made more "digestable").

The size of the package will not have a bearing on performance of the package.
Big code runs at the same speed as small code.  The biggest impact on
performance it tightly written, optimal code using optimal algorithms and fast
queries.

The size of the package has no bearing on anything relevant to you .
September 25, 2002 Nilesh -- Thanks for the question regarding "SQL Help", version 8.1.7
You Asked
Can you please refere me to URL(s) for finding out new SQL features introduced
with Oracle 8i?

By SQL features - I mean detail info about stuff like "Cast()", Multuiset()" and
other cool features related to Oracle Object Types.

I tried to look at the OTN but could not find a consolidated document the way I
am searching.

Thanks in advance!!
and we said...
cast and multi-set are new with 8.0 actually.

Now, something as "mundane" as CAST or MULTISET won't really be covered in
something general like:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76962/toc.htm
they are not really that "flashy". they are covered in the sql ref manual
however:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a85397/expressi.htm#1017895
...
Review & Followup
Rating: 2
Thanks a lot September 25, 2002
Reviewer: Nilesh from Parsippany, NJ,USA

September 25, 2002 Hamid -- Thanks for the question regarding "How can I execute an external procedure", version 8.1.7
You Asked
Hi Tom,
I would like to know how an external procedure can be executed on a database?
This external procedure will exec some Unix commands too.
Are there any security considerations here?
Many thanks.
Hamid
and we said...
Do you have my book "Expert one on one Oracle" -- in there I have a 70 page
chapter exclusively on extprocs.

If you search for

"external procedure"

on this site, you'll get tons of info as well.  The major security concern is
that if you use the listener that runs "as oracle", the extproc will "run as
oracle" and if you let them do ANY host command (eg: the extproc just does
"system( cmd )" and runs anything) then there are serious issues.

You can solve that by running the listener for the extproc user as a "non
privileged" account so the extproc runs as that account as well

Much better to use a java stored procedure to do this:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:952229840241
here you can grant execute on SPECIFIC commands -- much nicer.
Review & Followup
Rating: 3
September 26, 2002
Reviewer: Hamid from England

September 25, 2002 -- Thanks for the question regarding "Why no constraint PK violation", version 816
You Asked
Hello sir,
   Say I have a famous table dept with deptno as primary key in scott schema.
There is no emp table so no FKs (deptno).

If I run an update like
update dept set detpno = deptno + 10.
It runs successfully

Q1) Why no PK constraint violation?
Q2) Does this mean constraints are verified in the end of the Update and not for
intermediate rows updated.
Q3) How does this update or any update work? .
Q4) Update would have given integrity constraint violation if it had the famous
emp table of scott schema.
    So will that mean in this case that the update failed because only after
complete update of 4 rows in dept table it checked for constraints PK was not
violated but FK was, that to only for deptno = 10 which no longer exists as the
result of new update in dept but is still there in emp?.
and we said...
why should there be -- was there a constraint violated?

Actually, you give me a good opportunity here to ONCE AGAIN promote the best,
yet most under-read document on the planet, the Oracle Concepts guide.
Specifically for you:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c24integ.htm#2352
It answers your question very directly.  Hopefully, after you read that you'll
say "wow, very nice -- I'll need to start at chapter 1 and read the entire
thing!"
Review & Followup
Rating: 5
Hit the nail on the head September 25, 2002
Reviewer: A reader
Thanx much for the link.I could confirm what I had guessed.
Now Back to school.

September 25, 2002 edward -- Thanks for the question regarding "Locally managed tablespace and PCTINCREASE storage parameter", version 8.1.7
You Asked
Tom,

In our Oracle Application database, we have a tablespace that is locally managed
and we have objects in that tablespace with PCTINCREASE set to 0. According to
doc_id 105120.1 in metalink it says the following:

The storage parameters NEXT, PCTINCREASE, MINEXTENTS, MAXEXTENTS, and  DEFAULT
STORAGE are not valid for locally managed tablespaces.

We have our objects defined with a PCTINCREASE set to 0. Which made some of the
objects in that tablespace to have migrated rows

My question to you would be, Having a LOCALLY MANAGED Tablespace
Is it good to have object (Tables especially) with PCTINCREASE set to 0. This
question is specific to an Oracle Application database.

I thank you for your wonderful and high quality service.

Thanks

Edward Jayaraj
and we said...
You totally misunderstand the concept behind pctincrease if you feel that rows
migrated because of it!

PCTINCREASE is the worst parameter Oracle ever invented (IMHO).  It was used
when computing the NEXT extent size.  For example, if I create a table with
initial=1m pctincrease=50, my extents would be (approx)

1m
1.5*1m
1.5*1.5*1m
1.5*1.5*1.5*1m

and so on -- they would grow 50% each time.

Now, row migration is what happens when a row is updated and there is
insufficent space on the block to hold its new value.  We take the row and
"migrate" it to a new block at that point -- but -- we do not change the rowid.
So, an index access to that row will goto the original block, find the row is
not there, read the FORWARDING address of that block and go get it.

The size of an extent has nothing to do with this at all.  (see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4423420997870
for an interesting example with migrated rows).

Perhaps you really are thinking about PCTFREE -- the amount of a block we
reserve for future UPDATES?  In fact, I'm pretty sure you are thinking about
pctfree as it is the single most important setting with regards to reducing row
migrations, not pctincrease!
Review & Followup
Rating: 5
pctincrease September 25, 2002
Reviewer: Martin Guillen from Argentina
Hi Tom:
I'm reading about Oracle and I guessing:
If a segment can have a lot of extents with out affecting performace: What is
the reason to use PCTINCREASE != 0?.
What I mean is: I can design a segment with a low extent size so it has 10
instead of 5 extents.
Later it has 30, 50 100, 500 all without affecting performance.
Who cares?
What's the benefit of having PCTINCREASE != 0?
Thank You,
Martin.

Followup:
I despise pctincrease.

Use locallly managed tablespaces and forget that thing even exists.

There are those that will say "set it to 1, so smon will coalesce contigous free
extents"

I say "set it to zero, use initial=next and smon never needs to do anything!"

Use LMTS and forget about it.

Rating: 5
September 25, 2002
Reviewer: A reader
Tom,

The answer shows you knowledge on the topic.

Thanks You very much I got it figured and You are very right about it I did
realized I am was thinking about PCTFREE and not PCTINCREASE

Thanks for your assistance and you are too good in both promptness and in
knowledge.

Thank TOM

Rating: 5
I will second you - LTM rules September 25, 2002
Reviewer: msc
<rants> I still wonder why Oracle has included dictionary based tablespaces into
9i DBA-I course materials (incl several examples with pctincrease initial ..
next .. extents). This should qualify "Appendix Z"... for historians. </rants>

Nice thing that 9iR2 has LTM by default including system tablespace. And
automatically managed segment space seems also nice feature.

September 26, 2002 Ankit -- Thanks for the question regarding "any alternative for trigger", version 8.1
You Asked
we are developing an application that deals with incoming events and stores them
as records in the DB. then this event is pushed to a web based client which is
an applet.
we are using JDBC to insert the record.
now there is a requirement that we implement a counter for the related incoming
events (basically we aggregate events based on two fields say eventname and node
name, and differentiate them based on time stamps, so whenever an event having
the same nodename and eventname as a previous existing event is recieved we are
supposed to increment the counter :-) ). there can be two ways of doing this.
1. we implement the counter in application.
(This can be done by having all the related-event definitions in cache and
incrimenting the counter when we recieve an event based on which category7 it
belongs to. )
2. push it to database.
(have the counter incrementing business done at the DB )

the first approach has memory issues as the number of related records in db can
exceed 10,000,000. and we have J2SE implimentation where heap size is limited(
in the sense I don't want to kill other processes :-) )

on the DB side this can be done using triggers (for example for every insert in
DB i increment the respective counter in DB and then return back the new
counter, so it will be a combination of stored procedure and triggers.), but
that will take a lot of time to execute. and as such throughput will decrease.
can you suggest some other alternative on the DB side except for triggers (For
example some datastructure that i can exploit to have just a temporary storage
of counters which i can read at time when i need the info etc...) ??

thanks for your help
Ankit Chhibber
and we said...
You can:

insert into t
select count(*)+1, :event_name, :nodename
  from t
 where event_name = :event_name
   and nodename = :nodename;

Prepare that statment once and then simply bind and execute it over and over.

(index on event_name/nodename suggested of course)

It'll return a count (even if there are NO ROWS in the table) and do the insert
in one call.

Beware -- if two messages come in at about exactly the same time -- you will NOT
get the "count" (you would have to serialize).  If that is an issue, let me
know, we'll need to modify the logic to accomidate (and bear in mind, it will be
a fact that it would HAVE to be slower as you would be imposing serialization at
the event_name/nodename level)
Review & Followup
Rating: 4
thanks September 25, 2002
Reviewer: Ankit from India
Hi Tom,
the solution is pretty neat and simple.
but i have a small doubt
I have to insert about four more values apart from counter, eventName and
nodeName, in the same table, but the counter is based on just eventName and
nodeName.
To add to it the primary key happens to consist of eventname, nodeName and
timeStamp :-(. this is done to distinguish a unique event completely (also this
combination acts as foreign key in other tables).
I tried to form the query given by you but could not add the other values to it.

serialization will not be an issue as there is just one thread accessing DB and
inserting the values

thanks
Ankit

Followup:
insert into t
select count(*)+1, :event_name, :nodename, sysdate, :other_data, :yet_more_stuff
  from t
 where event_name = :event_name
   and nodename = :nodename;

just add it to the select list.  SYSDATE is your timestamp, :other_data,
:yet_more_stuff are your other supplied attributes.

September 26, 2002 Partha -- Thanks for the question regarding "Result set", version 8.1.7.4
You Asked
Hi, Tom

Problem: We have parent, child hierarchy. ID1 is related (called
relevance)
ID2, again this ID2 is related to some other ID. Basically CONNECT BY
solve
the problem of getting the parent-child relationship.

In my example, data is like this. The value of relevance get degraded
by 1/2
every level down it goes.

For example 1 related 2 by 50.
      1 related 5 by through 2  but the relevance becomes (50/2) + 80 =
105
      1 related 9 by through 2 and 5. 1/2 of the relevance of 1 to 5
plus
current relevance. That is (105/2) + 87.

                1
            /   |   \
           2    3    4
        /  |  \
       5   6   7
       |
       9

create table mytest
(
id1 number(38,0),
id2 number(38,0),
relevance number(38,0)
);

INSERT INTO MYTEST ( ID1, ID2, RELEVANCE ) VALUES ( 1, 2, 50);
INSERT INTO MYTEST ( ID1, ID2, RELEVANCE ) VALUES ( 1, 3, 25);
INSERT INTO MYTEST ( ID1, ID2, RELEVANCE ) VALUES ( 1, 4, 60);
INSERT INTO MYTEST ( ID1, ID2, RELEVANCE ) VALUES ( 2, 5, 80);
INSERT INTO MYTEST ( ID1, ID2, RELEVANCE ) VALUES ( 2, 6, 70);
INSERT INTO MYTEST ( ID1, ID2, RELEVANCE ) VALUES ( 2, 7, 50);
INSERT INTO MYTEST ( ID1, ID2, RELEVANCE ) VALUES ( 4, 8, 75);
INSERT INTO MYTEST ( ID1, ID2, RELEVANCE ) VALUES ( 5, 9, 87);

I wanted to use the SQL something like this. But I need previous row
calcuated value.

select id1, id2, relevance + decode( level, 1, 0, (prior relevance/2))
from mytest
where level <= 3
start with id1 =1
connect by prior id2 = id1;

Right now I am using following SQL's which does the job I am looking
for.

FOR LEVEL-1

select id1, id2, relevance from mytest where id1 = 1

FOR LEVEL-2

select T1.id1, T2.id2, (((T1.relevance/100)*0.85)*T2.relevance)
relevance
from
(select id1, id2, relevance from mytest where id1=1) T1,
(select id1, id2, relevance from mytest) T2
where T1.id2 = T2.id1
union
select id1, id2, relevance from mytest where id1 = 1

FOR LEVEL-3:

select TABLE1.id1, TABLE2.id2,
((TABLE1.relevance/100)*0.85)*TABLE2.relevance from
(select T1.id1, T2.id2, (((T1.relevance/100)*0.85)*T2.relevance)
relevance
from
(select id1, id2, relevance from mytest where id1=1) T1,
(select id1, id2, relevance from mytest) T2
where T1.id2 = T2.id1) TABLE1,
(select id1, id2, relevance from mytest) TABLE2
where TABLE1.id2 = TABLE2.id1
union
select T1.id1, T2.id2, (((T1.relevance/100)*0.85)*T2.relevance)
relevance
from
(select id1, id2, relevance from mytest where id1=1) T1,
(select id1, id2, relevance from mytest) T2
where T1.id2 = T2.id1
union
select id1, id2, relevance from mytest where id1 = 1

What is the best method? Only using stored-procedures or is their any
other method.

Thanks in advance
and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> column lvl format a10
ops$tkyte@ORA817DEV.US.ORACLE.COM> select rpad('*',level*2,'*') lvl, id1, id2,
relevance,
  2            (select sum( relevance/ power(2,level-1) )
  3                        from mytest t2
  4                          start with ( t2.id1 = t.id1 and t2.id2 = t.id2 )
  5                          connect by prior id1 = id2 ) rel
  6    from mytest t
  7   start with id1 = 1
  8  connect by prior id2 = id1
  9  /

LVL               ID1        ID2  RELEVANCE        REL
---------- ---------- ---------- ---------- ----------
**                  1          2         50         50
****                2          5         80        105
******              5          9         87      139.5
****                2          6         70         95
****                2          7         50         75
**                  1          3         25         25
**                  1          4         60         60
****                4          8         75        105

8 rows selected.

Seems to do it for me....  Why -- I'll leave that to you to reverse engineer ;)
Review & Followup
Rating: 4
Superb Answer September 24, 2002
Reviewer: A reader
Really this is a very good trick

Rating: 3
Good Answer September 25, 2002
Reviewer: Matt Turner from UK
Apologies for piggy-backing this but i've been searching for a solution for
ages.

We have a fact table and a dim structure table:

Fact ( Account_Code, Cost_Value )
Dim Structure( Account_Code_Child, Account_Code_Parent )

The cost is held @ the lowest level. However the account structure isn't simply
a sum of the costs:

ie. Dim Structure contains two records:

Turnover, Profit
Operating Cost, Profit

But: Profit = Turnover - Cost (This is simplified)

I added a cost_factor to the dim structure table and had
values of 1 and -1 to enable use of SUM function. My
problem is that because the cost is held against the
bottom level account code (dimension) in the fact table, how can I do a tree
walk and give the costs at each level.

It would be great to have a SQL solution for this.

Followup:
Do like the original poster did -- give me a simple create table, a couple of
rows, and example output explaining how you got to the answer.

It isn't clear to me from above how to compute what you want or what data I
could enter into each table.

Rating: 5
Thanks Tom September 25, 2002
Reviewer: Matt Turner from UK
create table fact ( account_code varchar2(8),
                              cost_value number )

insert into fact values ( '00000001',10 );
insert into fact values ( '00000002',5 );
insert into fact values ( '00000003',5 );
insert into fact values ( '00000004',8 );
insert into fact values ( '00000005',7 );
insert into fact values ( '00000006',11 );
insert into fact values ( '00000007',12 );
insert into fact values ( '00000008',13 );
insert into fact values ( '00000009',1 );
insert into fact values ( '00000010',1 );

create table account_structure( account_code_from varchar2(8),
                                                    account_code_to varchar2(8)
,
                                                    cost_factor number )

insert into account_structure values ( '00000001', 'INDIRECT', 1 );
insert into account_structure values ( '00000002', 'INDIRECT', 1 );
insert into account_structure values ( '00000003', 'INDIRECT', 1 );
insert into account_structure values ( '00000004', 'DIRECT', 1 );
insert into account_structure values ( '00000005', 'DIRECT', 1 );
insert into account_structure values ( '00000006', 'TURNOVER', 1 );
insert into account_structure values ( '00000007', 'TURNOVER', 1 );
insert into account_structure values ( '00000008', 'TURNOVER', 1 );
insert into account_structure values ( '00000009', 'TURNOVER', 1 );
insert into account_structure values ( '00000010', 'TURNOVER', 1 );
insert into account_structure values ('INDIRECT' , 'TOTAL', 1 );
insert into account_structure values ('DIRECT' , 'TOTAL', 1 );
insert into account_structure values ('TURNOVER', 'PROFIT',1 );
insert into account_structure values ('TOTAL', 'PROFIT', -1 );

Required Output :

Account Code          Sum(Cost*Cost_Factor)

PROFIT                    -3
  TOTAL                   35
     INDIRECT             20
         00000001         10
         00000002         5
     00000003         5
     DIRECT               15
         00000004         8
         00000005         7
  TURNOVER            38
         00000006          11
         00000007          12
         00000008          13
         00000009          1
         00000010          1

So : PROFIT = TURNOVER - TOTAL
     TOTAL = INDIRECT + DIRECT

I introduced a Cost_Factor so I could use the sum function.
If you can give me a SQL answer it would be great I have
tried analytical rollups and everything !!

Followup:
well -- as I see it -- there is a problem in that the hierarchy skips a level
for turnover, that complicates it for sure.

It is not "pretty" but this appears to get the answer (except I get +3, turnover
= 38, total = 35, 38-35 = +3?)

ops$tkyte@ORA920.US.ORACLE.COM> select 0 "LEVEL", 'PROFIT' account_code_from,
  2         (select sum(cost_value)
  3            from fact, account_structure as2
  4           where fact.account_code = as2.account_code_from
  5             and as2.account_code_to = 'TURNOVER')
  6        -(select sum(cost_value)
  7            from fact, account_structure as2
  8           where fact.account_code = as2.account_code_from
  9             and as2.account_code_to in ( 'DIRECT', 'INDIRECT' ) ) cv
 10  from dual
 11  union all
 12  select level,
 13         rpad('*', 2*level, '*' ) || account_code_from  account_code_from,
 14         case when account_code_from in ( 'TURNOVER', 'DIRECT', 'INDIRECT' )
 15              then (select sum(cost_value)
 16                      from fact, account_structure as2
 17                     where fact.account_code = as2.account_code_from
 18                       and as2.account_code_to =
account_structure.account_code_from )
 19              when account_code_from in ( 'TOTAL' )
 20              then (select sum(cost_value)
 21                      from fact, account_structure as2
 22                     where fact.account_code = as2.account_code_from
 23                       and as2.account_code_to in ( 'DIRECT', 'INDIRECT' ) )
 24              else (select cost_value
 25                      from fact
 26                     where fact.account_code =
account_structure.account_code_from)
 27           end  cv
 28    from account_structure
 29   start with account_code_to = 'PROFIT'
 30  connect by prior account_code_from = account_code_to
 31  /

     LEVEL ACCOUNT_CODE_FROM            CV
---------- -------------------- ----------
         0 PROFIT                        3
         1 **TURNOVER                   38
         2 ****00000006                 11
         2 ****00000007                 12
         2 ****00000008                 13
         2 ****00000009                  1
         2 ****00000010                  1
         1 **TOTAL                      35
         2 ****INDIRECT                 20
         3 ******00000001               10
         3 ******00000002                5
         3 ******00000003                5
         2 ****DIRECT                   15
         3 ******00000004                8
         3 ******00000005                7

15 rows selected.

Rating: 3
Thanks Tom September 26, 2002
Reviewer: Matt from UK
The problem I have however is that I can't hard-code
account codes as they will change and the example I have
given is simplified I have around 10 levels, so need
to make it data driven... I am coming to the rapid conclusion that it probably
isn't possible.

Thanks Tom.

Followup:
Not given your unbalanced hierarchy (it skips levels sometimes) -- as far as I
can see, no.

September 26, 2002 Arun -- Thanks for the question regarding "Does CBO considers HWM when calculating cost for a full table scan ?", version 8.1.7.3
You Asked
I have a theoretical question. Does the CBO consider all blocks upto highwater
mark when calculating the cost of a full table scan ? A firm yes or no answer
would be fine.
Thanks
and we said...
yes

it has to.  all of the blocks up to the HWM are going to be in the full scan.

Consider:

ops$tkyte@ORA920.US.ORACLE.COM> /*
DOC>
DOC>drop table t;
DOC>
DOC>create table t pctfree 90 pctused 5
DOC>as
DOC>select * from all_objects;
DOC>
DOC>*/

ops$tkyte@ORA920.US.ORACLE.COM> analyze table t compute statistics;
Table analyzed.

ops$tkyte@ORA920.US.ORACLE.COM> set autotrace traceonly explain;

ops$tkyte@ORA920.US.ORACLE.COM> select * from t;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=384 Card=29272 Bytes=2488120)
   1    0   TABLE ACCESS (FULL) OF 'T' (Cost=384 Card=29272 Bytes=2488120)

cost = 383

ops$tkyte@ORA920.US.ORACLE.COM> set autotrace off
ops$tkyte@ORA920.US.ORACLE.COM> delete from t;

29272 rows deleted.

ops$tkyte@ORA920.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM> analyze table t compute statistics;

Table analyzed.

ops$tkyte@ORA920.US.ORACLE.COM> set autotrace traceonly explain;
ops$tkyte@ORA920.US.ORACLE.COM> select * from t;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=384 Card=1 Bytes=128)
   1    0   TABLE ACCESS (FULL) OF 'T' (Cost=384 Card=1 Bytes=128)

card is downgraded but COST remains the same

ops$tkyte@ORA920.US.ORACLE.COM> truncate table t;

Table truncated.

ops$tkyte@ORA920.US.ORACLE.COM> analyze table t compute statistics;

Table analyzed.

ops$tkyte@ORA920.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA920.US.ORACLE.COM> select * from t;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=128)
   1    0   TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=1 Bytes=128)

same table, same number of rows -- but the truncate lowered the HWM so....
lower cost
Review & Followup
Rating: 3
Follow up question - CARD? September 25, 2002
Reviewer: JC from DC USA
Excellent and clear answer.

If I could, what actually is the CARD term next to the COST?

Followup:
CARD - cardinality -- estimated number of rows produced by that step in the plan
(output from that step in the plan)

Rating: 4
September 25, 2002
Reviewer: Arun Gupta from PA
Thanks very much Tom. Your answer helped clear many doubts.

Rating: 5
CBO September 26, 2002
Reviewer: Ashwani Singh from india
Great Tom!!!,

Ur Answers clear a lot of doubts.
One thing i wanted to ask was that in a Paramter file , when specifying
optimizer_mode. What is the diffference between all_rows and last_row.

Regards,
Ashwani Singh

Followup:
Easy - all_rows will let you start your database.

Setting it to last_row will not, it is not a valid value.

So, assuming you meant "whats the difference between ALL_ROWS and FIRST_ROWS
optimization"....

All_rows -- optimizes the query to get you the LAST row as fast as possible.
Eg, if you issue "select x,y from big_table where x is not null order by x" and
there is a single index on big_table on the column X -- all_rows optimization
will most likely FULL SCAN big_table, get x,y, sort and give you the result.
It'll be faster then millions of logical IOs a block at a time to access the
table via the index.

First_rows -- optimizes the query to get you the FIRST row as fast as it can.
Given the same scenario as above -- the optimizer would most likely choose the
index access and give you the first row instantly (instead of waiting for the
full scan+sort).  Getting that last row will take longer then the all_rows plan
but the result to the program of the first row is faster.

A stored procedure would benefit from all_rows since the client doesn't get to
see anything until its done (you want the best throughput).

An interactive application that issues SQL might benefit from first_rows since
the client is waiting for the first 10 rows to appear on their screen.

Rating: 4
Similar topic September 26, 2002
Reviewer: Dennis from Missouri, USA
Tom,

This is related to explain plans - if this is an inappropriate spot then please
say so and I'll wait in the queue.

In Tkprof, you get a rows processed count.  What is this representing?  I used
to think it was the actual number of rows for that step, however just yesterday
I experienced the number of rows returned from the query (one I was tuning) to
be higher than this count.  Here's visual info since that is always helpful:

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.07       0.33          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch     2153    320.83    1196.45    2341431    2343495         60       32275
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total     2155    320.90    1196.78    2341431    2343495         60       32275

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 119  (TESTTUNE)

Rows     Row Source Operation
-------  ---------------------------------------------------
  32275  HASH JOIN
    418   TABLE ACCESS FULL REF_ORGANIZATION
 414816   TABLE ACCESS FULL EXT_XPAYD_PAY_DETAILS

Adding a bitmap index, I saw different row counts in tkprof, which scared me
into thinking that somehow the query was altered, but I spooled the query
without the index and with the index, retrieving 32595 rows each time.

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.01          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch     2174     30.42     260.61     129576     138357         15       32595
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total     2176     30.43     260.62     129576     138357         15       32595

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 119  (TESTTUNE)

Rows     Row Source Operation
-------  ---------------------------------------------------
  32595  HASH JOIN
    418   TABLE ACCESS FULL REF_ORGANIZATION
 421154   INLIST ITERATOR
 421154    TABLE ACCESS BY INDEX ROWID EXT_XPAYD_PAY_DETAILS
 421728     BITMAP CONVERSION TO ROWIDS
    158      BITMAP INDEX SINGLE VALUE

The number of rows returned by the query is 32595:
--- original ---
32595 rows selected.

Elapsed: 00:17:1028.59
15:31:22 TESTTUNE@HIP2:sca01> spool off
---
--- added bitmap
32595 rows selected.

Elapsed: 00:03:203.53
15:07:13 TESTTUNE@HIP2:sca01> spool off
---

So...why did I get a different number of rows on the original explain plan?  Any
ideas??

Thanks,
Dennis

Followup:
I can only surmise that the first trace file was truncated and not all of the
information was in there.  According to that -- you made

2153 FETCH calls resulting in 32275 rows (15 rows/fetch -- looks like SQLplus's
array size)

The second one made 2174 FETCH calls resulting in 32595 rows -- 15 rows/fetch
again.

I would say the first trace file was truncated or otherwise incomplete and the
end statistics (the other 21 fetches) were "missing"

What was your max_dump_file_size and was the first trace file hitting that
limit?

Rating: 5
September 26, 2002
Reviewer: A reader
Referring to your answer to the original question:

After the delete, the autotrace shows the same Cost and a lower Cardinality. But
why is the bytes lower? If it is doing a FTS then I would have expected the
bytes remained same? So what is this bytes thing actually?

Followup:
bytes = card (estimated returned rows) * avg row length.

after delete, card = 1, bytes returned goes down.

cost of scan, stays the same, same number of IO's would be done to read

Rating: 5
not sure September 26, 2002
Reviewer: Dennis from Missouri, USA
Well the second trace file is larger.  Nobody should've modified the init
parameters...I'm pretty sure I was the only one in there, and it normally takes
a couple of meetings and a ripped off arm to get things like that changed (I
still do not understand why they think EVERY database needs to have the same
init parameters...), so I don't imagine anyone would've changed it, especially
since I usually make those suggestions and I don't recall losing an arm. ;)

I have 10240 for the max_dump_file_size.  That is in KB, right?  The first trace
file (the weird one) is 3.68 MB, the other is 5.00 MB.  It does say at the
bottom of the first file that it's 2xxx lines in the trace file, but the tkprof
output shows only 100 or so (I didn't weed out anything either, unless it does
so by default (used to have to weed out the sys, that change in 9iR2?  Guess I
need to look that up don't I...)).

At any rate, it doesn't really matter now that you've confirmed that I wasn't
hallucinating or anything.  Rows processed.  Anything else means that something
screwy happened to the file.  Works for me.

Thanks Tom, you're the greatest!  Sorry I didn't respond right away, I was in a
meeting...but I did check first thing when I got back (well...after I checked
out my tar).  You give us such prompt service, it's only right that we do the
same!
Dennis

Rating: 5
September 26, 2002
Reviewer: A reader
OK. So bytes is the number of bytes returned by the query and not the number of
bytes read in order to service the query. That was my confusion. Thanks.

September 26, 2002 Seok-bae -- Thanks for the question regarding "About Free space(TEMP & Other Tablepsaces)", version 8.1.7
You Asked
I've managed Oracle 8.1.7.
I doubt about temporary tablespace and dba_free_spaces dictionary.
When i exectue this script,usage of TEMP tablespace reaches nearly 100%.
This is the same for every execution.
How do i have to do about this appearance.
This is happened to not only Temp Tablespace but the other tablspaces.
But, there is no problem about Tablespacespace SPACE.
1) The resons, why?
2) How do i have to do this problem?

FILE_ID TBLSPACE             TOT_KB FREE_SPACE_K USED_SPACE_K    used(%)
------- --------------- ----------- ------------ ------------ --------
........
........
17 D0DD0002            921,600        5,680      915,920      99.38
34 D0DD0003            819,200            8      819,192        100
4  TEMP                819,200        1,152       818,048     99.86

........
........

<free.sql>

set feedback off
SET PAGESIZE 100

PROMPT
PROMPT ********************************************************
PROMPT *   list the used rate of the tablespaces of the db.   *
PROMPT ********************************************************
PROMPT

COLUMN tblspace       FORMAT a15
COLUMN file_id        FORMAT 999
COLUMN tot_KB         FORMAT 99,999,999
COLUMN used_space_k   FORMAT 99,999,999
COLUMN free_space_k   FORMAT 99,999,999

COMPUTE SUM of tot_mb ON tspace
BREAK ON tspace skip 1

SELECT DISTINCT d.file_id file_id,
       d.tablespace_name tblspace,
       d.bytes /1024 tot_KB,
           (d.bytes - SUM(e.bytes)) /1024 free_space_k,
       SUM(e.bytes) /1024  used_space_k,
       ROUND( SUM(e.bytes)/(d.bytes), 4)*100 "used(%)"
  FROM dba_extents e, dba_data_files d
 WHERE d.file_id = e.file_id(+)
 GROUP BY d.file_id ,d.tablespace_name, d.bytes
 ORDER BY SUBSTR(d.tablespace_name, 1, 15) ;

PROMPT
set feedback on
and we said...
it is normal, temp is supposed to appear full.

search this site for

v$sort_segment v$sort_usage

for why and how to see whats really used in temp
September 26, 2002 Nick -- Thanks for the question regarding "Using Rownum to limit a range of rows (Nth to Mth) returned from a Query", version 8i (8.1.5)
You Asked
I'm trying to limit the range of rows returned from a Select
statement. For eg. I have a table with 100 rows and I want to
return rows 15 to 30.

I tried doing this (based on one of your answers in this site)
but it does not work, as no rows are returned.

select * from
(
 select rownum rnum, a.*
 from  (select *
       from siteprodsubscribed) a
 where rownum <= 30
)
where rownum >= 15

Would be much obliged if you could provide some tips.

Thanks in advance,

Nick
and we said...
select * from
(
 select rownum rnum, a.*
 from  (select *
       from siteprodsubscribed) a
 where rownum <= 30
)
where rownum >= 15
      ^^^^^^^

that should be RNUM, not rownum.  where rownum >= N will
never return a single row unless N is 0 or 1.

see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:127412348064
for the complete example
Review & Followup
Rating: 4
Union makes any difference? April 02, 2002
Reviewer: Anna from USA
I need to do paging based on result of ordered union
Trying this:
select * from
(
 select rownum rnum, a.*
 from  (select c1,c2,c3
       from table1 union all
       select c1,c2,c3
       from table2
       order by 1,2) a
 where rownum < 10
)
where rnum >= 1
      and for some reason my order is not preserved.

Please, advise

Followup:
Try this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from
  2  (
  3   select rownum rnum, a.*
  4   from  (select * from (select owner, object_type, object_name
  5         from all_objects union all
  6         select owner, object_type, object_name
  7         from all_objects)
  8         order by 1,2) a
  9   where rownum < 10
 10  )
 11  where rnum >= 1
 12
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> /

      RNUM OWNER                          OBJECT_TYPE        OBJECT_NAME
---------- ------------------------------ ------------------
------------------------------
         1 A                              TABLE              TEST
         2 A                              TABLE              TEST
         3 A                              TRIGGER            TBI_TEST
         4 A                              TRIGGER            TBI_TEST
         5 ADMIN                          VIEW               V
         6 ADMIN                          VIEW               V
         7 APPS                           TYPE               X_TYPE
         8 APPS                           TYPE               X_TYPE
         9 C                              PROCEDURE          P

9 rows selected.

but -- be prepared to wait -- the union all will take place AND THEN the order
by ....

Rating: 4
in 9i can the anlytic function row_number achive the same September 26, 2002
Reviewer: john from USA
i want to do the simulary way like,

i get i_page_number,i_rows_per_page parameters:

i need to return the record set with a query something like:

open o_ref_cur for
select * from (
select a.*,a.rownum rnum from
(select * from emp order by sal desc) a where rownum <= i_page_number *
i_rows_per_page ) where rnum > (i_page_number - 1) * i_rows_per_page;

can we achieve the simular functionality with row_number function in 9i(9.2)(as
this can be used in pl/sql as well) for better performance

Followup:
it can but rownum would be more efficient, and easier...

September 26, 2002 Tripti -- Thanks for the question regarding "Monitoring Database", version Oracle 8.0.2.1
You Asked
In our Setup we have Various Instanaces of Oracle running on
different machines.

e.g. Oracle 8.0.5.2.0 on Unix
     Oracle 8.0.5.1   on Unix (3 instance on the same machine)
     Oracle 8.0.4     on NT
     PO * on  2-3 NT & 95 machines.

I need to know the way to monitor at least that all the Oracle
Instances are up sitting at one location.

If you can pls tell me the way out which I can try and then
Implement

Bye

With best regards
and we said...
Thats OEM (enterprise manager).  See
http://www.oracle.com/take_control/index.html?oem.html
for details.
Review & Followup
Rating: 1
April 20, 2001
Reviewer: Catherine from UK
I've found that the intelligent agent (needed for event monitoring) is very
unreliable for versions of Oracle prior to 8.1.6.

Rating: 3
The notification event does not work for one database September 26, 2002
Reviewer: Mike from TX
Tom,

I have set up the oem/oms/dbsnmp all in the HPs.

The IAs can discover all the nodes and the databases without a problem. The Oem
console works just fine for the database.

And I have setup the database down event for all the databases, all of them work
except one database. The alert is always shown the database is down, no matter
even the database is up actually. How can I troubleshoot the problem

TIA

Followup:
contact support for that one.  not much of an OEM user personally and have never
setup OMS.

September 27, 2002 murali -- Thanks for the question regarding "Allocation Error", version 8.1.6
You Asked
Hi

I am using Oracle 8.1.6.0.0 on Solaris 2.6.Database is
configured for Multi Threaded server.Maximum number of session
configured on this Instance is 60. IF more than 10 users are
connected and if I try to connect through SQL plus i am getting
the following error.

ORA-04031 unable to allocate 24 bytes of shared memory ("large
pool", "unknown object","session heap","trigger condition node")

If i shutdown and restart the database problem get solved. some
time i not able to connect through SVRMGRL. I used to kill the
Oracle process

Sun machine has 256MB of RAM and etc/system file details are
given below

set shmsys:shminfo_shmmax=209715200
set shmsys:shminfo_shmmin=1
set shmsys:shminfo_shmmni=100
set shmsys:shminfo_shmseg=10
set semsys:seminfo_semmni=100
set semsys:seminfo_semmsl=100
set semsys:seminfo_semmns=2510
set semsys:seminfo_semopm=100
set semsys:seminfo_semvmx=32767

SHOW SGA from ORACLE

Total System Global Area   74133488 bytes
Fixed Size                    69616 bytes
Variable Size               6864896 bytes
Database Buffers           67108864 bytes
Redo Buffers                  90112 bytes

Please help

Thanks & Regards

Murali
and we said...
When you use MTS, the UGA is allocated in the SGA -- not in the
process as normal.  That means you must allocate a larger SGA
then you would with a dedicated server configuration.

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:557222937288
and
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:295216240143
for some more details.

Your variable size is woefully undersized for 60 users.  I would
set asize a minimum of 1/2 to 1meg of RAM for each session you
expect in the large_pool for starters to see how that goes.  I
would set the large_pool to at least 30 and probably more like
60m.

Your shared_pool as it is -- 6m, is too small for real world
usage as well.  That is where we cache execution plans, plsql
code and so on.  I would start that at AT LEAST 20meg and then
monitor it -- trying to get it to a point where it is never more
then  95% allocated.

Definitely increase these settings.  256meg of ram for 60
concurrent users is a little tight.
Review & Followup
Rating: 4
I Too Got Same Problem September 27, 2002
Reviewer: samir from India
in the problem stated perameters having values like these

Total System Global Area   74133488 bytes
Fixed Size                    69616 bytes
Variable Size               6864896 bytes
Database Buffers           67108864 bytes
Redo Buffers                  90112 bytes

what are the limits for these perameters?

Followup:
they are not "parameters", they are the result of a report.

the total SGA is limited by your OS, your word size (32 bit vs 64 bit).  You
would read your OS admin guide for your software to determine the limit of the
size of the SGA.

September 27, 2002 -- Thanks for the question regarding "owa.vc_arr's mean and document ?", version 9ias
You Asked
hello!

i'm participate in SI project.

we are developing the program using 9ias(pl/sql cartridge), oracle9 database

owa.vc_arr is using in pl/sql program that passing multiple parameter passing

but
we don't have any document and reference with owa.

i want to know the owa's usage and the elements(vc_arr ... etc)

thanks
and we said...
owa.vc_arr is just a table type you can (but don't have to) use.

Me, I prefer to define my own type:

create or replace package types
as
   type array is table of varchar2(4000) index by binary_integer;
end;

and then use types.array instead of owa.vc_arr

The documented packages are in:
http://technet.oracle.com/docs/products/ias/doc_library/90200doc_otn/web.902/a90101/toc.htm
there should never be a need to use OWA directly.
September 27, 2002 Steve -- Thanks for the question regarding "General Development Advice", version 7.3.4
You Asked
Tom,

I am currently in the process of developing an application and would like your
thoughts on the strategy I have tried to use.

The system i'm working with only as 2 major base tables with another furhter 2-3
lookup tables. I have tried to encapsulate the business logic and rules by using
views over the base tables / lookup tables which are used to access the data
these also have formula columns in them which access two packages (which include
all of the business rules).  I have public synonyms on the two views and have
granted all rights via the views.  Does this sound like a good approach?  is
there somethnig else I could do better?  I know this is a bit sketchy but I just
want to make sure I am on the right lines, are their any general development
strategies that you could share with me.

Kind Regards
Steven
P.S. I could send you the scripts if you would like to know exact details.
please let me know and I'll send them
and we said...
Sounds good so far, I might avoid the public synonyms tho (what happens when the
next application comes along and wants to use your same name?)

I would put the application in a schema named after the application and use
"select * from APPLICATION_NAME.VIEW_NAME" personally.
Review & Followup
Rating: 3
current_schema September 27, 2002
Reviewer: msc
alter session set current_schema = <application schema>

is also better than public synonyms.

September 27, 2002 Bradley -- Thanks for the question regarding "Parsing Strings and Creating Rows In Tables", version 8.1.7.2
You Asked
Hi Tom,

I've been asked by our developers to provide a way of sending large amounts of
data to a database package via Weblogic and then have that package parse the
data and create rows in various tables - we don't want the client to make
repeated calls to the database and unfortunately we can't use collections.

Previously I have used methods such as passing in a string delimited by some
character and then parsing the string to pull out the data for the columns, this
allowed me to do everything in one transaction.

Is there a simpler more elegant way of doing this.

Thanks in advance

Bradley
and we said...
XML -- use the XDK and xmlsave...

search for xmlsave on this site for some examples, goto otn.oracle.com under
technologies to get the XDK to load up if you don't have it already.
Review & Followup
Rating: 5
Right on the money September 30, 2002
Reviewer: Bradley
Exactly what I need thanks.

Rating: 5
September 30, 2002
Reviewer: A reader

September 27, 2002 Sathiamathi -- Thanks for the question regarding "PLS-00327: Table is not in SQL scope here", version 8.1.6
You Asked
Hi Tom,

Greetings. I tried one of the Nested Table Join query in SQL Console. It works
fine. Same query when I tried it as function I got the compilation error like
"PLS-00905: object is invalid or "PLS-00327: Table is not in SQL scope here"

Let me know how to fix this issue.

Query:

select count(distinct a.ticketid) from tb_troubleticket a,
table(a.allocations) c where c.assignto='Sathya' - It works fine

select count(distinct ticketid) from tb_troubleticket ,
table(tb_troubleticket.allocations) c where c.assignto='Sathya' - It works fine

Function 1:

create function fn_ticketcount(puserid varchar)
return integer
as
rtemp integer;
begin
select count(distinct a.ticketid) into rtemp from tb_troubleticket a,
table(a.allocations) c where c.assignto=puserid;return rtemp;
end fn_ticketcount;

Compilation Error PLS-00905: object myuser.A is invalid ( Note: myuser is the
Schema)

Function 2:

create function fn_ticketcount(puserid varchar)
return integer
as
rtemp integer;
begin
select count(distinct ticketid) into rtemp from tb_troubleticket ,
table(tb_troubleticket.allocations)c where c.assignto=puserid;
return rtemp;
end fn_ticketcount;

Compilation Error PLS-00327: "TB_TROUBLETICKET" is not in SQL scope here.

Thanks

Sathya
and we said...
try

begin
  execute immediate 'select count(distinct a.ticketid)
                       from tb_troubleticket a, table(a.allocations) c
                      where c.assignto= :puserid' into rtemp using puserid;
...

8i PLSQL sometimes had issues with "new" SQL syntax, these issues are 100%
resolved in 9i.  Until then, dynamic sql is a workaround.
September 27, 2002 Nag -- Thanks for the question regarding "sqlplus hangs and takes for ever to do this ", version 8.1.5
You Asked
Tom

These days Iam facing a huge problem which is wasting a lot of mytime.

I have this table t , which has a million records.

For my work, I need to keep checking the number of total records , many number
of times in a day.

when I do a select count(*) the sqlplus hangs up , and never returns it is as if
it is going on for ever and ever.

How should I deal with it. How should I know what is happening within, whether
the sqlplus has hanged up , or it it is going to return with the total number of
records.

I have other tables in which I have 2 to 10 million records. I need a solution
becasue this is taking toomuch of effort and time.

I know it is going to take time to return the total number, but what I want is a
routine which can tell me how many records have been counted at a particular
given point of time, or whether the count has stopped inbetween due to some
reason.

How should I do it, is there a script which I can write, to do this.
Please help, I need it desperately.

thank you
and we said...
Well, sqlplus isn't hanging, its just waiting for the answer.

I do not see why you would need to count the totally number of records ( would
need to know more about your processing to suggest a way to AVOID having to do
that ) but you can monitor the progress.  Anything that takes more then a couple
of seconds will start to populate the v$session_longops table.  You can simply
open another session, use my showsql.sql to find your session (search for
showsql on my site) and then query v$session_longops like this:

 1  begin
  2     print_table( 'select *
  3                     from v$session_longops
  4                    where sid =16
  5                      and time_remaining > 0 ' );
  6* end;

(search my site for print_table as well)

It'll show you something like:

SID                           : 16
SERIAL#                       : 1556
OPNAME                        : Table Scan
TARGET                        : OPS$TKYTE.T
TARGET_DESC                   :
SOFAR                         : 30735
TOTALWORK                     : 38337
UNITS                         : Blocks
START_TIME                    : 16-aug-2001 19:32:51
LAST_UPDATE_TIME              : 16-aug-2001 19:33:03
TIME_REMAINING                : 3
ELAPSED_SECONDS               : 12
CONTEXT                       : 0
MESSAGE                       : Table Scan:  OPS$TKYTE.T: 30735 out of 38337
Blocks done
USERNAME                      : OPS$TKYTE
SQL_ADDRESS                   : 82E2A364
SQL_HASH_VALUE                : 737329258
QCSID                         : 0

which shows how long it took, how long it will probably take and where it is in
the processing..
Review & Followup
Rating: 5
A possible alternative August 17, 2001
Reviewer: Connor from UK
If you are not adding too many records per day, but counting them very
frequently, you could have a row-trigger than adds/subtracts deltas and stores
the count in a secondary table.  (Presumably a materialised view could achieve
the same thing)

Rating: 4
Connor , tell us more about that August 17, 2001
Reviewer: A reader
How should we do what you are suggesting

Followup:
See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:424949760671
for a materialized view exmaple.

alternatively you can:

create table COUNTER ( x int );
insert into counter select count(*) from t;

create or replace trigger t_trigger
after insert or delete on T for each row
begin
    if ( insertng ) then update counter set x = x+1;
    else                 update counter set x = x-1;
    end if;
end;

Beware -- both of the above would imply necessarily DECREASED concurrency on the
table T.  You would in effect be serializing inserts/deletes on T since every
insert/delete must update a single row.

Rating: 4
Analyze table August 17, 2001
Reviewer: Andrew from CA, USA
If you don't need an exact number, then consider analyzing the table using
"estimate".
analyze table ABC estimate statistics;
select table_name, num_rows from user_tables where table_name = 'ABC';

Rating: 5
Connor's solution adds work and that may not help August 17, 2001
Reviewer: Harrison from Haymarket, VA USA (sadly, not England)
Hi:  Connor's solution (a row level trigger) will add some processing to the
database, and that may not be something the users can afford.  It might be
better to use table growth to estimate the number of rows (after you have a
million, that should be pretty easy).  Anyway, the hard part of this question is
what can possibly be the difference between 1 million rows and 1.09 (the other
hard question is why a smart person like Conner wants to leave a nice place like
England for a colony? ).  Anyway, Tom's suggestion is interesting, another table
that I did not know existed, and a neat script to use it.  With a slight bit of
magic, we might make a web page updates the progress, and quiets the the
complainers who need that number (when "A lot!" would do.)

Rating: 5
but not working August 18, 2001
Reviewer: Mohammed Ahmed from Saudi Arabia
I searched and found print_table script which you use 8.0 and below. It doesnt
work.

It gives the following error:

PLS-00302: component 'DESC_TAB' must be declared

Followup:
The URL you reference is:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:584023239495
That has the script printtbl8.sql that I use in 8.0 (due to no autonomous
transactions).

In 7.3 and before, which you must be using, it'll not work since DBMS_SQL did
not have a "describe" functionality in that release.  I use that functionality
to get the column names -- without it, I cannot do that.

I don't have a print_table script for 7.3 and before for this reason.

Rating: 4
Alternative to PRINT_TABLE August 20, 2001
Reviewer: Andrew from CA, USA
I haven't tested this in 7.x, but it looks like it will be OK.
http://www.oracle.com/oramag/code/tips2000/index.html?120400.html

      Rating: 4
Alternative to
      PRINT_TABLE  August 20, 2001
Reviewer:
      Andrew  from CA, USA
Whoops the URL didn't come out:

www.oracle.com/oramag/code/tips2000/index.html?120400.html

      Rating: 4
what about DDL
      monitoring?  September 27, 2002
Reviewer:
      Dean  from Nashville
I am trying to create an index for datawarhousing that will be several gigs in
size.  When I query v$session_longops, I can't see it.

How can I track the progress of building a big index before it becomes a perm
segment?

TIA

          Followup:

It should be in v$session_longops.

I fired up a session (sid = 15).  I started a create unique index in it.  Popped
over to another window and:

ops$tkyte@ORA817DEV.US.ORACLE.COM> begin  print_table( 'select * from
v$session_longops where sid = 15' );
  2  end;
  3  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> /
SID                           : 15
SERIAL#                       : 100
OPNAME                        : Table Scan
TARGET                        : BIG_TABLE.BIG_TABLE
TARGET_DESC                   :
SOFAR                         : 1600
TOTALWORK                     : 22822
UNITS                         : Blocks
START_TIME                    : 27-sep-2002 16:47:07
LAST_UPDATE_TIME              : 27-sep-2002 16:47:19
TIME_REMAINING                : 159
ELAPSED_SECONDS               : 12
CONTEXT                       : 0
MESSAGE                       : Table Scan:  BIG_TABLE.BIG_TABLE: 1600 out of
22822 Blocks done
USERNAME                      : BIG_TABLE
SQL_ADDRESS                   : 01A7FBF4
SQL_HASH_VALUE                : 0
QCSID                         : 0
-----------------

PL/SQL procedure successfully completed.

It takes a couple of seconds -- but it shows up. In fact, I cannot get it to
"not" show up.

September 27, 2002 Anand -- Thanks for the question regarding
      "getting rows m through n", version

      You Asked
Hi Tom,

I have a problem,

i have to select a set of i.e.  rownum > 20 and rownum < 30
records based on a order by.

for example if i have payment
it have the following columns payment_method, type, amount,
date, agent.

first :- the records should be sorted on type, amount and agent.
second :- then the records ith rownum > 20 and < 30 should be
selected.

hope you got the problem..

Looking forward for your help.

Regds.
Anand

      and we said...
In Oracle8i, release 8.1 (this will *not* work in earlier
releases) you could code:

select *
  from ( select a.*, rownum rnum
           from ( select payment_method, type, amount, date,
agent
                    from T
                   where ....
                   order by type, amount, agent
                ) a
          where rownum < 30
       )
 where rnum > 20
/

the trick is to order by the inner most subquery.  AFTER the
order by, assign rownum.  Filter out rows that are past the end
of your set (30 in your example).  THEN after assigning the
rownum in that subquery, filter out rows less then your minimum
row.

Review &
      Followup

      Rating:
      5
  March 30, 2001
Reviewer:
      A reader

      Rating: 3
usefulness of
      selecting m through n rows  February 20, 2002

Reviewer:  Karthik  from Clemson, SC
how will this be more efficient than an operation like minus, to obtain the same
output.
is this being performed internally and are the comparisons computationally less
intensive?
thanks,
Karthik

          Followup:

it is much much more efficient then minus.  There are internal optimizations for
TOP-N type of queries -- which this is.

Benchmark it and see I always say!

      Rating:
      5
  August 09, 2002
Reviewer:
      A reader

      Rating: 5
total
      rows  September 27, 2002
Reviewer:  john

i have to return the query output on simular fashion like
by accepting page number and no of rows per page

but for the front to end to decide how many pages, i have to send the total row
count as well which i am doing as

select count(*) into o_total_rows from t where...

i remember you telling there is a better way of doing this like sending the
approximate total rows but not the exact total rows. can you please tell how is
that possible?

          Followup:

better way is to NOT do this at all -- let the user start at page one and page
forward -- or, let them THINK there are 10 pages and maybe more -- but they
might find there are not 10 pages after all.

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3489618933902
has that discussion.

September 28, 2002 Bradley -- Thanks for the question regarding
      "Obtaing Start and end Dates", version 8.1.7.2

      You Asked
Tom,

I have the following data in a table

ID  COB_DATE
-- ------------
 1 01-JAN-2002
 2 01-JAN-2002
     .
     .
30 31-JAN-2002
31 02-FEB-2002
32 02-FEB-2002
33 02-FEB-2002
     .
     .
80 28-FEB-2002
     .
     .

I want to run a query which will give one row containing the start and end dates
for each month so I get the following results.

 START_DATE  END_DATE
----------- -----------
01-JAN-2002 31-JAN-2002
01-FEB-2002 28-FEB-2002
01-MAR-2002 20-MAR-2002
05-APR-2002 26-APR-2002

Thanks in advance

Bradley

      and we said...
select min(cob_date), max(cob_date)
  from t
 group by to_char(cob_date,'mm')

(or group by to_char(cob_date,'mmyyyy') if you span years in the table)

ps$tkyte@ORA817DEV.US.ORACLE.COM>   1  select min(created), max(created)
  2  from all_objects
  3  group by to_char( created, 'mmyyyy' )
  4* order by 1
ops$tkyte@ORA817DEV.US.ORACLE.COM> /

MIN(CREAT MAX(CREAT
--------- ---------
28-AUG-01 28-AUG-01
09-SEP-01 30-SEP-01
11-OCT-01 31-OCT-01
12-NOV-01 29-NOV-01
03-DEC-01 31-DEC-01
03-JAN-02 29-JAN-02
02-FEB-02 27-FEB-02
03-MAR-02 21-MAR-02
10-APR-02 19-APR-02
07-MAY-02 30-MAY-02
17-JUN-02 27-JUN-02
02-JUL-02 30-JUL-02
06-AUG-02 31-AUG-02
04-SEP-02 27-SEP-02
Review & Followup

      Rating:
      4
Variation  September 27, 2002
Reviewer:
      A reader
What if we need the distinct start and end (of the month)dates of the month in
the date column.
The previous min and max wont work if those dates are not  present in the table.

ID  COB_DATE
-- ------------
  2 01-JAN-2002
     .
     .
31 02-FEB-2002
32 02-FEB-2002
33 02-FEB-2002
     .
     .
The output Still must be
START_DATE  END_DATE
----------- -----------
01-JAN-2002 31-JAN-2002
01-FEB-2002 28-FEB-2002

          Followup:

ops$tkyte@ORA920.US.ORACLE.COM> select to_date( dt, 'mmyyyy' ),
  2         last_day(to_date( dt, 'mmyyyy' ) )
  3    from ( select distinct to_char(created,'mmyyyy') dt
  4             from all_objects
  5             )
  6  /

TO_DATE(D LAST_DAY(
--------- ---------
01-MAY-02 31-MAY-02
01-AUG-02 31-AUG-02
01-SEP-02 30-SEP-02

ops$tkyte@ORA920.US.ORACLE.COM>

September 28, 2002 atul -- Thanks for the question regarding
      "Strange situation..", version 8.0.4

      You Asked
Sir,

I'm facing very confusing situation...

I have a table test having two indexes on it...
1)PK_TEST (EMPNO,NAME,ATT) is primaty key
2)TEST_IND(EMPNO,ATT,NAME) Is non-unique index...

i want to drop index TEST_IND...
when i give command to drop TEST_IND it gives error saying,

ORA-02429: cannot drop index used for enforcement of unique/primary key

But test_ind is non-unique index and not a primary key on a table..

also starnge is when we see in user_indexes it doesn't show PK_TEST index it
only shows TEST_IND..

But in constraints it shows primary key constarint..

Why this has happened and how to proceed..

Thanks.
atul

      and we said...
There are two concepts to understand here:

A primary key constraint can be enforced using ANY index (unique or not).  In
fact, a deferrable primary key constraint can ONLY use a non-unique index.

The order of the columns in the index are not material, in fact, the NUMBER of
columns in the index may be greater then the number of columns in the primary
key.  It is only necessary that that "leading edge of the index" contains the
primary key columns.

You created TEST_IND.  Then you created another index PK_TEST.  THEN you added a
constraint PK_TEST.  This constraint went to the data dictionary and found the
FIRST index that it was happy with -- one that it could use to enforce the
constraint.  That was TEST_IND in this case.  Consider:

ops$tkyte@ORA806.WORLD> create table t ( empno int, name int, att int );

Table created.

ops$tkyte@ORA806.WORLD>
ops$tkyte@ORA806.WORLD> create index test_ind on t(empno, att, name);

Index created.

ops$tkyte@ORA806.WORLD> create index pk_test on t(empno,name,att);

Index created.

ops$tkyte@ORA806.WORLD>
ops$tkyte@ORA806.WORLD> alter table t add constraint t_pk primary
key(empno,name,att);

Table altered.

ops$tkyte@ORA806.WORLD>
ops$tkyte@ORA806.WORLD> drop index test_ind;
drop index test_ind
*
ERROR at line 1:
ORA-02429: cannot drop index used for enforcement of unique/primary key

Now, in your case, all you need to do is drop PK_TEST:

ops$tkyte@ORA806.WORLD> drop index pk_test;

Index dropped.

and you have the set of indexes you want (an index on empno, att, name).
Alternatively, you can

o drop the constraint pk_test
o drop the index test_ind
o create the constraint pk_test and it'll now "kidnap" the pk_test index.
September 28, 2002 -- Thanks for the question regarding "Why include
      for table for all indexes for all indexed columns", version 817

      You Asked
Hi,
   Please tell me the difference between
a) Analyze table dept compute statistics
and
b)Analyze table dept compute statistics for
table for all indexes for all indexed columns

Will compute statistics not automatically do that?
Thanx

      and we said...
Compute statistics will do that  and more.  That is why you want to use
DBMS_STATS ;)  (really, in the real world, use dbms_stats to gather stats)

But, anyway, consider:

a@ORA817DEV.US.ORACLE.COM> create table t ( x int primary key, y int );

Table created.

a@ORA817DEV.US.ORACLE.COM> insert into t select rownum, rownum from all_objects;

23619 rows created.

a@ORA817DEV.US.ORACLE.COM>
a@ORA817DEV.US.ORACLE.COM> analyze table t compute statistics;

Table analyzed.

a@ORA817DEV.US.ORACLE.COM>
a@ORA817DEV.US.ORACLE.COM> select table_name, num_rows from user_tables;

TABLE_NAME                       NUM_ROWS
------------------------------ ----------
T                                   23619

a@ORA817DEV.US.ORACLE.COM> select index_name, num_rows from user_indexes;

INDEX_NAME                       NUM_ROWS
------------------------------ ----------
SYS_C0010998                        23619

a@ORA817DEV.US.ORACLE.COM> column column_name format a20
a@ORA817DEV.US.ORACLE.COM> select distinct table_name, column_name from
user_tab_histograms;

TABLE_NAME                     COLUMN_NAME
------------------------------ --------------------
T                              X
T                              Y

a@ORA817DEV.US.ORACLE.COM>
a@ORA817DEV.US.ORACLE.COM> analyze table t delete statistics;

Table analyzed.

a@ORA817DEV.US.ORACLE.COM> analyze table t compute statistics
  2  for table
  3  for all indexes
  4  for all indexed columns;

Table analyzed.

a@ORA817DEV.US.ORACLE.COM> select table_name, num_rows from user_tables;

TABLE_NAME                       NUM_ROWS
------------------------------ ----------
T                                   23619

a@ORA817DEV.US.ORACLE.COM> select index_name, num_rows from user_indexes;

INDEX_NAME                       NUM_ROWS
------------------------------ ----------
SYS_C0010998                        23619

a@ORA817DEV.US.ORACLE.COM> column column_name format a20
a@ORA817DEV.US.ORACLE.COM> select distinct table_name, column_name from
user_tab_histograms;

TABLE_NAME                     COLUMN_NAME
------------------------------ --------------------
T                              X

a@ORA817DEV.US.ORACLE.COM>

Note how compute statistics by itself gathered histograms on each and every
column in the table.  That can take quite a huge chunk of time.  In most cases
you either

a) do not need histograms
b) find them most useful on indexed columns only

so the second, more verbose command, provides you with the most commonly needed
stats, in less time

Review &
      Followup

      Rating:
      5
Thanx  September 28, 2002
Reviewer:
      A reader
Finally mystery solved.

September 28, 2002 Fenng -- Thanks for the question regarding "A
      question about SQL select ", version 8.1.7

      You Asked
Hi ,TOM .
So glad to have a chance to ask a question.

I have 3 tables:

SQL> select * from a;

A_ID  NAME
------------
1     zhang
2     li

SQL> select * from b;

ID A_ID
--------------------
1   2
2   2
3   1

SQL> select * from c;

ID A_ID
-------------------
1   2
2   1
3   1

SQL> select a.name,count(b.id) from a,b where a.a_id=b.a_id group by a.name;

NAME  COUNT(B.ID)
------------------ -----------
li    2
zhang 1

SQL> select a.name,count(c.id) from a,c where a.a_id=c.a_id group by a.name;

NAME COUNT(C.ID)
------------------ -----------
li      1
zhang   2

I wanna get the following result :

NAME  COUNT(B.ID)  COUNT(C.ID)
------------------ - ---------- -----------
li       2          1
zhang    1          2

so I wrote a sql statement,
SQL> select a.name,count(b.id),count(c.id) from a,b,c
 where a.a_id=c.a_id and a.a_id=b.a_id group by a.name;

but I  got :

NAME  COUNT(B.ID)  COUNT(C.ID)
------------------ ----------- - ----------
li     2             2
zhang  2             2

how to get the correct result ?Thank you!

      and we said...
either of:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select name,
  2            (select count(id) from b where b.a_id = a.a_id) b_count,
  3            (select count(id) from c where c.a_id = a.a_id) c_count
  4    from a
  5  /

NAME                              B_COUNT    C_COUNT
------------------------------ ---------- ----------
zhang                                   1          2
li                                      2          1

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select name, b_count, c_count
  2    from a,
  3            (select a_id, count(id) b_count from b group by a_id) b,
  4            (select a_id, count(id) c_count from c group by a_id) c
  5   where a.a_id = b.a_id(+)
  6     and a.a_id = c.a_id(+)
  7  /

NAME                              B_COUNT    C_COUNT
------------------------------ ---------- ----------
zhang                                   1          2
li                                      2          1

ops$tkyte@ORA817DEV.US.ORACLE.COM>

Review &
      Followup

      Rating: 5
Thank
      you!  September 28, 2002
Reviewer:  Fenng

Thank you!TOM

September 28, 2002 amar -- Thanks for the question regarding "how to
      change freelist groups for a table without recreating it", version 8.1.7

      You Asked
Hello Sir,

I am using parallel server with dual Compaq Tru64 unix with 8.1.7 as the
database.Some of the frequently used tables gets locked by one of the instance.
Due to this other session are just in waiting mode.

In one of your reply I have seen that set the freelist groups for a table to the
number of nodes in the cluster.

When I queried the dba_tables it is giving just 1 as the value for
freelist groups.

Then I try to alter the storage through
SQL> alter table abcd storage ( freelist groups 2);
It is giving
ORA-02170,"FREELIST GROUPS storage option not allowed."

In action heading it is giving.
"Remove this option and retry the statement or set the allow_freelist_groups
INIT.ORA parameter."

But setting this parameter in init.ora gives error in starting the database.

Please help me in this regard (Any other option except recreating the table with
new storage parameter could help me a lot).

      and we said...
wow, talk about legacy....

allow_freelist_groups was a v6 parameter, long long long since obsoleted (it is
always true now)

What you are seeing is that you cannot alter freelist groups, you must create
the object with freelist groups.

In 816, we allowed you to alter FREELISTS, but altering freelist groups
necessitates a rebuild.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t storage ( freelists 2 );

Table altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t storage ( freelist groups 2 );
alter table t storage ( freelist groups 2 )
                                        *
ERROR at line 1:
ORA-02170: FREELIST GROUPS storage option not allowed

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t move storage ( freelists 4
freelist groups 2 );

Table altered.

(and after the MOVE you would need to rebuild indexes -- which will need their
own freelists and freelist groups as well)

September 28, 2002 amit -- Thanks for the question regarding "SQL
      plus session not getting updated on saving changes in editor.", version
3.2

      You Asked
We have installed sql plus 3.2(client) in windows 2000 machine.Our server is
SCO-Unix. But notepad editor does not seems to be working in it.When we edit and
save it, sql buffer doesnot get updated instead it comes with old commands. This
seems to be problem in every windows 2000/XP machine.
I have checked that editor is defined as notepad.exe.There is one afiedt.buf in
C:\orant\bin path.

Same SQL Plus is working fine on Windows 98/ME machines.

Can you tell us what to do??Is there any patch for it??

      and we said...
That is version 7.2 of the database client software.

It predates Windows 2000 by more then 5 years.

I'm surprised you got it to install but there is no chance of having any
reasonable expectations that software the predates the operating system by 5
years would work.

There is nothing I know of to do here other then consider using software
developed for the operation system you want to use.  Your choices are

o use the version of the database client software supported on your OS choice
o use the OS that goes with your software you wish to use -- eg, in this case
that would be windows 95 and windows NT 3.5x

Review &
      Followup

      Rating:
      5
  September 28, 2002
Reviewer:  amit
       from India
Thanks a lot. I get most suitable answer for my oracle related queries on this
site only. Keep it up.

September 28, 2002 srinivasa rao -- Thanks for the question
      regarding "Preventing the truncation of a table", version 8.1.7

      You Asked
 Hi tom....
    after long time i got an opportunity to post a question ....
i have a table...but somebody is truncating that one....i want to restrict that
thing by using database trigger.? is it possibe to do that or any other way is
to restrict that one...
 please help me in this regard....
awaitng for u r kind reply.....
by
srinu

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> truncate table t;

Table truncated.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger no_truncate
  2  before truncate on schema
  3  begin
  4          if ( ora_dict_obj_name = 'T' )
  5          then
  6                  raise_application_error( -20001, 'You Lose' );
  7          end if;
  8  end;
  9  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> truncate table t;
truncate table t
               *
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-20001: You Lose
ORA-06512: at line 4

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review &
      Followup

      Rating: 4
Preventing the
      truncation of a table", version 8.1.7  October 05, 2002

Reviewer:  srinivasa rao bachina
i am very much delightful by seeing the answer....this solution to this question
is very much useful to me..
i need some more help i think Mr.Tom accept it...

September 28, 2002 Sridevi -- Thanks for the question regarding
      "Pivot Table like Interface", version 9i Release 2

      You Asked
Hello Tom,
Lets say I have an output to an SQL statement in this manner. This output being
pipe separated.

empno|sal|deptno|manager
1|20000|10|
2|10000|10|1
3|10000|10|1
4|5000|10|2
5|10000|20|1
6|5000|20|5
7|5000|20|5
8|10000|30|1

If I were to open this output in Microsoft Excel as a delimited file, I would be
able to create various combinations of pivot tables. deptno X manager matrix.
deptno X salary matrix ... as examples. (please excuse- this example is very
silly - but this is all I could think of at the moment)

Now my question is - Will I be able to build an application similar to the way
pivot table works in excel? If so, what tool can I use in the Application Server
- Forms/Reports/Java/XML - to do this?

In other words, I want to give the user a generic query module, wherein he would
be able to select what columns constitute the matrix and build the pivot table
himself - just like he can do so easily in Ms-excel.

Do you have any sample code / package in your code archives which does something
similar to this.

Thanks in Advance

      and we said...
Oracle Discoverer is the tool you are looking for.

It is an end user, ad-hoc, graphical query environment that supports pivoting
like that.

September 28, 2002 Piotr -- Thanks for the question regarding
      "Connect by and outer join", version 9.0.1.0.0

      You Asked
Does "connect by" work with outer joins?

This works:

select tms_subject, usr_realname, tms_date, m.tms_id, level, aset_id, thr_id, 0,
usr_forename
from portal.thread_message m, portal.users u
where for_id=:for_id and u.usr_id=m.usr_id
start with thr_id=:thr_id and tms_parent is null
connect by prior m.tms_id=tms_parent
order siblings by tms_date desc

This too:

select tms_subject, usr_realname, tms_date, m.tms_id, level, aset_id, thr_id, 0,
usr_forename, rt.read_date
from portal.thread_message m, portal.users u, portal.read_thread rt
where for_id=:for_id and u.usr_id=m.usr_id
and rt.usr_id(+)=:usr_id and rt.tms_id(+)=m.tms_id

But this doesn't (returns no rows):

select tms_subject, usr_realname, tms_date, m.tms_id, level, aset_id, thr_id, 0,
usr_forename, rt.read_date
from portal.thread_message m, portal.users u, portal.read_thread rt
where for_id=:for_id and u.usr_id=m.usr_id
and rt.usr_id(+)=:usr_id and rt.tms_id(+)=m.tms_id
start with thr_id=:thr_id and tms_parent is null
connect by prior m.tms_id=tms_parent
order siblings by tms_date desc

I'm sorry for not giving you the table definitions but I'm afraid that I won't
make it in my time slice to ask you a question. I will give it in follow-up if
necessary. Thank you in advance.

      and we said...
Just FYI -- once you get to the screen that lets you ask a question, you can
take as long as you like.  I do not have it such that you get rejected after
typing in the question.

I limit the number of people that can get to that screen.  If you got there, you
get to ask a question.  Period.

But anyway, looks like it is the outer join using the old syntax in the connect
by that is causing the issue.  Here are three queries that mimick yours and
return the answer:

scott@ORA920.US.ORACLE.COM> create table t ( deptno number, x int );

Table created.

scott@ORA920.US.ORACLE.COM> variable x number
scott@ORA920.US.ORACLE.COM> exec :x := 1

PL/SQL procedure successfully completed.

scott@ORA920.US.ORACLE.COM>
scott@ORA920.US.ORACLE.COM>
scott@ORA920.US.ORACLE.COM> select ename, dname, t.deptno
  2    from emp left join dept on (emp.deptno = dept.deptno)
  3      left outer join t on (t.deptno=emp.deptno and t.x = :x )
  4   start with mgr is null
  5   connect by prior empno = mgr
  6  /

ENAME      DNAME              DEPTNO
---------- -------------- ----------
KING       ACCOUNTING
CLARK      ACCOUNTING
MILLER     ACCOUNTING
JONES      RESEARCH
SCOTT      RESEARCH
ADAMS      RESEARCH
FORD       RESEARCH
SMITH      RESEARCH
BLAKE      SALES
ALLEN      SALES
WARD       SALES
MARTIN     SALES
TURNER     SALES
JAMES      SALES

14 rows selected.

scott@ORA920.US.ORACLE.COM>
scott@ORA920.US.ORACLE.COM> select ename, dname, t.deptno
  2    from emp, dept, (select * from t where x = :x) t
  3   where emp.deptno = dept.deptno
  4     and t.deptno(+) = emp.deptno
  5   start with mgr is null
  6   connect by prior empno = mgr
  7  /

ENAME      DNAME              DEPTNO
---------- -------------- ----------
KING       ACCOUNTING
CLARK      ACCOUNTING
MILLER     ACCOUNTING
JONES      RESEARCH
FORD       RESEARCH
SMITH      RESEARCH
SCOTT      RESEARCH
ADAMS      RESEARCH
BLAKE      SALES
ALLEN      SALES
JAMES      SALES
WARD       SALES
TURNER     SALES
MARTIN     SALES

14 rows selected.

scott@ORA920.US.ORACLE.COM> select ename, dname, (select t.deptno from t where
t.deptno = emp.deptno and t.x=:x)
  2    from emp, dept
  3   where emp.deptno = dept.deptno
  4   start with mgr is null
  5   connect by prior empno = mgr
  6  /

ENAME      DNAME          (SELECTT.DEPTNOFROMTWHERET.DEPTNO=EMP.DEPTNOANDT.X=:X)
---------- -------------- ------------------------------------------------------
KING       ACCOUNTING
JONES      RESEARCH
SCOTT      RESEARCH
ADAMS      RESEARCH
FORD       RESEARCH
SMITH      RESEARCH
CLARK      ACCOUNTING
MILLER     ACCOUNTING
BLAKE      SALES
ALLEN      SALES
WARD       SALES
JAMES      SALES
TURNER     SALES
MARTIN     SALES

14 rows selected.

scott@ORA920.US.ORACLE.COM>
scott@ORA920.US.ORACLE.COM>

But the one that is syntactically like yours does not:

scott@ORA920.US.ORACLE.COM>
scott@ORA920.US.ORACLE.COM> select ename, dname, t.deptno
  2    from emp, dept, t
  3   where emp.deptno = dept.deptno
  4     and t.x(+) = :x
  5     and t.deptno(+) = emp.deptno
  6   start with mgr is null
  7   connect by prior empno = mgr
  8  /

no rows selected

This could be a bug, or not -- not really sure.  We never were able to join in a
connect by (new in 9i) and I'm not sure what exactly the outcome should be.  It
"looks" like a bug -- but you have two workarounds above.  You could file a tar
on this using this as a testcase to get a bug opened if you like.
Review &
      Followup

      Rating: 5
Took the inline
      view approach  September 28, 2002
Reviewer:
      Piotr Jarmuz  from Braunschweig, Germany
Thank you so much! That was pretty hard! But I wanted to figure out single query
that would slurp all that stuff and now it looks:

select tms_subject, usr_realname, tms_date, x.tms_id, l, aset_id, thr_id, 0,
usr_forename, rt.read_date from
(select u.usr_id, tms_subject, usr_realname, tms_date,
    tms_id, level l, aset_id, thr_id, usr_forename
      from portal.thread_message m, portal.users u
      where for_id=:for_id and u.usr_id=m.usr_id
      start with thr_id=:thr_id and tms_parent is null
      connect by prior tms_id=tms_parent
      order siblings by tms_date desc) x,
portal.read_thread rt
where rt.tms_id(+)=x.tms_id and rt.usr_id(+)=:usr_id

I Spent 60 minutes alone to figure out the `l' alias for level from inner query!
But it feels good now!

If you think it is a bug I might file it to Oracle but... I don't know how and
to whom.

Could you give me a link? Alternatively you could talk to your collegues as you
are be able to explain it to them  much more clearly than I ever could. After
all I am relatively new to relational world and Oracle in particular although
day after day I realize how great software it is!

Best regards.

          Followup:

Do you have support?  metalink.oracle.com is the support website.

September 28, 2002 Ken -- Thanks for the question regarding "FGAC",
      version 9i

      You Asked
I asked some questions previously about FGAC and JAVA and have implemented your
suggestions to me and those others that I found in other threads...for this I
thank you.

The present question is regarding the LOGON trigger and the 'user' tables that
it uses to determine what role to assign to the newly connected user.

In your HR example you use the emp table where the emp # is the determining
factor for the SET_ROLE procedure.  In my instance, we are first determining the
COMPANY which the user is associated with and limiting them only to that data.
This works fine for the majority of the data tables but in these specific 'user'
tables it 'could' be a source of a future security problem.  My problem is that
this situation looks like the MGR or HR person role in you example in that the
user can see the the records in these tables for the company...there is no
facility built into the application to do this at this time, but I want to be
prepared for any possibility.

Can I restrict the selection and update of these "user tables" to ONLY the
procedure that needs to determine the user rights and/or add, edit them?
Obviously these tables would be accessible to a DBA or an ADMINISTRATOR with
appropriate priviledges to add/update/delete users.

I previously used ANO in this type of situation without any of the headaches.

Thanks in advance,
Ken

      and we said...
ANO wouldn't have done this.  ANO (the advanced networking option, now know as
ASO) provided

o data stream encryption for data in motion
o advanced authentication

it did nothing INSIDE the database as far as grants went.

It is trivial to restrict the select and update of these user tables to only the
procedure.  Just don't GRANT on these tables to anyone.  Done.  Thats the way
its always worked.

So, maybe I'm just not understanding the question, cause the answer seems very
plain?

Review &
      Followup

      Rating: 5
FGAC
      priviledges  September 28, 2002
Reviewer:  Ken
      Turnbull  from Vancouver, BC, Canada
Thanks for the real answer.

You are right about ANO/ASO, I forgot that I had the roles designed and in place
before I added ANO.

I was so wrapped up in the complexity of what I had to do, I lost sight of the
basics.  The Development database is wide open.  All users had select
priviledges, including the 'execute any procedure' one that you mentioned
elsewhere.

Five minutes of work (actually the basics) fixed that real fast.  I guess I
really didn't see the forest for the trees.

So thanks again. The FGAC is working correctly and now the business types can
work out what they want the roles to encompass.

September 29, 2002 rajesh -- Thanks for the question regarding
      "count", version 8.1.7

      You Asked
hi  tom

here is two tables day_one and  day_one_temp having same structure.
select count(*) from day_one_day where code ='2130' and accno ='01' and day_dt
='28-aug-2002'
union all
select count(*) from day_one where code ='2130' and accno ='01' and day_dt
='28-aug-2002'

the  above statement gives me
count(*)
2
6
instead i like to obtain the  total count  8 into a varible with out writing a
cursor.  is this is possible?

sincerely

rajesh

rajesh

      and we said...
select sum(cnt)
  from ( select count(*) cnt from day_one_day where code ='2130' and accno ='01'
and day_dt
=to_date('28-aug-2002','dd-mon-yyyy')
union all
select count(*) from day_one where code ='2130' and accno ='01' and day_dt
=to_date('28-aug-2002','dd-mon-yyyy') )

ALWAYS use TO_DATE on a string to compare a string to a date and use a FORMAT.
You'll never be sorry you did, only sorry if you DO NOT.
Review &
      Followup

      Rating: 3
Comparing dates
      to dates  September 22, 2002
Reviewer:  A
      Reader  from BG
Tom,
  Comparing date to a character string also gives correct results. I presume
that you are suggesting dates to dates to avoid Oracle using a TO_CHAR on that
column thus avoiding usage of indexes, if any. Am i correct?
Will comparing date to char return an error anytime? For eg:- the format used
here '28-aug-2002'. Is this format based on NLS_DATE_FORMAT?

regards

          Followup:

I told you -- you will NEVER be sorry you did, only sorry some day if you DON'T.

In this case, the string is automagically and implicitly converted to a date. We
can see that via:

ops$tkyte@ORA920.US.ORACLE.COM> create table t ( x date );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into t values ( sysdate );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select * from t where x = '23-sep-2002';

no rows selected

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> alter session set nls_date_format =
'mm/dd/yyyy';

Session altered.

ops$tkyte@ORA920.US.ORACLE.COM> select * from t where x = '23-sep-2002';
select * from t where x = '23-sep-2002'
                          *
ERROR at line 1:
ORA-01843: not a valid month

But that points out exactly why you'll be sorry some day.  Some day, someone
will change your default date mask.  Now your queries will all fail.  Even
worse, suppose you were expecting a default date mask of:

dd/mm/yyyy

and someone decides:

mm/dd/yyyy

would work better.  Now, think about what happens with your queries.  Some will
run but give the wrong answer (the dates are screwed up), others will fail.

Just be explicit and you'll never have to worry about that again!

      Rating:
      4
count  September 22, 2002
Reviewer:
      rajesg
hi tom

it seems very nice

thanks

rajesh

      Rating: 3
In forms
      6i  September 28, 2002
Reviewer:  sona

hi

the statement shown below causes exception in forms 6i.

select sum(cnt) into cnt
  from ( select count(*) cnt from day_one_day where code ='2130' and accno ='01'

and day_dt
=to_date('28-aug-2002','dd-mon-yyyy')
union all
select count(*) from day_one where code ='2130' and accno ='01' and day_dt
=to_date('28-aug-2002','dd-mon-yyyy') )

does this querry is not supported in forms 6i?

          Followup:

Use dynamic sql then -- or a view.

EXEC_SQL would work
populate_group_from_query would work
a view would work.

if forms cannot handle the syntax.

September 29, 2002 -- Thanks for the question regarding "How to
      delete duplicated check constraints ?", version 8.1.7

      You Asked
Dear Tom,

I found there are a lot of duplication checking constraitns in our database.

How can I delete all of duplicated checking constraints ?

Thanks !

      and we said...
alter table tname drop constraint constraint_name;

do that for each of the duplicated constraints.

You probably got these from doing repeated IMPORTS of dmp files and the you have
"un-named" (named with system generated names) constraints.

You can avoid that by NAMING all constraints.  That way, IMP will not "recreate
them" each time.
September 29,
      2002 Thiruvaiyaru -- Thanks for the
      question regarding "Rollback Segment ", version 8.1.6

      You Asked
Tom,

I run a batch job to delete records from a table and I
get the following error message :-

ERROR at line 1:
ORA-01555: snapshot too old: rollback segment number 1 with name "RBS01" too
small
ORA-06512: at line 20

How can I find out the size of a rollback segment in the database,
and how can I increase the size of it online ?

Thanks in Advance
Sai

Tom,

On your question :-
Are you deleting in a cursor for loop with "commits to save space"??

Answer :-  Yes I am deleting in a cursor for loop with commits.

Thanks,
Sai

      and we said...
Stop doing that.  Its the number one cause of this error.

Search for 1555 on my site, you'll see many examples and the reasons for this
error.

The #1 cause of 1555 is committing in a cursor for loop. You are causing it
yourself.

If you feel you truly must (and I am 100% of the opinion that you do not want
to) commit, alter your logic from something like:

  for x in ( select rowid r from t where <some condition> )
  loop
      delete from t where rowid = x.r;
      cnt := cnt+1;
      if ( cnt = 1000 ) then
            commit;
            cnt := 0;
      end if;
  end loop;

into:

  loop
      delete from t where <some condition> AND rownum <= 1000;
      exit when sql%rowcount = 0;
      commit;
  end loop;
  commit;

followup to comment two below

The second loop keeps NO cursors open across commits.  That is why is avoids the
1555.  It is a cursor opened against a table that you are modifying in a loop
and you commit frequently that causes the 1555.

I said:

<quote>
The #1 cause of 1555 is committing in a cursor for loop. You are causing it
yourself.
</quote>

Not:

the #1 cause of 1555 is commiting in a loop.

It is when you comitt across fetches that you do this to yourself.

followup to comment three

Yours is a different issue.  Yous is the issue that:

"The probability of getting an ORA-1555 with your Query is directly proportional
to the size of your SMALLEST rollback segment"

I really dislike the "have a big rollback segment" approach.  I am a firm
believe that all rollback segments should be created equal -- they should all be
the same size.

You see -- the ORA-1555 you are getting is against the table with 2million
records.  You have other transactions modifying that table while you are reading
it.  They are modifying and commiting their changes.  Lets say that at the
BEGINNING of your run, some process updates the "last row" in the 2million row
table and committs.  Their rollback entry is free to be reused now if that
rollback segment they put their UNDO in wraps around.  You go about processing
the table and eventually get to that last row.  You need to do a consistent read
on it -- you need their UNDO.  Unfortunately, their UNDO went into a small
rollback segment that wrapped a couple of times already.  The UNDO you need is
long gone, you get the ORA-1555.

For you, what you need to do, is to ensure that you have sufficient rollback
pre-allocated in ALL rollback segments so as these rollback segments do NOT get
overwritten during the course of your longest process.

In 9i, this might be a little easier to deal with as you can set an undo
rentention period (however, you must be prepared to have the disk available to
support it!)

My book goes into the ORA-1555 in some detail with examples showing how and when
you will hit it as well.

Review &
      Followup

      Rating:
      4
  July 26, 2001
Reviewer:
      A reader  from Switzerland / Zuerich

      Rating: 2
COMMITs and
      ORA-01555 errors  July 26, 2001
Reviewer:  Logo
      Palanisamy  from Santa Clara, USA
Tom,

I understand that ORA-1555 are caused mainly by the commits in a loop. But how
is the "loop .. exit when .. end loop" different from the "cursor for loop" as
far preventing ORA-1555 error. Both of them commit after every 1000 records.

Won't the "loop .. exit when ..commit end loop" also create the same ORA-1555
error?

      Rating: 4
LOOP
      ...................... END LOOP  July 27, 2001

Reviewer:  Bhawna R  from India
I understand the basic problem with the cursor for loop of the example, this has
to do with refering to the same table for LOOPING condition which is getting
updated within the LOOP. But, we have a job where a cursor is declared for
selecting records from table X within DECLARE section. And, in the procedure
body, there is a LOOP.....END LOOP, where the processing is done. Records are
fetched from the cursor, some records are inserted in 4-5 tables, andrecords are
deleted from 30-35 tables (including X). This job takes around 1 hour plus to
complete, and sometimes it gives ORA-1555. We have used SET TRANSACTION to use
BIG_ROLLBACK segment. There is COMMIT at the end of this (within LOOP), as
otherwise it would give rise to UNABLE TO EXTEND ROLLBACK SEGMENT. This is
actually a purging job and it runs everyday once. Now, we need to add 10 more
tables to it, as a few more modules have been added. Could you advice us how to
handle this situation? The main table (cursor table) has some 2 million records.

      Rating: 4
New
      Idea  July 30, 2001
Reviewer:  Pramodu
       from U.A.E, Middle East
Dear Tom
I was getting this error message, now i can solve my problem

Thanks

      Rating:
      4
Tricks  July 31, 2001
Reviewer:  Anthony
      Lai Cheuk Tung  from Hong Kong SAR
Thank you , Tom, it is a nice trick that we always ignore . May be, apart from
looking into your Wrox book, as a DBA or developer, we are required to log down
our knowledge and share with others. If this forum can be searched by catalogs,
it is excellent.

Work hard , Tom! I have to learn from you and myself as well as from all of
Oracle fans.

Anthony

      Rating:
      2
  August 02, 2001
Reviewer:
      A reader

      Rating: 4
what about
      UNDOTBS in oracle 9i  September 29, 2002
Reviewer:
      Irfan  from Kuwait
Hi Tom,

I am using UNDO tablespace in oracle 9i with undo_management=Auto;
How does it control rollback segment? Does system controls the size of rollback
segments ?

I tried to insert 95000 records in a table in oracle 9i with default
undo_retention. I am still getting ora-1555 and ora-6512. By the way I am doing
commit in cursor for loop. What should I do to resove this error ? should I
increase undo_retention value or use commit outside the loop?

Thanks

          Followup:

Yes, the entire purpose of AUTO is to have Oracle

o decide how many (we'll create new rbs's in this undo tablespace as we need)
o decide how big (based on your retention period)

If you are getting ora-1555, you set the undo retention TOO small -- it needs to
be set for longer then your longest running query to avoid the 1555.

(but YES, YES YES you should commit OUTSIDE THE CURSOR FOR LOOP)!!!!!

The "theory" behind committing inside the cursor for loop goes like this "it'll
save on the amount of rollback segment space I need".  Hopefully you can see how
woefully wrong this is.  YOU NEED that undo in order for your query to proceed.
Yet, when you commit, you released that undo for subsequent reuse -- which since
the undo retention period is small -- WE DID.  Hence, when you increase the undo
retention, you will increase the amount of undo we save, which means all your
commits in the cursor for loop are doing for you are precisely:

o making you run slower
o generating more resources
o removing data integrity from your database

Thats all those frequent commits are accomplishing!!!!

      Rating:
      4
followup  September 29, 2002
Reviewer:
      Irfan  from Kuwait
In your eg you select the rowid from table t and then use that rowid to delete
data from table same table t.
But in our case :
We select from table x and insert into table y;
eg

DECLARE
CURSOR C1 IS
   SELECT * FROM PGIT_POLICY
   WHERE  POL_APPR_STS = 'A'
   AND    POL_CLASS_CODE = '001'
   AND    POL_SEG_03 = '02'
   AND    TO_CHAR(POL_ISSUE_DT,'YYYY') = '2000';

BEGIN

FOR C1REC IN C1
LOOP
INSERT INTO PGITH_POLICY (POLH_SYS_ID)
SELECT
POL_SYS_ID
FROM PGIT_POLICY
WHERE POL_SYS_ID = C1REC.POL_SYS_ID;

INSERT INTO PGITH_POL_ADDL_INFO (PAIH_SYS_ID)
SELECT
PAC_SYS_ID
FROM  PGIT_POL_APPL_CURR
WHERE PAC_POL_SYS_ID   = C1REC.POL_SYS_ID  ;

like this there are another 8 tables.

COMMIT;
END LOOP;
END;

So here our tables from where we select is different and table where we insert
is different. Do u still think putting COMMIT in this kind of FOR LOOP is wrong
and it will generate ora - 1555.

          Followup:

yes.  see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:895410916429
for example -- there I query a read only tablespace (read only) and generate the
1555 all by myself by committing in the cursor for loop.

Only commit in the cursor for loop if:

a) you want to waste resources (generate more redo, waste cpu cycles)
b) you want to run slower
c) you want to lose data integrity

If any of those bother you -- don't commit in the cursor for loop!

September 29, 2002 -- Thanks for the question regarding "what is the
      correct answer?", version 9i

      You Asked
Hi,
It is a question for Oracle9i Self Test Software, I am not satisfied with thw
answer that Is why I need your help. Question is as follows:-

You maintain a partitioned table  named ORDER. You issue following query;

ALTER TABLE ORDER
DROP PARTITION P1
UPDATE GLOBAL INDEXES
PARALLEL (DEGREE 3);

Which statement represents the result of result of including UPDATE GLOBAL
INDEXES clause?

A) ALL GLOBAL INDEXES ON THE ORDER TABLE WILL REMAIN INTACT THROUGHOUT THE DDL
OPERATION.
B) OTHER DML STAEMENTS CAN CONTINUE USE THE ANY GLOBAL B-TREE INDEXES WITHOUT
INTERRUPTION.
C) AFTER P1 IS DROPPED, ALL GLOBAL B-TREE AND FUNCTION-BASED INDEXES ARE
AUTOMATICALLY REBUILT.
D) AFTER P1 IS DROPPED, ALL GLOBAL INDEXES MARKED UNUSABLE WILL BE AUTOMATICALLY
REBUILT.

In my opinion the answer should be option (D) but it says that the correct
answer is (B).

now you please tell me what is the right answer?
Thanks A Lot.
Natalia

      and we said...
right from the docs on the ALTER TABLE command:

update_global_index_clause

When you perform DDL on a table partition, if a global index is defined on
table, then Oracle invalidates the entire index, not just the partitions
undergoing DDL. This clause lets you update the global index partition you are
changing during the DDL operation, eliminating the need to rebuild the index
after the DDL.

UPDATE GLOBAL INDEXES Specify UPDATE GLOBAL INDEXES to update the global indexes
defined on table.

INVALIDATE GLOBAL INDEXES Specify INVALIDATE GLOBAL INDEXES to invalidate the
global indexes defined on table. If you specify neither, then Oracle invalidates
the global indexes.
....

The update global indexes maintains the indexes in real time during the DDL
operation.

(b) is correct (but I'm assuming you've made some typos in there as the syntax
of the sentence in (b) is non-sensible)

(d) is definitely NOT correct.
Review &
      Followup

      Rating:
      4
  September 30, 2002
Reviewer:
      A reader
Thanks
Now it is clear to me.
Natalia

September 30, 2002 -- Thanks for the question regarding "Use of
      xpath, support for namespaces", version 8.1.7

      You Asked
I am unable to use xpath since i have read somewhere that 9i does not support
namespaces. Is this true ? What is the workaround?

Thanks.

      and we said...
I asked Sean Dillon, our local XML guru, to take a look at this and here's what
he said:
--

In Oracle9i release 1, you could not specify namespaces in XMLType functions.
You can submit XPATH queries and you will get result whether or not they are in
the default namespace or not.  In release 2, this limitation has been lifted.
Consider the following:

In a 9i release 1 database:
==================================================
SQL> connect sdillon/sdillon
Connected.

SQL> create table xml_documents(my_xml sys.xmltype);
Table created.

SQL> insert into xml_documents values (
  2    sys.xmltype.createxml('<?xml version = ''1.0''?>
  3  <ROWSET>
  4     <ROW num="1">
  5        <EMPNO>7369</EMPNO>
  6        <ENAME>SMITH</ENAME>
  7        <JOB>CLERK</JOB>
  8        <MGR>7902</MGR>
  9        <HIREDATE>12/17/1980 0:0:0</HIREDATE>
 10        <SAL>800</SAL>
 11        <DEPTNO>20</DEPTNO>
 12     </ROW>
 13     <ROW num="2">
 14        <EMPNO>7499</EMPNO>
 15        <ENAME>ALLEN</ENAME>
 16        <JOB>SALESMAN</JOB>
 17        <MGR>7698</MGR>
 18        <HIREDATE>2/20/1981 0:0:0</HIREDATE>
 19        <SAL>1600</SAL>
 20        <COMM>300</COMM>
 21        <DEPTNO>30</DEPTNO>
 22     </ROW>
 23  </ROWSET>'));
1 row created.

SQL> -- now an xml doc w/ a different namespace
SQL> insert into xml_documents values (
  2    sys.xmltype.createxml('<?xml version = ''1.0''?>
  3  <ROWSET xmlns="www.oracle.com/asktom.xml">
  4     <ROW num="1">
  5        <EMPNO>7369</EMPNO>
  6        <ENAME>SMITH</ENAME>
  7        <JOB>CLERK</JOB>
  8        <MGR>7902</MGR>
  9        <HIREDATE>12/17/1980 0:0:0</HIREDATE>
 10        <SAL>800</SAL>
 11        <DEPTNO>20</DEPTNO>
 12     </ROW>
 13     <ROW num="2">
 14        <EMPNO>7499</EMPNO>
 15        <ENAME>ALLEN</ENAME>
 16        <JOB>SALESMAN</JOB>
 17        <MGR>7698</MGR>
 18        <HIREDATE>2/20/1981 0:0:0</HIREDATE>
 19        <SAL>1600</SAL>
 20        <COMM>300</COMM>
 21        <DEPTNO>30</DEPTNO>
 22     </ROW>
 23  </ROWSET>'));
1 row created.

SQL> -- notice the second row of the result set has the fully qualified name:
SQL> select rownum, x.my_xml.extract('/ROWSET/ROW/EMPNO').getClobVal() empno
  2    from xml_documents x;

    ROWNUM EMPNO
---------- -----------------------------------------------------------------
         1 <EMPNO>7369</EMPNO>
           <EMPNO>7499</EMPNO>

         2
<www.oracle.com/asktom.xml:EMPNO>7369</www.oracle.com/asktom.xml:EMPNO>

<www.oracle.com/asktom.xml:EMPNO>7499</www.oracle.com/asktom.xml:EMPNO>
==================================================

In a 9i release 2 database:
==================================================
SQL> connect sdillon/sdillon
Connected.
SQL> create table xml_documents(my_xml sys.xmltype);
Table created.

SQL> insert into xml_documents values (
  2    sys.xmltype.createxml('<?xml version = ''1.0''?>
  3  <ROWSET>
  4     <ROW num="1">
  5        <EMPNO>7369</EMPNO>
  6        <ENAME>SMITH</ENAME>
  7        <JOB>CLERK</JOB>
  8        <MGR>7902</MGR>
  9        <HIREDATE>12/17/1980 0:0:0</HIREDATE>
 10        <SAL>800</SAL>
 11        <DEPTNO>20</DEPTNO>
 12     </ROW>
 13     <ROW num="2">
 14        <EMPNO>7499</EMPNO>
 15        <ENAME>ALLEN</ENAME>
 16        <JOB>SALESMAN</JOB>
 17        <MGR>7698</MGR>
 18        <HIREDATE>2/20/1981 0:0:0</HIREDATE>
 19        <SAL>1600</SAL>
 20        <COMM>300</COMM>
 21        <DEPTNO>30</DEPTNO>
 22     </ROW>
 23  </ROWSET>'));
1 row created.

SQL> -- again, an xml document with a different namespace
SQL> insert into xml_documents values (
  2    sys.xmltype.createxml('<?xml version = ''1.0''?>
  3  <ROWSET xmlns="www.oracle.com/asktom.xml">
  4     <ROW num="1">
  5        <EMPNO>7369</EMPNO>
  6        <ENAME>SMITH</ENAME>
  7        <JOB>CLERK</JOB>
  8        <MGR>7902</MGR>
  9        <HIREDATE>12/17/1980 0:0:0</HIREDATE>
 10        <SAL>800</SAL>
 11        <DEPTNO>20</DEPTNO>
 12     </ROW>
 13     <ROW num="2">
 14        <EMPNO>7499</EMPNO>
 15        <ENAME>ALLEN</ENAME>
 16        <JOB>SALESMAN</JOB>
 17        <MGR>7698</MGR>
 18        <HIREDATE>2/20/1981 0:0:0</HIREDATE>
 19        <SAL>1600</SAL>
 20        <COMM>300</COMM>
 21        <DEPTNO>30</DEPTNO>
 22     </ROW>
 23  </ROWSET>'));
1 row created.

SQL> -- notice the second row has no data due to the namespace
SQL> select rownum, x.my_xml.extract('/ROWSET/ROW/EMPNO').getClobVal() empno
  2    from xml_documents x;

    ROWNUM EMPNO
----------
----------------------------------------------------------------------
         1 <EMPNO>7369</EMPNO>
           <EMPNO>7499</EMPNO>

         2

SQL> -- but now we can use a second parameter in the EXTRACT() function to
SQL> -- specify a namespace for our XPath expression:
SQL> select rownum,
  2         x.my_xml.extract('/ROWSET/ROW/EMPNO',
  3            'xmlns="www.oracle.com/asktom.xml"').getClobVal() empno
  4    from xml_documents x;

    ROWNUM EMPNO
---------- -------------------------------------------------------------------
         1
         2 <foo:EMPNO xmlns:foo="www.oracle.com/asktom.xml">7369</foo:EMPNO>
           <foo:EMPNO xmlns:foo="www.oracle.com/asktom.xml">7499</foo:EMPNO>

This second parameter applies to all the 9i release 2 XMLType functions, such as
EXTRACT(), EXTRACTVALUE(), EXISTSNODE(), and UPDATEXML().

There is namespace support THROUGHOUT the XML Developer's Kit as well, but
considering your question the XMLType XPATH/namespace support was what you were
interested in.  For more information about namespace support in the Oracle XML
Developer's Kits (XDKs), see the Oracle Technology Network (OTN) at
http://otn.oracle.com/tech/xml/content.html
(You need an account to use OTN, but the accounts are free you just need to take
the few minutes to register an account here:
http://otn.oracle.com/membership
Hope that helps!
____________________________________________________________________
Sean Dillon - Senior Technologist, Oracle Corporation
Author "Beginning Oracle Programming"
http://www.amazon.com/exec/obidos/ASIN/186100690X
Review
      & Followup

      Rating: 2
Getting data
      from XML file and Saving in database  September 30, 2002

Reviewer:  Chaitanya Sravanth P  from India

I've a scenario for which I need help.
My client will be sending his data through an XML file. Those XML files will be
uploaded by our applications admin to a particular folder and the path to that
folder is stored in the database. What I should do is, basing the path
specified, I should go to the XML file location, search for the data inside
those files and extract them and store in a seperate table (different values in
different columns). I'm new to the SQL - XML concept. Can you please guide me
regd. this.

          Followup:

goto otn.oracle.com

click on technologies -> XML

lots of stuff there.  Tons of it.

September 30, 2002 Hank -- Thanks for the question regarding "How to
      use NVL for this case?", version 8.1.7

      You Asked
Hi,Tom
I have such SQL statement in procedure:
SELECT sum(amount)
  INTO v_discounted
  FROM claimcost
 WHERE emp_id = v_empid AND
       claim_id = v_claimid AND
       to_char(costoccurred,'YYYY') = to_char(v_accidentdate,'YYYY')
 GROUP BY emp_id,claim_id;
If there's no record to meet the where clause,there will be
error:no_data_found,in this case,I want to assign 0 to v_discounted.
I tried to use this way:
v_discounted := NVL((the above sql statement),0);
but it didn't work.In order to solve this problem, I have to define another
variable v_rowcnt,and did the following way:
SELECT count(*)
  INTO v_rowcnt
  FROM claimcost
 WHERE emp_id = v_empid AND
       claim_id = v_claimid AND
       to_char(costoccured,'YYYY') = to_char(v_accidentdate,'YYYY');
IF v_rowcnt > 0 THEN
   SQL statement;
ELSE
   v_discounted := 0;
END IF;
I know it is a stupid method,please give me a good solution.
Thanks in advance.

      and we said...
Just code:

SELECT nvl(sum(amount),0)
  INTO v_discounted
  FROM claimcost
 WHERE emp_id = v_empid AND
       claim_id = v_claimid AND
       to_char(costoccurred,'YYYY') = to_char(v_accidentdate,'YYYY');

then.  You do not need (or really what) a group by with a select into.  An
aggregate without a group by will return

o at most one row
o at least one row

and the sum will be zero!!!

No exception block needed.  Using your query it would be:

begin
SELECT sum(amount)
  INTO v_discounted
  FROM claimcost
 WHERE emp_id = v_empid AND
       claim_id = v_claimid AND
       to_char(costoccurred,'YYYY') = to_char(v_accidentdate,'YYYY')
 GROUP BY emp_id,claim_id;
exception
 when no_data_found then v_discounted := 0;
end;


Subject | Article | Last Updated (East coast time)
truncating spaces in a char value | article only | Oct 01, 2002 08:47 am
rollback segment | article only | Oct 01, 2002 09:04 am
Gathering Statistics | article only | Oct 01, 2002 09:50 am
Process Diagram | article only | Oct 01, 2002 09:57 am
First and rest... | article only | Oct 01, 2002 09:59 am
Oracle8i new features: Sort and Top-N queries | article only | Oct 01, 2002 10:10 am
Dependent drop down lists in Pl/SQL cartridge | article only | Oct 02, 2002 09:21 am
Long Raw Datatypes | article only | Oct 02, 2002 09:32 am
Rollback Segments -- negative values in v$rollstat | article only | Oct 02, 2002 09:38 am
Indexing a variable length field | article only | Oct 02, 2002 09:42 am
Date Field as Primary Key ... | article only | Oct 02, 2002 09:56 am
Schemas/Data Files | article only | Oct 02, 2002 10:22 am
How RBS stores Insert | article only | Oct 02, 2002 10:39 am
Pin SQL Cursors | article only | Oct 02, 2002 07:29 pm
Use of FGAC in multiple session applications | article only | Oct 02, 2002 07:33 pm
Mutating Tables | article only | Oct 02, 2002 07:35 pm
ABOUT PRIVILIGES | article only | Oct 03, 2002 10:40 am
Top most things a startup dba should be proficient | article only | Oct 03, 2002 06:41 pm
Sharing complex query parts - Views vs PL/SQL Functions vs Packaged Cursors | article only | Oct 03, 2002 06:54 pm
how to set the order of the evaluation of the where clause | article only | Oct 03, 2002 07:08 pm
Grant on create | article only | Oct 03, 2002 07:24 pm
ordering the result | article only | Oct 03, 2002 07:29 pm
analytical function (lag, sum etc) | article only | Oct 04, 2002 08:17 am
dead connections | article only | Oct 04, 2002 08:18 am
rollback segments | article only | Oct 04, 2002 08:29 am
Reducing size of Temporary Tablespace,"TEMP" | article only | Oct 04, 2002 08:34 am
Sequence in Application | article only | Oct 04, 2002 06:34 pm
rman register database | article only | Oct 04, 2002 07:07 pm
Table Attributes and Execution Plans | article only | Oct 04, 2002 07:10 pm
To check logical dump file for corruption | article only | Oct 04, 2002 07:35 pm
How to get rid of the message 'Input truncated to n characters' | article only | Oct 05, 2002 10:59 am
Oracle hot backup | article only | Oct 05, 2002 11:03 am
pl/sql tables, varrays, global temporary tables | article only | Oct 05, 2002 11:12 am
Recovery | article only | Oct 05, 2002 11:19 am
Last Balance | article only | Oct 05, 2002 11:21 am
problem with parameter | article only | Oct 05, 2002 12:49 pm
Getting Cumulative sum on a field | article only | Oct 05, 2002 12:56 pm
Oracle Cursors | article only | Oct 05, 2002 01:36 pm
Upgrading 816 to 817 | article only | Oct 05, 2002 04:42 pm
Select while sum() < x | article only | Oct 06, 2002 09:32 am
Import Procedures, functions and packages | article only | Oct 06, 2002 09:48 am
DB_CACHE_ADVICE | article only | Oct 06, 2002 10:24 am
sequence next value too large for the column | article only | Oct 06, 2002 10:53 am
SQL tuning for retrieval from a large table | article only | Oct 06, 2002 11:16 am
Update Queries with Multiple Tables to Join | article only | Oct 06, 2002 02:10 pm
Regarding On Update Cascade | article only | Oct 07, 2002 07:38 am
bind variable | article only | Oct 07, 2002 08:18 am
A Few Questions | article only | Oct 07, 2002 09:07 am
Creating table using select statement | article only | Oct 07, 2002 09:22 am
lost dbms_output, http output over db link? | article only | Oct 07, 2002 10:47 am
Refresh "complex" materialized view with a trigger | article only | Oct 07, 2002 01:05 pm
An outer join or a function call in the select statement? | article only | Oct 07, 2002 01:06 pm
To generate a queryable & updateable form with fields from three different tables | article only | Oct 07, 2002 01:13 pm
Accessing Files from DB | article only | Oct 07, 2002 06:49 pm
User environment | article only | Oct 07, 2002 06:52 pm
row(s) 1 - 55 of 55
October 01, 2002 mohd -- Thanks for the question regarding "truncating spaces in a char value", version 7.3
You Asked
hello sir,

I have a doubt ? This may be a basic one, but I am unable to
find alternative.  I have a primary key field of type varchar2.
It is distinguishing values say '507-a', '507-a ',etc as
seperate values.  I would like to know is it possible to get rid
of space or spaces of a varchar value before inserting into a
table or retrieving from a table.  Please let me know all the
possible ways.  I will be verymuch thank full to you.

best regards
abdulla
and we said...
Well, it depends on how you BIND the variable on input whether
we blank trim or not. What will ALWAYS work however is to either
code:

   insert into t values ( ..., RTRIM(:THAT_COLUMN), .... );

That is -- use RTRIM on the insert statement.  A better method
perhaps that works without updating the applications would be
to:

create or replace trigger TRIGGER_NAME
before insert or update of THAT_COLUMN on THAT_TABLE
for each row
begin
   :new.THAT_COLUMN := rtrim(:new.THAT_COLUMN);
end;
/

That'll ensure that all inserted rows are blank trimmed for you.
Review & Followup
Rating: 2
TRIM in PRO*C May 14, 2002
Reviewer: Kalpesh from USA
Hi Tom,
I have declared two variables like this in Pro*C code.
EXEC SQL BEGIN DECLARE SECTION;
int strpos;
char  symbol_list_corp[100][25];
char  sym_corp[25];

After that I am doing following thing:

EXEC SQL SELECT RTRIM(CONCAT(RTRIM(:symbol_list_corp[j]),'.O')) into :sym_corp
from DUAL;
EXEC SQL SELECT LENGTH(:sym_corp) into :str_len from DUAL;
 EXEC SQL INSERT into tkdaily.tbnewsym values(:sym_corp);
 EXEC SQL COMMIT;

It is giving error:
ORA-01401: inserted value too large for column

TBNEWSYM is
SYMBOL_NAME   VARCHAR2(25)

When I have printed the symbol length in log it is giving
length = 254

What could be the reason Tom ?
I am trimming the variable also then why it is happening.
Please let me know.
Thanks a lot

Followup:
well.... without the code that fills in symbol_list_corp -- i can only guess
there is a bug in your c code somewhere.

Seems it would be trivial to supply an entire test case (along with versions,
platforms, compile options - you know - the stuff we need to see whats wrong)...

Rating: 3
Trimming Spaces between words? September 30, 2002
Reviewer: Martin from Amsterdam, NL
Tom,

Is there any posibility to convert 'this is a  line of text' (two spaces between
a and line) to 'this is a line of text'? The difficulty is I want replace any
number of spaces between two words with a single space. Do you know of a (easy)
way to do this?

My main goal is to find out that the sentence contains ' a line ' where (any
number of) unnecessary spaces should be ignored. So,I  need to know if 'a' and
'line' appear directly next to each other as seperate words.

I think this is not that easy but you've surprised me often enough, so i'll give
it a try ;)

Followup:
Well, an intermedia text index would be my first choice -- it removes all kinds
of issues from text searches, including the above issue.
http://otn.oracle.com/docs/products/oracle8i/doc_library/817_doc/inter.817/a77063/toc.htm

We can use multiple replaces

  replace( replace( replace( replace( string, '  ', ' ' ), '  ', ' ' ) .... )

that will change 2 spaces to one, then two to one then two to one and so on (so,
if you had:

hello.....world

where the dots were a space,

ops$tkyte@ORA817DEV.US.ORACLE.COM> select replace( replace( replace(
'Hello.....world', '..', '.' ), '..', '.' ), '..', '.' )
  2    from dual;

REPLACE(REP
-----------
Hello.world

it would do it -- but if you had an ARBITRARY number of spaces:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select replace( replace( replace(
'Hello.................world', '..', '.' ), '..', '.' ), '..', '.' )
  2    from dual;

REPLACE(REPLA
-------------
Hello...world

it would not.  You would be having to write a "parser" at that point.

October 01, 2002 ted -- Thanks for the question regarding "rollback segment", version 8.1.6
You Asked
thomas,

1. chap 6 rbs snapshot too old, there are 3 cases of 1555 are listed
A. rbs indeed too small
B. fetch across commits
C.delayed block cleanout.

there two causes as listed by oracle doc
a. rbs itself gotten rewritten
b. transaction slot in tbs transaction table got rewritten

Qestion: How are A.B.C. above related to a.b such as

            A.      B.      C.
           ----     ---     ----
 causes     a       ab       b

2. when rbs wrap around, oracle will wrap to next extend even the next extent
contain active trasaction on it. Is this correct or it will only use space
belongs to inactive transaction?

3. page 190 example :

begin
  for x in (select        ...

  loop
     if
        commit;
        set transaction use
     end if;

     update t

  end loop
  commit;
end;

"At some point in time, we will revist a block with our query
that was modified with our update"

question:
If for loop cursor open one time (got all the qualified rows), when
and why revisiting would taken place ?
Is it true when a cursor is open all qualified rows are in the cursor ?

thnx as always,
ted chyn
and we said...
1) Well, as I said in the book - case b is really a special case of a but one
that gets people all of the time so I treated it special.  A and B are both
caused by "rbs itself got rewritten".  My case C is the transaction slot
overwrite.

2) We can only wrap to an extent that has NO active transaction in it.  If the
extent we want to wrap into has an active transaction, we attempt to EXTEND the
rollback segment adding a new, empty extent between the current and the one we'd
like to wrap into.

3) No, it is not true that the rows are "all in the cursor".  Consider this:

select * from a_one_billion_row_table

We do not "pre-fetch" the data anywhere.  We process and find the data as we go
along.  If you fetched just one row from that query, we do 1 IO to get it.  If
you fetch all billion records, we do lots more IO.  The bottom line is -- we do
NOT pre-answer queries in general (when we don't have to).  Thats why a query
such as "select from really_big_table" can start returning rows immediately.

The OPEN typically does very little work, the FETCH -- it does the work as we go
along.
Review & Followup
Rating: 3
September 30, 2002
Reviewer: Sagi from India
Hi Tom !

One of the reason for ORA-1555 is to small rollback segment. Basically this is
encountered when we try to over right during the wraps's. Right?

My doubt is that say when the the RBS is trying to WRAP and if that extent has
an active transaction then it would try to alloacate one more extent. If it
reaches the maximum extents during this process, then we encounter the error
"ORA-01562:".

So where is the question of over right?

It would be of great help if you could give us an example of it.please.

Thanx in advance.

Regards,
Sagi

Followup:
Syntax error line 2: Tom cannot understand the question -- parse failure.

"so where is the question of over right?"????

1555 happens when we overwrite UNDO (wrap into it -- there are no active
transactions in this example, you've been committing in a cursor for loop
remember) we need for a query.

If we WRAP and you needed the stuff we overwrite -- ora 1555.

Rating: 3
September 30, 2002
Reviewer: Sagi from India.
Hi Tom!

You said

"1555 happens when we overwrite UNDO (wrap into it -- there are no active
transactions in this example, you've been committing in a cursor for loop
remember) we need for a query.  "

I am getting confused. When there was no active transaction
then what UNDO could be there, which we might be needing, which in turn causes
ORA-1555.

I mean in simple:
  when no active transaction then
        no undo information  then
             where is the question of requesting for
             UNDO and encountering the ORA-1555

Thanx in Advance.

Sagi

Followup:
you query:

   for x in ( select rowid, * from t )  <<=== the results of this are FIXED in
time
                                       we will use the RBS to reconstruct the
                                       data in the table as we need to
   loop
       update t set x = y where rowid = x.rowid;
       commit;  <<<=== here you are releasing the undo for table T you just
                       generated.
   end loop

The select you are running may well need the undo you generated however you
released it.

If you have my book "Expert one on one Oracle" I spend a large amount of time on
this very topic and many others.

October 01, 2002 Ik -- Thanks for the question regarding "Gathering Statistics", version 8.1.7
You Asked
Tom,
    Can you please guide as to how to interpret the output of autotrace
statistics.
   Taking the case of a query (SELECT * FROM table) in SQL Plus, consistent gets
is a function of (Rows returned / Arraysize of SQL Plus) + Number of Blocks.
This is termed as logical IO. And Physical reads is Physical IO. What is the
difference between the two.
   I remember reading in one of your answers that consistent gets going high
should be interpreted as 'Your query requires tuning'. Why is that so?
But, when the same query is executed second time Consistent gets changes. Why is
that?

Thank you in advance for your patience and for providing the most effective
solutions.

regards
and we said...
Physical IO is IO performed to disk.  A read to put a block into the buffer
cache.

Consistent gets is the number of reads from the buffer cache.  A phyisical IO
will always result in a consistent get as well (physical io to fill the buffer
cache, consistent get to read the buffer out of the cache).

You'll want to goto www.hotsos.com and read "Are you still using cache hit
ratios?" to find out why high logical IOs implies your query needs tuning --
well written in depth paper on that very subject.

Since consistent gets includes reads to the rollback segments to provide for
consistent reads -- a query can have different consistent gets every time it is
executed in an active system -- if others are updating the table.  Also, if you
do lots of recursive sql (due to a hard parse for example) their consistent gets
are added into yours.
Review & Followup
Rating: 5
Physical Read from Autotrace September 30, 2002
Reviewer: A Reader from USA
Hi Tom,
I have three identical tables (each has about 1m rows).  When I run a query like
"SELECT COUNT(*) FROM T", I get much different speed.  T1 returns result in less
than 1 second, T2 returns result in about 4 seconds, T3 returns results in more
than 1 minute.  The tables all have indexes in ID column (T1 has non-unique
index, the other two have unique indexes). I run a excution plan using autotrace
and notice that T1 has no physical read, while T3 (the slowest one) has tons of
physical reads and consistant gets.  Can you please advise where is the possible
problem for T3 and where should I start to tune on T3?
Thank you for your help in advance.

Followup:
how about the plans.

you should be using CBO, the tables should be analyzed, in which case, we would
find any index on the table that included a column that is NOT NULL and would
have fast full scanned that instead of the table.

T1 -- could have been in the cache
T2 -- less in the cache
T3 -- no more room in the inn.  out of cache.

October 01, 2002 chirayu -- Thanks for the question regarding "Process Diagram", version 8.1.7
You Asked
Dear Tom,

  I visit your site on a daily basis ( bread and butter sort of thing !).  In
one of postings you had mentioned.

<quote>

Show me you know what
processes/threads there are, what their jobs are.  Describe all of the files
involved in the database, what they do, what they mean.  Show how they interact

<unquote>

I do not have access to the documentation, can you let me know from where can I
get this info. I really want to understand the core of Oracle and what better
place to ask for help then this site.
and we said...
If you have access to asktom and read it frequently -- then you should know by
now that you have 100% access to all documentation!!!!!

All you need is a web browser.  Point it to otn.oracle.com, click on
documentation and start reading (read the CONCEPTS guide under the database
stuff first)
Review & Followup
Rating: 4
I know it September 30, 2002
Reviewer: Chirayu Sutaria from Ahmedabad, India
Dear tom,

  I know it, but I have a very limited access to
  the net.  What I wanted to know from you is the
  specific URLs for the process diagrams and all
  the other stuff that you would expect from a
  seasoned URL.

  The documentation set is too lengthy for my limited
  access to the net.  Hope you can guage the nature of
  my problem and help me accordingly

Followup:
download the pdf version then and read it at your leisure.

The problem is that there isn't a page, or 5 pages or 6 pictures you need to
know.

Every page of the concepts guide is as important at the one in front of it and
the one that follows it.

All of the pdfs are on otn, you can even order a documentation CD if you wish.

Rating: 5
hope this helps October 01, 2002
Reviewer: murali from india
http://otn.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c01intro.htm#18748

October 01, 2002 Cesar -- Thanks for the question regarding "First
      and rest...", version 8.1.6

      You Asked
Hi Tom, I need your help again.

Look this:

data_values
------------------------
month         date
comment       varchar2(100)
cost          number(12,2)

Example records:

DATE_PROCESS   COMMENT          COST
--------------------------------------
01-JUL-01      A                100
02-JUL-01      B                50
03-JUL-01      A                20
01-JUL-01      D                5
04-AUG-01      A                60
05-AUG-01      C                10
05-AUG-01      D                1
01-SEP-01      A                1
01-SEP-01      B                30
05-SEP-01      C                500
06-SEP-01      B                70
07-SEP-01      D                1
...

The view that I want must have 3 record:

Record 1: The [comment] with the first top value of sum on SEP
Record 2: The [comment] with the second top value of sum on SEP
Record 3: Sum of all others comments by month

                  (sum JUL)  sum(AUG)   sum(SEP)
COMMENT           MONTH01    MONTH02    MONTH03 ....
-----------------------------------------------------
C                 0          10         500
B                 50         0          100
REST              125        61         2

And again I get that with this query, but it don't like me... Can you help me?

select
 comment,
 m1,
 m2,
 m3
from
(
  select
    comment,
    sum(decode(trunc(date_process, 'month'),
      add_months(trunc(sysdate, 'month'), -3), cost, 0)) m1,
    sum(decode(trunc(date_process, 'month'),
      add_months(trunc(sysdate, 'month'), -2), cost, 0)) m2,
    sum(decode(trunc(date_process, 'month'),
      add_months(trunc(sysdate, 'month'), -1), cost, 0)) m3
  from data_values
  group by comment
  order by 4 desc
) d
where rownum <= 2
union all
select
 'REST',
 t.m1 - f.fm1,
 t.m2 - f.fm2,
 t.m3 - f.fm3
from
(
  select
    sum(m1) fm1,
    sum(m2) fm2,
    sum(m3) fm3
  from
  (
    select
      comment,
      sum(decode(trunc(date_process, 'month'),
        add_months(trunc(sysdate, 'month'), -3), cost, 0)) fm1,
      sum(decode(trunc(date_process, 'month'),
        add_months(trunc(sysdate, 'month'), -2), cost, 0)) fm2,
      sum(decode(trunc(date_process, 'month'),
        add_months(trunc(sysdate, 'month'), -1), cost, 0)) fm3
    from data_values
    group by comment
    order by 4 desc
  )
  where rownum <= 2
) f,
(
  select
    sum(decode(trunc(date_process, 'month'),
      add_months(trunc(sysdate, 'month'), -3), cost, 0)) m1,
    sum(decode(trunc(date_process, 'month'),
      add_months(trunc(sysdate, 'month'), -3), cost, 0)) m2,
    sum(decode(trunc(date_process, 'month'),
      add_months(trunc(sysdate, 'month'), -3), cost, 0)) m3
  from data_values
) t

I know that you can do it...

Thanks!!!

      and we said...
scott@ORA717DEV.US.ORACLE.COM> select decode(b.r, 1, t.comm, 2, t.comm,'REST'),
  2         nvl( b.r, 3 ),
  3         sum( decode( months_between(trunc(date_process,'mm'),:start_month),
  4                 -2, t.cost, 0 ) ) m01,
  5         sum( decode( months_between(trunc(date_process,'mm'),:start_month),
  6                 -1, t.cost, 0 ) ) m02,
  7         sum( decode( months_between(trunc(date_process,'mm'),:start_month),
  8                 0, t.cost, 0 ) ) m03
  9  from t,
 10  ( select rownum r, a.*
 11     from ( select comm, sum(cost) cost
 12             from t
 13            where date_process
 14                    between to_date( :start_month )
 15                        and last_day(to_date( :start_month ))
 16            group by comm
 17            order by cost desc NULLS LAST
 18         ) a
 19    where rownum <= 2
 20  ) b
 21  where t.comm = b.comm(+)
 22  group by decode( b.r, 1, t.comm, 2, t.comm, 'REST' ), nvl( b.r, 3 )
 23  order by nvl( b.r, 3 )
 24  /

DECO NVL(B.R,3)        M01        M02        M03
---- ---------- ---------- ---------- ----------
C             1          0         10        500
B             2         50          0        100
REST          3        125         61          2

probably needs an explaination doesn't it....

Well, this part of the query:

scott@ORA717DEV.US.ORACLE.COM> select rownum r, a.*
  2     from ( select comm, sum(cost) cost
  3             from t
  4            where date_process
  5                    between to_date( :start_month )
  6                        and last_day(to_date( :start_month ))
  7            group by comm
  8            order by cost desc NULLS LAST
  9         ) a
 10    where rownum <= 2
 11  /

         R C       COST
---------- - ----------
         1 C        500
         2 B        100

returns the highest and next highest (along with the numbers 1 and 2).  These
picked our highest two comments for the month in question (:start_month -- our
bind variable).

Now, we OUTER join that subresult to the original table to "rank" each row with
a 1, 2, or 3 and also to break out the columns m01, m02, m03:

scott@ORA717DEV.US.ORACLE.COM> select decode( b.r, 1, t.comm, 2, t.comm, 'REST'
),
  2         nvl( b.r, 3 ),
  3         (decode( months_between(trunc(date_process,'mm'),:start_month),
  4                 -2, t.cost, 0 ) ) m01,
  5         ( decode( months_between(trunc(date_process,'mm'),:start_month),
  6                 -1, t.cost, 0 ) ) m02,
  7         ( decode( months_between(trunc(date_process,'mm'),:start_month),
  8                 0, t.cost, 0 ) ) m03
  9  from t,
 10  ( select rownum r, a.*
 11     from ( select comm, sum(cost) cost
 12             from t
 13            where date_process
 14                    between to_date( :start_month )
 15                        and last_day(to_date( :start_month ))
 16            group by comm
 17            order by cost desc NULLS LAST
 18         ) a
 19    where rownum <= 2
 20  ) b
 21  where t.comm = b.comm(+)
 22  /

DECO NVL(B.R,3)        M01        M02        M03
---- ---------- ---------- ---------- ----------
REST          3          0          0          0
REST          3          0          0          0
REST          3        100          0          0
REST          3         20          0          0
REST          3          0         60          0
REST          3          0          0          1
B             2         50          0          0
B             2          0          0         70
B             2          0          0         30
C             1          0         10          0
C             1          0          0        500
REST          3          5          0          0
REST          3          0          1          0
REST          3          0          0          1

14 rows selected.

(you could add a where clause to this on the process_date to ensure you only get
and t.date_process between
  add_months(:start_month, -2 )
  and
  last_day( :start_month )

if you want...)

Now, that we have that -- its a simple matter of a sum/group by and order by to
get the final answer.
Review & Followup

      Rating:
      5
!  October 26, 2001
Reviewer:  Cesar
      Salas  from Veracruz, Mexico
Thanks!!!

      Rating: 5
Not bad
      ;-)  April 23, 2002
Reviewer:  Igor
       from France

      Rating: 4
Add
      question  October 01, 2002
Reviewer:  Chris

Tom,
how to count days in a given month.If I pass x=1, then it will say that this
month is january and it has 31 days. If I pass x=2 then it will said the month
is february and it has 28 days.

          Followup:

select to_number( to_char( last_day( to_date( :x, 'mm' ) ), 'dd' ) ) from dual;

ops$tkyte@ORA920.US.ORACLE.COM> variable x number

ops$tkyte@ORA920.US.ORACLE.COM> exec :x := 1

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> select to_number( to_char( last_day( to_date(
:x, 'mm' ) ), 'dd' ) ) from dual;

TO_NUMBER(TO_CHAR(LAST_DAY(TO_DATE(:X,'MM')),'DD'))
---------------------------------------------------
                                                 31

ops$tkyte@ORA920.US.ORACLE.COM> exec :x := 2

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> select to_number( to_char( last_day( to_date(
:x, 'mm' ) ), 'dd' ) ) from dual;

TO_NUMBER(TO_CHAR(LAST_DAY(TO_DATE(:X,'MM')),'DD'))
---------------------------------------------------
                                                 28

      Rating: 3
Perfect
      !  October 02, 2002
Reviewer:  Chris

Thx Tom :)

October 01, 2002 Andre -- Thanks for the question regarding
      "Oracle8i new features: Sort and Top-N queries", version 8.1.6

      You Asked
Hi Tom,

Going over some new features of Oracle8i, I´d like you to shed
some light on two topics:

1- What sort tuning features have changed from 8 to 8i ? What
parameters were added and what are obslolete ? In practice,
what´s their influence on Oracle performance tuning?

2- What are Top-N queries and how have they changed from 8 to 8i
?

Regards,

André Whittick Nasser

      and we said...
From the getting to know Oracle8i guide (available at

http://technet.oracle.com/doc/oracle8i_816/server.816/a76962/toc.htm
)

<quote>
Sort Improvements

The sort routines used throughout the Oracle database server
have been rewritten to eliminate performance problems. Sort now
performs more predictably and consistently, given the same
input, system configuration, and parameter settings. Memory is
utilized more efficiently, enabling more rapid sorts when
additional memory is specified. The following table shows
initialization parameters that have been dropped or added as a
result of this rewrite:

Dropped Initialization Parameters SORT_DIRECT_WRITES
SORT_WRITE_BUFFERS
SORT_WRITE_BUFFER_SIZE
SORT_READ_FAC
SORT_SPACEMAP_SIZE

Added Initialization Parameter
SORT_MULTIBLOCK_READ_COUNT

For more information, see Oracle8i Designing and Tuning for
Performance.
..........................
</quote>

Top-N queries are a whole new thing in Oracle8i release 2.  They
are very *cool* and much much easier to do.

Let's say you wanted the top 2 salary earners by deptno in the
EMP table.  This can be interpreted at least 3 ways:

o find the top two salaries by deptno and give me everyone who
makes that

o find upto three people who make the highest salaries.  If 4
people make the highest salary the answer will be the NULL set.
If 2 people make the highest and 2 people make the next highest
-- the answer will be 2 rows (just the highest) since if we
returned both -- we'd get four rows

o return me the first three highest paid people you happen to
find.

We can do all three easily now:

ops$tkyte@DEV816> select *
  2    from ( select deptno, ename, sal,
  3                  dense_rank()
  4                  over ( partition by deptno
  5                         order by sal desc nulls LAST ) dr
  6            from emp )
  7   where dr <= 2
  8  /

    DEPTNO ENAME             SAL         DR
---------- ---------- ---------- ----------
        10 KING             5000          1
        10 CLARK            2450          2
        20 SCOTT            3000          1
        20 FORD             3000          1
        20 JONES            2975          2
        30 BLAKE            2850          1
        30 ALLEN            1600          2

7 rows selected.

ops$tkyte@DEV816>

What we did here was to use the DENSE_RANK() function to get the
top two salaries.  We assigned the dense rank to the salary
sorted descending.  Notice that when sorting, especially
descending sorts, it is critical to add NULLS LAST to the order
by (new in 8.1.6).  By default NULLS are “greater” than any
other value in a sort.  If the salary fields had been null for
any row – it would have been considered the largest value, not
what you would want.

A dense rank does not skip numbers and will assign the same
number to those rows with the same value.  Hence, after the
result set is built in the inline view – we can simply select
all of the rows with a dense rank of three or less – that gives
us everyone who makes the top three commissions by department
number.

Lastly, we had to use an inline view and alias the dense rank
column to DR.  This is because we cannot use analytic functions
in a where clause directly so we had to compute the view and
then filter just the rows we wanted to keep.

Now for method 2:

ops$tkyte@DEV816> select *
  2    from ( select deptno, ename, sal,
  3                  count(*)
  4                  over ( partition by deptno
  5                         order by sal desc nulls LAST ) cnt
  6             from emp )
  7    where cnt <= 2
  8  /

    DEPTNO ENAME             SAL        CNT
---------- ---------- ---------- ----------
        10 KING             5000          1
        10 CLARK            2450          2
        20 SCOTT            3000          2
        20 FORD             3000          2
        30 BLAKE            2850          1
        30 ALLEN            1600          2

6 rows selected.

This one was a little tricky. What we are doing is counting
everyone who makes what we make or more.  By counting everyone
who makes what we make or more, we can retrieve only rows such
that the count (CNT) of people making the same or more is less
then or equal to 2.  Notice how for department 20, SCOTT and
FORD both have a count of 2 – they are the top 2 salary earners
in that department hence they are both in the same window with
regards to each other.

For the last question we can:

ops$tkyte@DEV816> select *
  2    from (select deptno, ename, sal,
  3                 row_number()
  4                 over ( partition by deptno
  5                        order by sal desc nulls LAST ) rn
  6            from emp )
  7   where rn <= 3
  8  /

    DEPTNO ENAME             SAL         RN
---------- ---------- ---------- ----------
        10 KING             5000          1
        10 CLARK            2450          2
        10 MILLER           1300          3
        20 SCOTT            3000          1
        20 FORD             3000          2
        20 JONES            2975          3
        30 BLAKE            2850          1
        30 ALLEN            1600          2
        30 TURNER           1500          3

9 rows selected.

That works by sorting each partition by SAL DESC (with NULLS
LAST) and then assigning a sequential row number to each row in
the partition.  We use a where clause after doing this to get
just the first three rows in each partition.

and I haven't even used any of the other 25 analytic functions
-- nor have we looked at "windows" which can be used with them
as well....

Review &
      Followup

      Rating: 5
I dont get the
      count(*) part  September 05, 2002
Reviewer:
      A reader
hi

the count(*) part looks pretty tricky, even you explained afterwards...

I ran the following query

SQL>  select ename,
  2          deptno,
  3          sal,
  4          comm,
  5          count(*) over (partition by deptno order by sal desc) cnt
  6   from emp;

ENAME          DEPTNO        SAL       COMM        CNT
---------- ---------- ---------- ---------- ----------
KING               10       5000                     1
CLARK              10       2450                     2
MILLER             10       1300                     3
SCOTT              20       3000                     2
FORD               20       3000                     2
JONES              20       2975                     3
ADAMS              20       1100                     4
SMITH              20        800                     5
BLAKE              30       2850                     1
ALLEN              30       1600        300          2
TURNER             30       1500          0          3
WARD               30       1250        500          5
MARTIN             30       1250       1400          5
JAMES              30        950                     6

I dont understand why I get 2 for SCOTT or FORD and 5 for WARD and MARTIN. Well
I see how (sort of), 2 would be the second highest salary for deptno 20 and 5
the fifth highest salary for deptno 30 however I  cant see why 1 (deptno 20) and
4 (deptno 30) is bypassed
I would actually expect see 1 and 4 instead of 2 and 5 since 3000 is the highest
salary in 20 and 1250 the fourth

I am not sure what are we counting here, if we were counting the people who earn
same salary I would expect see 2 for WARD and JONES as well. This looks like
ranking function but it is not actually, a bit confusing

          Followup:

They make the same salary -- the column:

count(*) over (partition by deptno order by sal desc) cnt

says "break the results into groups by deptno.   Within each group -- sort by
sal.  Then count the rows with the same or less salary (since we sorted by sal
-- that defines the 'key')"

So, when you do that with deptno = 20, SCOTT and FORD both make the same --
hence they get counted together (when you sort by SAL, SCOTT and FORD are the
"same", neither comes before NOR after the other see).

We are counting ROWS (count(*) always counts ROWS)

So, say we change the key from "sal desc" to "sal desc, ROWID" -- what then?

scott@ORA817DEV.US.ORACLE.COM>  select ename,
  2          deptno,
  3          sal,
  4          comm,
  5          count(*) over (partition by deptno order by sal desc) cnt,
  6          count(*) over (partition by deptno order by sal desc, ROWID) cnt
  7   from emp;

ENAME          DEPTNO        SAL       COMM        CNT        CNT
---------- ---------- ---------- ---------- ---------- ----------
KING               10       5000                     1          1
CLARK              10       2450                     2          2
MILLER             10       1300                     3          3

SCOTT              20       3000                     2          1
FORD               20       3000                     2          2
JONES              20       2975                     3          3
ADAMS              20       1100                     4          4
SMITH              20        800                     5          5

BLAKE              30       2850                     1          1
ALLEN              30       1600        300          2          2
TURNER             30       1500          0          3          3
WARD               30       1250        500          5          4
MARTIN             30       1250       1400          5          5
JAMES              30        950                     6          6

14 rows selected.

Now, count(*) is counting the rows in the group such that the row is less then
"sal desc, rowid" and since rowid it unique -- it assigns the count based on
physical location in the datafile (eg: if you dump and reload the data -- FORD
might come before SCOTT next time).

You could use EMPNO instead of ROWID as well.

      Rating: 5
Thanks!
      Something new in every answer!  September 05, 2002

Reviewer:  Chuck Jolley  from OKLAHOMA CITY, OK USA

I didn't know 'NULLS LAST' even existed!

      Rating: 5
should we see 2
      in...  September 05, 2002
Reviewer:
      A reader
WARD               30       1250        500          5
MARTIN             30       1250       1400          5

these should show 2 isntead of 5 if we are counting....?

          Followup:

BLAKE              30       2850                     1
ALLEN              30       1600        300          2
TURNER             30       1500          0          3
WARD               30       1250        500          5
MARTIN             30       1250       1400          5
JAMES              30        950                     6

You are counting the salaries in the partition that are less then or equal to
yours.

For blake -- thats 1
allen -- 2
turner -- 3
WARD -- well, there are 5 that are less then or equal in that deptno...

      Rating: 4
Further
      Question   September 30, 2002
Reviewer:
      mike
I find row_number() over (parition by....)does not work in pl/sql program.  How
can I simulate this feature in pl/sql program?

THANKS.

          Followup:

read

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3027089372477

      Rating:
      4
  October 01, 2002
Reviewer:  Mahomed
      Suria  from U.K
Hi Tom,
    I have been trying to ask a question for a number of days, but every
time I log on to your site you are not accepting any more questions. So this
time.
if you do not mind, I will take the opportunity to ask the question via the
review
here as it could possibly be related :

I have a table of approx 2000 rows as follows :

itemno    quantity
------  --------
1    40
13    35
8    46
4    2
5    50
11    50
7    50
6    45

where 'itemno' represents a box/container and 'quantity' is the number of cases.
The standard quantity is 50 cases, but sometimes a box may have less than the
standard quantity. Given a quantity  required for selection ( say 160 ) ,
I would like to determine which combination of the boxes gives me the closest
total (+ or - ) to the required quantity.

Presently, I am using a ProC function. I read all records into memory ordered by
quantity (so that non-standard boxes are selected first) , then divide the
quantity required by the standard quantity for the minimum number of boxes
required. Then I go through various combinations of minimum + N boxes
until I get the exact quantity or something close to it but restricting the
number of search cycles to say 5000 ( a configurable number).

This method is crude to say the least, cpu intensive and does not always give
the desired result. I was wondering if there was a more efficient way of doing
this directly in the database.

Thanks,
Mahomed

          Followup:

(yes, well, can't do questions when I keep getting them here -- sort of a self
fulfilling prophecy you know)

you'll be picking N items at a time - combinations and such.  Its a mathematical
optimization issue that could be done with a nasty N-way cartesian product.

Procedural code will be the way to go here. You might consider plsql over pro*c,
but other then that -- you'll have to come up with a good algorithm and encode
it.

October 02, 2002 Karl -- Thanks for the question regarding
      "Dependent drop down lists in Pl/SQL cartridge", version 8.05

      You Asked
I am developing a web front end for a client and they require a search screen
that has drop down lists. These drop down list choices are dependent, i.e. the
choices of the second list are dependent on what the user chooses from the first
list.

Can't find info on how to do this, do you have any idea where I may be able to
find any examples or decent documentation.

      and we said...
I've put a quick demo up.  It work on EMP and DEPT.  I have a list of deptno's,
as you change their value - the list of enames in that DEPT updates itself.  To
test it see:

http://osi.oracle.com/pls/ask/ask_tom.test_onchange.main
What we've done is to use frames.  One hidden frame actually rewrites itself
every time we change the DEPTNO.  We send the new deptno up to the server and
get the list of enames for that DEPTNO.  This hidden frame then truncates the
old list and fills it with the new values.  The code for doing this is as
follows (sample for the emp and dept tables, you would recode for your
environment of course)

set define off

create or replace
package test_onchange as

  procedure main;

  procedure frame_main;

  procedure frame_hidden(p_deptno varchar2 default null);

end test_onchange;
/

create or replace
package body test_onchange as

g_addToList constant varchar2(512) default '
<script language="javascript">
function addToList( n, v )
{
  with ( document.forms[0].p_ename )
    options[length] = new Option( n, v );
}
</script>';

g_lookUp constant varchar2(512) default '
<script language="javascript">
function lookUp( deptNo )
{
  document.forms[0].p_deptno.value = deptNo;
  document.forms[0].submit();
}
</script>';

procedure main is
begin

  htp.framesetOpen( crows=> '100%,0' );
    htp.frame( cname => 'mainFrame',
               csrc => 'test_onchange.frame_main' );
    htp.frame( cname => 'hiddenFrame',
               csrc => 'test_onchange.frame_hidden',
               cattributes => ' MARGINWIDTH=0 ' ||
                              ' MARGINHEIGHT=0 ' ||
                              ' NORESIZE' );
  htp.framesetClose;

end main;

procedure frame_main
is
begin

  htp.p( g_addToList );
  htp.formOpen( null );

  htp.p( 'Deptno:' );

  htp.formSelectOpen( 'p_deptno',
         cattributes => 'onChange="parent.hiddenFrame.'||
                           'lookUp( this.options[' ||
                           'this.selectedIndex].value );"' );

  for c in ( select deptno, decode(deptno,10,'Y',null) sel
               from dept )
  loop
     htp.formSelectOption( c.deptno, c.sel,
           cattributes => 'value="' || c.deptno || '"' );
  end loop;

  htp.formSelectClose;
  htp.br;

  htp.p( 'Ename:' );
  htp.formSelectOpen( 'p_ename' );
  for c in ( select ename from emp where deptno = 10 )
  loop
     htp.formSelectOption( c.ename );
  end loop;
  htp.formSelectClose;

  htp.br;
  htp.formClose;
end frame_main;

procedure frame_hidden( p_deptno varchar2 default null )
is
    l_not_found boolean := true;
begin

  htp.p( g_lookUp );

  htp.formOpen( 'test_onchange.frame_hidden' );
  htp.formHidden( 'p_deptno', null );
  htp.formClose;

  htp.p( '<script language="javascript">' );
  htp.p( 'with( parent.mainFrame )' );
  htp.p( '{' );
  if p_deptno is not null
  then
    htp.p( '    document.forms[0].p_ename.length = 0;' );
    for c in ( select ename
                 from emp
                where deptno = p_deptno
                order by ename )
    loop
       htp.p( '    addToList( "' || c.ename ||
                 '", "' || c.ename || '" );' );
       l_not_found := false;
    end loop;
  end if;

  if l_not_found
  then
    htp.p( '  addToList( "NULL", "" );' );
  end if;

  htp.p( '  document.forms[0].p_ename.' ||
                 'selectedIndex = 0;' );
  htp.p( '  document.forms[0].p_ename.focus();' );
  htp.p( '}' );
  htp.p( '</script>' );

end frame_hidden;

end test_onchange;
/

set define on

Review &
      Followup

      Rating: 5
Excellent
      Answer  December 09, 2001
Reviewer:  Munzer

Tom is the best there was, is and will be

      Rating: 5
Easier
      Way  February 21, 2002
Reviewer:  Munzer
       from Reston. VA
Tom:

There is much easier way to do dependent lists in Javascript and without using
frames.

You bascially define an array for all lookup values and then create a function
that assigns the array values to lookup column based on the values in other
column.

If you are interested I can show you the code for a simple example.

Also, I managed to build the array dynamically using PL/SQL so that when you
create the page the array will be defined from values in oracle table.

Thank you,

          Followup:

Easier to you maybe BUT -- it would demand you pull ALL of the data to the
client.

Say you have 100 departments.
Each department has 100 employees.

You would pull 10,000 employee records to the client when the page first comes
down.

I would pull 100 employee records to the client ON DEMAND.

This supplied implementation scales very well as the data grows (it can be used
for non-trivial sets of data).  As well, it gets the current data from the
database as you click on the "master" record -- were as you would show the data
when the page was generated.

Me -- I like to keep web pages as SMALL as possible and look for ways to
accomplish that goal over all else.  Nothing worse then a 500k html page -- I
prefer a 20/40k page at most (given that I work on dialup/isdn frequently).

      Rating: 3
I would like to
      see Munzer 's solution   June 22, 2002
Reviewer:
      Asha
Hi Munzer ,
          I am interested in your solution . Could you please email it to me at
asha_patil9999@yahoo.com

Thanks,
Asha

      Rating:
      5
lookup  July 19, 2002
Reviewer:
      Munz  from Reston, USA
Tom:

1.  I did create a description lookup for an item number.  If user select a
stock number, description will pop up.  If user selects a '99999' then I ask
user to enter a description as this part does not have an assigned number.  I
just need to enter a number in table so I store a 99999.

Problem now is that user wants the description also to be dropdown, so he can
select description and item will pop up which is not a problem.  However how can
you handle the '9999' part number problem.  If the description is a drop down
then user can not enter a free text in the description.  Do you know of a
solution?

      Rating:
      5
lookup  July 21, 2002
Reviewer:
      munz  from Reston, USA
Tom:

1.  I did create a description lookup for an item number.  If user select a
stock number, description will pop up.  If user selects a '99999' then I ask
user to enter a description as this part does not have an assigned number.  I
just need to enter a number in table so I store a 99999.

Problem now is that user wants the description also to be dropdown, so he can
select description and item will pop up which is not a problem.  However how can

you handle the '99999' part number problem.  If the description is a drop down
then user can not enter a free text in the description.  Do you know of a
solution?

          Followup:

not off the top of my head but now that you've seen what javascript can do --
you can buy a javascript book and really learn it!

      Rating:
      5
Frames  September 26, 2002
Reviewer:
      mo
Tom:

1.  Can you explain what you mean by a hidden frame?

2. Is it different than a regular frame in that you can not tell it in a frame
by itself?

3.  If you can not use a regular frame since it does not support handicapped
people I think because you can not tab into it would a hidden one be different.
I see that you can tab into it like a regular form?

4.  If I added a submit button, would it submit deptno and employeeid.  or you
have to so something different.

Thank you,

          Followup:

1) a frame you cannot see. (obviously?)

2) it just has no dimensions, it is 0 by 0, you cannot see it, but yet -- its
there.

3) we don't want anyone to tab into it.  it is a scratch pad for programming
purposes -- handicapped or not, there is no user interaction in this frame.

4) of course, it is just a form after all.

      Rating: 5
dependent
      dropdown lists  September 28, 2002
Reviewer:
      mo
Tom:

1.  Can you explain to me how you are doing this using a hidden frame.

I see that you have one procedure "main" where you are creating two frames and
each frame is calling a pl/sql procedure.  The main frame procedure creates a
form with deptno and employee. The frame hidden procedure reads dept no
parameter and then run a sql statement to find employees.

How are you setting the empname in the first frame to the results of the second
frame?

2.  Sine I have several web pages or sometime one web page with different sets
of dependent dropdowns do i have to create this package for each two dependent
dropdowns.

3. I use owa_util.listprint a lot if times except when I need to attach onCHange
javascript event handler with the <select> tag because I guess you can not do it
with the owa_util.listprint.

Can you implement this code for dropdwons using owa_util.listprint?

4.  ALso, my dropdown usually show department name but I save department code.
Would this create a problem here in this code?  Would you only change the
frame_main procedure?

Thank you,

          Followup:

1) suggest you read the resulting pages so you can see the javascript.  the
hidden frame just writes to the javascript variables in the visible frame.

2) don't know, guess it depends on how you code the hidden frame doesn't it.
You can write a generic routine that could be reused, I'm sure.

Take the *concepts*, the *ideas* presented and mutate them to meet your needs.

3) suppose so, you have the code for listprint (it is shipped as source code).
you can make it do whatever you want!

4) it would not cause a problem.

Before you use this -- you should understand javascript, frames and how this
stuff all works.  If you do -- simply looking at the generated pages should
cause you to smack your forehead and go "of course, I see".

      Rating:
      5
DROPDOWN  September 28, 2002
Reviewer:
      mo
Tom:

Your example resultin pgage is:
<FRAMESET ROWS="100%,0">
<FRAME SRC="ask_tom.test_onchange.frame_main" NAME="mainFrame">
<FRAME SRC="ask_tom.test_onchange.frame_hidden" NAME="hiddenFrame"
MARGINWIDTH=0  MARGINHEIGHT=0  NORESIZE>
</FRAMESET>

As you can see there is nothing there from html or javascript.

2. I do not know how hard it would be to change the code for the listprint but
it may take considerable time as you have to undertand the whole code someone
else wrote. I rather use <SELECT> and insert an implicit cursor to generate the
values. It is easier and faster unless you think otherwise?

3. I do understand javascript and frames reasonably well. A frame simply another
html page within a page. Nothing complex about it. To display 10 pages in one
windows ou create 10 frames.

4.  To make a generic package I have to subsitute every reference to an object
in either pl/sql or javascript as a parameter and name the object in the
parameter?Correct.

Thank you,

          Followup:

(hint -- view the frame source, heck run it in SQLPLus and see what it spits
out)

PLAY with it.  Install it on your system.

2) i wrote listprint.  It is pretty trivial.  It is up to you.

      Rating:
      4
excellent  October 01, 2002
Reviewer:
      sanjay
hi

how is this possible on oracle forms 6i.

i am new to forms

sanjay

          Followup:

this is even easier in forms -- but -- heres the catch -- you'll really need to
read the documentation.

This is a master detail block basically.

      Rating: 4
Would like to
      see Munzer's solution  December 24, 2002
Reviewer:
      Neeta Ranchod  from Johannesburg, South Africa
Hi Munzer ,
  I deperately need a solution to this. I am new to web PL/SQL and to Java
Scripting. I am interested to see your solution . Could you please email it to
me at
neeta.ranchod@za.didata.com. Your response will be greatly appreciated.

Thanks,
Neeta

October 02, 2002 Lynn -- Thanks for the question regarding "Long
      Raw Datatypes", version Version 8.1.5

      You Asked
I have 2 tables with 2 columns each; first column is VARCHAR2 &
the second is LONG RAW - to store images - for both tables.

I am trying to create 2 tables (for trial purposes) based on
these tables using the following statement:-
CREATE TABLE <name>
AS
SELECT * FROM emp;
It gives me an error saying 'Illegal use of Long datatype'.

I tried inserting into the tables after creating them, but that
also gives me the same error.

Could you suggest how to create the tables and also how to merge
records in them?

Thanks & regards
Lynn

      and we said...
In Oracle8i, releases 8.0 and up, you should not be using LONG
RAW. Both LONG and LONG RAW are only provided for backwards
compatibility -- they are deprecated datatypes.

You should be using blob -- then its as easy as above (create
table as select).  LONGS and LONG RAWS come with many many
limits (no use in WHERE clause, no insert into select LONG, no
create table as SELECT, no chance of manipulation in PLSQL --
only in 3gls, no replication, etc, etc, etc).  BLOBS and CLOBS
do not suffer from these limits.

To copy a long raw into another table without writing a program
in C or VB or Java or some other 3gl you can:

o export the table (emp in your example)
o rename the table emp to <name> using the RENAME command
o import the export you just created

To merge them (say you wanted to take the table EMP and the
table NAME and have EMP have all of the records at the end) you
would:

o export that table NAME
o drop table NAME
o rename EMP to NAME
o import with ignore=y to add the rows to NAME
o rename NAME to EMP

remember to set BUFFER really large for the export and import to
avoid issues with exp and imp and large columns.

With LONGS (not LONG RAWS) you can use the sqlplus copy command
as well, see

http://osi.oracle.com/~tkyte/Misc/MoveLongs.html
for details.

Review &
      Followup

      Rating: 4
Long Raw
      Datatypes  July 10, 2001
Reviewer:  Lynn
       from Bahrain

      Rating: 2
But BLOBs are
      _not_ compatible with Forms5 & Long Raw IS!!  November
      20, 2001
Reviewer:  Suzanne Michelle, Rod Patwell
       from Brooklyn, New York
The title says it all.  We have exactly same scenario, wanted to copy from one
to another, but couldn't.  Tried exporting, no good.  We are still using Forms5,
because we are just not ready to go to Forms 6i.  And, to our knowledge, Forms5
doesn't work with LOBs.  Enlighten if this is not true, thanks!

          Followup:

tell me why export didn't work (it does, used it many times myself)?

Make sure to set buffer= to a large value for the EXP but it sure does work...

You'll have to supply more information.

      Rating: 1
We have to do
      it!  December 13, 2001
Reviewer:  Platinum
       from Singapore
Is there any other method where we can replicate the long raw datatype?
I need to replicate the long raw data for audit purpose, surely a mass copy of
the table will not serve the purpose.
Please advise.
Thanks

          Followup:

don't use long raw. period.  its a very simple answer.  use a BLOB.

You can always implement a flag delete as well -- don't UPDATE the row, end_date
the old row and insert a new row.  That's a more efficient "audit" anyway,
rather then copy the entire old row AND update the old row, just update an
end_date column and insert a new row.

      Rating:
      4
Thanks!  June 19, 2002
Reviewer:  Bill
      Coulam  from CO
Very helpful. I was starting down the usual road of vainly searching through the
Oracle docs, when it occurred to me to check your site FIRST this time. You
probably saved me hours of fruitless reading, thanks!

      Rating: 5
Changing
      Tablespaces  October 01, 2002
Reviewer:
      A reader
Tom....
Great Site...
We have some existing tables that have LONG and LONG RAW columns in them..
We are shifting from Dictionary Managed to Locally Managed tablespaces.
Alter table move tablespace results into an error..
ORA-00997: illegal use of LONG datatype
Can Export/Import accomplish that...
If yes how?

Database: 8.1.7.3.4
OS: UNIX

Thanks..

          Followup:

sqlplus copy can work with longs if you like

export import will do both longs and long raws.

Make sure to have a backup! before doing the export/import trick as you
typically DROP the table after the export, before the import.  If something were
to go wrong with IMP, you would like to have a backup of course!

October 02, 2002 Ravinder -- Thanks for the question regarding
      "Rollback Segments -- negative values in v$rollstat", version 8.1.5 on Win
      NT 4.0

      You Asked
Hi Tom,

I was just going thru the rollback segments using v$rollstat and
I was surprised to see -ve figures for RSSIZE and HWMSIZE.

I have dropped these rollback segments and created bigger rbs in
their place.

Why it shows -ve figures for RSSIZE and HWMSIZE and what is the
implication of the same on my database. Till now, I havn't seen
anything wrong with the database. Even then , I am scared 'may
be something is wrong '

Thanks

Ravi

      and we said...
creating really large RBS is the underlying cause of the problem
-- making them bigger makes them negative.  Its because the v$
view uses a signed 4byte integer.  Here is a support note on the
subject:

<Note:100292.1>

Problem Description
-------------------

Selecting records from V$ROLLSTAT, you may see that some columns
have negative values like HWMSIZE column for example.

Due to <bug:668924>, these columns cannot record values larger
than 2GB (2 147 483 647). If the value is greater than that,
value turns negative.

This error doesn't prevent Rollback Segment to grow larger than
this value. It's just a recording problem in V$ROLLSTAT.

Solution Description
--------------------

This bug is fixed in 8.0.6 and 8.1.6. You can upgrade to one of
these two rdbms versions to fix the problem. At this point the
maximum value will be 4 GB  (4 294 967 295).

Workarounds:
============

1. Check Storage Clause

Check that no storage option implies in any column a value
greater than 2 GB (like RSSIZE and so HWMSIZE for example).

If so, either alter the storage clause or drop and recreate
the rollback segment with a smaller initial size (initial_extent
* min_extents).

If you don't want negative values to appear in rssize, writes,
hwmsize, at any moment, set max_extents to the right value.

Warning : Doing so, you will prevent success of transactions
generating more than 2GB of undo. So you should probably modify
such transactions.

2. Negative values will disappear at next instance start if not
initial storage values.

As v$rollstat is a dynamic performance table which is reset at
each instance start, at next instance start negative values will
disappear if these negative values are not due to initial size
of Rollback Segment.

Review &
      Followup

      Rating:
      4
  October 01, 2002
Reviewer:  Sagi
       from India
Hi Tom!

I wanted to test it for myself i.e. the negative value. Therefore I tried. But
getting error.

create public rollback segment testrbs
storage (initial 2g next 2g minextents 2) ;
SQL> /
storage (initial 2048m next 2048m minextents 2)
                     *
ERROR at line 2:
ORA-02218: invalid INITIAL storage option value

Am I doing something wrong? How can i check this for myself.

Regards,
Sagi

          Followup:

why would you want to? anyway, if you read it you'll see you need 2gig+ rbs's,
not 2gig extents.  And then you would need to process 2gig+ of undo in that rbs.

October 02, 2002 chirayu -- Thanks for the question regarding
      "Indexing a variable length field", version 8.1.7

      You Asked
Dear Tom,
   I have a table named voudtl where we store anlys code, which is of
   variable length. For eg, six characters, eight characters etc.
   This is the most important table of my system, The users routinely
   execute reports, in which this table is used. And the length of
   the anlys code comes into play as well.

   anlys code  is highly selective field, however using the length(anlys)
   defeats the very purpose of the index.

 Please see the spool file shown below.

ORC1>select count(*)
  2  from voudtl where unit = 21
  3  and length(anlys) = 8 ;

    COUNT(*)
------------
       60876

1 row selected.

 real: 3080
ORC1>select count(*)
  2  from voudtl where unit = 21
  3  /

    COUNT(*)
------------
      265771

1 row selected.

 real: 390
ORC1>spool off

  What i want is that the query no.1 should run, if not as fast as the
  query no 2, significantly faster then it is running now.
  I have an index on the anlys field.

  Can you please suggest some workaound like how can i achieve
  a faster speed  for queries that use the length function on
  the anlys column. I am even ready to change the design of the table
  for that.  Is this a case of function Index.
  What are your general suggestions for fields which are of variable
  length and length forms an important part of the query

  Thanks in advance for the time you devote.

  regards,
  chirayu

      and we said...
Well, my guess here:

you have an index on unit.

This index does not include anlys in it.

When you select count(*) from voudtl where unit = 21, we can answer the query
directly from the index, never hitting the table.

When you add "and length(anlys) = 8", we must perform LOTS of random IO's to
access the table to find the length of anlys.  Consider this example:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( unit number(2), anlys
varchar2(8) ) pctfree 50;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t
  2  select 21, rpad( '*', mod(rownum,8)+1 )
  3  from big_table.big_table
  4  where rownum <= 265771
  5  /

265771 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table t compute statistics for table;

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx on t(unit);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace on
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from t where unit = 21 and
length(anlys) = 8;

  COUNT(*)
----------
     33221

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=11)
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=2 Card=27 Bytes=297)
   3    2       INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE) (Cost=1 Card=27)

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

So, for every row in the index it found unit=21, it did a table access by
rowid, 1420 logical IO's (LIOs)

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from t where unit = 21;

  COUNT(*)
----------
    265771

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=11)
   1    0   SORT (AGGREGATE)
   2    1     INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE) (Cost=1 Card=2658
Bytes=29238)

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

and there, it never had to leave the index.  about 1/3 the LIO's to get an
answer that included many more records

So, adding anlys to the index:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx2 on t(unit,anlys);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from t where unit = 21 and
length(anlys) = 8;

  COUNT(*)
----------
     33221

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=11)
   1    0   SORT (AGGREGATE)
   2    1     INDEX (FAST FULL SCAN) OF 'T_IDX2' (NON-UNIQUE) (Cost=1 Card=27
Bytes=297)

Statistics
----------------------------------------------------------
          0  recursive calls
          6  db block gets
        726  consistent gets
        725  physical reads
          0  redo size
        369  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          1  rows processed

we can get the answer in 50% of the LIO's of the first example but using a
function based index:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create index t_idx3 on t(unit,LENGTH(anlys));

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set query_rewrite_enabled=true;

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set
query_rewrite_integrity=trusted;

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*) from t where unit = 21 and
length(anlys) = 8;

  COUNT(*)
----------
     33221

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=11)
   1    0   SORT (AGGREGATE)
   2    1     INDEX (RANGE SCAN) OF 'T_IDX3' (NON-UNIQUE) (Cost=1 Card=27
Bytes=297)

Statistics
----------------------------------------------------------
         27  recursive calls
          0  db block gets
         86  consistent gets
         80  physical reads
          0  redo size
        369  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          3  sorts (memory)
          0  sorts (disk)
          1  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

We can get the answer in 1/16th the LIO's

Review &
      Followup

      Rating:
      4
Wonderful  September 29, 2002
Reviewer:
      Chirayu Sutaria  from ahmedabad, India
Dear Tom,
  It's just a wonderful explanation ( and I always expect it ).  actually we
have four indexes on the table and anlys field is also indexed.  I will try
using a function based
indexed. We are still using the RBO.

However, I didn't understand the purpose of this

alter session set query_rewrite_enabled=true;

alter session set query_rewrite_integrity=trusted;

Thanks in terabytes.

          Followup:

read

http://asktom.oracle.com/~tkyte/article1/index.html
....

      Rating:
      4
  September 30, 2002
Reviewer:
      A reader
Tom,

any info or links on articles of 9i new features that you are doing...

Thanks...

          Followup:

Amazingly enough, there is

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/nav/docindex.htm#index-NEW
but -- the function based indexes are new with 8i, not 9i!

      Rating: 4
RBO
      Alert!  October 01, 2002
Reviewer:  Andrew
      Clarke  from London, England
Tom

Another exemplary answer.

It's worth pointing out that the questioner says in their subsequent posting
they are still using the RULE-BASED OPTIMIZER.  Now according to the
Administrator's Guide, Function-based indexes only work with tables that have
statistics.  This invokes the COST-BASED OPTIMIZER.

I think they should be chary of drifting into CBO without considering its impact
on their current system.  Analyzing one table without analyzing any other tables
to which it joins could have a detrimental effect on some of their queries.

We can analyze individual indexes but I don't know if that would still work with
function-based indexes.

Cheers, APC

          Followup:

An index hint would work nicely without affecting any other query.

      Rating:
      3
  October 01, 2002
Reviewer:
      A reader
Thanks Tom for the link, but my question was regarding your article.....I like
your way of defining things with examples...Lot more easier to understand...

          Followup:

Yes, well, given that my new features for 9ir1 over 8ir3 is 168 slides with no
pictures and my new features for 9ir2 is 70+ slides -- i'm not going to rewrite
the new features guide here on a single web page.

It is slightly bigger then a bread box you know.

      Rating: 4
Function Based
      Indexes !  October 01, 2002
Reviewer:  Rama
       from Stamford, CT USA
I use it. But, with date columns I am getting the following error.

SQL> create table test( d date, n number);

Table created.

SQL> insert into test values
  2  (to_date( to_char(sysdate,'dd-MON-YYYY HH24:MI:SS'), 'dd-MON-YYYY
HH24:MI:SS'), 1);

1 row created.

SQL> insert into test values
  2  (to_date( to_char(sysdate+3, 'dd-MON-YYYY HH24:MI:SS'), 'dd-MON-YYYY
HH24:MI:SS'), 1);

1 row created.

SQL> commit;

Commit complete.

SQL> select to_char(d, 'dd-MON-YYYY HH24:MI:SS') , n from test;

TO_CHAR(D,'DD-MON-YY          N
-------------------- ----------
01-OCT-2002 12:00:27          1
04-OCT-2002 12:00:40          1

SQL> create index test_indx on test(trunc(d));
create index test_indx on test(trunc(d))
                                     *
ERROR at line 1:
ORA-01031: insufficient privileges

Am I doing something wrong ? And if I index just column "d", then when I use
function on that column, would it use the index ?

          Followup:

read the article

http://asktom.oracle.com/~tkyte/article1/index.html

it has the prereqs right there.

      Rating: 1
Read the
      documentation!  October 01, 2002
Reviewer:
      Andrew Clarke  from London, England
From the Administrator's Guide:

"For the creation of a function-based index in your own schema, you must be
granted the CREATE INDEX and QUERY REWRITE system privileges. To create the
index in another schema or on another schema's tables, you must have the CREATE
ANY INDEX and GLOBAL QUERY REWRITE privileges."

Cheers, APC

          Followup:

yes, amazingly enough -- there are lots of goodies like that in the
documentation (thats where I discover them all anyway)

October 02, 2002 Pramod -- Thanks for the question regarding "Date
      Field as Primary Key ...", version 8.1.5

      You Asked
Sometimes a column with "date" Datatype needs to be part of
Primary Key or Unique Key. While retrieveing result sets in a
SQL Query, we have to use TRUNC(date column) for date
comparision or ( Selecting rows greater or less than of database
field with TRUNC(date column)). I assume if I use TRUNC
function, optimizer may not be able to use Indexes for
retrieving result sets.

How do we handle this type of situation ? (Creating Function
Based Indexes ??? or Set NLS_DATE_FORMAT ???)

      and we said...
where date_field between to_date( '01-jan-2000' ) and
                         to_date( '01-jan-2000' ) + 1-1/24/60/60

is the same as:

where trunc(date_field) = to_date( '01-jan-2000' )

That would be my preferred way to get all records for a given
DAY.
Review
      & Followup

      Rating: 4
Useful
      Info....More on this  January 27, 2002
Reviewer:
      Kishan  from Singapore.
Hi Tom:

Is there any disadvantages using DATE datatype as part of Primary Key. I got
this information from one of my colleagues, taken from the net, source of which
I don't know:

<quote>
Are there any problems if I select a date column as a primary key for an Oracle
8i database?

There are many problems when having a date field as a primary key:
a) Exact fetch is practically impossible to do.
b) Referential integrity is very, very hard to accomplish.
c) In most cases you will have to do a convert function on the column and that
will eliminate the use of an index.

What I suggest is to:

First, if you can, do not choose a date; choose number(8) or number(12) and when
you update the field,
use to_number(sysdate,'yyyymmdd')

Second, to enforce a foreign key, when you update the father in a transaction,
save the date/time entered in a temporary variable, and when updating the child,
use the same variable.

If you must carry with you the info about date and time, use another column
number(4) with the hh and mm value.

Hope that's a help.
</quote>

How far is this reasoning valid? Or is this one of the Great Myths of Database
Design? If indeed its so, any suggestions as to how we can best design the
database?

Thanks for your time.

Take care,
Kishan.

          Followup:

Well, my followup to their comments are:

a) exact fetch is practically impossible to do.... How so, how is the number:

20020128075256

any harder to use then a number generated from a sequence?  I mean, a date is
just a value, a number is just a value, a value is a value -- it is not ANY
harder using a date then a number

b) referential integrity is practically impossible to do... How so, how is it
harder then using a sequence?  Its just a VALUE -- its not magic, it doesn't
morph into some other value, its not liquid -- it is a stable, constant, never
changing value -- just like "sequence.nextval"

c) In most cases you will have to do a convert function ... How so?  Only if
you write really really bad code.  I cannot imagine this being an issue at all

If the date is to be part of the primary key -- so be it, it is not any
different from any other datatype.  It is not hard to use, it is just like using
a number.  I view it as NO different whatsoever.

Now -- back to the original question "if i select a date column as a primary
key".... I do have issues with a DATE being the primary key.  Dates only go down
to the second in Oracle8i and before.  What happens when two people insert at
about the same time?  You'll get the same date.  It is my opinion that a primary
key consisting of a single DATE column is an incorrect design.  You should use a
sequence instead.

      Rating: 5
Thank You Very
      much....  January 29, 2002
Reviewer:  Kishan
       from Singapore
Thank you very much. I reckoned that if a date column is not supposed to be used
as PK, then Oracle would certainly not allow it. But a word from an authority,
it surely helps.. :-) Thanks..

      Rating: 5
why between
      ?  September 30, 2002
Reviewer:
      A reader
Hi tom,

  why would you use between operator over
 date_field >= to_date( '01-jan-2000' )
and date_field <= to_date( '01-jan-2000' ) + 1-1/24/60/60

Thanks,

          Followup:

less keystrokes?

reads better?

means more semantically (it is one standalone predicate compared to your two
disjoint ones)

      Rating: 5
will the
      performance be same ?  October 01, 2002
Reviewer:
      A reader
Hi tom,

  thanks, for answring the question.
will the performance be same using between and
two disjoint stmts ?
 also that is primary key attributes
and about 1 mil records ( will it use index ?)

Thanks,

          Followup:

they will be about the same.  they do the same thing.

October 02, 2002 David -- Thanks for the question regarding
      "Schemas/Data Files", version 8.1.7

      You Asked
Tom,

Please compare a unique versus shared schema? I know a unique schema is
associated with an individual user who creates objects. I have been informed by
another DBA that we can set up a couple of schemas that can be shared by users
to make the process of managing privileges much less cumbersome.  My question is
what is the syntax for creating a shared schema, how are they implemented and in
what situations should I use them?

Is there a way to tie a specific process to a CPU? If so, how is this measured?

If a tablespace is implemented with three datafiles, does Oracle try to spread
the workload out amongst the three files or does is use all the space in the
first file and that start using the second? The reason I ask is because I have
one datafile that's getting pounded and I feel that this may be hurting
performance.

I have been reading your book and it is the best Oracle resource I have found to
date.

Thanks for your help!

      and we said...
There is no such thing as a "shared schema", its more that schema A can be
granted access to various objects in schema B.  If schema A owned everything, it
would have access to everything.  If you split the objects up, you can limit
what schema A can see or not see.

You just create more then one schema to hold objects -- instant "shared schema".

There is no way in Oracle (there is in the OS sometimes) to tia a process to a
CPU.  It would be a really truly bad idea to do this.

We allocate extents around the datafiles in a round robin fashion.  I find it
easiest to use raid to just create striped volumes and spread the stuff out that
way.  Give me a single 32gig striped file instead of spreading 3 10gig files
over three devices anyday.
Review & Followup

      Rating:
      3
Reader  October 01, 2002
Reviewer:
      A reader
Tom,

"We allocate extents around the datafiles in a round robin fashion"

In the Production database

SQL> select file_name,file_id,bytes/1024/1024 MB, tablespace_name
     from dba_data_files where file_name like '%p%'
     order by 4,2;

FILE_NAME                        FILE  MB   TABLESPACE_NAME
-------------------------------- ---   ---  ---------------------------
/u26/oradata/NICS_P30/p101.dbf   15    2001 P1
/u26/oradata/NICS_P30/p102.dbf   16    2001 P1
/u26/oradata/NICS_P30/p103.dbf   17    2001 P1
/u05/oradata/NICS_P30/p104.dbf   18    2001 P1
/u05/oradata/NICS_P30/p105.dbf   19    2001 P1
/u05/oradata/NICS_P30/p106.dbf   20    2001 P1

SQL> select * from dba_extents where file_id in (16,17);

no rows selected

SQL> select * from uet$ where file# in (16,17);

no rows selected

SQL> select * from fet$ where file# in (16,17);

       TS#      FILE#     BLOCK#     LENGTH
---------- ---------- ---------- ----------
        12         16          2     256127
        12         17          2     256127

256127 (8k) blocks = 2001 M - 8k ( for OS file )

SQL> select bytes/1024/1024 mb, file_name,file_id from dba_data_files
     where file_id in (16,17);

        MB FILE_NAME         FILE_ID
---------- ---------------- --------
      2001 p102.dbf               16
      2001 p103.dbf               17

SQL> select segment_name from dba_segments where tablespace_name = 'P1';

SEGMENT_NAME
----------------------------------------------------------------
TABLE_P

Here there are 7 datafiles allocated to tablespace P1 and this tablespace
has only one segement TABLE_P1. The files (id 16, 17) are completely empty
not even used for segment header where as files ( ID 15,18,19,20) has data
in them. If files are used in round robin fashion, how could this happen
I think, at some point all the blocks in the files were empty and would
have let Oracle free the whole file. Is this possible

Thanks

          Followup:

never said it had to go sequentially.  Just that we tend to go around so you'll
end up with an equal number of extents for the object in each file over time as
it extends.

Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create tablespace testing
  2  datafile '/tmp/testing1.dbf' size 1m reuse,
  3           '/tmp/testing2.dbf' size 1m reuse,
  4           '/tmp/testing3.dbf' size 1m reuse,
  5           '/tmp/testing4.dbf' size 1m reuse,
  6           '/tmp/testing5.dbf' size 1m reuse,
  7           '/tmp/testing6.dbf' size 1m reuse,
  8           '/tmp/testing7.dbf' size 1m reuse
  9  /

Tablespace created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t tablespace testing storage (
initial 64k next 64k pctincrease 0 )
  2  as
  3  select * from all_objects where 1=0;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2      loop
  3          insert into t select * from all_objects where rownum < 100;
  4          commit;
  5      end loop;
  6  end;
  7  /
begin
*
ERROR at line 1:
ORA-01653: unable to extend table OPS$TKYTE.T by 8 in tablespace TESTING
ORA-06512: at line 3

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select extent_id, file_id, blocks
  2    from dba_extents
  3   where owner = user
  4     and segment_name = 'T'
  5  /

 EXTENT_ID    FILE_ID     BLOCKS
---------- ---------- ----------
         0         17         10
         1         10         10
         2         12         10
         3         13         10
         4         14         10
         5         15         10
         6         16         10

 17, 10, 12, 13, 14, 15, 16 -- repeat:

         7         17         10
         8         10         10
         9         12         10
        10         13         10
        11         14         10
        12         15         10
        13         16         10

just like on the back of shampoo -- lather, rinse, repeat...

        14         17         10
        15         10         10
        16         12         10
        17         13         10
        18         14         10
        19         15         10
        20         16         10
        21         17         10
        22         10         10
        23         12         10
        24         13         10
        25         14         10
        26         15         10
        27         16         10
        28         17         10
        29         10         10
        30         12         10
        31         13         10
        32         14         10
        33         15         10
        34         16         10
        35         17         10
        36         10         10
        37         12         10
        38         13         10
        39         14         10
        40         15         10
        41         16         10
        42         17         10
        43         10         10
        44         12         10
        45         13         10
        46         14         10
        47         15         10
        48         16         10
        49         17         10
        50         10         10
        51         12         10
        52         13         10
        53         14         10
        54         15         10
        55         16         10
        56         17         10
        57         10         10
        58         12         10
        59         13         10
        60         14         10
        61         15         10
        62         16         10
        63         17         10
        64         10         10
        65         12         10
        66         13         10
        67         14         10
        68         15         10
        69         16         10
        70         17         10
        71         10         10
        72         12         10
        73         13         10
        74         14         10
        75         15         10
        76         16         10
        77         17         10
        78         10         10
        79         12         10
        80         13         10
        81         14         10
        82         15         10
        83         16         10

84 rows selected.

all I can say is your object isn't in alot of extents yet, it hasn't had a
need to go into file 16/17

All of this will be affected by other outside things (like other users on the
system, other objects in fet$ and so on).  I'm running this in a "dust free lab"
environment (my own machine, no on to bother me)

October 02, 2002 -- Thanks for the question regarding "How RBS
      stores Insert ", version 8.1.5

      You Asked
Hi,
 The RBS stores only the rowid for INSERT statements bcos to undo the INSERT,
Oracle has to only delete that row. I was wondering with Oracle 8i how does
Oracle manage it!! In the Transportable Tablespaces, if we have Transported TS
to a database then the rowid might not be unique for that database. In this case
what will happen? Is it that new rows will however have unique rowids but the
mention of duplication is regarding already existing rows...

 I am finding the information from this site very, very useful. And it makes me
wonder how you manage to answer questions ranging from development to DBA
activities. I really appreciate your knowledge base.

Thanks,

Nitin

      and we said...
Thanks for the feedback.

No one ever said a rowid must be unique!!!  They don't have to be, consider this
tiny example:

tkyte@TKYTE816> create cluster emp_dept_cluster
  2  ( deptno number(2) )
  3  size 1024
  4  /

Cluster created.

tkyte@TKYTE816> create index emp_dept_cluster_idx
  2  on cluster emp_dept_cluster
  3  /
Index created.

tkyte@TKYTE816> create table emp
  2  cluster emp_dept_cluster(deptno)
  3  as
  4  select * from scott.emp
  5  /
Table created.

tkyte@TKYTE816> create table dept
  2  cluster emp_dept_cluster(deptno)
  3  as
  4  select * from scott.dept
  5  /
Table created.

tkyte@TKYTE816> select rowid from emp
  2  intersect
  3  select rowid from dept;

ROWID
------------------
AAAGB0AAFAAAAJyAAA
AAAGB0AAFAAAAJyAAB
AAAGB0AAFAAAAJyAAC
AAAGB0AAFAAAAJyAAD

Every rowid assigned to the rows in DEPT have been assigned to the rows in EMP
as well.  That is because it takes a TABLE + ROWID to uniquely identify a row.
The ROWID pseudo column is unique only within a table.

Review &
      Followup

      Rating: 4
Size of stored
      ROWID  April 30, 2002
Reviewer:  Andrew
      Clarke  from London, England
I'm trying to estimate the rollback segment size for a very large insert
transaction (potentially 20 million rows).

Does Oracle store the ROWID in its binary form (13 bytes) or in character form
(18 bytes)?  I would guess the former, but I don't know where to look to find
definitive answer.

cheers, APC

          Followup:

You'll never do it that way.  The amount of rollback generated will be a
function of the indexes as well.

Best bet:

o run your insert transaction for a subset of the rows, say 1,000, then 10,000
then 100,000

o look at your used_ublk in v$transaction after each one (rollback after each
insert)

o extrapolate up to 20,000,000 with those three observations.

      Rating:
      4
ROWID  April 30, 2002
Reviewer:  Shajan
      Joseph  from Chennai, India
Tom, is this correct?

In a non-clustered table, ROWID will be unique. On the other hand, tables belong
to same cluster and their rows can have the same ROWID.

          Followup:

No really, not strictly speaking in a semantic sense.

In a TABLE (clustered or otherwise) a rowid will alwyas be UNIQUE.
In a database, a rowid does not have to be UNIQUE, you can have the same rowid
for two different tables..

      Rating: 4
How is
      that  May 01, 2002
Reviewer:  A reader

Tom,

If i understood right what you are trying to say. You mean that there will be
two tables inside one block.

This was discussed in one of the groups and thrashed by HJR and You saying that
there can be only one table in a Block.

So Now the rowid will change sice the Block is going to change. so how can a
database have the same rowid for different tables.

Your comments pls.

Thanks.

Regards,
Ganesh R

          Followup:

Exactly.

You'll have to point me to that discussion, I know that is false.  There can be
dozens of tables using a single block -- the data dictionary is full of it -- I
spend many pages in my book explaining that.  Give me the link into google.com
that shows this discussion.

I don't know what you mean by:

"So Now the rowid will change sice the Block is going to change."

at all.  As for "how can a database ..." -- the example above shows HOW.  You
need an object_name+rowid to get a row.  The block format includes a table and
row directory on it.  If you have my book -- read the chapter on tables, i go
into this in detail.

      Rating: 4
One Segment
      Inside a Block.  May 01, 2002
Reviewer:
      A reader
Tom,

Sorry About this but i am not able to lay my hands on that particular thread.

Can i say that A Block can Have Only One Segment inside it. If it is a Heap
Table than there will be only table/Segment Right inside the Block. When you
take a Cluster it is still one Segment Inside the Block But more than One Table
Sharing the same Block and Hence can have the same rowid.

Your comments Pls.

Thanks.

Regards,
Ganesh R

          Followup:

Ahh - that was the discussion - it was about SEGMENTS, not about tables.

I believe I made an over generalization regarding that with regards to SEGMENTS.

A cluster is a segement.
A table created in a cluster is not.

A table created normally (no cluster) is a segment (or may be made up of
segments/partitions)

A segment/ block is accurate.  If you were to complete the example above with:

  1* select segment_name, segment_type from user_segments
a@ORA817DEV.US.ORACLE.COM> /

SEGMENT_NAME                   SEGMENT_TYPE
------------------------------ ------------------
EMP_DEPT_CLUSTER               CLUSTER
EMP_DEPT_CLUSTER_IDX           INDEX

it shows the EMP table and DEPT table are *not* segments -- the CLUSTER is.

      Rating: 5
Thanks a Ton
      !!!  May 01, 2002
Reviewer:  A reader

Thanks...

Regards,
Ganesh R

      Rating:
      5
Fine  May 01, 2002
Reviewer:
      A reader
Dear Tom,
So can we conclude that if it is not clusters, than rowid would still be unique
accross tables?
Thanks,

          Followup:

yes.

      Rating:
      3
  October 02, 2002
Reviewer:  Sagi
       from India
Hi Tom!

Is it true that accessing a table is faster than a clustered table? I read this
very long ago in some book.  But today when i was trying your example i was
surprised to see that accessing clustered tables is faster than normal table.

Say we had the below two tables in SCOTT shcema:

Default EMP & DEPT Tables and
EMP1 & DEPT1 (Clustered Tables -- as shown by you in the example)

I say "SET TIMING ON" at SQLPLUS

I find that
SELECT * FROM EMP1 is faster than
SELECT * FROM EMP...

So it shows that accessing the clustered tables are faster. Is there any
performance issues say if the tables are huge. Would you suggest to have
clustered tables created in Production Environment?

Finaly, the table
    ---- Picked below from your example ---
    create table emp1
    cluster emp_dept_cluster(deptno)
    as
    select * from scott.emp

is this a cluster table. If not then can you explain regarding it.

Regards,
Sagi

          Followup:

just keep reading that chapter on tables in my book - over and over.

If one table type was "FASTER" then some other table type for all operations --
tell me -- why would we have it?

All tables are not created equal.  They have different characteristics.  Your
job -- understand what these characteristics are, know when they might apply to
a problem at hand (eg: don't run out and make EVERY table an IOT), and use them
after benchmarking and testing to destruction.

It would be an utter waste of time for Oracle to have invented a really slow
table.

As my book says "you use clustered tables each and every day of your life with
Oracle.  the data dictionary is built on them".

Read about the different types....
Think about how they are implemented....
Apply them to particular problems when they are relevant....

October 02, 2002 Ed -- Thanks for the question regarding "Pin SQL
      Cursors", version 8.1.7.2

      You Asked
Hi Tom--
We've implemented Outlines in our environment and they've been a great success.
Thanks (as I relied on your book tremendously in implementation).  I'm trying to
now pin those statements in the Shared Pool by doing the following:
1. Database trigger on database startup will call stored procedure
2. Stored procedure selects sql_text from dba_outlines where used = 'USED' into
cursor
3. SQL_text is parsed via dbms_sql.parse(sql_text)

The odd thing is my primary cursor has 1407 entries, and my procedure will pin 2
of those.  Coincidentally (or not), the two statements it does pin do not
contain bind variables and most (although not all) of the others do contain bind
variables.

Given this generic description, is there anything wrong with my approach?
Thanks in advance! -Ed Stuart
===========================================
Well-- since I've got the space--  here's the whole procedure:

CREATE OR REPLACE PROCEDURE sls.Pin_Outlines1 AS
CURSOR c_cursor IS SELECT name,sql_text
FROM DBA_OUTLINES
WHERE owner LIKE 'SLS';

CURSOR c1 IS SELECT DISTINCT RAWTOHEX(address) addr,hash_value
FROM v$sql
WHERE outline_category = 'SFA'
AND kept_versions = 0;

l_cursor INTEGER;
l_status INTEGER;
v_name VARCHAR2(30);
v_sql_text LONG;
v_file_id_num utl_file.file_type;
v_file_dir VARCHAR2(30) := '/u16/oradata/utl/';
v_file_name VARCHAR2(30) := 'pin_outlines.txt';
v_count NUMBER;

addr_plus_hash VARCHAR2(100);
BEGIN
v_file_id_num := UTL_FILE.FOPEN(v_file_dir,v_file_name,'w',32000);
UTL_FILE.PUT_LINE(v_file_id_num,'Utl file open');
v_count := 0;
EXECUTE IMMEDIATE 'ALTER SESSION SET USE_STORED_OUTLINES = SFA';
OPEN c_cursor;
LOOP
  v_count := v_count+1;
  FETCH c_cursor INTO v_name,v_sql_text;
  EXIT WHEN c_cursor%NOTFOUND;
  BEGIN
       l_cursor := dbms_sql.open_cursor;
         dbms_sql.parse (l_cursor, v_sql_text, dbms_sql.native);
         dbms_sql.close_cursor (l_cursor);
  END;
  UTL_FILE.PUT_LINE(v_file_id_num,'-- Statement
:'||v_count||v_name||v_sql_text);
  UTL_FILE.PUT_LINE(v_file_id_num,' ');
END LOOP;
CLOSE c_cursor;
/* to pin cursors run */
FOR C IN C1 LOOP addr_plus_hash := c.addr||','||c.hash_value;
  UTL_FILE.PUT_LINE(v_file_id_num,'-- Statement :'||addr_plus_hash);
--SYS.DBMS_SHARED_POOL.KEEP(addr_plus_hash,'C');
END LOOP;
UTL_FILE.FCLOSE(v_file_id_num);
EXCEPTION
etc.
=============================
Hi Tom--
I thought I updated this once but it sure doesn't look like it stuck.

The goal to pinning these is because we have noticed the sql ages out of the SQL
cache.  I would like it available in the SQL Cache so it does not have to be
reloaded. I've always understood this to be a performance enhancement-- I would
be curious as to your view that it's not.  Or perhaps it's your view we
shouldn't need to pin because we shouldn't be aging out because we should have
bind variables/reusability on most code against the database.  Yes, in a perfect
world and we are trying to get there but in the meantime....

We do have additional applications that do not use bind variables. They are by
far the smaller applications.  The application we are most concerned about is
the one that we instituted bind variables and Outlines and for which we are
trying to pin.

All that aside and even if you don't recommend pinning, the technical issue
continues to puzzle me.  Some queries pin properly?  Others do not?  Whenever I
find something like this it usually indicates some nuance of Oracle behavior
that is iluminating.  I'd really like to know why this works for some queries
but not work for most queries.
Thanks.
-Ed
==========================================================
Hi Tom--
The C1 cursor loops twice only and that is due to how it is constructed-- it's
SELECT is dependent on outline_category being filled in with the category name
'SFA'.  When I do a select on v$sql or v$sqlarea, all the other queries are in
the SQL cache but with outline_category= NULL.  The queries may look the same,
but I don't trust they are following the guidelines of the Outline without the
outline_category column indicating it.

This leads me to think (maybe incorrectly) that two and only two of the queries
are verbatim matches.  The other 1405 queries must not be and therefore are
being ignored by the stored Outlines when a match is attempted.  I notice
nothing different about these two queries that are recognized and the majority
of those that are ignored.

This is what is puzzling: I take the statement verbatim from the Outline view (I
have also tried directly from the Outline table ol$), load into a parameter of
the same type (long), and then run through dbms_sql.parse.  I don't manipulate
it in any other way.  How could it possibly NOT match is what is so darn
frustrating.
-Ed

      and we said...
In 8172 -- I cannot reproduce.  My test case is below -- try it on a test
system.

One thing I can think of is that the owner of the procedure is UNABLE to parse
the third query it fetches (eg: the query is "select * from t" and the owner of
the procedure either doesn't have SELECT on T or T would need to be qualified
for them to parse it successfully).  That throws an exception -- breaks out of
the procedure and never processes the remaining queries.  I don't see your
exception handler but if its just "when others then null;", this could very
easily be the case.

Here is my counter case on solaris showing this at work:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view testing
  2  as
  3  select outline_category , sql_text
  4    from v$sql
  5   where sql_text like 'select * from dual%'
  6   order by 1, 2
  7  /
View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2          for x in ( select category from user_outlines )
  3          loop
  4                  outln_pkg.drop_by_cat( x.category );
  5          end loop;
  6  end;
  7  /
PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter system flush shared_pool;
System altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set create_stored_outlines =
my_cat;

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from dual q1;

D
-
X

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from dual q2;

D
-
X

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from dual q3;

D
-
X

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set create_stored_outlines =
false;

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec print_table( 'select * from
user_outlines' )
NAME                          : SYS_OUTLINE_020303140131580
CATEGORY                      : MY_CAT
USED                          : UNUSED
TIMESTAMP                     : 03-mar-2002 14:01:31
VERSION                       : 8.1.7.2.0
SQL_TEXT                      : select * from dual q1

-----------------
NAME                          : SYS_OUTLINE_020303140131651
CATEGORY                      : MY_CAT
USED                          : UNUSED
TIMESTAMP                     : 03-mar-2002 14:01:31
VERSION                       : 8.1.7.2.0
SQL_TEXT                      : select * from dual q2

-----------------
NAME                          : SYS_OUTLINE_020303140131676
CATEGORY                      : MY_CAT
USED                          : UNUSED
TIMESTAMP                     : 03-mar-2002 14:01:31
VERSION                       : 8.1.7.2.0
SQL_TEXT                      : select * from dual q3

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

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec print_table( 'select * from testing'  )
OUTLINE_CATEGORY              :
SQL_TEXT                      : select * from dual q1
-----------------
OUTLINE_CATEGORY              :
SQL_TEXT                      : select * from dual q2
-----------------
OUTLINE_CATEGORY              :
SQL_TEXT                      : select * from dual q3
-----------------

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure p
  2  as
  3          l_cursor integer default dbms_sql.open_cursor;
  4  begin
  5          execute immediate 'alter session set use_stored_outlines = my_cat';
  6
  7          for x in ( select sql_text from dba_outlines where category =
'MY_CAT' )
  8          loop
  9                  dbms_sql.parse( l_cursor, x.sql_text, dbms_sql.native );
 10          end loop;
 11          dbms_sql.close_cursor( l_cursor );
 12
 13          execute immediate 'alter session set use_stored_outlines = false';
 14
 15          for x in ( select distinct rawtohex(address) || ',' || hash_value
addr_hash,
 16                                            '"' || sql_text || '"' sql_text
 17                               from v$sql
 18                                  where outline_category = 'MY_CAT' )
 19          loop
 20                  SYS.DBMS_SHARED_POOL.KEEP(x.addr_hash,'C');
 21          end loop;
 22  end;
 23  /

Procedure created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec p

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec print_table( 'select * from testing'  )
OUTLINE_CATEGORY              : MY_CAT
SQL_TEXT                      : select * from dual q1
-----------------
OUTLINE_CATEGORY              : MY_CAT
SQL_TEXT                      : select * from dual q2
-----------------
OUTLINE_CATEGORY              : MY_CAT
SQL_TEXT                      : select * from dual q3
-----------------
OUTLINE_CATEGORY              :
SQL_TEXT                      : select * from dual q1
-----------------
OUTLINE_CATEGORY              :
SQL_TEXT                      : select * from dual q2
-----------------
OUTLINE_CATEGORY              :
SQL_TEXT                      : select * from dual q3
-----------------

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2          print_table( 'select *
  3                          from v$db_object_cache
  4                         where kept = ''YES''
  5                           and namespace= ''CURSOR'' ' );
  6  end;
  7  /
OWNER                         :
NAME                          : select * from dual q2
DB_LINK                       :
NAMESPACE                     : CURSOR
TYPE                          : CURSOR
SHARABLE_MEM                  : 10120
LOADS                         : 1
EXECUTIONS                    : 1
LOCKS                         : 0
PINS                          : 0
KEPT                          : YES
-----------------
...
-----------------
OWNER                         :
NAME                          : select * from dual q3
DB_LINK                       :
NAMESPACE                     : CURSOR
TYPE                          : CURSOR
SHARABLE_MEM                  : 1440
LOADS                         : 1
EXECUTIONS                    : 0
LOCKS                         : 0
PINS                          : 0
KEPT                          : YES
-----------------

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger startup_trigger
  2  after startup on database
  3  begin
  4          p;
  5  end;
  6  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> @connect "/ as sysdba"

sys@ORA817DEV.US.ORACLE.COM> shutdown
Database closed.
Database dismounted.
ORACLE instance shut down.

sys@ORA817DEV.US.ORACLE.COM> startup
ORACLE instance started.

Total System Global Area  247398560 bytes
Fixed Size                    73888 bytes
Variable Size             150904832 bytes
Database Buffers           90112000 bytes
Redo Buffers                6307840 bytes
Database mounted.
Database opened.

sys@ORA817DEV.US.ORACLE.COM> @connect /

ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2      print_table( 'select *
                    from v$db_object_cache
  3    4                     where kept = ''YES''
                     and namespace= ''CURSOR'' ' );
  5    6  end;
/  7
OWNER                         :
NAME                          : select * from dual q2
DB_LINK                       :
NAMESPACE                     : CURSOR
TYPE                          : CURSOR
SHARABLE_MEM                  : 5236
LOADS                         : 1
EXECUTIONS                    : 0
LOCKS                         : 0
PINS                          : 0
KEPT                          : YES
-----------------
...
-----------------
OWNER                         :
NAME                          : select * from dual q3
DB_LINK                       :
NAMESPACE                     : CURSOR
TYPE                          : CURSOR
SHARABLE_MEM                  : 1372
LOADS                         : 1
EXECUTIONS                    : 0
LOCKS                         : 0
PINS                          : 0
KEPT                          : YES
-----------------

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec print_table( 'select * from testing'  )
OUTLINE_CATEGORY              : MY_CAT
SQL_TEXT                      : select * from dual q1
-----------------
OUTLINE_CATEGORY              : MY_CAT
SQL_TEXT                      : select * from dual q2
-----------------
OUTLINE_CATEGORY              : MY_CAT
SQL_TEXT                      : select * from dual q3
-----------------
OUTLINE_CATEGORY              :
SQL_TEXT                      : select * from dual q1
-----------------
OUTLINE_CATEGORY              :
SQL_TEXT                      : select * from dual q2
-----------------
OUTLINE_CATEGORY              :
SQL_TEXT                      : select * from dual q3
-----------------

PL/SQL procedure successfully completed.

-------------------------------------------------------------------------
Ok -- it is a bind variable issue (and if you have my book, you know how I feel
about that.  You should really fix the problem, not the symptom.  Even for
"small" things)

It looks ok -- but I need to know something more.  You say:

"The odd thing is my primary cursor has 1407 entries, and my procedure will pin
2
of those. "

Now -- does the cursor for loop C1 loop 2 times or 1407 times?  Does your debug
indicate that you are calling keep on 1407 or 2?

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

before I look at this -- i'd like to know:

why are you trying to pin them?  whats the goal?

do you have applications that do not use bind variables?

pinning is something I do not recommend, don't like, and for which there is no
need these days (used to be IF you ran MTS in 7.3 and before)
Review &
      Followup

      Rating: 3
Pin SQL
      Cursors  March 08, 2002
Reviewer:  Ed Stuart
       from Rochester, NY USA
Hi Tom-- Thanks for your assistance on this.  The proof you supplied was useful
but one of your comments tipped me in the right direction-- that was that one of
the statements was unable to be parsed.  That is correct as some of my
statements were using bind variables, and I had made no provision for supplying
those values.  When I added a dbms_sql.bind_variable call for each bind variable
that was being used, that solved that problem.  I now have a working procedure.
Thanks again!

          Followup:

hmm, it does not seem that like that could be it -- you only need to BIND
after parsing!!!

I don't see how binding could or would affect this at all.

      Rating: 3
Pin SQL
      Cursors  March 08, 2002
Reviewer:
      A reader
Hi Tom--
Couldn't find another way to update your followup.  Let me think about the
sequence here-- what I got was successful parsing but unsuccessful pinning of
any statements that used bind variables.  The pinning in my program was
dependent on the outline_category field of v$sql being not null (I wanted to
make sure the Outlines were being used in the parse).  The statement with binds
would be in sql cache but I wouldn't trust they were exactly the same statement
unless the outline_category field was filled in (indicating to me that the
statement went through the Outline matching process).

Then I thought, let's execute this statement and see if the outline_category
field will be filled in for those statements containing bind variables.  THAT
was when I started to encounter problems with bind_variables not being bound,
etc etc.  The solution to getting ALL statements was to parse, bind (if bind
variables were present), execute.  The last two steps were not necessary for the
statements without bind variables but I needed them for statements with bind
variables.

If you're still curious, perhaps if you changed your demo you gave me using a
bind variable you would similarly see the outline_category field as NULL when
you only parsed.  You may conclude you could trust that the sql was the same but
could you really trust to that?
-Ed

          Followup:

Interesting -- yes I reproduced by changing one query to 'select * from dual q1
where dummy = :x'.

you need to bind/execute that one.

Here is my theory - 9i added a feature whereby the parse "halts" on the first go
around and doesn't do the query plan until you bind and execute for the first
time (gives the optimizer a chance to peek at the binds).  I believe this
stealth feature might be in 8i as well -- just not fully enabled.  Hence, simply
parsing the statement with binds isn't enough, we have to parse AND execute.

do you need a plsql routine that returns a plsql table of bind variable names
given a sql statement?  I have one if you do, used to do generic binds like
this...

      Rating: 4
Pin SQL
      Cursors  March 11, 2002
Reviewer:  Ed Stuart
       from Rochester, NY USA
Hi Tom--
Yes!  If you have one, that would be great.  I kind of fudged to get it to work
by using IF statements in the LOOP (I had to feed the SQL Text into a VARCHAR2
and then do an INSTR, eg. IF INSTR(':V001',sql_text)>0 Then...bind :V001).  It
works, but the coding sucks.

Thanks again for you help (and the validation I'm not really going crazy).
-Ed

          Followup:

Here you go -- returns an array of binds from a statement -- just loop from 1 ..
returned_array.count to iterate over them:

function get_binds( p_stmt  in varchar2 ) return dbms_sql.varchar2_table
as
    l_binds      dbms_sql.varchar2_table;
    l_ch         varchar2(2);
    l_str        varchar2(255);
    l_stmt       long default p_stmt;
    l_added      long default ':';

    n            number;
    m            number;
    o            number;
begin
    loop
        n := nvl( instr( l_stmt, '/*' ), 0 );
        exit when (n=0);
        m := nvl( instr( l_stmt, '*/', n+1 ), length(l_stmt) );
        if ( m = 0 ) then m := length(l_stmt); end if;
        l_stmt := substr( l_stmt, 1, n-1 ) || substr( l_stmt, m+2 );
    end loop;
    loop
        n := nvl( instr( l_stmt, '--' ), 0 );
        exit when (n=0);
        m := nvl( instr( l_stmt, chr(10), n+1 ), length(l_stmt) );
        if ( m = 0 ) then m := length(l_stmt); end if;
        l_stmt := substr( l_stmt, 1, n-1 ) || substr( l_stmt, m+1 );
    end loop;
    loop
        n := nvl( instr( l_stmt, '''' ), 0 );
        exit when (n=0);
        o := n;
        loop
            m := nvl( instr( l_stmt, '''', o+1 ), length(l_stmt) );
            if ( m = 0 ) then m := length(l_stmt); end if;
            if ( substr(l_stmt,m+1,1)  = '''' )
            then
                o := m+1;
            else
                exit;
            end if;
        end loop;
        l_stmt := substr( l_stmt, 1, n-1 ) || substr( l_stmt, m+1 );
    end loop;

    loop
        n := nvl( instr( l_stmt, ':' ), 0 );
        exit when ( n= 0 );
        l_str := upper(substr(l_stmt,n,31));
        for j in 2 .. length(l_str)
        loop
           l_ch := substr( l_str, j, 1 );
           if ( l_ch not between 'A' and 'Z' and
                l_ch not between '0' and '9' and
                l_ch <> '_' )
           then
                l_str := substr(l_str,1,j-1);
                exit;
           end if;
        end loop;
        if ( length(l_str) > 1 )
        then
            if (instr( l_added, l_str||':' ) = 0)
            then
                l_added := l_str || l_added;
                l_binds(l_binds.count+1) := l_str;
            end if;
        end if;
        l_stmt := substr( l_stmt, n+1 );
    end loop;
    return l_binds;
end get_binds;

Also, I found this to be interesting enough to be of general purpose interest
and have published it.

      Rating: 3
Can we do this
      ?  March 11, 2002
Reviewer:  reader

Hi tom,

  If I execute a procedure with refcursor in one session,
e.g.

     exec get_data('01-mar-2002',sysdate-1,:x);

now my :x is pointing to my restult set.

 Can I publish this pointer (:x) to multiple sessions ?

Thanks,
vivek

          Followup:

nope, a cursor is local to a session.

      Rating: 5
pin cursor at
      start up  October 01, 2002
Reviewer:
      A reader
hi

to pin sql cursor when we start the database we must first parse the sql cursor
we want to pin and then get the sql address and hash value?

          Followup:

yes, but, since 8.0 with the addition of the large pool to separate out MTS
memory allocations from the sql area and such -- pinning is sort of obsolete and
a waste of your time and energy if you ask me.  Size the shared pool correctly,
use bind variables and don't worry about it.

      Rating: 4
I am not using
      MTS  October 02, 2002
Reviewer:
      A reader
Hi

I am not using MTS, is it still a waste of time....?

          Followup:

.  Size the shared pool correctly,
use bind variables and don't worry about it.

October 02, 2002 Gabriel -- Thanks for the question regarding "Use
      of FGAC in multiple session applications", version 8.1.7

      You Asked
Hi Tom, here's the picture:

- N 'companies' which share a common schema definition but have their own data.
- Users that must be able to work on some (up to N) of the companies.
- Old application that works on schema data, not aware of multiplicity of
companies.
- Separate database for each company, same schema name for each.

Goal: have a single database that holds the N companies' data. Ideally a single
schema. Make it so user (and app) thinks he is connecting to a separate
database.

Possibilities I thought of:
1) Single database, N schemas, use current_schema to transparently let each
connection see the intended company's data.
2) Single database, single schema, use FGAC to achieve the same purpose.

I like 2) best, it may not be the exact use intended for FGAC (it's not strictly
a security issue) but looks fine.

The problem:
A) The app is a Forms 6i one. Once the user chooses a company and connects, he
is running a menu form. This menu form calls the 'real' application forms. Since
the user is allowed to open many instances of the same form (and others)
simultaneously, each form is open using a new, private session.
B) From the application the user submits requests to a Reports 6i Server (that
keeps a pool of connections open as a form of optimization). Of course the
report ends up running in a different session than that of the menu form.
C) The application context used to enforce FGAC policies does not depend on the
(static) username, but on the choice he made when connecting (when opening the
first session, that of the menu). I need some way to "inherit" the context
setting for every form/report that is called from the menu. Ideally without
having to re-set the context from every new form/report open, since that would
imply modifying the code. But even if I was willing to modify the forms/reports
to first thing call the set context procedure (or set the current schema), I
need a way for them to know to which company they should connect.

Workarounds I thought of :
i. Have the menu pass the company as a parameter to forms/reports.
ii. Use (somehow) a table
iii. Have a different DB account for each (user,company) that allows me to
statically determine which company a user is connected to.

Both i and ii mean having to change the code, but iii doesn't look good at all.
So I would go for either "1)i" or "2)i.". What would you do in a case like this?
Can you tip me?

Thanks!

      and we said...
#2 is exactly what i've done -- with great success, on EXISTING apps.

It is exactly one of the intended uses for FGAC and in fact how
salesonline.oracle.com works!

You will need to pass COMPANY around -- or use (iii)

If forced to choose, I like

2(iii)

but could live with 2(i)

Review &
      Followup

      Rating: 3
What about
      replication?  September 17, 2002
Reviewer:
      Andrew Clarke  from London, England
I have a similar set-up as this case, only it's web-based rather than forms.  We
opted for explicitly passing the COMPANY-equivalent variable each time a user
connects to the database i.e. 2(i).  What with an unknown number of users, EJBs
and connection pooling I don't think we have any option.

I'm intrigued to know why your first choice would be creating separate schemas
for each company.  I would have though that would generate a lot of additional
work whenever some DDL is called for.

Cheers, APC

          Followup:

In this case -- 2(iii) prevents them from having to TOUCH and existing
application (always best I think).

Having thousands and thousands and thousands of schemas is no big deal.  I don't
see where DDL comes into play (especially since on an existing in production
system -- there won't be any DDL really)

      Rating: 4
So be
      it  September 18, 2002
Reviewer:  Gabriel
      Canton  from Bs. As., Argentina
I was kind of hoping that you would come up with one of your magical solutions
to my problem, but hey, if that's also what YOU did, I can quit worrying about
finding a better solution.

Just wanted to add a couple of comments:

1) I think that for Reports (at least when caching them) the only option is to
pass a company parameter. This is because the Reports Server detects 'duplicate
jobs' based on parameters only, and thus it would probably end up mixing the
companies.

2) For Forms I think there's another, totally irrelevant solution. From within
Forms you can retrieve the connect string used to open the session, and I'm
quite sure forms open using open_form (session) are open using the calling
form's connect string. This means that you would still need to set the context
from every form, but you wouldn't have to pass it as a parameter. All you would
need to do is create N aliases pointing to the same database. In my case this
little difference doesn't help much, I just mention it in the dubious case it
comes handy to someone.

Tom: thank you very much, and don't ever quit!

      Rating: 4
Insertion of
      new records  September 30, 2002
Reviewer:
      Gabriel Canton  from BA., Argentina
What would you recommend to (if possible) let the application insert new records
without having to modify it's insert statements to fill in the new column that
holds the company (the one on which the FGAC predicate is based)? Of course the
value should be the company to which the user is 'connected', which is held in
the context.
Insertion triggers? Anything better?
Sorry but I really hadn't thought of this at the time I wrote the initial
question ...

          Followup:

I would rename the tables from X to X_Table

I would create views X as select ..... from x_table and grant on those, revoke
on the tables.

alter the tables, add the column, put a trigger on there to maintain the value.

      Rating: 4
Why different
      tables?  October 02, 2002
Reviewer:  Gabriel
      Canton  from BA, Argentina
Could you explain a little the purpose of creating separate tables and a view?
I'd like to understand this better.
To begin with, I thought one of the main advantages of FGAC vs. current_schema
was that you needn't create multiple identical tables.
If I did create multiple tables, I might as well place them in different
schemas, and then I also might use current_schema instead of FGAC.
If the purpose of having multiple tables was partitioning, well, I could use
partitioning instead.
What I am testing now is:
Schema THE_APP (one and only one). Table THE_APP.X, partitioned by company (X_1,
..., X_N). Predicate: 'company =
to_number(sys_context('THE_APPs_CTX',''current_company'))'.
To handle inserts I created this sample trigger:

 create or replace trigger ins_X before insert on (THE_APP.)X
 for each row
 begin
   :new.company := to_number(sys_context('THE_APPs_CTX',''current_company'))';
 end;

I am not trying to persuade you, only it's obvious I must be missing something.
Is it related to performance?

          Followup:

the purpose of the view is to hide this new column from the app -- it sees the
old tables with no new columns.

current_schema doesn't work in general -- it is NOT a good solution.  consider
your stored procedures (you would have to use invokers rights, it would flood
the shared pool with lots more sql versions, you would have a NIGHTMARE of
grants to manage)

what you are testing is exactly what I was suggesting, the view just hides this
company column from existing code that might do a select * (and would choke on
the company column) or insert into t values ( .... ) and would choke on the
company column

      Rating: 5
Got
      it!  October 03, 2002
Reviewer:  Gabriel
      Canton  from BA, Argentina
Why, of course! I hadn't thought of that, thanks a lot, Tom. Everything fits
now.

October 02, 2002 Punit -- Thanks for the question regarding
      "Mutating Tables", version 8

      You Asked
In a Database Trigger whenever a Select statement is issued on a Table on which
the Trigger is being induced, a Table Mutation error occurs..what is the
solution to this problem... ?

For example, say I want to code a trigger that dis-allows increment in salary of
an employee above the salary of his Manager in the EMP Table, How can this be
done ?

      and we said...
see

http://osi.oracle.com/~tkyte/Mutate/index.html
Yours is a PERFECT example of why mutating table errors are a GOOD thing.

Consider this update:

update emp set sal = sal * 1.1;  -- give everyone a 10% raise.

Lets say we let you do this row by row as you tried. Now, say I have this data
in the table:

empno       ename       sal       mgr
1           tom         100       null
2           bill         95       1

So, bill works for tom (mgr=1) and bill makes 95.

Now, we run that update.

Suppose for whatever reason, bills record gets updated first (remember, there is
no ORDER in the rows in a table, we do not know which record will get updated
"first").  That would have bill's salary get set to 104.5.  Thats more then
100!!!  Bummer, it fails.

On the other hand, suppose tom's record gets done first.  Tom gets 110, bill
gets 104.5.  No problems - works great.

So what you say -- thats OK, sometimes it works, sometimes it doesn't.  Well,
the problem is that not only is is unreliable, it can mess up our data integrity
as well!  consider this update now:

update emp set sal = decode( mgr, NULL, 0.99, 1.05 ) * sal;

Here (it was a lean year, tough economic times) management is taking a 1% pay
cut and everyone else gets a 5% increase.  Great -- what happens if:

bill gets updated first?  well, he gets bumped upto 99.75.  No problem, that is
less then 100.  But then tom gets lowered to 99, thats less then 99.75 --
problem!!  The rule will have let this update go through but the data at the end
is WRONG.

Constraints like this, that need to see many rows simultaneously and
consistently, must be done AFTER the update is done (in the AFTER UPDATE, not a
row level trigger).

Just like we enforce UNIQUE constraints *AFTER* the update is done -- not
DURING, you must wait until AFTER to check your constraint (consider this
update:

update emp set empno = decode( empno, 1, 2, 2, 1 );

if we did the check row by row for uniqueness -- it would fail on the first row!
Review &
      Followup

      Rating: 5
Excellent
        October 20, 2001
Reviewer:  Doug Cowles
       from CT, USA
I think the original idea is bad to begin with.  Some employees deserve more
than their managers. :)  That's another way to avoid the mutating table problem.

      Rating: 4
So, can't there
      be any select at all inside this type of triggers?  October
      01, 2002
Reviewer:  Prince
Does that mean, I can't do any query at all from within that trigger?

Why does this case fail, though it doesn't change anything?

CREATE OR REPLACE TRIGGER AU_SAL AFTER
UPDATE OF "SAL" ON "EMP" FOR EACH ROW declare
  dummy_var varchar2(30);
begin
    select 1 into dummy_var from emp where empno = :new.empno ;
end ;

SQL>  update emp set SAL = 1000 where EMPNO = 5149 ;
 update emp set SAL = 1000 where EMPNO = 5149
        *
ERROR at line 1:
ORA-04091: table DBUSR.EMP is mutating, trigger/function may not see it
ORA-06512: at "DBUSR.AU_SAL", line 5
ORA-04088: error during execution of trigger 'DBUSR.AU_SAL'

SQL> desc emp
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 EMPNO                                     NOT NULL NUMBER
 DEPTNO                                             NUMBER
 EMPNAME                                            VARCHAR2(30)
 MGRNO                                              NUMBER
 SAL                                                NUMBER(8)

          Followup:

You can't read the table you are modifying.

you can read pretty much every other table in the database.

It fails cause the documentation says "it will fail, utterly, miserably and
every time and we are doing it on purpose".

See the link,the page it points to points to the docs

      Rating: 5
thanks
      Tom!  October 02, 2002
Reviewer:  Prince

I had this issue where, when I modify the column colX, I need to increment the
column colY, of its parent row.

Since, I can not issue select on the same table to identify its parent, I guess
I have to follow the other mechanisms mentioned in the doc.

          Followup:

http://asktom.oracle.com/~tkyte/Mutate/index.html

October 03, 2002 lalain -- Thanks for the question regarding
      "ABOUT PRIVILIGES", version 8.0

      You Asked
HI SIR

i create a user with priviliges of "CREATE SESSION" & "CREATE TABLE"
so this user connects to database but when i try to create a table so it is not
created and displayed an error that is

ORA-01950: NO PRVILIGES ON TABLESPACE 'SYSTEM'

sir please tell me what is reason of this and how can i solve this problem?

i think, table should create because i have granted the priviliges of
"CREATE TABLE".

this same problem automaticaly solved when i granted the role of "RESOURCES"
what is reason of this please tell me clearly.

thanks

waiting for reply in my INBOX

      and we said...
the resource role has the
UNLIMITED TABLESPACE

priv, meaning it can use as much of any tablespace as it wants.

By default, you have a 0k quota on tablespaces.  Without resource (which has
unlimited tablespace) you cannot allocate any storage.

I recommend you

alter user USERNAME default tablespace <something OTHER than system> quota
unlimited on <something OTHER than system>;

that'll fix him right up.

That user can actually create tables, they just cannot create tables that
allocate space ;)

Consider this example (in 8.0 you don't have temporary tables but it just
illustrates the point):

ops$tkyte@ORA817.US.ORACLE.COM> create user a identified by a;
User created.

ops$tkyte@ORA817.US.ORACLE.COM> grant create session, create table to a;
Grant succeeded.

ops$tkyte@ORA817.US.ORACLE.COM> connect a/a
Connected.

ops$tkyte@ORA817.US.ORACLE.COM> create table t (x int);
create table t (x int)
*
ERROR at line 1:
ORA-01950: no privileges on tablespace 'SYSTEM'

ops$tkyte@ORA817.US.ORACLE.COM> create global temporary table t ( x int );
Table created.

ops$tkyte@ORA817.US.ORACLE.COM> connect /
Connected.

ops$tkyte@ORA817.US.ORACLE.COM> alter user a default tablespace users
  2  quota unlimited on users;
User altered.

ops$tkyte@ORA817.US.ORACLE.COM> connect a/a
Connected.

ops$tkyte@ORA817.US.ORACLE.COM> create table tt ( x int );
Table created.

Review &
      Followup

      Rating:
      3
  October 03, 2002
Reviewer:
      A reader
where in the data dictionary can we see that the resource role has the unlimited
tablespace privilege

          Followup:

Well, semantically speaking, I mispoke.

the resource role doesn't "have it".

granting someone the resource role implicitly grants UNLIMITED TABLESPACE to the
user.

revoking resource implicitly revokes it from them.

UNLIMITED TABLESPACE cannot actually be granted to a role at all!  so, it is not
in the data dictionary.

      Rating:
      4
  October 03, 2002
Reviewer:
      A reader
which other privileges are implicitly granted through the roles
is it possible to list them somehow

          Followup:

that is the only anomaly I am aware of.

It follows with this advice:

do not use connect, resource or DBA unless you have to.  Somethings require DBA
(the products look for that role unfortunately) but you should not need connect
(which has WAY too much power) and resource (even more)

create your OWN roles and use those instead.

      Rating:
      5
  October 03, 2002
Reviewer:
      A reader
thanks tom!

October 03, 2002 Sam -- Thanks for the question regarding "Top
      most things a startup dba should be proficient", version 8.1.7

      You Asked
Tom

Can you give your piece of mind on the following..

We were having a discussion out here , and I opined that a startup dba or a dba
with round about 2 to 3 years experience should be good enough for the
following.

1.Sound knowledge of the architechture.
2.Good experience of Backup , and at least theoritically sound in Recovery.
3.Proficient in Tablespace management.
4.Proficient in Schema creation and privilege management.
5.Sound in Security concepts.
6.Sound knowledge of Sql loader
7.Reasonably good in Shell scripting( assuming that the dba is working on unix).
8.Good knowledge of data dictionary views.
9.Sound knowledge of daily activities like alert log monitoring etc

What else would you like to add to the above.

Further, if some one were to ask you to divide the most important tasks of a dba
into daily , weekly, and monthly , how would that appear.

Thank you

      and we said...
How about the most important one:

0) has excellent interpersonal skills, truly likes working in a team
environment.  Has a good "can do" attitude.  Understands that the developers, as
well as end users, are his/her customer and that when the customer isn't happy
-- they are not doing their job.  Having the database up and running is one of
their tasks, their main job is customer satisfaction. Understands the difference
between "keeping the database protected" and "getting the job done" (meaning
that draconian rules such as "no views in my database, no procedures in my
database" are stupid).  Has the ability to use imagination and technical
know-how to solve problems, not just "book stuff" they know, more like common
sense.

I can teach anyone the database, I cannot make them a good co-worker.

Your list sounds good for your environment (i suppose).  I would encourage you
to (when interviewing) ask each candidate to simply go to the whiteboard and
"draw" a picture of Oracle -- its main processes/threads, whats in the SGA, how
the files are layed out -- what each file does, the importance of each and so
on.  I'll betcha 80% cannot do it (even more I think) -- even though its clearly
depicted early on in the concepts guide.  Ask them if they've ever read that
document and if so, give a brief outline of the topics covered therein (most
will say they read it, most will say "i dont know" to whats in it)I say it looks
good for your environment because there is no mention of "GUI" skills -- no OEM,
no system mgmt tools.  Many shops rely on these tools (i myself wouldn't make
the cut there ;)

I don't really have any weekly/daily/monthly jobs for DBA's.  In a smoothly
running shop -- it should be "business as usual" day in, day out.  Everything
should be automated -- so the nightly backups are done every day (outlining how
my databases run) in HOT mode, there are at least 3-5 backups stored offline,
the every other nightly export happened, the database/OS pages us when something
bad happens, etc.  In this fashion, the DBA can spend their day helping people,
fixing things that go awry, etc.

Review &
      Followup

      Rating:
      4
Excellent  January 03, 2002
Reviewer:
      Shivaswamy

      Rating:
      5
  January 03, 2002
Reviewer:
      A reader
"I say it looks
good for your environment because there is no mention of "GUI" skills -- no OEM,
no system mgmt tools.  Many shops rely on these tools (i myself wouldn't make
the cut there ;)"

Tom, can you give us a list of popular GUI tools out there, for DB
administration.

What do you mean by  'i myself wouldn't make
the cut there'

          Followup:

I cannot really -- for the simple reason that "i myself wouldn't make the cut
there" means I have no gui skills.  I get totally lost in them trying to find
the darn commands in a menu structure (which is really hard when you actually
KNOW all of the commands and cannot find them).

I use the command line -- i use sqlplus -- telnet is my gui.

      Rating: 4
Top most things
      a startup dba should be proficient  January 03, 2002

Reviewer:  shenif  from edmonton,alberta
I think you are right on the ball. But what about knowledge of pl/sql? How much
of it should be known?

          Followup:

for a DBA, it depends.  They need just enough to do simple loops and such --
scripting at the OS level seems more important.  If they can write sh, ksh, csh
-- they can write a little plsql when needed.

      Rating: 4
Making the
      cut  January 03, 2002
Reviewer:  Ralph
      Wagner  from Virginia USA
I think what he means is that he doesn't use the GUI based tools - so he
wouldn't make the cut with a 'GUI' series of questions.

If you've ever had the opportunity to see Tom speak (with a keyboard nearby)
you'd see that he's a SQL*Plus kind of guy - No Point-and-click stuff.  This
comes from spending your day in a telnet session rather than on a Windows
client.  :)

There are lots of 3rd party Tools out there for DB management -

These links (I think) are good starting points for DBAs or developers.  I'm sure
others have their favorites as well...

http://www.orafaq.com/tools/index.htm
and

http://www.natcapoug.org/list_of_oracle_tools_1.html
(about 3/4 down are DBA tools).

      Rating: 5
A good DBA is
      using one's own scripts for the job.  January 03, 2002

Reviewer:  Sean Bu  from Atlanta

      Rating: 5
Disagreement
      with Sean Bu   January 04, 2002
Reviewer:
      A reader
Sorry Sean but I disagree on your statement - As Tom said its all about getting
the job done and keeping users happy.

If this means using code provided by other people for whatever you are trying to
achieve and you are not violating copyrights etc. then by all means save
yourself and your users the time.

What is important is the knowledge to fully understand the code and realise when
it might not work on your system and being able then to make enhancements to it
should you need to.

But saying only self written code in my db is very ignorant,and it comes down to
what Tom called "draconian rules" in his response.
There is always someone who can do it better than you.

If the resources are there to use then use it.

      Rating:
      4
  February 27, 2002
Reviewer:  Colin
      Davies  from Los Angeles, California, USA
>2.Good experience of Backup , and at least theoritically sound in Recovery<

Backups are useless unless the DBA knows how to recover the database and has
practised it and documented it, including all the main scenarios (corrupt
datafile, corrupt rollback segment, etc.).  This should receive much greater
emphasis, especially in a 24x7 environment.

      Rating: 4
shell
      scripting  October 02, 2002
Reviewer:  Mike
       from TX
Since all agree that the unix shell scripting is one of major skills a dba must
have, I ask the following:

Until recently I had been working mostly on NT and limited on UNIX. Having not
been exploring much on the Unix shell scripting. The current job requires
considerably shell scripting skill, because the ETL is mainly based shell
scripts (though I believe it is not good idea).

What is the learning curve for acquiring the scripting skill? I want to be
reasonably good to understand and enable to make some modifications of the
current scripts.
Is it the deep as the same as to learn such as pl/sql?

TIA

          Followup:

I don't agree with the "since all agree".

Maybe in 1990 that would have been true, but in 2002 -- it is most certainly
not.

Is it helpful? (Yes)  Does it make them better? (sometimes, depends on the
quality of their code like anything else).  Is it necessary?  (no, i don't think
so.  If you can manage Oracle on NT, you can manage Oracle on Unix IMO.  You
need to learn some unix'isms like you did with NT..)

I cannot imagine ETL based mainly on scripting.  Sounds painfully slow unless
there ETL is painfully trivial or their data volumes are tiny.

Cannot really say how long it takes to learn script -- it was sooo long ago that
I was faced with learning.  It is rather learning the breadth of tools you'll be
using like grep sed awk nawk perl cut etc....

      Rating: 5
Thanks for the
      insight and info, Tom.  October 03, 2002
Reviewer:
      Robert  from Memphis, USA

      Rating: 4
Tracking down
      BV-Violaters  October 03, 2002
Reviewer:  Robert
      C  from PA
Tom, in your opinion, should a DBA keep an active eye (or 2) out for programmers
not using bind variables in their code ?
which may involve having to view their source code (pl/sql , java, vb...) if the
BV information is not readily apparent. Should the DBA take it up then with the
managers ?
Thanks

          Followup:

YES, definitely YES.

You don't need to read the code.  You'll find it in TEST by querying the v$
tables easily.  You'll find it in TEST when you hit lots of library latch
contention.  (what, you don't have test?  back to square one then)

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1163635055580
and yes, you need to speak to THEM first (not their manager, give them a chance,
teach them why this is bad).  then talk to their mgr ;)

A dba has the responsibility to make sure the DB is being utilized properly.

      Rating: 5
ETL process and
      shellscripts  October 03, 2002
Reviewer:
      A reader
Hi Tom

you said shell scripts is not very good idea for ETL processes but then how
would we code them?

Currently we use extensively combinations between

Shell scripts
PL/SQL, SQL scripts
PL/SQL Stored procedures
PRO*C
ETI*Extract (this generates shell scripts code)
SQL*PLUS
SQL*Loader
BMC Control-M

Mainly Shell scripts calls the tools and get the return codes

If these tools are not used I wonder in what language should we code ETL
processes?

          Followup:

shell scripts call tools -- so the shell scripts do almost nothing.  the shell
scripts aren't doing ETL, they are just being used like JCL on a mainframe would
be -- to execute programs.

October 03, 2002 Lachlan -- Thanks for the question regarding
      "Sharing complex query parts - Views vs PL/SQL Functions vs Packaged
      Cursors", version 9.2.0

      You Asked
Hi Tom,

What I would like to ask relates to your preferred method of sharing common
complex query parts between PL/SQL methods.

Initially use views and switch them to materialized views if performance is an
issue?
i) Our queries (and views) are hideously complex and tend not to allow outer
predicates to be pushed into the inner query. (Current relational schema needs a
serious refactoring).
ii) Our views are sometimes based on CONNECT BYs and therefore cannot be FAST
REFRESH ON COMMIT - (along with a fairly interesting policy from on high of "NO
TRIGGERS").  If we want to process an entity value on mutation we have to code
it explicitly in all methods that update that value.

Use PL/SQL functions of appropriate purity?
i) Obviously a performance hit if used below the FROM clause - though it does
ease development and promote reuse. Difficult to get more than one related value
back in a column though.

Use packaged parameterised cursors?
i) Very good reuse concept - except for one thing. I don't yet know of a way
that we can return an EXPLICIT packaged cursor back through a REF CURSOR (our
client dataset approach). (Perhaps there is a way with TABLE functions?)  (Are
there any thoughts of this approach in 10i?) I suppose the advantages of this
are that the parameters become BINDS in the sql to promote sharing and the
parameters can restrict the candidate set quicker than a view.  Would be
extremely helpful if we could compose CURSORS for things like generic paging.

Any wisdom, rules of thumbs or experience you would like to share on these
concepts?

Thanks muchly for your sterling efforts,

Lachlan Pitts
PS: What are your thoughts on comments in queries? (I see they do come through
in the SQL in the performance monitoring tools [I assume this could affect
cursor sharing capabilities] - should they be removed from views and set above
or below the queries in PL/SQL?)

      and we said...
I use views.

If the views do not perform in a given case, I either

a) create a new view that does (typically what I call a parameterized view).
see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1448404423206
for an example
b) code the query in the code so that it does (you would call this a packaged
cursor)

In general, unless you are a data warehouse, MV's don't really apply.

"interesting policy".  I have a different term for that.

You would not return a packaged cursor, you would return:

   open ref-cursor for select * from table where column = PLSQL_VARIABLE and
                          ........;

that is, the query wouldn't be in a package body/spec (i hate them there
personally), it would be "inline" (you know, where you can ACTUALLY see what the
code is doing!).  and it would be parameterized (whereever a plsql variable is
referenced in the query)

Comments in queries can be nice and if you use plsql -- the cursor sharing isn't
an issue -- everyone is using the same query!

Review &
      Followup

      Rating: 3
So where do I
      put the Ref Corsor's SQL ?  October 03, 2002

Reviewer:  Robert  from PA
>>open ref-cursor for select * from table where column = PLSQL_VARIABLE........;
>>...the query wouldn't be in a package body/spec (i hate them there
personally), it would be "inline" (you know, where you can ACTUALLY see what the

code is doing!).

Tom, you'd TYPE the Ref Cursor in the pkg spec, then OPEN it in the body in a
procedure/function, this not right ?
so what do you mean you hate the SQL in the body ? where else would you put the
SQL ?
"inline" where ? in the client app ?

          Followup:

No, I don't use typed cursors.  I use weak ref cursors

I don't put sql in the spec or body -- outside of a procedure/function that is
(sorry, was as clear as mud above I guess).

I don't use explicit cursors is what I meant.  I don't use "cursor c is ...." in
a spec or body.  I just reference the query inline -- in the code.

      Rating: 3
So where do I
      put the Ref Corsor's SQL ?  October 03, 2002

Reviewer:  Robert  from PA
>>open ref-cursor for select * from table where column = PLSQL_VARIABLE........;
>>...the query wouldn't be in a package body/spec (i hate them there
personally), it would be "inline" (you know, where you can ACTUALLY see what the

code is doing!).

Tom, you'd TYPE the Ref Cursor in the pkg spec, then OPEN it in the body in a
procedure/function, this not right ?
so what do you mean you hate the SQL in the body ? where else would you put the
SQL ?
"inline" where ? in the client app ?

      Rating: 4
Pertinent,
      clear and concise  October 03, 2002
Reviewer:
      Lachlan Pitts  from Brisbane, QLD, Australia
Thanks, Tom.

Another question, (ok a set of questions) answered masterfully :)

October 03, 2002 Freek -- Thanks for the question regarding "how
      to set the order of the evaluation of the where clause", version 8.1.7

      You Asked
Sir,

I have following table:

CREATE TABLE individual_attributes
 (
  iat_ind_urn                NUMBER(10) NOT NULL,
  iat_inr_uid                NUMBER(10) DEFAULT 0 NOT NULL,
  iat_att_code               VARCHAR2(5) NOT NULL,
  iat_batch_number           NUMBER(6),
  iat_value                  VARCHAR2(60) NOT NULL,
  iat_create_date            DATE,
  iat_create_user            VARCHAR2(30),
  iat_update_date            DATE,
  iat_update_user            VARCHAR2(30)
 )
/

ALTER TABLE individual_attributes
 ADD CONSTRAINT iat_pk PRIMARY KEY (iat_ind_urn,iat_inr_uid,iat_att_code)
/

when iat_att_code = 'DLVDT', then the iat_value field contains a date in the
format 'ddmmyyyy'.
For other values iat_att_code, non date values could be stored in the iat_value
field.
Now for a query I need to select all the records where the iat_att_code =
'DLVDT' and that iat_value_field contains a date in july 2002.

To do this I use a query with an inline view that selects the records where
iat_att_code = 'DLVDT' and converts the iat_value to a date.
In the parent query I compare the date with an other date.

select
    *
from
(
    select
        trunc(to_date(iat_value, 'ddmmyyyy'), 'mm') delivery_date
    from
        individual_attributes
    where
        iat_att_code = 'DLVDT'
) A
where
    A.delivery_date = to_date('01/07/2002', 'dd/mm/yyyy');

I use this inline view because the order of evaluation of the where clause in a
SQL statment is undefined (correct?).

This approach however does not seem to work (ORA-01858: a non-numeric character
was found where a numeric was expected).
I now that the values in the iat_value field are correct, because when I just
issue the inline query, everything works fine.

When I do an explain plan I get following result:

SQL> set autotrace traceonly explain
SQL> select
  2      *
  3  from
  4  (
  5      select
  6          trunc(to_date(iat_value, 'ddmmyyyy'), 'mm') delivery_date
  7      from
  8          individual_attributes
  9      where
 10          iat_att_code = 'DLVDT'
 11  ) A
 12  where
 13      A.delivery_date = to_date('01/07/2002', 'dd/mm/yyyy')
 14  /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=145 Card=173 Bytes=2422)
   1    0   TABLE ACCESS (FULL) OF 'INDIVIDUAL_ATTRIBUTES' (Cost=145 Card=173
Bytes=2422)

So it looks like the optimizer has merged the inline view and as a result of
that the restriction ia_att_code = 'DLVDT' is not
certain to be applied first anymore.

When adding a no_merge hint, the query performed ok.

SQL> set autotrace traceonly explain
SQL> select
  2      *
  3  from
  4  (
  5      select
  6          /*+ NO_MERGE */
  7          trunc(to_date(iat_value, 'ddmmyyyy'), 'mm') delivery_date
  8      from
  9          individual_attributes
 10      where
 11          iat_att_code = 'DLVDT'
 12  ) A
 13  where
 14      A.delivery_date = to_date('01/07/2002', 'dd/mm/yyyy');

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=145 Card=173 Bytes=1038)

   1    0   VIEW (Cost=145 Card=173 Bytes=1038)
   2    1     TABLE ACCESS (FULL) OF 'INDIVIDUAL_ATTRIBUTES' (Cost=145 Card=173
Bytes=2422)

Question is how sure can I be that the optimizer will still not take the first
execution plan, since a hint is nothing more than a hint.
Is there any other way to build this query, so that the to_date function only
will be applied to the correct selection of the table?

Thanks

Freek D'Hooge

      and we said...
the answer is -- you shouldn't.  You should write the query so the order of
evaluation is not relevant.

the order of evaluation should not have a bearing on your query -- in SQL the
order of evaluation is purposely "not defined" -- i can show you a query using
bottom up evaluation and then the same query using top down.

Your query could be written as:

SQL> select
  2      *
  3  from
  4  (
  5      select
  6          trunc(to_date(iat_value, 'ddmmyyyy'), 'mm') delivery_date
  7      from
  8          individual_attributes
  9      where
 10          iat_att_code = 'DLVDT'
 11  ) A
 12  where
 13      A.delivery_date = to_date('01/07/2002', 'dd/mm/yyyy')
 14  /

select trunc(to_date(iat_value, 'ddmmyyyy'), 'mm') delivery_date
  from individual_attributes
 where iat_att_code = 'DLVDT'
   and decode( iat_att_code, 'DLVDT',
           trunc(to_date(iat_value, 'ddmmyyyy'), 'mm'), null ) =
                          to_date( '01/07/2002', 'dd/mm/yyyy' );

or, easier:

select trunc(to_date(iat_value, 'ddmmyyyy'), 'mm') delivery_date
  from individual_attributes
 where iat_att_code = 'DLVDT'
   and iat_value = '01072002';

use CASE and/or DECODE to make it not matter what the order of evaluation is.
Review &
      Followup

      Rating:
      5
thanks  October 04, 2002
Reviewer:
      Freek D'Hooge  from Belgium
I knew I should create the query independent from the order of evaluation, I
just didn't know how to do it.
Never thought of using a decode.

thanks

Freek D'Hooge

      Rating: 5
Some other
      options  October 04, 2002
Reviewer:  Connor
      McDonald  from UK
Not really relevant in this case, but there is also an ORDERED_PREDICATES hint
that can be used if required.  I also recently saw a 9.2 example where the
optimizer reordered the predicates based on the anticipated cpu cost of those
predicates...very impressive indeed.

October 03, 2002 Frédéric -- Thanks for the question regarding
      "Grant on create", version 8.1.6

      You Asked
Platform:  Oracle 8.1.6.0.0 EE on Windows NT SP4

Hi Tom,
I want to write a system trigger that will automatically grant select to a role
when a table is created in a particular schema.
Because of the rule that you can’t commit in a trigger, my “execute immediate
‘grant select on…’ won’t work.
Saying I insert my grants into a table, what mechanism would you suggest to
“execute” them?  Do you know anything fancier than executing a procedure
scheduled in dbms_job to run every 5 minutes?  I would prefer not using any kind
of daemon that listens for an event (no need to start any procedure if I restart
the database).

Thank you for your time.

      and we said...
me, I would use DBMS_JOB:

  dbms_job.submit( l_job, 'execute immediate ''grant role to username''; ' );

and when the transaction committed, the job queue process would pick up the job
and execute it.  I would do it this way to make the grant TRANSACTIONAL with
respect to the create table -- if the create table rolls back -- so does the
queued job request (and the grant never happens)

You can also use autonomous transactions:

http://asktom.oracle.com/~tkyte/autonomous/index.html
as well.

October 03, 2002 Margaret -- Thanks for the question regarding
      "ordering the result", version 8.0.5

      You Asked
Dear Tom:
I have a table like this:
desc article_info:
===================
Name                            Null?    Type
 ------------------------------- -------- ----
 ID                              NOT NULL VARCHAR2(24)
 IDSPLIT                                  VARCHAR2(24)
 ARTICLE_TYPE_ID                          NUMBER(10)
 ASSIGNER_GID                             NUMBER(20)
 ASSIGNER_ID                              VARCHAR2(8)
 ATTRIBUTE                                VARCHAR2(2)
 CHAR_COUNT                               NUMBER(10)
 CHAR_SUGGEST                             NUMBER(10)
 CORRECT_STATUS                           NUMBER(10)
 DELETE_FLAG                              NUMBER(10)
 EDITION_ID                               NUMBER(10)
 FILE_NAME                                VARCHAR2(32)
 IMPORTANCE                               VARCHAR2(10)
 INPUT_DATE                               DATE
 KEY_WORD                                 VARCHAR2(128)
LINE_COUNT                               NUMBER(10)
 MODIFIED_DATE                            DATE
 PAGE                                     NUMBER(10)
 PAGE_NAME                                VARCHAR2(256)
 PAGE_SUGGEST                             NUMBER(10)
 POST_DATE                                DATE
 POST_TITLE                               VARCHAR2(128)
 PUBLICATION_ID                  NOT NULL NUMBER(10)
 REG_HEADER1                              VARCHAR2(1)
 REG_HEADER2                              VARCHAR2(1)
 REG_HEADER3                              VARCHAR2(1)
 REG_NAME                                 VARCHAR2(1)
 REGION_ID                                NUMBER(10)
 REPORTER_ID                     NOT NULL VARCHAR2(8)
 SECTION                                  NUMBER(10)
 SECTION_PAGE                             NUMBER(10)
 SPLIT_FLAG                               NUMBER(10)
 STATUS_FLAG                              NUMBER(10)
 TITLE                                    VARCHAR2(128)
 TOPIC                                    VARCHAR2(256)
 USED_FLAG                                CHAR(1)
 USER_ID                                  VARCHAR2(8)
VERSIONCOUNT                             NUMBER(10)
 WORD_COUNT                               NUMBER(10)
 WORK_FLAG                                CHAR(1)

Originally I have two indexes indx_01 on post_date and indx_02 on reporter_id.
And When we do the following query:

SELECT DISTINCT  t0.ID, t0.IDSPLIT, t0.ARTICLE_TYPE_ID,
t0.ASSIGNER_GID,t0.ASSIGNER_ID, t0.ATTRIBUTE, t0.CHAR_COUNT, t0.CHAR_SUGGEST,
t0.CORRECT_STATUS, t0.DELETE_FLAG, t0.EDITION_ID, t0.FILE_NAME, t0.IMPORTANCE,
t0.INPUT_DATE, t0.KEY_WORD, t0.LINE_COUNT, t0.MODIFIED_DATE, t0.PAGE,
t0.PAGE_NAME, t0.PAGE_SUGGEST, t0.POST_DATE, t0.POST_TITLE, t0.PUBLICATION_ID,
t0.REG_HEADER1, t0.REG_HEADER2, t0.REG_HEADER3, t0.REG_NAME, t0.REGION_ID,
t0.REPORTER_ID, t0.SECTION, t0.SECTION_PAGE,
t0.SPLIT_FLAG, t0.STATUS_FLAG, t0.TITLE, t0.TOPIC, t0.USED_FLAG,
t0.USER_ID,t0.VERSIONCOUNT, t0.WORD_COUNT, t0.WORK_FLAG
FROM
 ASSIGN_INFO t1, ARTICLE_INFO t0 WHERE ((t0.ARTICLE_TYPE_ID >=
  :ARTICLE_TYPE_ID0  AND t0.ARTICLE_TYPE_ID <= :ARTICLE_TYPE_ID1 ) AND
  t0.POST_DATE <= :POST_DATE2  AND t0.POST_DATE >= :POST_DATE3  AND
  t0.SPLIT_FLAG = :SPLIT_FLAG4  AND t1.RECEIVER_GID = :RECEIVER_GID5  AND
  t0.PUBLICATION_ID = :PUBLICATION_ID6 ) AND t0.ID  = t1.ARTICLE_ID

The result it displayed will be sorted by reporter_id, but it has changed(won't
be ordered by reporter_id ) after I have added an index:indx_03 on
(article_type_id, post_date, split_flag, publication_id)
My question is how could this happen? and can I fix it( to sort the result by
reporter_id)inside the database? However, I have no access to change the sql
statement(ie. using an order by clause)

      and we said...
If a query has no order by -- then by definition, by all reasonable
expectations, by the very rules of relational databases the result set will
have no order.

No Order by -- no reason to expect the data to be IN ANY order in ANY
circumstances.

You are plumb "out of luck".  if the results were meant to be ordered -- the
developer who wrote this code has what we refer to as "a bug"

October 04, 2002 Prince -- Thanks for the question regarding
      "analytical function (lag, sum etc)", version 8.1.7.4

      You Asked
CREATE TABLE EMP (    EMPNO        NUMBER NOT NULL,
            DEPTNO        NUMBER,
            EMPNAME        VARCHAR2(30),
            MGRNO        NUMBER,
            SAL        NUMBER(8),
            CONSTRAINT PK_EMP PRIMARY KEY(EMPNO)
        );

SQL> select * from emp where rownum < 10 ;

     EMPNO     DEPTNO EMPNAME         MGRNO       SAL
---------- ---------- ---------- ---------- ---------
         1          2 czueltcagt                20000
         2          3 yxycarvtqn          1     30000
         3          4 ucbaicclek          1     40000
         4          5 gzlocwxque          1     50000
         5          6 bbukploxzc          1     10000
         6          7 hcxiuyiudc          1     20000
         7          8 ygrnqwjdjj          1     30000
         8          9 nwppvwwdmi          1     40000
         9         10 htchnvnpvs          1     50000

Now, I want to apply say,  100,000 on the sal column deducting first row's sal
from 100K, then 2nd rows sal from the remainder etc, until my original 100K
become zero or negative.

Note: the following is just a test case.

     EMPNO     DEPTNO EMPNAME         MGRNO       SAL Remaining
---------- ---------- ---------- ---------- --------- ---------
         1          2 czueltcagt                20000 0   (100 - 20 -> 80K) We
applied full 20K and now have 80K on hand
         2          3 yxycarvtqn          1     30000 0   (80 - 30 -> 50K) we
applied full 30K from the remaining 80K
         3          4 ucbaicclek          1     40000 0   (50 - 40 -> 10K) we
applied full 40K from the remaining 50K
         4          5 gzlocwxque          1     50000 40K (10 - 50 -> 40K) we
applied remaining 10K here and stop applying anymore.
         5          6 bbukploxzc          1     10000 10K
         6          7 hcxiuyiudc          1     20000 20K
         7          8 ygrnqwjdjj          1     30000 .
         8          9 nwppvwwdmi          1     40000 .
         9         10 htchnvnpvs          1     50000 .

I tried with sum OVER (ORDER BY empno RANGE UNBOUNDED PRECEDING) and lag etc,
but couldn't find a way to stop when it reaches the first negative value.

      and we said...
something like this perhaps:

scott@ORA920.LOCALHOST> select ename, sal, sum_sal,
  2         case when ( sum_sal <= 10000 )
  3             then 10000-sum_sal
  4             else null
  5             end bal
  6    from (
  7  select empno,
  8         ename,
  9         sal,
 10             sum(sal) over ( order by empno ) sum_sal
 11    from emp
 12         )
 13   order by empno
 14  /

ENAME             SAL    SUM_SAL        BAL
---------- ---------- ---------- ----------
SMITH             800        800       9200
ALLEN            1600       2400       7600
WARD             1250       3650       6350
JONES            2975       6625       3375
MARTIN           1250       7875       2125
BLAKE            2850      10725
CLARK            2450      13175
SCOTT            3000      16175
KING             5000      21175
TURNER           1500      22675
ADAMS            1100      23775
JAMES             950      24725
FORD             3000      27725
MILLER           1300      29025

14 rows selected.

scott@ORA920.LOCALHOST>
Review & Followup

      Rating: 4
I need a bit
      different result  October 03, 2002
Reviewer:
      Prince
May be, I was not clear when I explain the case.

Assume, the SAL column is what I have to pay, and I have 10K on my hand. I need
to pay them off in the order of empno and show the the remaining each employee
has to get.

Infact, the emp table is not a good example. This is the table I had it in
handy.

ENAME             SAL    SUM_SAL        BAL  ToRecieve
---------- ---------- ---------- ----------  --------------
SMITH             800        800       9200  0
ALLEN            1600       2400       7600  0
WARD             1250       3650       6350  0
JONES            2975       6625       3375  0
MARTIN           1250       7875       2125  0
BLAKE            2850      10725             2850-2125 = 725
CLARK            2450      13175             2450
SCOTT            3000      16175             3000
KING             5000      21175             5000
TURNER           1500      22675             ..
ADAMS            1100      23775             ..
JAMES             950      24725             ..
FORD             3000      27725             ..
MILLER           1300      29025             ..

What I had was something like,
select * from (select empno, sal, ssum, lag(ssum) over (order by empno) lag_s ,
        nvl(10000 - (lag(ssum) over (order by empno)) , 10000) diff, sal -
nvl(10000 - (lag(ssum) over (order by empno)) , 10000
) rem
        from (SELECT empno, sal, SUM(sal) OVER (ORDER BY empno ) ssum from emp)
t1 )

I can now apply the case on "diff" and
   if it is >= 0
        display "0" if sign("sal - diff" ) = -1
        esle display "sal - diff"
   else display "SAL" as "ToRecieve"

But the one I have has so many level of subquery. I want it to be simple and
efficient. Also, Can I stop the sum() when it reaches the value greater than
10K. I guess I need another nesting for this to work as "window  functions are
not allowed" in the where cluse.

Thanks for the case statement. I wasn't thinking about the case at all. What I
had before was justy the SQL I pasted above.

          Followup:

Using inline views makes it simple.

Using inline views does not mean "inefficient"

Use inline views, use case....

      Rating: 4
I am not
      corcerned about the inline views, but  October 04, 2002

Reviewer:  Prince.
Thanks Tom,

I am not concerned much about the inline views. But I am wondering whether I am
using too many inline views and whether there is a better way to do this.

It looks to me that, I am following a convoluted way!

Do you see any simpler way of doing this? else, I will continue with what I have
right now.

          Followup:

use analytics, use case and you are going down the right path IMO

October 04, 2002 atul -- Thanks for the question regarding "dead
      connections", version 8.0.4

      You Asked
Sir,

I used your script to show sessions who are there for more than 3 minutes In
that i found one session which is there for 1 hour and 30 min..

so i took sid of that session and gave query like,

select event from v$session_wait where sid=102

and got output as "SQL*Net message from client"...
what is the meaning of that?? is this a Dead Connection or what??

Thanks.
atul

      and we said...
that means they the server is just waiting for the client to send a request.

Do this:

a) log into sqlplus in a window
b) don't touch that window for 1.5 hours
c) look for v$session_wait

you'll see a wait on sql net message from client -- you have an IDLE, probably
not dead, session here.

To avoid "dead" sessions, read the net admin guide and search for DCD or dead
client detection.
Review & Followup

      Rating:
      5
Thanks.  October 04, 2002
Reviewer:
      atul  from Inida
Sir,

If i have 4 or 5 such sessions giving messages like
"SQL*Net message from client"

will that affect on my overall db performance??
Is that means i have to set DCD??

thanks.
atul

          Followup:

It is normal,
It is expected.

It just means that those clients are connected and just aren't doing anything
right now.

You need do nothing in all probability.  Only if the clients are really dead -
(eg: they turned off their computer and went home but still have a session)
would any action be needed on your part.

October 04, 2002 Andre, from Brazil -- Thanks for the question
      regarding "rollback segments", version 817

      You Asked
Hi.
I have a question about rollback segments ...

Imagine that i have just 2 rollback segs RBS1 and RBS2.
A transaction uses extent 1 of RBS1. It ends (commit) . Another transaction goes
to extent 1 of
RBS2 like a circular queue. But, when a third transaction occurs, wich extent of
RBS1 it
will use ? If i´m correct, it will use the E1 of RBS1 again. But if this happen
, why Oracle dont
goes to E2 of RBS1, and a fourth transaction should go to the E2 of RBS2, and a
fifth transaction
should go to E3 of RBS1, and so on.
This will help to avoid ORA 1555 ...

        RBS1    RBS2
E1
E2
E3
E4
E5
E6

      and we said...
We can have many transactions sharing the same extent at the same time.  A block
in the extent is dedicated to a single transaction but many transactions can use
the same extent.

Your approach would not help avoid the 1555 (which happens when we WRAP the rbs
faster then queries complete).  In fact, it would hasten the onset of 1555 since
the RBS will wrap faster.

Say you have 6 extents in each RBS and each extent has 10 blocks.  Using a
transaction/ extent -- you would wrap after 12 commits.  Now, say each
transaction only needs a block.  We'll use block1 from E1/RBS1, then block1 from
E1/RBS2, then

block2 from E1/RBS1
block2 from E1/RBS2
...
block10 from E1/RBS1
block10 from E1/RBS2
block1 from E2/RBS1
block1 from E2/RBS2
....
block10 from E6/RBS1
block10 from E6/RBS2
block1 from E1/RBS1  <<<=== here is the wrap, after 120 commits.

Review &
      Followup

      Rating:
      3
  October 04, 2002
Reviewer:  Sagi
       from India
Hi Tom !

As usual, the explanations are excellent.

Say now a few more transactions (say 5 trans) request more space and assuming
that Block-1 in Extent-1 of RBS1 is having an active transaction. Therefore
after block10 from E6/RBS2 is allocated, now when it tries to WRAP. But since
there is an active transaction it will do the below:

Assuming that RBS1 has >6 extents:
==================================
block1 from E7/RBS1  <<<=== Wld it allocate new extent?
block1 from E1/RBS2
block2 from E7/RBS1
block2 from E1/RBS2
block3 from E7/RBS1

q1) Would now RBS1 have Extent-7.... created. Is this

q2) Say after 2transaction request space, so we give:
block1 from E7/RBS1  <<<=== Wld it allocate new extent?
block1 from E1/RBS2

Now at this stage we create one more RBS3 with same storage config as RBS1 &
RBS2..

How would have this affected the sequence of allocation.

Assuming that RBS1 & RBS2 = 6 extents:
======================================
Since we can WRAP into RBS1 ther user transactions requesting space from RBS1
get error "saying can't extend RBS1..."

So would it have been something like this:

q3)
block1 from E1/RBS1  <<< Cant wrap so error
block1 from E1/RBS2
block2 from E1/RBS1  <<< Cant wrap so error
block2 from E1/RBS2
block3 from E1/RBS1  <<< Cant wrap so error

Is this correct?

I would appreciate if you could show us a example considering the above
scenarios. Your examples are really great.

I think TOM stands for "Talk/Take help of Oracle Master"
                        =    =            =      =

Thanx in advance.

Regards,
Sagi

          Followup:

I don't follow.  You say RBS1 has >6 extents -- so extent 7 already exists and
if extent 7 has no transactions -- we go there.

if the rbs has 6 extents and extent 1 was in use, yes, according to all that was
written above, we would allocate extent 7 and stick it in the circle.

We would use extent 7 till it was full and then try to advance into extent 1 all
over again (this is just a circular buffer here, don't make it more complex then
it is -- it is really very straightforward, you might be thinking it is more
complex and missing the forest for the trees)

The existence (or lack thereof) of other rbs's in the system and their storage
has NO BEARING at all on this story.  a transaction is assigned to a single RBS
and won't consider other RBS's at all.  Transactions do not span RBS's

October 04, 2002 BY -- Thanks for the question regarding "Reducing
      size of Temporary Tablespace,"TEMP"", version 8.1.5

      You Asked
Dear Tom.

Today, I found a huge tablespace in our system.

It is "TEMP" tablespace enlarged by somebody....by 5 GigaBytes...wow!!

Though I try to resize it, I can't fix it..

---------------------------------------------------------------------
SQL> alter database datafile '/data/oradata/ORA2/temp01.dbf'
resize 100m;

alter database datafile
*
ERROR at line 1:
ORA-04031: unable to allocate 4184 bytes of shared memory ("shared
pool","unknown object","sga heap","state objects")
---------------------------------------------------------------------

About our DBMS system,

main memory : 2GB
shared_pool_size = 10485760
shared_pool_reserved_size = 1048576

I think there is two-way solutions in this situation.
One is adjusting INIT.ora parameters and resizing "TEMP" tablespace,
the other is rebuilding "TEMP" tablespace .

what is a better way?
Please let me know the way....detailed working sequence.

thanks.

      and we said...
10m is too small for most shared pools.

I would drop and recreate it.  Especially since you are using a datafile and NOT
a tempfile.  You could recreate it using CREATE TEMPORARY tablespace temp
TEMPFILE ....;

and set autoextend on maxsize <a number reasonable to you>

October 04, 2002 Naveen -- Thanks for the question regarding
      "Sequence in Application ", version 8.1.7

      You Asked
Tom ,

In our organization people are using Sequence as primary key..
..The reason for that they are not sure about the column in Primary key.
They are using Either Oracle Sequence or Sequence Based on Table.
I want to ask what is batter option in the case.

Regds
Naveen

      and we said...
No brainer here, not even a pause in typing this in:

Oracle Sequence

period.  DO NOT EVEN CONSIDER the table based approach under any circumstances.
Review &
      Followup

      Rating: 2
Sequence as a
      PK generator  October 04, 2002
Reviewer:
      vinitk  from India
I doubt using the PK as a sequence generator. what if i compulsorily need a
sequencial PK (ie continuous without any breaks) and one of the transactions
fail? in that case, the sequence no is lost since it cannot be rolled back.
Instead, updating the no kept in a table is very suitable.

          Followup:

You don't even want to know what my opion of "gap free primary keys" are then.

I hate them.

They don't have any place in a transactional system.

They will cause you SERIOUS and UNAVOIDABLE serialization (eg: everyone get in
line behind me to insert and wait until my entire transaction is done, in case I
actually roll back and don't use the number I said I was going to use)

I have YET, after many many many years, to see a real, documented business case
for them.  Just "well, its the way we did it on paper, we had a little lady hand
out numbers one at a time and if we didn't use one we gave it back to her".

If you build real systems today with this "gap free" stuff -- you are building
the least scalable systems in the world.

(i guess you now do know my opinion...)

October 04, 2002 -- Thanks for the question regarding "rman
      register database", version 8.1.6

      You Asked
Tom:

i have serveral database and a rman database,
my question is how do i know which database is registerd
to rman, which is not?

i query the "db" table under schema rman,
it gives me following information:

SQL> select * from db;

    DB_KEY      DB_ID CURR_DBINC_KEY
---------- ---------- --------------
         1 1649793964              2
        35 4003605971             36

but how do i know which database has id 1649793964?
which one has id 4003605971?

      and we said...
select * from the rc_database instead.

I can find the DBID for a database in v$database:

ops$tkyte@ORA8I.WORLD> select dbid from v$database;

      DBID
----------
4080044148

so I can goto a database and see if it is cataloged in rman or not.  Going the
other way, use the views documented in the recovery manager users guide.
Review &
      Followup

      Rating:
      5
  October 04, 2002
Reviewer:
      A reader
Tom,

Can you explain more about the DB_ID and DB_key? Are they used only in RMAN? We
tried to register a cloned database to our RMAN catalog and it returned error
saying the database is already registered(The DB_Key of the cloned database is
same as the parent database). Oracle support said we can not get around this and
we have to use either a different catalog or re-create the cloned database. Why
is this so? I mean why can't RMAN use the combination of DB_key and SID to
uniquely identify a database in the catalog (ie. is there any technical
difficulty for it?). I could not get enough explanation from Doc and Metalink.
Hope you will explain the mechanism in detail. Thanks.

          Followup:

The dbid (database identifier) is a unique id burned into the header of the
datafiles.  When you clone a database, it doesn't give you a new one.  Hence,
RMAN which relies on this to uniquely identify a database gets confused.

It is the DBID that is the same, not the db_key, and that is causing the issue.

In 9iR2 and up, there is:

[tkyte@tkyte-pc-isdn bin]$ nid
DBNEWID: Release 9.2.0.1.0 - Production
Copyright (c) 1995, 2002, Oracle Corporation.  All rights reserved.

Keyword     Description                    (Default)
----------------------------------------------------
TARGET      Username/Password              (NONE)
DBNAME      New database name              (NONE)
LOGFILE     Output Log                     (NONE)
REVERT      Revert failed change           NO
SETNAME     Set a new database name only   NO
APPEND      Append to output log           NO
HELP        Displays these messages        NO

NID is "new id" and can be used to give each cloned database a new unique id.
Prior to that, support is accurate in that you would have to use a different
repository for each cloned database -- given that their "primary keys" are the
same.  Just like having two EMPNOS the same -- you cannot do it.

The technical reason is "they used the database identifier, so they can just
look at a file and tell what database it belongs to without asking".

October 04, 2002 -- Thanks for the question regarding "Table
      Attributes and Execution Plans", version 8.1.7

      You Asked
Hi Tom,

Thank you for your replies. I have some questions today.

1. What are the things to be taken into consideration when a table is to be
created?

Case a. If the table is used by 10,000 users at a time.

Case b. If the table is used by a batch job that runs every one hour.

2. Can you please explain the meaning of the terms in the Execution Plans.

a. Index full scan

b. Index fast full scan

c. Index unique scan

d. Index range scan

Regards,
Vijaya Chander V.S

      and we said...
1) you need to get a book or two on the subject.  I can recommend one "Expert
one on one Oracle" -- or Jonathan Lewis's "Pratical Oracle8i".  We don't have a
direct answer to this but the information is contained therein.

2)
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76992/toc.htm
fortunately Oracle comes with an excellent set of documentation that address
common questions such as this....

briefly:

a) index full scan -- read the entire index block by block in order from start
to finish (read data a single block at a time in the order of the sorted data in
the index

b) index fast full scan -- read the entire index OUT OF ORDER using multi-block
IO (faster then single block io).  In short, pretend the index was a table and
full scan it in order of the blocks on disk -- not sorted order

c) index unique scan -- should be obvious.  search the index for the exact
single row.  start at the root block, branch through the index and find the row
(or not)

d) index range scan - should be obvious -- just like index unique scan HOWEVER,
don't assume there is just one matching row.  once you find the first match,
continue forward in the index to find all matches (range scan).

Review &
      Followup

      Rating:
      5
  October 04, 2002
Reviewer:
      A reader
Thanks verymuch. Can you please explain little more about the differences of
indexe full scan and index fast full scan? (like the advantages/disadvantages,
how to induce it etc.). Also how is a table scan different from and index scan
(you were comparing index fast full scan to table scan - i did not understand
it). Thank you very much.

          Followup:

index full scan -- reads the root block (single block IO). Then it goes down the
index branch blocks until it hits the first leaf block in the index (using
single -- eg 1 block -- IO).  When it hits the first leaf block in the index, it
just reads the rest of the leaf blocks ONE AT A TIME (single block IO) to read
the entire index.  The data is read in the same order it appears in the index.

An index fast full scan -- it just reads N blocks of the index using FAST
multi-block IO.  It throughs out all blocks that are not leaf blocks that it
read and then processes the leaf blocks.  The data is read in any old order but
it is read much FASTER.

A table scan and index fast full scan are very similar but an index is typically
SMALLER so if we can use an index fast full scan in place of a full table scan
-- we do less io (it is faster)

October 04, 2002 Ashiq -- Thanks for the question regarding "To
      check logical dump file for corruption", version 8.1.7

      You Asked
Hi Tom,

 Its nice to ask question again.

 1.I've taken a logical dump one month back ,before i want to imp I want to
check whether the logical dump file is free from corruption.how to do it??

2.Is it possible to write a query to print days between from  01/01/2002 to
12/31/2002.

eg:
01/01/2002
01/02/2002
01/03/2002
.
.
.
12/31/2002

      and we said...
1) imp show=y will scan through it and catch "many" but not "all" things.  the
only way to catch all things is to actually import it (into a test database
perhaps)

2)
ops$tkyte@ORA920.US.ORACLE.COM> select trunc(sysdate,'year')+r
  2    from (select rownum-1 r
  3            from all_objects
  4                   where rownum <=
add_months(trunc(sysdate,'year'),12)-trunc(sysdate,'year')
  5             )
  6  /

TRUNC(SYS
---------
01-JAN-02
02-JAN-02
03-JAN-02
04-JAN-02
05-JAN-02
06-JAN-02
....
29-DEC-02
30-DEC-02
31-DEC-02

365 rows selected.

Review &
      Followup

      Rating: 5
To check
      logical dump file for corruption  October 05, 2002

Reviewer:  Ashiq Shamsudeen  from Bangalore , INDIA

Thanx for explaining me in details.

October 05, 2002 Vinod -- Thanks for the question regarding "How
      to get rid of the message 'Input truncated to n characters'", version
      Oracle 7.3.4

      You Asked
Hi tom, i have an sql file containing PL/SQL block statements
which does some processing say TEST.SQL.
This TEST.SQL when called by a UNIX shell script, i get an
message 'Input truncated to n characters' which is very
frustrating for our development team. When this SQL file is run
seperately in the SQLPLUS environment, i did'nt get this
message.
Is this message is displayed due to some junk characters ?

How to get rid of this message ?

Kindly reply as soon as possible u can

Regds
Vinod

      and we said...
Make sure the last line of the file has a newline after it --
this message occurrs when the last line of the input file with
command text on it does not have a newline.
Review &
      Followup

      Rating:
      3
  April 30, 2001
Reviewer:
      A reader

      Rating: 4
Just in
      time  August 02, 2001
Reviewer:
      A reader  from Pittsburgh, PA
This was very helpfull.  I have been running a similiar script for a number of
months now and was unable to determine what was causing this.

Thanks Tom!

      Rating:
      4
  October 22, 2001
Reviewer:
      A reader

      Rating:
      5
Perfect  December 18, 2001
Reviewer:
      Alan  from Canberra, Australia
This message has puzzled me for a long time. Thanks Tom!

      Rating: 5
Has puzzled for
      a long time... Thanks.  June 25, 2002
Reviewer:
      Robert  from Memphis

      Rating: 4
call sql file
      after sql file  October 04, 2002
Reviewer:
      A reader
I am doing the following in a file one.sql:
set linesize 300
set feedback off
set verify off
@two.sql; --this sql file has pl/sql in it which calls a proc and inserts values
into table y,displays some values using dbms_output.
set heading off
spool z.sql
select something from y;
spool off
truncate table y
@z.sql

when i execute the file one.sql, it gives error
ERROR at line 29:
ORA-06550: line 29, column 2:
PLS-00103: Encountered the symbol "SET"

basically after executing two.sql, it does not allow me to put any comand in
one.sql. why?
How do i do the above if required?

The thing is that i am setting some variables (variable x varchar2 ,variable y
refcurcor etc...which are strings populated based on the input parameters to
one.sql and these strings are inserted into table y by two.sql) and i want to
spool that in a file 'z'...and then execute that file so that those variables
are set in my sql environment.

          Followup:

you must be doing a get/run instead of just

SQL> @one

although it does say "line 29" so maybe you have something in one.sql besides
what you have.

In any case -- full examples, in the manner I present mine, are always helpful:

ops$tkyte@ORA920.US.ORACLE.COM> set echo on
ops$tkyte@ORA920.US.ORACLE.COM> get one
  1* set linesize 300
ops$tkyte@ORA920.US.ORACLE.COM> /
set linesize 300
    *
ERROR at line 1:
ORA-00922: missing or invalid option

ops$tkyte@ORA920.US.ORACLE.COM> run one
  1* set linesize 300
set linesize 300
    *
ERROR at line 1:
ORA-00922: missing or invalid option

ops$tkyte@ORA920.US.ORACLE.COM> @one
ops$tkyte@ORA920.US.ORACLE.COM> set linesize 300
ops$tkyte@ORA920.US.ORACLE.COM>

(@one works -- nothing else will)

October 05, 2002 Mahomed -- Thanks for the question regarding
      "Oracle hot backup", version 7.3.4

      You Asked
Hi Tom,

I am using the oracle hot backup facilty in ARCHIVELOG mode. I
would like some advice/guidance on a particular matter .

Currently I am performing a hot backup every day as follows :

1. Copy each tablespace in turn by setting tablespace into hot
backup mode , copy the tablespace files using unix copy command
and then finally releasing the tablespace from backup mode.
2. Force a log switch .
3. copy all the archived log files.
4. Backup the control file.

My database has the following tablespaces :

LIVE_DATA  - Has all tables for 1 owner
LIVE_INDEX - Has all indexes for the same owner
LIVE_TEMP  - Temp tablespace
RBS
SYSTEM
TEMP
TOOLS
USERS

My application consists of 1 owner/user and relies on all
objects from LIVE_DATA and LIVE_INDEX. Currently the backup
takes about 2.5 hours and I feel I am backing up too much data.
I would like to know If I can miss out backing up of the
following :

1. All the archived log files that have accumulated before the
start of the backup procedure.
2. The RBS, TEMP and LIVE_TEMP tablespaces. ( These take about
20 minutes each to backup).

The problem is I cannot easily try any new backup procedures on
my system as it has gone live and is a 24 hour operation with
very occassional down time. There is a down time due this
weekend for 8 hours but I would like to get some advice/guidance
from yourself before I try anything.

Regards,
Mahomed

      and we said...
1) Well, hopefully you keep at least 2 backups -- the current
one you are making AND the last successful one you made.
Therefore, you would need all of the archive -- in the event the
backup you are making right now fails.

True story:  someone is making a hot backup.  only keeps the
current backup -- overwrites the last backup on the tape drive.

Disk fails in the middle of the backup.  They now have no
backups and a media failure!!!  They are hosed.  They cannot
recover the datafile that failed and was being backed up because
the backup they were making did not complete AND the backup they
had was destroyed.

I would archive all of the log files before beginning the backup
and copy those log files to where ever my last successful backup
was.  You would then take that tape out of the machine and put a
new one in.

Now, hot backup all of the tablespaces, after you do that,
archive all online redo logs and copy them to tape as well.  You
now have on tape something that can be recovered to a consistent
state with.  During the week, keep adding your archives to this
tape -- next week do the whole thing over again -- adding the
last archives to this tape and using the other tape you popped
out last week.

2) you had better backup rollback !!!! that is so critical, so
crucial. A backup without rollback is totally useless.

In 7.x as you have - before true tempfiles, you should backup
TEMP.  You could bypass it but it complicates the recovery
process and when you need to recover -- you want things to go
smoothly -- easily.  You don't want glitches and gotchas.  You
could recover without them (if they are true temporary
tablespaces without data).  In 8 with tempfiles (not datafiles,
tempfiles) you won't be backing them up (cannot back them up
actually).

2.5 hours is OK to run a backup... It isn't affecting the
performance of anything.  You might be able to make it go faster
by copying >1 datafile at a time if you have enough physical
devices....

Review &
      Followup

      Rating: 4
Same
      situation...  October 22, 2001
Reviewer:  Sven
      Bleckwedel  from Santos, SP - Brazil
Hi,

I had the same situation on my site and changed the backup  of the tablespaces
(datafiles and also the archived redologs, configuration files like
init<sid>.ora, password files and others...) to disk-to-disk.  The new backup
strategy had decreased the backup time (from 1 hour to 5 minutes) and the
possibility of instance problems (redo log increasing with the tablespaces in
backup mode and so on...).  Later, this backup area is sended to tape, and the
two copies of the same backup are guaranteed.  This is a good approach for
faster recovery, too...

Rgds,
Sven Bleckwedel

      Rating: 4
Oracle hot
      backup  May 31, 2002
Reviewer:  Anthony, Cheuk
      Tung, LAI  from Hong Kong SAR, China
Most of us would like to save time and effort, however, when disaster comes,
there is no shortcut to save.
This is a great approach to remind ourselves:
Keep 2 Backup copies and don't shutdown the database for backup indeed.

By the way, I believe that it is necessary to set up a staging machine for
backup and recovery plan rehearsal regularly. It likes a firefighting rehersal,
am i correct?
so as to keep our plan fresh in mind and keep people/team updated.

          Followup:

Yes it is correct.

Here is a fun thing as well.  On this backup/staging machine -- do a restore.
Now, split your DBA team in half (assuming you have more then one DBA).  One DBA
is the "evil" DBA, the other the "good" DBA.  Evil DBA will do something to the
test machine to simulate a failure -- anything they want.  Good DBA must fix it.
 Good DBA wins if they can -- Evil DBA wins if they can fix it when good DBA
cannot.  Both lose if neither can recover from it (but they should learn from
that and be able to recover next time).  Now switch posititions.

You'll cover lots of ground that way (you'll find out how to recover from
anything)

      Rating: 5
Hot backup
      question  October 04, 2002
Reviewer:  atul
       from india
Sir,

When in my system hot backup is on...I checked status colum from v$backup view
it shows active..
so when i query v$recover_file and v$recover_logs it shows no rows..
I want to know why?
because when you are doing hot backup some files are in recovery stage..
so we should get this info in v$recovery_file and v$recover_logs views..
or
we get this info whenever our database is mount but not open

Thanks.
atul

          Followup:

the files are not in recovery mode, they are in hot backup mode.  huge, big,
total difference.

The files are NOT in need of recovery.

      Rating: 5
Great Game
      Idea!  January 23, 2003
Reviewer:  Robert
       from Memphis, USA
Tom,

Thanks for the idea of the game!
I think playing this regularly would go far in making a crack DBA team, not to
mention fun.

Thanks,

Robert.

October 05, 2002 Bala Gopal -- Thanks for the question regarding
      "pl/sql tables, varrays, global temporary tables", version 8.1.6

      You Asked
Which is better performancewise and why?
when to choose what?
I tried searching the site for the same but could't get a comparison anywhere?
thanks in advance
bala

      and we said...
None of the above, all of the above.

Think about it -- if one were inheritly faster, better, cheaper then the others
-- we would not have invented the others!

They all have their place, you cannot create a table with a PLSQL table type as
a datatype -- but you can with a SQL Varray.  You cannot permanently store data
in a temporary table, but you can in a real table.

They are different features to be used when you need them in the appropriate
places.  they are not interchangeable (just try to use a plsql table in a
subquery-- no go.  a varray or temp table, sure).  they are different with
different features.
Review & Followup

      Rating: 3
Asking for
      more  October 05, 2002
Reviewer:  Bala Gopal
       from Hyderabad, India
Tom,
   I understood what you meant, but i was expecting more . Could you give a case
by case usage. By this what I mean is

Pl/sql tables - used in these situations not for this   situtation for this
reason

varrays       - in these situations not for this   situtation for this reason

Global Temporary tables - in these situations not for this   situtation for this
reason
and ....

I am reading your book currently, it's one of the best works i have ever come
across.In the mean time I myself am trying to figure out how they are used
differently. In case you think it's easy to find for oneself please do not
bother (now that i have started in that direction)

          Followup:

Use each when they appear natural to use.

When writing procedural code, an array might make more sense.  I prefer to use
PLSQL table types over varray's personally.  I cannot really remember ever using
a varray actually (nested tables, yes, varray's -- no)

global temporary tables -- well, they make sense when doing SQL stuff.

This is like asking "when should I use an OVEN vesus a TOASTER OVEN".  Well, you
use the toaster oven when it feels right - when the food fits.  When it is more
convienent.  You use the oven otherwise.

There is no wrong time, there is no right time, there are no case by case usages
(I do not, will not "pigeon hole" these features -- they are both used in
thousands and thousands of cases)

      Rating: 3
Need more
      experience  October 07, 2002
Reviewer:  Bala
      Gopal  from Hyderabad, India
Thanks for the follow up. I guess the only way to have a real feel is come
across situations where i need to use them. I'll come back when I am confronted
with a situation and am not able to make a choice (Oven vs Toaster oven). Guess
it shouldn't be hard.
Thanksalot,
bala

October 05, 2002 atul -- Thanks for the question regarding
      "Recovery", version 8.0.4

      You Asked
Sir,

Could you tell me the conditions where we can apply only full import to recover
the database in archivelog mode with both hotbackup and export/import daily..

Ie only full import is the solution to recover database..
without taking much time to decide we could aplly full import..

Thanks.
atul

      and we said...
Import/Export and Archivelog/Noarchivelog have nothing whatsoever to do with
eachother, nothing.

imp/exp are tools you can use to move data around.  I personally do not consider
them "backup" tools at all.

In archivelog mode, all you need is your backups and logs to fully recover.  You
do not need to use the DMP files at all.

You can use the DMP files to recover an "accidently" dropped table or procedure
but thats about it.  You can use your backups to do the same easily enough but
if the stale data in the DMP file (you CANNOT point in time recover with logs
data in the DMP files, they are a snapshot at a point in time) suffices, you can
use that.  I wouldn't be looking at the DMP file as a recovery point at all.
Review &
      Followup

      Rating:
      3
Recovery  October 05, 2002
Reviewer:
      Abdal-Rahman Agha  from Yemen
Hi Tom,
  arround this I need to help me how can I restore and then recover my database
if backup stored in a tape within the archived logs and that my hard disk(s)
crash(es) fully, i.e. nothing can be restored from them. Should I have to
install oracle in new machine and then copy the most recent backup from tap? How
can I do that, because RMAN (in the new software installed) would not recognise
my backup of the gone database!!!

          Followup:

You were running the respository on the same machine as the database you were
protecting?  Thats like running your standby on the same hardware, not the best
way to do it.

Yes, you would just restore the database to another machine after installing
Oracle fresh on there.

October 05, 2002 rajesh -- Thanks for the question regarding "Last
      Balance", version 8i

      You Asked
Dear tom

here is the structure of a table in my data base.

br_cd     char(1)
accno    varchar2
day_dt    date
credit    number
debit    number
bal    number

containg data  as follows

2    1005    26-dec-2001    100    0    100
2    1006    26-dec-2001        500    100
2    1005    26-dec-2001    200        300
2    1005    26-dec-2001        100    200
2    1005    26-dec-2001      50        250

i want to find the balance from  the last transaction of the account number 1005
   on 26-dec-2001.
is that is possible to find the balace from the last transaction by finding the
maximum rowid , with out adding an additional field  for serial number for a
particual account number.

sincerely

rajesh

      and we said...
does that date column have a TIME component as well -- if so,

select *
  from ( select *
           from t where accno = 1005
            and day_dt = to_date('26-dec-2001','dd-mon-yyyy')
          order by day_dt desc )
        )
where rownum = 1;

it is does not, you need SOMETHING to order by (not rowid, not rownum -- there
is nothing in the database you can use to order rows by insertion time unless
you put it there)
Review & Followup

      Rating: 4
finding minimum
      balance  September 30, 2002
Reviewer:
      rajesh
Sir

i had modified the earlier mentioned structue as follows
trans_cd varchar2(3)
day_dt date
depo_accno number(8)
credit number(14,2)
debit number(14,2)

for maintaining the details of deposit holders. here i had not included a field
for storing the balance. i need to find the minimum of balace ( sum (credit) -
sum(debit))
 between two  given dates.

following is the example of data . here i had also displayed the balace of each
transacion for convenience. in this the minimum balance is 60 and the date is
22-aug-2002.
trans_cd|day_dt|depo_accno|credit     debit|balance (not a database field)
1000|01-aug-2002|10012        1000             1000
1000|02-aug-2002|10012                  50      950
1000|03-aug-2002|10012                      40      910
1000|03-aug-2002|10012         200             1110
1000|05-aug-2002|10012         1000             2110
1000|05-aug-2002|10012                    100     2010
1000|11-aug-2002|10012         1000             3010
1000|22-aug-2002|10012        50             3060
1000|18-aug-2002|10012                   2000     1060
1000|22-aug-2002|10012                   1000       60
1000|30-aug-2002|10012           1000             1060
1000|31-aug-2002|10012                     50     1010

now i am finding the minimum balance by inserting credit, debit  and balace of
each transaction to a temporary
table and then finding the min(balance) . but this is time consuming.

can i achieve the  same by using a single query.

sicerely

rajesh

          Followup:

what a jumble of numbers you've given me there.  nothing lines up at all.

      Rating:
      4
sorry  October 01, 2002
Reviewer:
      rajesh
sorry sir
i am really apologizing.it is actually

trans_cd|day_dt|depo_accno|credit| debit|  balance (not a database field)
1000|01-aug-2002|10012     1000             1000
1000|02-aug-2002|10012                 50    950
1000|03-aug-2002|10012                 40    910
1000|03-aug-2002|10012      200             1110
1000|05-aug-2002|10012     1000             2110
1000|05-aug-2002|10012                100   2010
1000|11-aug-2002|10012     1000             3010
1000|22-aug-2002|10012       50             3060
1000|18-aug-2002|10012               2000   1060
1000|22-aug-2002|10012               1000     60
1000|30-aug-2002|10012     1000             1060
1000|31-aug-2002|10012                 50   1010

          Followup:

read up on analytic functions (data warehousing guide, my book "expert one on
one oracle" if you have it, searching here on asktom - tons of examples here)

they'll be very very helpful:

ops$tkyte@ORA920.LOCALHOST> select trans, tdate, depo_acc, credit, debit,
  2         sum(credit) over (partition by trans, depo_acc order by tdate)
scred,
  3         sum(debit) over (partition by trans, depo_acc order by tdate) sdeb,
  4         nvl(sum(credit) over (partition by trans, depo_acc order by
tdate),0)
  5         - nvl(sum(debit) over (partition by trans, depo_acc order by
tdate),0) bal
  6    from t
  7  /

ops$tkyte@ORA920.LOCALHOST>

TRANS TDATE     DEPO_ACC CREDIT DEBIT      SCRED       SDEB        BAL
----- --------- -------- ------ ----- ---------- ---------- ----------
 1000 01-AUG-02    10012   1000             1000                  1000
 1000 02-AUG-02    10012           50       1000         50        950
 1000 03-AUG-02    10012           40       1200         90       1110
 1000 03-AUG-02    10012    200             1200         90       1110
 1000 05-AUG-02    10012   1000             2200        190       2010
 1000 05-AUG-02    10012          100       2200        190       2010
 1000 11-AUG-02    10012   1000             3200        190       3010
 1000 18-AUG-02    10012         2000       3200       2190       1010
 1000 22-AUG-02    10012     50             3250       3190         60
 1000 22-AUG-02    10012         1000       3250       3190         60
 1000 30-AUG-02    10012   1000             4250       3190       1060
 1000 31-AUG-02    10012           50       4250       3240       1010

12 rows selected.

      Rating:
      5
wonderful  October 03, 2002
Reviewer:
      rajesh
dear tom

wonder ful answer. till now i am not aware about such querries. any way now
i had modified your answer as

select min (lbal) from
(select sum(nvl(credit,0)-nvl(debit,0) )
over (partition by accno order by day_dt) lbal from day_one where
day_dt between    <date>    and  <date>     ;

and it is working well in sql plus. but when i had converted this qurry
as a function it causes compilation errors . is this querry is not supporetd in
pl sql?
if not how can i  create a function for using in oracle forms 6i.

oracle version oracle 8.1.7
i am not aware about configuring and using proc.
so a detailed reply seems well.

sicerely

rajesh

          Followup:

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3027089372477

      Rating:
      5
wonderful  October 03, 2002
Reviewer:
      rajesh
dear tom

wonder ful answer. till now i am not aware about such querries. any way now
i had modified your answer as

select min (lbal) from
(select sum(nvl(credit,0)-nvl(debit,0) )
over (partition by accno order by day_dt) lbal from day_one where
day_dt between    <date>    and  <date>     ;

and it is working well in sql plus. but when i had converted this qurry
as a function it causes compilation errors . is this querry is not supporetd in
pl sql?
if not how can i  create a function for using in oracle forms 6i.

oracle version oracle 8.1.7
i am not aware about configuring and using proc.
so a detailed reply seems well.

sicerely

rajesh

      Rating:
      5
thanks  October 05, 2002
Reviewer:
      rajesh
hi tom

thank u for that link. it looks very valuable.

but when i had converted the querry i had mentioned yearler to a function like
mentioned in the link it causes compilation errors.

now i had created a view and getting result.

but it will be better if u may help me to create a function which can return the
value

sincerely

rajesh

          Followup:

Sorry, but beyond what is in that link --- which has a full, 100% complete and
total example -- I don't know what to tell you.

"causes compilation errors" -- can only mean you typed something in wrong, try
again.  Native dynamic sql isn't that hard - it is rather straightforward
actually.

October 05, 2002 -- Thanks for the question regarding "problem
      with parameter", version 9.0.1

      You Asked
Hi Tom,

Just installed DB9.0.1, I tried to modify parameter in init.ora file:
compatible=9.0.0(default) to 8.1.0. After I restarted the 901 DB, I got error
below when I login to sqlplus:

ERROR:
ORA-01033: ORACLE initialization or shutdown in progress

Anything wrong with that? If I change back, everything is ok.

Thanks.
John

      and we said...
The database could not start up.  If you start the database manually, from the
command line -- you would discover this.  For example:

idle> startup pfile=initora920.ora
ORACLE instance started.

Total System Global Area  143725064 bytes
Fixed Size                   451080 bytes
Variable Size             109051904 bytes
Database Buffers           33554432 bytes
Redo Buffers                 667648 bytes
Database mounted.
ORA-00402: database changes by release 9.2.0.0.0 cannot be used by release
8.1.0.0.0
ORA-00405: compatibility type "Locally Managed SYSTEM tablespace"

.....

Generally, compatible cannot be set DOWN as you are already using new features
many times that are not compatible with the older release.

You would have had to of created the database with 8.1 file formats (compatible
set to 8.1 from the very beginning)

October 05, 2002 srinivasa rao bachina -- Thanks for the question
      regarding "Getting Cumulative sum on a field", version 9i

      You Asked
Hello tom....
 one more question....
i have emp table and the data is like this...
 empno  sal
  1     100
  2     200
  3     400
i want result like this
  empno sal  cum-sum
    1    100  100
    2    200  300
    3    400  700
i wrote a query.....it is working in 9i but not in 7.2 pls help
the query i tried in 9i is
 select a.empno,a.sal,(select sum(b.sal) from emp b where
    b.rowid<=a.rowid) cum-sum
  from emp a.
pls help me..............

      and we said...
The right way to do this in 9i would be:

select empno, sal, sum(sal) over ( order by empno, rowid )
  from emp;

by the way. And in 7.2, the features you are trying to use just plain didn't
exist (way back in 1995).

You can write a PLSQL function to be called from SQL instead:

create function get_sals( .... ) return number
as
   l_sal number;
begin
   select sum(sal) into l_sal
     from emp
    where ....;

   return l_sal;
end;

and select empno, sal, get_sals( ... ) from emp;

October 05, 2002 David -- Thanks for the question regarding
      "Oracle Cursors", version 8i

      You Asked
I want to create a stored proc with a cursor. The sp should query a large table
& return 2 things...

#1. A count of the total number of rows found (for example 57)

#2. The first 25 rows from the result (the actual data).

Is this possible? Do you have sample code? Can you do it for me? :)

If your wondering why... we have tables with 1.2 million recs. Users run ad-hoc
queries that sometimes find 500k rows. We obviously dont want to send them all
those rows but we want to show them what the total was & encourage them to
narrow down rows with more criteria etc.

P.S. I love your site & I'm happy to see another like it for java.

      and we said...
How about this suggestion:

you send back the first 25
you tell them if there are more then 25 and that they should tighten their
search.

searching 1.2 million records for the first 500k will consume tons of time,
energy and resources.  You should just limit it to 25 (thats all you are going
to show) and then say "there are more, you cannot see them, your search isn't
fine grained enough"

No need to waste the resources on counting -- it would take lots more resources
to count 500k rows then it would to transmit them on the network.

Yes -- I can do this query.  No, I don't think I want to!  It would be an utter
waste.

If you use interMedia text to do your index, it does have an "approximate the
returned results".   I use it on this site.  It might be more what you want.
Review &
      Followup

      Rating: 4
not so
      fast...  October 03, 2002
Reviewer:  david
      schwartz  from NJ, usa
Let me clarify...
we need to know the total number of rows found regardless of how many are
returned to the browser for actual viewing.

For example, I want to find list all the customers who live in new york with
phone number. Say there were 257. While I may only view 25 per screen I still
want to know what the total is.
I find users like to have an idea of "what's there".

          Followup:

Painfully I type in

ops$tkyte@ORA920.LOCALHOST> select *
  2  from
  3  (
  4  select rownum r, a.*
  5    from ( select ename, job, count(*) over () total
  6             from emp
  7                    where ename like '%'
  8                    order by ename ) a
  9  where rownum <= 10
 10  )
 11  where r >= 5
 12  /

         R ENAME      JOB            TOTAL
---------- ---------- --------- ----------
         5 FORD       ANALYST           14
         6 JAMES      CLERK             14
         7 JONES      MANAGER           14
         8 KING       PRESIDENT         14
         9 MARTIN     SALESMAN          14
        10 MILLER     CLERK             14

6 rows selected.

ops$tkyte@ORA920.LOCALHOST>

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3027089372477
if you are using 8i and want to do this in plsql.

It's slowwww..... as we have to build the entire result set.  I never tell
people the real hit count (neither does google and google being *very cool*
should be the standard!!!)

      Rating: 3
Change of
      heart  October 04, 2002
Reviewer:  David
      Schwartz  from NJ, USA
before I give up on the cursor idea...

can I get a total number of rows in the cursor without actually fetching through
the cursor?

          Followup:

Nope, well, not really -- the analytic function

count(*) over ()

returns the total count of the entire result set with every row -- so yes sort
of but, we have to of course BUILD the entire result set before you get the
first row back!!!!

      Rating: 4
Number of
      records by a query without actually firing it  October 04,
      2002
Reviewer:  A reader  from BG
Tom,
   If my users want to see an approximate number of records that could be
fetched, is it an advisable method to run an explain plan and query out the
cardinality column for the main record from plan_table?

How accurate would that information be, assuming that all tables are analyzed up
to date.

regards

          Followup:

That would be one approach.  It would be an estimate, yes.

it'll not be 100% but it would be preferable to actually running the query in
most all cases (most all cases -- there can and will be exceptions but in
general...)

And hey -- in 9i, you won't even have to EXPLAIN -- v$sql_plan will already have
it.

      Rating: 5
Or use
      all_tables.num_rows...  October 04, 2002
Reviewer:
      Jan van Mourik  from Houston, TX,
instead of plan_table or v$sql_plan.

          Followup:

and how does that help you figure out:

select * from foo where x > 5 and y between 11 and 20 and z = 'xxx';

???

      Rating: 5
No
      idea..  October 05, 2002
Reviewer:  Jan van
      Mourik  from Houston, TX
about figuring out
select * from foo where x > 5 and y between 11 and 20 and z = 'xxx';

but doing

select num_rows from user_tables where table_name = 'FOO';

would tell aproximately how many rows are in the table. Just as an alternative
to using v$sql_plan, that's all...

          Followup:

But -- that won't answer the question "about how many rows will my query return"
at all.  It would be relatively rare to ask "select * from t", more frequently
than not there are predicates, joins, etc.  If it were as easy as knowing the
number of rows in a table.....

      Rating: 5
The quarter
      fell finally...  October 05, 2002
Reviewer:  Jan
      van Mourik  from Houston, TX
yeah, of course, didn't think of that obviously...

I'll put on my donkey ears and stand in the corner of the room for a while...

October 05, 2002 Abdal-Rahman -- Thanks for the question regarding
      "Upgrading 816 to 817", version 816 & 817

      You Asked
Hi Tom,

  Continuing my comments on the recover database, let me present my situation:
  I have now database 816 installed. Now we have a new server and we feel that
we need to replace our old server which has 816 installed in it, to the new
server which we are going to install 817 in it. What is the best way to transmit
that one to this one? Can I take complete offline copy of all my database
components and create the same database there (so as to create the services
automatically) using Database Configuration Assistant and then delete this one
then copy that old one in the same location and then startup this database?
Or is it preffered to full export that one and then import it?
Thanks.

      and we said...
You need to upgrade the database as well -- read the entire MIGRATION GUIDE that
comes with each and every release of Oracle to see what upgrade steps you need
to take in order to accomplish this!!!! Very important.

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/index.htm
Read you OS Admin Guide for how to create a new service using oradim.

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/win.817/index.htm
faster then creating and then trashing a starter database just to get the
services registered.

That is another document, if you are wanting to admin a NT Oracle instance, that
you need to read from *cover to cover*.

(yes, you will be able to backup the 816, restore to the 817 machine and then
upgrade it. If you wanted the "easiest", most straightforward way -- I would
suggest:

a) full backup of the 816 machine
b) install 817 on that machine
c) use the gui to upgrade it
d) backup and restore the upgraded instance to the new machine)

Review &
      Followup

      Rating:
      5
Upgrading  October 06, 2002
Reviewer:
      Abdal-Rahman Agha  from Yemen
Thanks Tom.

October 06, 2002 juan carlos -- Thanks for the question regarding
      "Select while sum() < x", version 9.2

      You Asked
Hi Tom,

If you please can illuminate with your experience.

The table Hi
sequence    Values
1            3
2            6
3            4
4            9
5            3
6            2

I need to select in the order of sequence the records until they sum an X value

select * from
( select sequence, values
  from hi
  order by sequence )
???????

For example if X is 12
it must return
1            3
2            6
3            4
             __
             13
if its 6
it must return
1            3
2            6
             __
             9

if x is 20
sequence    Values
1            3
2            6
3            4
4            9
             __
             22
Is this possible?

Thank you

      and we said...
something like this:

ops$tkyte@ORA920.US.ORACLE.COM> variable x  number
ops$tkyte@ORA920.US.ORACLE.COM> exec :x :=12

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> select *
  2    from (
  3  select s, v, sum(v) over (order by s) sum_v
  4    from t
  5         )
  6   where sum_v - v < :x
  7  /

         S          V      SUM_V
---------- ---------- ----------
         1          3          3
         2          6          9
         3          4         13

ops$tkyte@ORA920.US.ORACLE.COM> exec :x := 6

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> /

         S          V      SUM_V
---------- ---------- ----------
         1          3          3
         2          6          9

ops$tkyte@ORA920.US.ORACLE.COM> exec :x := 20

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> /

         S          V      SUM_V
---------- ---------- ----------
         1          3          3
         2          6          9
         3          4         13
         4          9         22

ops$tkyte@ORA920.US.ORACLE.COM>
Review &
      Followup

      Rating: 5
And in an
      update??  October 05, 2002
Reviewer:
      A reader
Thank you
Please Now if I can abuse a little more
Can now be possible to apply it to only one
UPDATE statement to update the column used???

The table Hi
sequence    Values  Used
1            3      0
2            6      0
3            4      0
4            9      0
5            3      0
6            2      0

Coud be possible to update in this way
For example if X is 12
it must return  Used
1            3   3
2            6   6
3            4   3
             __
             13
if its 6
it must return  Used
1            3   3
2            6   3
             __
             9

if x is 20
sequence    Values  Used
1            3      3
2            6      6
3            4      4
4            9      7
             __
             22

Thank you

          Followup:

You cannot do the update in one statement.  The statement uses a sort, which can
be non-deterministic (eg: running the same statement two times could result in
different rows getting updated if there were duplicate S values).  Hence, we
don't do that.

You don't need to however, You can easily materlize "used" whenever you need to:

ops$tkyte@ORA920.US.ORACLE.COM> variable x  number
ops$tkyte@ORA920.US.ORACLE.COM> exec :x :=12

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> select s, v, sum_v, case when sum_v < :x
  2                           then v
  3                           else v - (sum_v-:x)
  4                      end
  5    from (
  6  select s, v, sum(v) over (order by s) sum_v
  7    from t
  8         )
  9   where sum_v - v < :x
 10  /

         S          V      SUM_V CASEWHENSUM_V<:XTHENVELSEV-(SUM_V-:X)END
---------- ---------- ---------- ----------------------------------------
         1          3          3                                        3
         2          6          9                                        6
         3          4         13                                        3

ops$tkyte@ORA920.US.ORACLE.COM> exec :x := 6

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> /

         S          V      SUM_V CASEWHENSUM_V<:XTHENVELSEV-(SUM_V-:X)END
---------- ---------- ---------- ----------------------------------------
         1          3          3                                        3
         2          6          9                                        3

ops$tkyte@ORA920.US.ORACLE.COM> exec :x := 20

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> /

         S          V      SUM_V CASEWHENSUM_V<:XTHENVELSEV-(SUM_V-:X)END
---------- ---------- ---------- ----------------------------------------
         1          3          3                                        3
         2          6          9                                        6
         3          4         13                                        4
         4          9         22                                        7

ops$tkyte@ORA920.US.ORACLE.COM>

      Rating: 5
Thank
      you  October 05, 2002
Reviewer:
      A reader

      Rating: 5
Great! So,
      can't be change the "Values" to the remaining  October 05,
      2002
Reviewer:  gs
So, in our cse

For example if X is 12
it must return  Used
Seq         Values
-----       -----
1            3
2            6
3            4
4            5

What do I do, if I want to apply the 12 to the "values" column and uodate the
DB, so that the next time, I will not apply anything to these (they all are 0 by
now). Is the procedural method the only way then?

X is 12, afer the update, it should be

Seq         Values (remaining)
-----       -----------------
1            0
2            0
3            1
4            5

Note: This is almost the same question I posted earlier.

          Followup:

When the defining query implies "order", we cannot update using it.  Analytics
imply "order".

So yes, Procedural or two step (insert into temp, update using temp) will be the
options.

October 06, 2002 khalid -- Thanks for the question regarding
      "Import Procedures, functions and packages", version 8.1.7

      You Asked
Hi Tom.
1. How can I import procedure, function and packages?
2. Where do my procedures, functions and packages are stored in the database.

Thanks.

      and we said...
You cannot import just those objects.  You can import

o all things owned by a user
o a list of tables
o the entire contents of the dmp file

So, if you want the procedure, functions and packages, you would have to import
the entire dmp file OR all things owned by a user.

If you search this site for
getcode getallcode

I have a script that I use to extract all procedures/functions/package to
scripts you might find useful.

All schema objects definitions are stored in the SYSTEM tablespace, in the data
dictionary. Hence, your compiled code is in a table owned by SYS in the SYSTEM
tablespace.
Review & Followup

      Rating:
      5
Thanks  October 06, 2002
Reviewer:
      Khalid  from Yemen
Thanks Tom.
How about imp userid = / rows=n constraints=n indexes = n.

          Followup:

that'll import table structures (no data, but the create's), MV's, views, etc.

And if you listed TABLES=non_existent_table_name, it would import nothing.

So, no, that would not do it.

October 06, 2002 -- Thanks for the question regarding
      "DB_CACHE_ADVICE", version 9i

      You Asked
Following is a critical confusion from Self Test Software.

Q)You want to enable statistics gathering from your instance by enabling the
buffer cache advisory feature. You have currently DB_CACHE_ADVICE initialization
parameter set to OFF. What should you do?

A. Set DB_CACHE_ADVICE to ON using ALTER SYSTEM SET command.
B. Set DB_CACHE_ADVICE to READY using ALTER SYSTEM SET command.
C. Set DB_CACHE_ADVICE to ON by manually editing the init.ora or
   Spfile.
D. Set DB_CACHE_ADVICE to READY by manually editing the init.ora or
   Spfile.

the option A is given as the correct answer but I do not agree with it
because I have read in Chapter#15 Page#21 of Oracle9i New Features for
Administrators
that if DB_CACHE_ADVICE is OFF then attempting to setting it to ON will
lead to an error ORA-4031 but if it is in READY state then attempting to
setting it to ON will succeed. So I think that the correct answer should
be option B but I am not sure that is why need your confirmation.

Thanks
Regards
Natalia

      and we said...
read

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4293209555538

If set to OFF, you might not be able to set to EITHER OF ready or on (depends on
memory).

The correct answer to the question "you want to enable db cache advice, what
should you do" is "set it to on".

It might fail, but it would fail in the setting of READY in that case as well.

Review &
      Followup

      Rating:
      4
  October 07, 2002
Reviewer:
      Natalia  from Pakistan
thanks

October 06, 2002 Pauline -- Thanks for the question regarding
      "sequence next value too large for the column", version 8.1.7.4

      You Asked
Tom,
We run some sql statement to insert records to a table as below:
SQL> INSERT INTO party_address_search
  2  (
  3     party_address_search_id -- Primary key column.
  4    ,pty_id
  5    ,ptytyp_id
  6    ,ptystat_id
  7    ,clientsegment_id
  8    ,was_ever_cds_client_flg
  9    ,cds_profile_status_id
 10    ,party_nm
 11    ,first_nm
 12    ,first_nm_phonetic
 13    ,org_or_last_nm
 14    ,org_or_last_nm_phonetic
 15    ,org_or_last_nm_sort
 16    ,ptyaddr_id
 17    ,ptyaddr_deleted_flg
 18    ,ptyaddr_ctpusetyp_id
 19    ,primary_address_flg
 20    ,address_id
 21    ,address_line_txt
 22    -- ,address_line_txt_nl
 23    -- ,address_line1_txt
 24    -- ,address_line2_txt
 25    -- ,address_line3_txt
 26    ,city_nm
 27    ,greg_st_prov_id
 28    ,postal_code
 29    ,greg_country_id
 30    ,search_column
 31  )
 32  SELECT /*+ ORDERED USE_HASH(p i o pad ad) */
 33     pk_seq.NEXTVAL
 34    ,p.pty_id
 35    ,p.ptytyp_id
 36    ,p.ptystat_id
 37    ,p.clientsegment_id
 38    ,p.was_ever_cds_client_flg
 39    ,p.cds_profile_status_id
 40    ,p.party_nm
 41    ,DECODE(p.ptytyp_id,472,i.first_nm,NULL) first_nm -- if ptytyp is ind use
first nm, else null.
 42    ,gen_soundex_list(DECODE(p.ptytyp_id,472,i.first_nm,NULL))
first_nm_phonetic
 43    ,DECODE(p.ptytyp_id,472,i.last_nm,o.organization_nm) org_or_last_nm -- if
ptytyp is ind use last nm, els
e org nm.
 44    ,gen_soundex_list(DECODE(p.ptytyp_id,472,i.last_nm,o.organization_nm))
org_or_last_nm_phonetic
 45    ,DECODE(p.ptytyp_id,472,i.last_nm
 46         ,DECODE(
 47            p.ptytyp_id
 48           ,474
 49           ,SUBSTR(RTRIM(o.organization_nm),INSTR(RTRIM(o.organization_nm),'
',-1,1)+1)
 50           ,475
 51           ,SUBSTR(RTRIM(o.organization_nm),INSTR(RTRIM(o.organization_nm),'
',-1,1)+1)
 52           ,o.organization_nm
 53          )
 54        ) org_or_last_nm_sort
 55    ,pad.ptyaddr_id
 56    ,pad.deleted_flg ptyaddr_deleted_flg
 57    ,pad.ctpusetyp_id ptyaddr_ctpusetyp_id
 58    ,pad.primary_address_flg
 59    ,ad.address_id
 60
,ad.address_line1_txt||chr(10)||ad.address_line2_txt||chr(10)||ad.address_line3_t
xt address_line_txt
 61    -- ,ad.address_line1_txt||' '||ad.address_line2_txt||'
'||ad.address_line3_txt address_line_txt -- OLD C
ODE.
 62    -- ,NULL address_line_txt_nl
 63    -- ,NULL address_line1_txt
 64    -- ,NULL address_line2_txt
 65    -- ,NULL address_line3_txt
 66    ,ad.city_nm
 67    ,ad.greg_st_prov_id
 68    ,ad.postal_code
 69    ,ad.greg_country_id
 70    ,NULL search_column
 71  FROM
 72     parties p
 73     -- (SELECT * FROM parties WHERE ROWNUM <= 100) p -- REPLACED BY LINE
ABOVE.
 74    ,individuals i
 75    ,organizations o
 76    ,party_addresses pad
 77    ,addresses ad
 78  WHERE
 79     p.pty_id = pad.pty_id (+)
 80     AND ad.address_id (+) = pad.address_id
 81     AND i.pty_id (+) = p.pty_id
 82     AND o.pty_id (+) = p.pty_id
 83  ;
   pk_seq.NEXTVAL
   *
ERROR at line 33:
ORA-01401: inserted value too large for column

I check that pk_seq, it was created as

Create sequence ADMIN.PK_SEQ
  INCREMENT BY 1
  START WITH  1
  MAXVALUE 999999999999999999999999999  --no limit for maxvalue
  MINVALUE 1
  NOCYCLE
  ORDER
  CACHE 60
;

Now after statement failed, I check something as below:

SQL> select admin.pk_seq.nextval from dual;

 153403053

SQL> desc party_address_search
 Name                                                    Null?    Type
 ------------------------------------------------------- --------
-------------------------------------
 PARTY_ADDRESS_SEARCH_ID                                 NOT NULL NUMBER
 PTY_ID                                                           NUMBER
 PTYTYP_ID                                                        NUMBER
 PTYSTAT_ID                                                       NUMBER
 CLIENTSEGMENT_ID                                                 NUMBER
 WAS_EVER_CDS_CLIENT_FLG                                          VARCHAR2(1)
 CDS_PROFILE_STATUS_ID                                            NUMBER
 PARTY_NM                                                NOT NULL VARCHAR2(70)
 FIRST_NM                                                         VARCHAR2(35)
 FIRST_NM_PHONETIC                                                VARCHAR2(35)
 ORG_OR_LAST_NM                                                   VARCHAR2(150)
 ORG_OR_LAST_NM_PHONETIC                                          VARCHAR2(150)
 ORG_OR_LAST_NM_SORT                                              VARCHAR2(150)
 PTYADDR_ID                                                       NUMBER
 PTYADDR_DELETED_FLG                                              VARCHAR2(1)
 PTYADDR_CTPUSETYP_ID                                             NUMBER
 PRIMARY_ADDRESS_FLG                                              VARCHAR2(1)
 ADDRESS_ID                                                       NUMBER
 ADDRESS_LINE_TXT                                                 VARCHAR2(250)
 CITY_NM                                                          VARCHAR2(25)
 GREG_ST_PROV_ID                                                  NUMBER
 POSTAL_CODE                                                      VARCHAR2(11)
 GREG_COUNTRY_ID                                                  NUMBER
 SEARCH_COLUMN                                                    CHAR(1)

SQL> select max(pty_id) from organizations;

  153317045

SQL> select max(pty_id) from individuals;

  153317109

The datatype for party_address_serch_id is number. It can be up to 38
digits. Now the pk_seq is only up to 9 digits.
I was wondering why we got such error. Please help.

As always thank you very much for your help.

Pauline

      and we said...
It is one of the other columns -- no necessarily that one.  Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t
  2  ( x number,
  3    y varchar2(2),
  4    z date
  5  )
  6  /

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop sequence t_seq;

Sequence dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create sequence t_seq;

Sequence created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t
  2  select
  3  t_seq.nextval
  4  , substr(username,1,rownum)
  5  , sysdate
  6  from all_users
  7  /
t_seq.nextval
*
ERROR at line 3:
ORA-01401: inserted value too large for column

it just points to the first selected item in 9iR1 and before...

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t
  2  select
  3  t_seq.nextval
  4  , substr(username,1,2)
  5  , sysdate
  6  from all_users
  7  /

46 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

see, it wasn't the sequence at all -- but the substr'ed column

But maybe this is reason 1132451 for going to 9iR2

ops$tkyte@ORA920.US.ORACLE.COM> insert into t
  2  select
  3  t_seq.nextval
  4  , substr(username,1,rownum)
  5  , sysdate
  6  from all_users
  7  /
, substr(username,1,rownum)
  *
ERROR at line 4:
ORA-01401: inserted value too large for column

But for now, you'll have to look at the columns to see which ones COULD be
generating data longer then the target field accepts.  Or you could run:

 32  SELECT /*+ ORDERED USE_HASH(p i o pad ad) */
 38    ,max( length(p.was_ever_cds_client_flg) )
 40    ,max( length(p.party_nm) )
....

once to see the offending column quickly.
Review &
      Followup

      Rating:
      5
  October 07, 2002
Reviewer:
      Pauline  from NY, U.S.A.
Thanks for pointing  out the key point about this error. It is very helpful.

October 06, 2002 Reprakash -- Thanks for the question regarding
      "SQL tuning for retrieval from a large table", version 8.1.7

      You Asked
Hi Tom,

Sorry for asking your help tune a very specific query. But this has been
bothering us for quite sometime as our database is growing very fast and has
reached peak load.

The number of rows in the table is 18.5 million and is divided among 12
partitions. This SQL is required to allocate numbers for concurrent users.

The statistics given under is just after Oracle start-up.

Despite our best efforts we have not been able to get this query retrieving in
less than a few seconds which is high enough for web-based applications. Can you
suggest something for such table and/or queries ?

Any help would be most welcome.

Thanks and Regards
Reprakash

select  /*+  INDEX(PTN_INV, PTN_INV_4IX)  +*/
ROWID
,PARTITION_ID
,NPA ,NXX
,LINE_NUMBER
,TO_CHAR(SYS_CREATION_DATE,'YYYYMMDDHH24MISS')
,TO_CHAR(SYS_UPDATE_DATE,'YYYYMMDDHH24MISS')
,OPERATOR_ID
,APPLICATION_ID
,DL_SERVICE_CODE
,DL_UPDATE_STAMP
,RESOURCE_STATUS
,TO_CHAR(LAST_TRX_DATE,
'YYYYMMDDHH24MISS')
,LAST_TRX_CODE
,LAST_TRX_UID
,TO_CHAR(RMINV_LOGIC_DT,
'YYYYMMDD')
,NMB_ID
,NL
,NGP
,HOLD_REASON
,TO_CHAR(SPECIAL_TRX_DATE,
  'YYYYMMDDHH24MISS')
,CONV_RUN_NO
,RUN_ID
,PTN_TYPE
,RATE_CENTER
,STATE_CODE

from
 PTN_INV where (((((((NPA=:b0 and NXX=:b1) and NMB_ID=:b2) and NL=:b3) and
  RESOURCE_STATUS=:b4) and LINE_NUMBER>=(DECODE(length(to_char(:b5)),1,'000',
  2,'00',3,'0','')||to_char(:b5))) and PTN_TYPE=:b7) and rownum <=:b8) order
  by LINE_NUMBER

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        2      0.00       0.00          0          0          0           0
Execute      2      0.00       0.00          0          0          0           0
Fetch        2      0.30      15.06       1846      14030          0          22
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        6      0.30      15.06       1846      14030          0          22

Misses in library cache during parse: 1
Optimizer goal: RULE
Parsing user id: 27

Rows     Row Source Operation
-------  ---------------------------------------------------
     11  COUNT STOPKEY
     11   TABLE ACCESS BY GLOBAL INDEX ROWID PTN_INV PARTITION: ROW LOCATION ROW
 LOCATION
   5927    INDEX RANGE SCAN (object id 439540) -- index 4ix
_____________________________________________________________________

indexes in table are (
(a) partition id,npa,nxx,line_number
(b) nmb_id,nl,npa,nxx
(c) nmb_id,ngp,nl,npa,nxx,resource_status
(d) npa,nxx,line_number
(e) nmb_id,npa,nxx,line_number.

PK is (a).
______________________________________________________________________

      and we said...
Do you have my book "Expert one on one Oracle"  - if so, read chapter 10.

Anyway, look at the tkprof -- you are experiencing massive waits.  0.30 seconds
of CPU time -- 15+ seconds on the wall clock.  In my book I show how to enable
tracing to get waits (and binds) and how to read the trace file to diagnose
this.

If you ran statspack, I betcha you would discover that you have massive,
litterally massive IO waits on your system.  Have you looked at that?

Either that or you are so totally CPU bound that it took 15 seconds to get 1/3
of a cpu second of time.

Given that you did physical IO here, I would tend to lean toward "look at your
IO".  However, I also see a miss in library cache -- so make sure you are using
BIND VARIABLES As well.

Also -- you are kidding yourself by trying to force the optimizer to RULE when
using a partitioned table!!!!!  The optimizer will ALWAYS be COST when using a
partitioned table (index organized table, parallel tables, etc etc etc)

Lastly, you do realize that this predicate:

PTN_INV where
(
 (
  (
   (
    (
     (
      (NPA=:b0 and NXX=:b1
      )
      and NMB_ID=:b2
     ) and NL=:b3
    ) and RESOURCE_STATUS=:b4
   ) and LINE_NUMBER>=
        (DECODE(length(to_char(:b5)),1,'000',2,'00',3,'0','')||to_char(:b5))
  ) and PTN_TYPE=:b7
 ) and rownum <=:b8
) order by LINE_NUMBER

will get :b8 number of hits AND THEN sort them.  It'll not get the top-n :b8
number of hits sorted (eg: the sort takes place AFTER the predicate, you'll get
:b8 rows, then sort them.  If there were :b8+N rows that would have matched the
predicate -- you won't be getting the :b8 with the "least" line_number)

Also, if this is "the important query", a single concatenated index on:

npa,nxx,nmb_id,nl,resource_staus,ptn_type,line_number

might be useful to lower the logical IO's needed to get these 11 rows -- it
should take lots less then 7k+ LIOS

October 06, 2002 Ricky -- Thanks for the question regarding
      "Update Queries with Multiple Tables to Join", version 8.1.7

      You Asked
Dear Ask Tom:

Please see the SQL below.  I need to get the values from CPTInsType in to the
Charges table.  I tried the Update statement at the end of this script, but it
won't work because the values are not key-preserved, even though the first four
columns of CPTInsType are in a unique index. I also tried this query with a
where clause for the update statement, but it still updated all the records
applying nulls for values not found.

I expect the Charges table to change in this manner after the script is
completed:

  Row 1 (1, 1, '99283', '23') Change to 69453-15
  Row 2 (1, 2, '99281', '23') No Change
  Row 3 (2, 1, '99283', '')   Change to 78238-26
  Row 4 (2, 2, '99284', '23') No Change
  Row 5 (2, 3, '45361', '13') Change to 54383-  (modifier null)

Create Table Claims
(
   ClaimId           Number,
   InsuranceType     VarChar2(1),
   Facility          VarChar2(2)
);

Insert Into Claims Values(1, '1', 'HP');
Insert Into Claims Values(2, '2', 'PM');

Create Table Charges
(
   ClaimId           Number,
   ChargeSequence    Number,
   CPTCode           VarChar2(5),
   Modifier          VarChar2(2)
);
Insert Into Charges Values(1, 1, '99283', '23');
Insert Into Charges Values(1, 2, '99281', '23');
Insert Into Charges Values(2, 1, '99283', '');
Insert Into Charges Values(2, 2, '99284', '23');
Insert Into Charges Values(2, 3, '45361', '13');

Create Table CPTInsType
(
   Facility          VarChar2(2),
   InsuranceType     VarChar2(1),
   OldCPTCode        VarChar(5),
   OldModifier       VarChar2(2),
   NewCPTCode        VarChar2(5),
   NewModifier       VarChar2(2)
);

Insert Into CPTInsType Values('HP', '1', '99283', '23', '69453', '15');
Insert Into CPTInsType Values('HP', '2', '99283', '23', '89201', '');
Insert Into CPTInsType Values('PM', '1', '99283', '23', '89201', '22');
Insert Into CPTInsType Values('PM', '2', '99283',  '',  '78238', '26');
Insert Into CPTInsType Values('PM', '2', '45361', '13', '54383',  '');

Create Unique Index FaciltyCPT ON CPTInsType
(
   Facility,
   InsuranceType,
   OldCPTCode,
   OldModifier
);

Update
   (
     Select
         Charges.CPTCode,
         Charges.Modifier,
         CPTInsType.NewCPTCode,
         CPTInsType.NewModifier
      From
         Claims,
         Charges,
         CPTInsType
      Where
         Charges.ClaimId = Claims.ClaimId And
         CPTInsType.Facility = Claims.Facility And
         CPTInsType.InsuranceType = Claims.InsuranceType And
         CPTInsType.OldCPTCode = Charges.CPTCode And
         NVL(CPTInsType.OldModifier, '0') = NVL(Charges.Modifier, '')
   )

   Set CPTCode = NewCPTCode,
       Modifier = NewModifier
;

Sincerely,

Ricky Mitchell

      and we said...
First -- thanks so much for CREATE TABLES that run, INSERTS that generate the
data!!!! You have no idea how helpful that is, really!!! (hint hint everyone
else)

However, you neglected to add the primary keys ;(

In any case, your update could be:

ops$tkyte@ORA817DEV.US.ORACLE.COM> update charges
  2     set (cptcode,modifier) = ( select cptInsType.newCptCode,
cptInsType.newModifier
  3                                  from claims, cptinsType
  4                                 where Charges.ClaimId = Claims.ClaimId
  5                                   And CPTInsType.Facility = Claims.Facility
  6                                   And CPTInsType.InsuranceType =
Claims.InsuranceType
  7                                   And CPTInsType.OldCPTCode =
Charges.CPTCode
  8                                   And NVL(CPTInsType.OldModifier, '0') =
NVL(Charges.Modifier, '') )
  9  where exists ( select cptInsType.newCptCode, cptInsType.newModifier
 10                   from claims, cptinsType
 11                  where Charges.ClaimId = Claims.ClaimId
 12                    And CPTInsType.Facility = Claims.Facility
 13                    And CPTInsType.InsuranceType = Claims.InsuranceType
 14                    And CPTInsType.OldCPTCode = Charges.CPTCode
 15                    And NVL(CPTInsType.OldModifier, '0') =
NVL(Charges.Modifier, '') )
 16  /

2 rows updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

All you need is the where exists to restrict the rows updated to those rows that
have mates.
Review & Followup

      Rating: 5
Update Queries
      with Multiple Tables to Join  October 07, 2002

Reviewer:  Ricky Mitchell  from Roanoke, Virginia,
      USA
Dear Mr. Ask Tom:

Thank you kindly for your prompt reply to my question.  Your help is invaluable!

Ricky

October 07, 2002 Arun -- Thanks for the question regarding
      "Regarding On Update Cascade", version 8i

      You Asked
Dear Tom,
We know that when we delete a parent record, automatically
child record also will be deleted if we used "on delete cascade".
Is it possible to update automatically a child record when we
update parent record?
(Do we have "On Update Cascade" option? Or any other like..)

      and we said...
There is not "on update cascade" automagically.

There are ways to do it,

o deferrable constraints.  defer the foreign key check until commit, update the
parent, update the child and then commit.

o use the technique here:

http://asktom.oracle.com/~tkyte/update_cascade/index.html

Personally -- I've never found a need or use for update cascade.  I'm opposed to
it.  If your design requires it -- change your design now if you can.

Primary keys are supposed to be imutable, never changing, constant.  It is an
excessively bad practice to have to update them ever.  If there is a 0.00001%
chance you will have to update a primary key -- then it is not a primary key,
its a surrogate key and you need to find the true primary key (even if you have
to make it up via a sequence)
Review &
      Followup

      Rating: 3
Surrogate
      keys  October 07, 2002
Reviewer:  Tony
       from UK
Tom,

I agree with your advice, but query your use of terms.  A primary key that is
made up via a sequence surely IS a surrogate key.  The natural primary key may
well be the one that has a 0.00001% chance of change - e.g. due to a typing
error.  The only primary keys that could never require an update are surrogate
keys, where the key value has no meaning (like a sequence number).

          Followup:

A primary key is something that doesn't change.  If your primary key changes, it
is not a primary key -- a candidate key if you will but not a primary key.

Your right, I should have called it a "candidate key".

      Rating: 4
Thanks
      Mr.Tom  November 10, 2002
Reviewer:  Arun Kumar
      K T  from Chitradurga, Karnataka, India
Thank you very much Mr.Tom for your timely response.

October 07, 2002 Krishnamoorthi -- Thanks for the question
      regarding "bind variable", version 8.1.7.4

      You Asked
Dear Tom,

Our development team says that the bind variables will be useful only in 'loop
type' programs whereas in other pl/sql programs, the pl/sql variables themselves
are bind variables. Are they correct?
I told them that whenever there is repeated usage of the same sql statements(not
only in loop type but also in the case of many concurrent users issuing the same
statement, SQL or pl/sql) instead of using the hard coded values, using bind
variable will lead to good performance.

I quoted the example given by you in your book (usage of bind variable in a
pl/sql program) . Can you give one more example with some more explanation on
this for our dev. team to understand this better?

Regards,
Venkat

      and we said...
In PLSQL -- any reference to a plsql variable in your static sql is in fact a
bind variable.  So:

declare
    x number := 5;
    c number;
begin
    select count(*) into c from t where some_column = x;
end;

X is a bind variable in that query.  It is very hard NOT to use bind variables
properly in PLSQL unless you use dynamic sql.  Then you must take care to ensure
that you do use bind variables.

If you use static sql in PLSQL -- you are covered 100%.  Even if you use a hard
coded literal in static SQL, that is perfectly OK -- for the simple reason that
the query will never change (it is static)

It is when coding using DYNAMIC sql that you want to make sure you use binds.
For example, if you find yourself doing something like:

   execute immediate
   'select count(*) from ' || table_name || ' where x = ' || x
   into C;

then you know you have a problem!  the code should be:

   execute immediate
  'select count(*) from ' || table_name || ' where x = :x'
   into C
   using X;

If the developers are using VB or Java or some 3gl -- make sure they use bind
variables constantly in that code as well!!!!   It is not automagic in the 3gls
as it is in PLSQL.

Review
      & Followup

      Rating: 5
Bind Variables
      !  October 07, 2002
Reviewer:  Ram
       from Ct USA
well explained !

      Rating: 4
bind
      variable  October 08, 2002
Reviewer:  Venkat
       from Chennai, India
Tom,

Good explanation. Very clear now.

Regards,
Venkat

October 07, 2002 Sagi -- Thanks for the question regarding "A Few
      Questions", version 8.1.7

      You Asked
Hi Tom !

I was trying from a long time to post these question. Finally, I got a chance
after long time. Therefore my list is little big. If you can then please do
answer all the questions. Esle as per your policy 5 questions only.

Either way i appreciate your help

1) Can you provide some link where i can get info with examples for OWA.UTIL

2) What is the difference between:
   a) Native Dynamic SQL
   b) DBMS_SQL
   c) DBMS_UTILITY.EXEC_DDL_STATEMENT

Is there any difference from way of execution, performance etc. Which one is the
best.

3) Does'nt sgadef.dbf exists in NT environment? Is this only for Unix O/S? I
read that when the DB is up and running this file is created. I searched under
NT when my DB was up. But could not find.

4) Can we have wildcard pattern in a IN Clause. Something like this:

select object_name, object_type
from all_objects
where owner='SCOTT' and object_name IN ('EMP%', 'DEPT%') ;

I know we have to use "OBJECT_NAME LIKE 'EMP%' OR OBJECT_NAME='DEPT%'. But then
I thought you must definetly be haveing some trick for achiving this.

5) If I create a rollback segment as below:

   CREATE ROLLBACK SEGMENT TESTRBS
   STORAGE (INTIAL 1M NEXT 1M MINEXTENTS 10 MAX 255)

   CREATE ROLLBACK SEGMENT TESTRBS1
   STORAGE (INTIAL 2M NEXT 2M MINEXTENTS 25 MAX 255)

So now when I talk about the rollback segments and size. I say that I have 2
RBS. How about the size?
   TESTRBS is 10MB or 255MB? and
   TESTRBS1 is 50mb or 500MB?

Is this is what column "RSSIZE" of V$ROLLSTAT show?

Is always 1block (depnding upon our db_block_size) less is shown in RSSIZE. Is
this because of the starting block is used as header block i.e. for transaction
table.

6) Can't a SYSTEM user drop the db_links created by a user?
SQL> select owner, db_link from dba_db_links ;

OWNER     DB_LINK
------------------------------------------------
SCOTT     RK_CONN.US.ORACLE.COM

SQL>  drop database link RK_CONN.US.ORACLE.COM ;
 drop database link RK_CONN.US.ORACLE.COM
                    *
ERROR at line 1:
ORA-02024: database link not found

SQL> drop database link SCOTT.RK_CONN.US.ORACLE.COM ;
drop database link SCOTT.RK_CONN.US.ORACLE.COM
                   *
ERROR at line 1:
ORA-02024: database link not found

8) Where can I find more info about events (dumping internal structure).

9) How to set a default page to open in DAD. Because if i type
http://asktom.oracle.com/
it
automatically changes to
http://asktom.oracle.com/pls/ask/f?p=4950:1:
and
What is this "f?p=xxxxxx"

Thanx in advance.

Regards,
Sagi

      and we said...
1)
http://technet.oracle.com/docs/products/ias/doc_library/90200doc_otn/web.902/a90101/psutil.htm#1005550
documents each API calls inputs and outputs.  There are some examples in there
as needed (most are self explanatory)

2) I wrote an entire chapter on that in my book.  Tells you when to use which
kind.

NDS = "more integrated with the language", "non procedural" approach to dynamic
sql.  You need to know the number of inputs and outputs however at compile time
(the into and using clauses are hard coded at compile time)

DBMS_SQL = procedural approach to dynamic sql.  You do not need to know the
number or types of outputs or inputs (you can "describe" a query and process it
procedurally at runtime).

the execute ddl is just like "execute immediate", it just executes a statement.
It is a convienence routine.

3) sgadef.dbf is os and version specific.  It doesn't exist in current versions
of Oracle at all.  It is not a good way to see if Oracle is "up" as it can be
left behind in some circumstances (an unclean shutdown).  Ignore its very
existence.

4) The trick is to use LIKE with or's.  There is no "trick" really.

5) you would have to query the dictionary to find out currently how big each on
is.  They have a minimum and maximum size but could be any of the values in
between.  DBA_EXTENTS could be used to tell you how big they are right now.

Handy dandy "Reference Guide", which documents the v$ views, says:

<quote>
RSSIZE:

Size in bytes of rollback segment. This values differs by the number of bytes in
one database block from the value of the BYTES column of the
ALL/DBA/USER_SEGMENTS views.
</quote>

so yes, it is off by a block -- on purpose -- for that header.

6) nope.  Look at the name -- why wouldn't RK_CONN.US.ORACLE.COM be a db link
owned by RK_CONN named US.ORACLE.COM.  It would be ambigous.  Anyway, a private
db link is a private db link.

8) you will get this information from support if and when you need it.  Events
are undocumented, change from release to release and can have side effects in
one version but not another.  In short, they can be dangerous.

You know -- there is a TON to learn without learning the "mystical, secret,
undocumented stuff".  Believe it or not, I don't use events myself very much at
all.  The one I do use is the trace event -- but that is pretty well known and
documented.  Beyond -- only that which support would point me to in a specific
case.

Don't miss the forest for the trees.  If you cannot spit out every fact in the
Concepts guide without reading from the concepts guide, you haven't learned all
you need to know yet anyway.

9) F is our main procedure.  P is its input.  We choose F cause it is very short
(makes a page with lots of links very very small -- <a href=f?p=......>).  P is
a generic input that we glue information together in -- page numbers, state
values and so on.  We did that again for genericity and compactness.

In iAS, you configure apache using this section:

<VirtualHost asktom.oracle.com>
    ServerName asktom.oracle.com
    Redirect /index.html
http://asktom.oracle.com/pls/ask/f?p=4950:1:
    Alias /papers/ "/export/home/tkyte/public_html/"
</VirtualHost>

that simply says when someone connects to asktom.oracle.com -- redirect requests
for index.html to f?p.....

Review &
      Followup

      Rating:
      4
  October 07, 2002
Reviewer:  Sagi
       from India
Thanx for your time and explanation. We all are lucky 2have this site.

Cheers,
Sagi

      Rating:
      5
Great!  October 07, 2002
Reviewer:
      Robert  from Memphis, USA
Tom,

I especially liked the comment abount the Concepts Guide vs. the mysterious,
undocumented stuff, which some of us are drawn to... Thanks for the instruction
and reproof.

Robert.

October 07, 2002 hrishab -- Thanks for the question regarding
      "Creating table using select statement", version 8.1.6

      You Asked
hi Tom,

   I have been reading this forum regularly and learned most of oracle from this
site.

Here goes my question.

I was creating a table using select :

SQL> create table t as select * from very_big_table;

I open another session and queried v$sql and found some queries being executed.
The queries were like :

select file#, block#....

when I rerun the query again, I found some other query being executed like :

delete from fet$.......

Can you please explain what are these queries and how creation of table works
when you create using select statement

Regards,

      and we said...
You are using dictionary managed tablespaces (in 8i and up, you should use
LOCALLY MANAGED tablespaces).

fet$ is "free extents", where we store free extents for tablespaces.  You were
seeing an extent getting added to your table as it was growing.

This is what is known as recursive SQL (sql performed in order to perform your
SQL).  This space management stuff can be quite expensive and is why you want to
use locally managed tablespaces -- not dictionary managed ones.  The space
managment overhead pretty much goes away with LMTS.

October 07, 2002 andrew -- Thanks for the question regarding "lost
      dbms_output, http output over db link?", version 8.1.7

      You Asked
Tom
I was unable to get DBMS_OUTPUT and HTTP to return output from a proc/funct when
called over a db link. Am I doing something wrong or is it a restriction. If so,
does JAVA output buffer also have a similar restriction? Is there any way to
retrieve the remote buffer contents?
Regards
Andrew

      and we said...
You need to understand how dbms_output and htp work.

They both write to an array in a package.  If you call a remote procedure and it
calls its "local" dbms_output - its output will goto an array on the REMOTE
server.  Hence, when the procedure is complete and sqlplus goes to print out the
contents of the local dbms_output buffer -- it won't include ANY of the remote
stuff.

Same with HTP.  HTP just writes to an array.  If you call a remote procedure
that makes LOCAL htp calls (local relative to itself that is), the data will be
stored in the array on the REMOTE server, not locally.  When OWA (or mod_plsql)
goes to print out this array -- the remote data will not be there.

So, can you work around this?  sure, but you'll have to help.  Say you have a
remote procedure:

create or replace procedure p
as
begin
        dbms_output.put_line( 'hello' );
        dbms_output.put_line( 'world' );
end;
/

and you run:
scott@ORA815DEV.FOO> declare
  2          l_data dbms_output.chararr@ora817dev.us.oracle.com;
  3          l_lines number default 999999999;
  4  begin
  5          dbms_output.enable@ora817dev.us.oracle.com( 1000000 );
  6          p@ora817dev.us.oracle.com;
  7          dbms_output.get_lines@ora817dev.us.oracle.com( l_data, l_lines );
  8          for i in 1 .. l_lines
  9          loop
 10                  dbms_output.put_line( l_data(i) );
 11          end loop;
 12  end;
 13  /
hello
world

PL/SQL procedure successfully completed.

All we did was to setup dbms_output on the remote server via enable, run the
procedure, and then grab the dbms_output output and add it to ours...

The same would be true with OWA packages.  This example:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:347617533333
shows the relevant api calls you need to make to dump an "owa" page (although it
might take a tad more work as the HTTP headers will be intermingled in there,
you'll have to ignore them)

Review &
      Followup

      Rating: 5
Would putting
      frequent DBMS_OUTPUT .put_line reduce Performance   October
      07, 2002
Reviewer:  pasko  from HH,Germany

Hi Tom,

Sometimes i  put frequent dbms_output.put_line statements in my Packages for
Debugging purposes.

My question is,  would these statements be a Performance Bottle-neck when they
are left un-commented in a package.

Thanks in advance

          Followup:

Well, what I did was run this procedure:

create or replace procedure do_output
as
begin
    for i in 1 .. 50000
    loop
        dbms_output.put_line( 'Hello World' );
        null;
    end loop;
end;
/
alter session set sql_trace=true;
exec do_output;

I did it

o with serveroutput on
o without serveroutput on
o with the dbms_output.put_line commented out

Results are:

BEGIN do_output; END;

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.01          0          0          0           0
Execute      1      4.37       4.36          0          0          0           1
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      4.37       4.37          0          0          0           1

with serveroutput

BEGIN do_output; END;

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.00          0          0          0           0
Execute      1      0.34       0.34          0          0          0           1
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      0.35       0.34          0          0          0           1

WITHOUT serveroutput

BEGIN do_output; END;

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.05       0.05          0          0          0           1
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      0.05       0.05          0          0          0           1

with dbms_output commented out

So, I would say in the grand scheme of things (having 50k calls to dbms_output
would be quite a bit after) -- it would be OK to leave in even in production (i
do).

      Rating: 5
Thanks very
      much Tom !!!  October 08, 2002
Reviewer:
      Pasko  from HH,Germany

October 07, 2002 John -- Thanks for the question regarding
      "Refresh "complex" materialized view with a trigger", version 8.1.6

      You Asked
We have a "complex" materialized view which can only be
refreshed on demand. We want to execute the refresh using a
trigger so that as soon as a user updates certain tables, the
update statement triggers the refresh of the materialized view.

The DBMS_MVIEW package can not be used in a trigger because it
contains a commit. We tried using pragma AUTONOMOUS_TRANSACTION
which is of course useless because the autonomous transaction
does not "see" the changes the user made.

Question:
How is it possible to refresh a "complex" materialized view
using a trigger.

      and we said...
You can do it asynchronously with DBMS_JOB.

As you found, you cannot use the refresh in the trigger because
of the commit.  Doing it autonomously doesn't work either since
the changes are not visible to any other session yet.  By using
DBMS_JOB in the following fashion:

create or replace trigger X
after insert or update or delete on T
declare
   l_job number;
begin
   dbms_job.submit( l_job, 'dbms_mview.refresh( ''MV_NAME'' );'
);
end;

shortly after you commit, not right away but shortly, the job
will run.  You need to set:

job_queue_interval
job_queue_processes

for dbms_job to work (but if you are using MV's you probably
already have this setup).

Review &
      Followup

      Rating: 2
RE: refresh
      "complex" materialized view with a trigger  March 12, 2001

Reviewer:  John Muyres  from The Netherlands

Thank you for your response.

We did think about the suggested solution but rejected it because:
1. The dependency on job_queue_interval. There is a small time interval before
the refresh. In that interval MV data is not correct.

2. The fact that the master tables are almost static (insert/update takes place
only a few times a year) which doesn't warrent the extra overhead.

Perhapse yet an other solution is posible ?

      Rating:
      5
  March 12, 2001
Reviewer:
      A reader

      Rating: 3
Works great but
      ...  March 07, 2002
Reviewer:  Dave
       from Richmond, VA USA
We found this response to be very helpful but we are having trouble implementing
it. It seems to work great if the schema owner makes the change that cause the
trigger to fire. However, when another user (who has privilege to change the
master table) makes a change, the job gets queued up but fails when picked up
for execution. We think this is because the LOG_USER is not the schema owner.

Any ideas?

          Followup:

make that trigger be:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger X
  2  after insert or update or delete on T
  3  declare
  4     l_job number;
  5  begin
  6     dbms_job.submit( l_job, 'dbms_mview.refresh( ''OPS$TKYTE.T_MV'' );' );
  7  end;
  8  /

Trigger created.

that is, put the SCHEMA NAME in the refresh call (else dbms_mview puts USER in
there and it'll try to refresh USER.T_MV and that mv doesn't exist.

sorry about that!

      Rating:
      5
perfect  March 08, 2002
Reviewer:
      DAVE  from Richmond, VA USA

      Rating: 5
JOB
      Question  September 26, 2002
Reviewer:
      Ashok  from USA
 dbms_job.submit( lv_checkjob_num, 'DEAL_REFRESH_PACKAGE.deal_mv_refresh;' );

I have submitted JOB command to refresh the materailized view but nothing
happens why?i am able to see the record inuser_jobs

I checked the JOB_que_processes = 1 and job_QUE_INTERVAL = 60 sec.
After changing JOB_QUE_PROCESSES we have shutdown and restart?we are running
oracle 8.1.7.4

          Followup:

add

commit;

sure you can see the job -- but, until you commit it -- the job queue process
cannot see it.

      Rating: 3
Time Interval
      Problem  September 27, 2002
Reviewer:  Tony
       from India
Hi Tom,

1. Let us assume that job_queue_interval=60 seconds and the number of jobs are
too huge to finish within 60 seconds. Next Job execution should start even if
the current queue is not ye over. What will happen in this case?

2. What will be the impact on DB performance if so many complex jobs are
scheduled?

          Followup:

1) the job queue process will start by sleeping 60 seconds.

it will wake up and run every job in the queue that is ready to run.

it will then sleep 60 seconds.

it will wake up and run ........

2) think about it.  same thing that would happen if you fired up N sqlplus
sessions and ran the procedures interactively.

      Rating: 5
Refreshing
      Materialized view in a Group  October 07, 2002

Reviewer:  Ashok  from USA
When i try to refresh the materialized view in a group(using
dbms_refresh.refresh('Group Name') it is   supposed to do delete then Insert.If
above statement is correct why i am getting  zero record in the Middle of
refresh when i try to do count(*) from different session......

          Followup:

turn on sql_trace, run the dbms_refresh and look at the sql.

Maybe you didn't actually make a group -- not seeing your statements, I cannot
really tell.  But the sql_trace will show you the exact sql being used.

      Rating: 5
Refreshing
      Materialized view in a Group   October 07, 2002

Reviewer:  Ashok  from USA
It's doing delete & Insert ,i found it writing  a trigger on a MV table for
delete.The command i used to create a group is.
 Begin
 dbms_refresh.make(
 name             =>'deal_dtl_refresh',
 list             =>'deal_dtl_mv',
 next_date    =>sysdate,
 interval         =>'sysdate+1/24',
 implicit_destroy =>true);
 end;

          Followup:

If it does a delete + insert and you don't see any commit records in there (in
the tracefile) then what you observed must be explained some other way (eg: you
made a mistake somewhere and what you think happened did not happen that way).

      Rating: 5
Refreshinh
      Materialized View  October 07, 2002
Reviewer:
      Ashok  from USA
>>turn on sql_trace, run the dbms_refresh and look at the >>sql
Tom You are right it's doing Truncate & Insert Instead of delete and Insert.What
i am doing wrong here,is my creating MV group is wrong.
This is how i created the MV group
 Begin
 dbms_refresh.make(
 name             =>'deal_dtl_refresh',
 list             =>'deal_dtl_mv',
 next_date    =>sysdate,
 interval         =>'sysdate+1/24',
 implicit_destroy =>true);
 end;

          Followup:

I see what it is now.

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1389964969205
you need at least 2 mv's in the list in order to get it to use delete+insert.
If you have but one, just create a "dummy" MV (with a "where 1=0" predicate or
something) in order to have two that can be refreshed together.

October 07, 2002 Subrata -- Thanks for the question regarding "An
      outer join or a function call in the select statement?", version 8.1.7

      You Asked
Objective is to optimize a query for fetching description.
There are two ways, outer join or function call from the select statement.
Which one will perform better? Assume that the driving table is really huge and
all relevant indexes are there and the number of tables for join is more than 5

1. Outer join

   select T2.fi,
          T3.fj,
          ......
   from   T1, T2, T3...
   where  T1.f1=T2.f(+)
   and    T1.f2=T3.f(+)
   ....

2. select function1(T1.f1),
          function2(T1.f2),
          function3(T1.f3),
          .......
   from   T1

   ***********************
   function1(p_var1 in number) return number is
   l_var number;
   begin
       select T2.fi
       into   l_var
       from   T2
       where  T2.f=p_var1;

       return(l_var);

   exception
       when others then
            return (null);
   end;
   **************************

      and we said...
Forget option 2, add option 3

select t1.*, (select t2.fi from t2 where t2.f = t1.f),
             (select t3.fi from t3 where t3.f = t1.f),
              .....

and then benchmark option 1 against option 3 to see what fits best in your
enviroment.  Suggestion:  create a view V that hides the method of access so you
can switch back and forth without modifying any code.

Review &
      Followup

      Rating: 4
Theoretical
      input  September 04, 2002
Reviewer:  Subrata
      Saha  from Bangalore, India
Can we expect some theoretical input from you on this. Like some comparison on
the overhead of calling function versus the cost of joining huge tables etc.
Any link in your site for benchmarking such cases?

Thanks

          Followup:

To get the last row from the query, outer joining will probably be best.

To get the first row of the query as fast as possible, select (select) will
probably be best.

http://asktom.oracle.com/~tkyte/runstats.html
for my benchmark thingy...

      Rating:
      3
Doubt  September 04, 2002
Reviewer:  A
      reader  from Bangalore
Tom,
   Execution wise this will be the almost same as separate function calls right?
Because the tables T2 and T3 will be accessed per record and if the number of
records are huge then it could prove costly. Also, function will have the
advantage of being cached.

Can you please give a reason for your choice of Option3.

regards

          Followup:

Execution wise, calling PLSQL from SQL is hugely expensive when compared to
calling SQL from SQL (context switch).

I have no idea where you are going with "function will have the adv of being
cached" -- without the function (my select (select)) there is NOTHING to be
cached!

(i don't know why it seems almost blatantly obvious to me that calling a stored
function from sql would be hugely expensive compared to calling SQL from SQL?)

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function foo( p_object_id
in number ) return varchar2
  2  as
  3          l_object_name all_objects.object_name%type;
  4  begin
  5          select object_name into l_object_name
  6            from all_objects
  7           where object_id = p_object_id;
  8
  9          return l_object_name;
 10  end;
 11  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set sql_trace=true;

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select object_id, (select object_name from
all_objects where object_id = X.object_id )
  2    from all_objects X
  3  /

23626 rows selected.

Execution Plan
----------------------------------------------------------
ERROR:
ORA-01039: insufficient privileges on underlying objects of the view

SP2-0612: Error generating AUTOTRACE EXPLAIN report

Statistics
----------------------------------------------------------
         14  recursive calls
          4  db block gets
     237853  consistent gets
         35  physical reads
          0  redo size
    1137662  bytes sent via SQL*Net to client
     175250  bytes received via SQL*Net from client
       1577  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      23626  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM> select object_id, foo( X.object_id )
  2    from all_objects X
  3  /

23626 rows selected.

Execution Plan
----------------------------------------------------------
ERROR:
ORA-01039: insufficient privileges on underlying objects of the view

SP2-0612: Error generating AUTOTRACE EXPLAIN report

Statistics
----------------------------------------------------------
      26315  recursive calls
          4  db block gets
     263883  consistent gets
          0  physical reads
          0  redo size
    1098058  bytes sent via SQL*Net to client
     175250  bytes received via SQL*Net from client
       1577  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
      23626  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

********************************************************************************

select object_id, (select object_name from all_objects where object_id =
X.object_id )
  from all_objects X

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.03       0.04          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch     1577      9.24       9.38         35     237849          4       23626
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total     1579      9.27       9.42         35     237849          4       23626

********************************************************************************

select object_id, foo( X.object_id )
  from all_objects X

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.02       0.04          0          2          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch     1577     11.87      12.71          0      80437          4       23626
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total     1579     11.89      12.75          0      80439          4       23626

********************************************************************************

Now, at first glance you might say "Oh, well, they are pretty close", but
you've forgotten the recursive SQL

SELECT OBJECT_NAME
FROM
 ALL_OBJECTS  WHERE OBJECT_ID = :b1

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.02       0.02          0          0          0           0
Execute  23626      3.51       3.53          0          0          0           0
Fetch    23626     15.06      14.57          0     183442          0       20952
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    47253     18.59      18.12          0     183442          0       20952

You have to add those two times together (and that doesn't even include the
time for the content switch back and forth from SQL -> PLSQL -> SQL).  A little
wall clock time here:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select object_id, (select object_name from
all_objects where object_id = X.object_id )
  2    from all_objects X
  3  /

23626 rows selected.

Elapsed: 00:00:10.58

ops$tkyte@ORA817DEV.US.ORACLE.COM> select object_id, foo( X.object_id )
  2    from all_objects X
  3  /

23626 rows selected.

Elapsed: 00:00:27.73

anytime you can do it exclusively in SQL -- do it.  It'll be better

      Rating: 4
Similar
      ...  September 04, 2002
Reviewer:
      A reader  from CA USA
I have a similar, but not so, situation.

These are relevent columns from Table A and table B

Table A                Table B
-----------           ----------
Id (PK)                Id (PK)
p_col1                   c_col1
                       tmstmp

Table A is parent of table B with none or multiple child possible.

Query needs to retrieve few rows from Table A and for each row it needs to pick
up the value of c_col1 in latest ( based on tmstmp) corresponding ( joined on
PK) row  from Table B. Column c_col1 is used in where clause also.

I have used function to handle this. But I was wondering whether inline view be
used in this case and how ?

Appreciated.

          Followup:

pretending TABLE A is DEPT
and TABLE B is EMP

and tmstmp is HIREDATE
and c_col1 is ENAME

either of these will do you:

scott@ORA920.US.ORACLE.COM> select dname,
  2         substr((select max(to_char( hiredate,'yyyymmddhh24miss' )||ename)
  3                   from emp
  4                  where emp.deptno = dept.deptno ), 15 ) ename
  5    from dept
  6  /

DNAME          ENAME
-------------- ----------
ACCOUNTING     MILLER
RESEARCH       ADAMS
SALES          JAMES
OPERATIONS

scott@ORA920.US.ORACLE.COM>
scott@ORA920.US.ORACLE.COM> select dname, ename
  2    from ( select dname, ename,
  3                  row_number() over (partition by dept.deptno
  4                                     order by hiredate desc
  5                                     nulls last ) r
  6            from dept, emp
  7           where dept.deptno = emp.deptno(+)
  8        )
  9   where r = 1
 10  /

DNAME          ENAME
-------------- ----------
ACCOUNTING     MILLER
RESEARCH       ADAMS
SALES          JAMES
OPERATIONS

      Rating: 4
Create a
      view?  October 07, 2002
Reviewer:  Kashif
       from Houston, TX USA
Hi Tom,

Again, thanks for the great discussion. You suggest the following to the
original poster:

"
Suggestion:  create a view V that hides the method of access so you
can switch back and forth without modifying any code.
"

Not sure what you mean here. Any explanation would be helpful. Thanks.

Kashif

          Followup:

They asked:

which method of querying is faster:

  Query1
  query2

My suggestion -- try both, but, hide the syntax of both query1 and query2 from
the application by

create view V as query1;

so you can easily;

create or replace view V as query2;

if and when you want without having to change the code.

      Rating:
      5
  February 20, 2003
Reviewer:
      Mirjana  from Barcelona

October 07, 2002 Vikram -- Thanks for the question regarding "To
      generate a queryable & updateable form with fields from three
      different tables", version 2.2

      You Asked
Hi Tom,
       I am having a problem in building a form, built on fields
of three different tables. I tried to create a view with the
required fields from the three tables and build a form on that.
But when we try to add or update, either it says 'not more than
one table is updatable at a time' or 'ORA-01779: cannot modify a
column which maps to a non key-preserved table'. I got to do
this form and i can't figure out a way to make it happen. Can
you please, help me out. The database we are using is Oracle816.

Thanks

      and we said...
We'll have to use INSTEAD-OF triggers to accomplish this.  In
order to remove the ambiguity that arises from a multi-table
view, we can programmatically describe how to perform the
insert/update correctly.  Here is a small 3 table example:

scott@8i> create table t1 ( x int primary key, a int );
Table created.

scott@8i> create table t2 ( y int primary key, b int );
Table created.

scott@8i> create table t3 ( z int primary key, c int );
Table created.

scott@8i> create or replace view v
  2  as
  3  select x, a, b, c
  4  from t1, t2, t3
  5  where x = y
  6    and x = z
  7  /

View created.

scott@8i> update v set a = 1 where x = 1;
0 rows updated.

scott@8i> update v set b = 1 where x = 1;
0 rows updated.

scott@8i> update v set c = 1 where x = 1;
0 rows updated.

scott@8i> update v set a = 1, c = 1 where x = 1;
update v set a = 1, c = 1 where x = 1
                    *
ERROR at line 1:
ORA-01776: cannot modify more than one base table through a join
view

So that shows the problem, we can update any column one at a
time but never together since they come from more then one
table.  We would have a hard time inserting into this as well
since the primary keys for t1-t3 are not all included in the
view (there is nothing to insert into t2(y) for example -- no
primary key would be supplied even if we could insert into a
multi-table view which we can't).  So, lets teach the database
how to do these operations:

scott@8i>
scott@8i> create or replace trigger v_Itrigger
  2  INSTEAD OF INSERT ON V
  3  begin
  4      insert into t1 values ( :new.x, :new.a );
  5      insert into t2 values ( :new.x, :new.b );
  6      insert into t3 values ( :new.x, :new.c );
  7  end;
  8  /

Trigger created.

scott@8i> create or replace trigger v_Utrigger
  2  INSTEAD OF UPDATE ON V
  3  begin
  4      update t1 set a = :new.a, x = :new.x where x = :old.x;
  5      update t2 set b = :new.b, y = :new.x where y = :old.x;
  6      update t3 set c = :new.c, z = :new.x where z = :old.x;
  7  end;
  8  /

Trigger created.

scott@8i>
scott@8i>
scott@8i> update v set a = 1, c = 1 where x = 1;

0 rows updated.

scott@8i> insert into v values ( 0, 1, 2, 3 );

1 row created.

scott@8i> select * from t1;

         X          A
---------- ----------
         0          1

scott@8i> select * from t2;

         Y          B
---------- ----------
         0          2

scott@8i> select * from t3;

         Z          C
---------- ----------
         0          3

scott@8i>

And that shows we can now insert/update this view as naturally
as a "real" table.
Review & Followup

      Rating: 4
The same
      message  May 28, 2002
Reviewer:  Adrian
       from Peru
hi Tom, i have the same message, the code is sohw below:

update (select mec_swift, dirswi
          from me_banqueros,
               bslbmex.cexf25@gtwdev
     where mecpk_cod_bco = codban)
   set mec_swift = dirswi;

where mecpk_cod_bco is the pk of the table, thaks in advance

          Followup:

yes, well -- not knowing what columns come from which table, what table has the
primary key on it and so on makes it really difficult to give you any input.

mecpk_cod_bco needs to be the primary key of the table containing DIRSWI.  is
that the case?

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table me_banqueros;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table cexf25;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table me_banqueros( mecpk_cod_bco int
primary key, dirswi int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table cexf25( mec_swift int, codban
int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> update (select mec_swift, dirswi
  2                    from me_banqueros,
  3                             cexf25
  4           where mecpk_cod_bco = codban)
  5  set mec_swift = dirswi;

0 rows updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table me_banqueros;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table cexf25;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table me_banqueros( mecpk_cod_bco int
primary key, mec_swift int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table cexf25( dirswi int, codban int
);

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> update (select mec_swift, dirswi
  2                    from me_banqueros,
  3                             cexf25
  4           where mecpk_cod_bco = codban)
  5  set mec_swift = dirswi;
set mec_swift = dirswi
    *
ERROR at line 5:
ORA-01779: cannot modify a column which maps to a non key-preserved table

      Rating: 5
Is this Update
      Correct....  May 29, 2002
Reviewer:  Pasko
       from HH,Germany
Hi Tom

Thanks for a Great Response to the Queries above..

I have this little Update here...

"I would like to set DATE_TO to  SYSDATE where the value of DATE_TO  for a
particular ID is NULL for a maximum VALUE"

Is it correct assuming there  could be many IDs  with many values ...and
:input_ID   is inputted from as a parameter from a procedure...

Update   my_table  a
set      date_to  =  sysdate
where  ( ID  ,  value  )   =
(  select ID ,  max(value )
   from my_table  b
   where a.ID  =  b.ID
   and   b.date_to  is NULL
   group by   b.ID )
and  ID  =   :input_ID   ;

Thanks in advance

pasko

          Followup:

update T
   set date_to = SYSDATE
 where (id,value) = (select :input_id, max(value)
                       from T
                      where id = :input_id
                        and date_to is null )

would be perhaps a little more efficient (rather then running the correlated
subquery for each ID file in T, we run the query once and then update the
corresponding rows).

That'll find the max value for a given ID and update the row(s) that have that
ID/Value pair.

      Rating: 5
continue from
      above....  May 29, 2002
Reviewer:  Pasko

Hi Tom

Suppose i have a Primary key now on ID column..
and i want to update values corresponding to  fk_ID ( which was ID from the
above comment )....

Which one is better/correct/wrong ...

Update    my_table a
set       date_to = sysdate
where     ID   =
(   select ID from   my_table  b
    where  a.fk_ID  =  b.fk_ID
    and  b.value  =
    ( select max(c.value)  from my_table c  where c.fk_ID  =  b.fk_ID )
           and   b.date_to is NULL
           and   b.fk_ID     =   :input_ID   ;

      Rating: 5
Sorry and
      thanks  May 29, 2002
Reviewer:  Adrian
       from Peru
thanks very much, sorry about the columns, i forget it.... but the second case
showme why happen that.

      Rating: 4
Updateable view
      on 7.3  October 07, 2002
Reviewer:  Dinesh
      Sivasankaran  from St. Petersburg, FL
In reference to the original response:

http://asktom.oracle.com/pls/ask/f?p=4950:8:1402128::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:373418158423,%7Bnon%7D%20and%20%7Bkey%7D%20and%20%7Bpreserved%7D
Is there a way to achieve the INSTEAD OF trigger functionality in version 7.3?

Thanks,
dinesh ...

          Followup:

Short of upgrading it to 8.x and up?

No.  The feature was added in about 1997 when version 8.0 was released over 5
years ago....

October 07, 2002 Bala -- Thanks for the question regarding
      "Accessing Files from DB", version 91

      You Asked
Operating System: Windows
DB: 9i

There used to be restriction on accessing files on network from within DB
(LOB's, UTL_FILE etc.). In an NT environment, the files phsically got to be in
the Box where DB is installed. Is this still true in 9i? Are there any work
arounds?

Thanks

      and we said...
Never has there been such a restriction.

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:241814624807
It is all in understanding how NT the operating system works under the covers.
Sometimes a pretty GUI tricks you into thinking "its easy".  A unix mount is
looking pretty "easy" all of a sudden ;)

October 07, 2002 Anu -- Thanks for the question regarding "User
      environment", version 8.1.7

      You Asked
Hi Tom,
         This is a simple procedure, I wrote to know the Operating system
username and ip address.

PROCEDURE print_osuser IS
   v_os_user   varchar2(50);
   v_ip_address varchar2(30);
BEGIN
  select sys_context('USERENV','OS_USER')
     into V_OS_USER
    from DUAL;
  htp.htmlopen;
  htp.p('your username is '|| v_os_user);
  htp.nl;
  htp.p('your ip address is '|| v_ip_address);
  htp.htmlclose;
END;
          If I execute this procedure from web as
https://myservername/pls/dadname/print_osuser , it always returns SYSTEM as my
operating system username.

          If I execute select sys_context('USERENV','OS_USER')  from DUAL;
works fine and return my username.

          Do I need to do any changes in DAD settings.

          Thanks in advance.

      and we said...
The OS_USER is the OS_USER of the client -- the client is the webserver -- it is
printing the OS username that is running the webserver.

There is no way to get the os username of the webbrowser.  They do not pass that
to us.  You can use

owa_util.print_cgi_env;

to see what environment variables you do have access to.
Review &
      Followup

      Rating: 4
OS User
      Name  October 08, 2002
Reviewer:  Anu
       from San Antonio, TX USA
Thanks Tom, It Helps a lot !


Subject | Article | Last Updated (East coast time)
Encryption-Access Control | article only | Oct 08, 2002 07:42 am
Time Series Data and Buffer Gets | article only | Oct 08, 2002 07:42 am
Database Transfer to high level Configuration Win NT | article only | Oct 08, 2002 08:20 am
free text search | article only | Oct 08, 2002 10:49 am
Dynamic Selection of Table | article only | Oct 08, 2002 10:52 am
dynamic function | article only | Oct 08, 2002 12:39 pm
Bulk collect with cursor variable | article only | Oct 08, 2002 12:52 pm
Unindexed Foreign Keys, but using triggers | article only | Oct 08, 2002 06:31 pm
Segregating procedures/functions in large packages automatically | article only | Oct 09, 2002 06:57 am
Execute Immediate does not work in forms. | article only | Oct 10, 2002 06:45 am
SQL Loader Problem | article only | Oct 10, 2002 07:17 am
oracle.exe modes | article only | Oct 11, 2002 07:29 pm
PL/SQL WAS script -- generate a table with links to update rows.. | article only | Oct 11, 2002 07:37 pm
Need to finde the count | article only | Oct 11, 2002 08:27 pm
using application context and also statspack | article only | Oct 11, 2002 08:41 pm
Updating a Table Row from a %RowType Record (Collection) | article only | Oct 12, 2002 10:57 am
spfile autoconfiguration | article only | Oct 12, 2002 11:23 am
PDML | article only | Oct 12, 2002 11:29 am
Problem of the rows getting updated while they are being currently displayed on the online screen | article only | Oct 12, 2002 05:45 pm
Design of copying data | article only | Oct 14, 2002 07:21 am
External table (again) | article only | Oct 14, 2002 08:05 am
is there anyway to exclude tables while importing? | article only | Oct 14, 2002 10:40 am
Eg for DESEncryption and DESDecryption | article only | Oct 14, 2002 08:21 pm
Tables that have been accessed... | article only | Oct 14, 2002 08:46 pm
Storing Decimals in Number datatype | article only | Oct 14, 2002 09:00 pm
row(s) 1 - 25 of 25
October 08, 2002 Sam -- Thanks for the question regarding "Encryption-Access Control", version 8.1.7
You Asked
Hi Tom!
Since its quite difficult to find a slot to submit a question, i will take the
opertunity to ask you two questions

1- Oracle provides its Encryption algorithms like 3DES etc to be used to encrypt
the data over the wire, as a part of the advanced security options. However my
problem is i have my own algorithms developed and i want to use them , is there
any way i can use those algorithms instead of the ones provided with oracle, if
no, is there a work arround? i have form6i front end and database 8.1.7

2- does oracle provide you any option to grant or revoke preveliges on table
columns? if no how would u do it ? well i dont want to use views.

Cheers
Sam
and we said...
1) no.  what would be the business case for such a thing (i mean, given we
provide out of the box support for the strongest encryption algorithms out
there??)

2) On update and insert -- yes.  On select and delete -- no.

You do not have to use views (although they are a pretty good choice).  You can
use vertical partitioning (put public columns in T1, private columns in T2)...
Review & Followup
Rating: 4
October 08, 2002
Reviewer: Abdal-Rahman Agha from Yemen
Hi Tom,

   what is the "vertical partitioning"? Do you mean projection?

Followup:
I mean splitting the table into two "down the middle"

create table emp
( empno number primary key,
  ename varchar2(30),
  hiredate date
);

create table emp_priv
( empno number primary key,
  sal   number,
  comm  number
);

Rating: 2
ah geez! October 08, 2002
Reviewer: Sam from islamabad
well i am not asking about the in built support for out of the box encryption
algorithm, i am just asking IFF we want to use our own encryption
algorithm...how can i do that..mayb using a workarround like using the Net8 Api
to encrypt the data before sendind it etc..?

can you eleborate more on vertical partitioning?

Followup:
and I answered that question as plainly and simply as I could:

no

is what I said.

there is no way to replace the out of the box encryption APIS or snap your own
in -- it would break the jdbc drivers (where you would need to put your code)
and every oracle client install (where again, you would need to put your code).
We do not allow you to add your code to the TNS layer.  Think of the support
nightmare, they security nightmare (trojan horse pops into my head among other
things) something like this would introduce.

Rating: 1
disappointing October 08, 2002
Reviewer: A reader

Rating: 5
Why disappointing? October 08, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
Sam and/or A Reader:

Just curious why this particular feedback received 1 star and a "disappointing"?
 Here is an answer that is accurate, easily understood, free, and given in a
timely manner - what is wrong with that?  Is it just because the answer wasn't
what you wanted to hear?

I am seriously asking this - I mean, Tom has answered questions virtually
everyday (including days like Christmas), and I really fail to understand why
people respond with such ratings...

Anyway, perhaps I (nor anyone else) will never know...

- Mark

Rating: 5
Encryption-Access Control October 09, 2002
Reviewer: Venkat from Hyderabad
Reader,

This is one of richest resource for Oracle. You have given rating of 1, when you
gave the rating like this, you also need to tell what made you to give the
rating so that Tom would have given you the best solution, just ranking 1 with
out any comments is very bad.

- Venkat

Rating: 5
Good that you can't... November 18, 2003
Reviewer: A reader from Birmingham, UK
As a crypto kinda guy, I'm very glad that people can't use their own algorithms.
With the exception of government institutions, home-grown crypto is generally
slower and far less secure than things like 3DES.

October 08, 2002 Brij -- Thanks for the question regarding "Time Series Data and Buffer Gets", version 817/9
You Asked
Hi Tom,

Wow...what a site. My conceptual/practical knowledge grows every time I visit
it!!

Now to my questions...:-)

i) From a theoretical perspective, if I understood how this works correctly, the
amount of effort and resource required by oracle to maintain read-consistency is
considerably reduced by reducing the number of commits; When a commit is
performed oracle does not write to datafile, it initially performs a 'fast
commit' by modifying he rollback header and marking the transaction as
'committed', however, the block on file remains uncommitted.

Over-committing leads to transaction headers in rollback segments which are
needed to maintain read consistency being wiped out, and hence oracle must take
further action to maintain read consistent data. In this scenario is it common
for oracle to maintain multiple versions of data in memory i.e. for each DML/DQL
operation?

How can I see what data buffer data is being held, and if multiple versions are
being held?

Would using a larder rollback segment help matters? Any other action I can
take???

ii) We store a number of 'ladder type' tables within our database which
effectively store the same data primary key, except a ladder date which is
update each day.

For example...

CREATE TABLE MARK_PRICE_LADDER (
  LADDER_DATE    DATE          NOT NULL,
  RFK_ISEC_ID    NUMBER (9)    NOT NULL,
  VALUE_SPEC_ID  NUMBER (9)    NOT NULL,
  BID_VFID       NUMBER (9),
  BID_PRICE      NUMBER (31,16),
  BID_UPD_DT     DATE,
  ASK_VFID       NUMBER (9),
  ASK_PRICE      NUMBER (31,16),
  ASK_UPD_DT     DATE,
  PRICE_CCY      VARCHAR2 (3)  NOT NULL,
  AUDIT_NOTES    VARCHAR2 (255)  NOT NULL,
  NOTES          VARCHAR2 (4000),
  UNIQUE (LADDER_DATE, RFK_ISEC_ID, VALUE_SPEC_ID, BID_UPD_DT),
  UNIQUE (VALUE_SPEC_ID, RFK_ISEC_ID, LADDER_DATE),
  PRIMARY KEY ( LADDER_DATE, RFK_ISEC_ID, VALUE_SPEC_ID ) ) ;

For each unique rfk_isec_id, value_spec_id combination, there are multiple
'ladder date' rows i.e the primary key is the same for each row.

Now, this leads us to all sorts of problems in terms of performance with high
volumes of data. I am aware of the Oracle time series cartridge and was
interested in its capabillities and/or whether it can be used in this context?

Are there any new features in 9i, or proposed features in 10(i?) which would be
of use also?

Thanks in advance,

Brij
and we said...
i) over committing is bad -- but not for the reason you mention.

o over committing is a resource hog (the session doing the commits pays a
performance penalty)

o over committing generates more redo log

o over committing DESTROYS TRANSACTIONAL INTEGRITY.  a transaction, a logical
unit of work, is what dictates when you commit.  Nothing more -- nothing less.
A transaction defines its scope.  Performance, fear of "too big", none of those
factor into the decision to commit (or not).  The transaction is the only thing
that factors.

The condition you describe in i) is a precursor to an ora-1555 due to the
segment headers getting overwritten and some long running query not being able
to figure out what version of the block it can use anymore.

Oracle maintains multiple consistent read versions of a block in cache
regardless of how frequently or not you commit.  It is natural, normal and the
way it works.

V$BH is the view you want.

ii)

  UNIQUE (LADDER_DATE, RFK_ISEC_ID, VALUE_SPEC_ID, BID_UPD_DT),
  UNIQUE (VALUE_SPEC_ID, RFK_ISEC_ID, LADDER_DATE),
  PRIMARY KEY ( LADDER_DATE, RFK_ISEC_ID, VALUE_SPEC_ID ) ) ;

is bizzare.   Why the

  UNIQUE (VALUE_SPEC_ID, RFK_ISEC_ID, LADDER_DATE),

constraint when the primary key is already there on the same columns?  And the
other unique

  UNIQUE (LADDER_DATE, RFK_ISEC_ID, VALUE_SPEC_ID, BID_UPD_DT),

is likewise "already happening" since 3 of the columns of that one are assured
to be unique as per the primary key!

Anyway....  You would have to specify what sort of queries you execute against
this data that lead to these performance problems.  What questions do you ask of
this table.  For example, if you always want the current record give a
rfk_isec_id, value_spec_id, then I would want the primary key index to be on

rfk_isec_id, value_spec_id, ladder_date

and a query like:

select *
  from ( select *
           from mark_price_ladder
          where rfk_isec_id = :x
            and value_spec_id = :y
          order by ladder_date DESC )
 where rownum = 1;

would get that rather fast (reads the index "backwards", gets just the first
record -- would be about the same as a simple keyed read)

For example:

SQL> REM
SQL> REM drop TABLE MARK_PRICE_LADDER;
SQL> REM
SQL> REM CREATE TABLE MARK_PRICE_LADDER (
SQL> REM   LADDER_DATE    DATE          NOT NULL,
SQL> REM   RFK_ISEC_ID    NUMBER (9)    NOT NULL,
SQL> REM   VALUE_SPEC_ID  NUMBER (9)    NOT NULL,
SQL> REM   BID_VFID       NUMBER (9),
SQL> REM   BID_PRICE      NUMBER (31,16),
SQL> REM   BID_UPD_DT     DATE,
SQL> REM   ASK_VFID       NUMBER (9),
SQL> REM   ASK_PRICE      NUMBER (31,16),
SQL> REM   ASK_UPD_DT     DATE,
SQL> REM   PRICE_CCY      VARCHAR2 (3)  NOT NULL,
SQL> REM   AUDIT_NOTES    VARCHAR2 (255)  NOT NULL,
SQL> REM   NOTES          VARCHAR2 (4000)
SQL> REM )
SQL> REM /
SQL> REM create unique index mark_price_ladder_pk on
mark_price_ladder(RFK_ISEC_ID, VALUE_SPEC_ID, ladder_date ) compress 2;
SQL> REM
SQL> REM alter table mark_price_ladder
SQL> REM add constraint mark_price_ladder_pk
SQL> REM   PRIMARY KEY ( RFK_ISEC_ID, VALUE_SPEC_ID, ladder_date );
SQL> REM
SQL> REM
SQL> REM begin
SQL> REM        for i in 1 .. 1000
SQL> REM        loop
SQL> REM             insert /*+ append */ into mark_price_ladder
SQL> REM             select sysdate+rownum, 12345+i, 6789+i, 0, 324.1234,
sysdate,
SQL> REM                    23542, 43234.23432, sysdate, 'abc',
rpad('*',80,'*'),
SQL> REM                    rpad('*',80,'*')
SQL> REM               from all_objects
SQL> REM          where rownum <= 100;
SQL> REM              commit;
SQL> REM        end loop;
SQL> REM end;
SQL> REM /
SQL> REM
SQL> REM analyze table mark_price_ladder
SQL> REM compute statistics
SQL> REM for table
SQL> REM for all indexes
SQL> REM for all indexed columns
SQL> REM /
SQL>
SQL> variable x number
SQL> variable y number
SQL> variable z varchar2(25)
SQL>
SQL> alter session set nls_date_format = 'dd-mon-yyyy hh24:mi:ss';

Session altered.

SQL> column ladder_date new_val L
SQL> column audit_notes noprint
SQL> column notes       noprint
SQL>
SQL>
SQL> exec :x := 12345+1; :y := 6789+1;

PL/SQL procedure successfully completed.

SQL>
SQL> select *
  2    from ( select *
  3             from mark_price_ladder
  4                    where rfk_isec_id = :x
  5                      and value_spec_id = :y
  6                    order by ladder_date desc )
  7   where rownum = 1
  8  /

LADDER_DATE          RFK_ISEC_ID VALUE_SPEC_ID   BID_VFID  BID_PRICE BID_UPD_DT
           ASK_VFID  ASK_PRICE
-------------------- ----------- ------------- ---------- ----------
-------------------- ---------- ----------
ASK_UPD_DT           PRI
-------------------- ---
14-jan-2003 20:38:45       12346          6790          0   324.1234 06-oct-2002
20:38:45      23542 43234.2343
06-oct-2002 20:38:45 abc

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1 Bytes=2239)
   1    0   COUNT (STOPKEY)
   2    1     VIEW (Cost=3 Card=1 Bytes=2239)
   3    2       TABLE ACCESS (BY INDEX ROWID) OF 'MARK_PRICE_LADDER' (Cost=3
Card=1 Bytes=223)
   4    3         INDEX (RANGE SCAN DESCENDING) OF 'MARK_PRICE_LADDER_PK'
(UNIQUE) (Cost=2 Card=1)

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

SQL>
SQL> exec :z := '&L';

PL/SQL procedure successfully completed.

SQL>
SQL> select *
  2    from mark_price_ladder
  3   where rfk_isec_id = :x
  4     and value_spec_id = :y
  5     and ladder_date = to_date( :z, 'dd-mon-yyyy hh24:mi:ss' )
  6  /

LADDER_DATE          RFK_ISEC_ID VALUE_SPEC_ID   BID_VFID  BID_PRICE BID_UPD_DT
           ASK_VFID  ASK_PRICE
-------------------- ----------- ------------- ---------- ----------
-------------------- ---------- ----------
ASK_UPD_DT           PRI
-------------------- ---
14-jan-2003 20:38:45       12346          6790          0   324.1234 06-oct-2002
20:38:45      23542 43234.2343
06-oct-2002 20:38:45 abc

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=223)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'MARK_PRICE_LADDER' (Cost=2 Card=1
Bytes=223)
   2    1     INDEX (UNIQUE SCAN) OF 'MARK_PRICE_LADDER_PK' (UNIQUE) (Cost=1
Card=100000)

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

SQL>
SQL>

See, the difference between "getting the most recent record" and getting a
record by primary key values is exactly the same there.

So, it really depends on the questions you are asking -- there are things like
the top-n query above, analytic functions (great, absolutely AWESOME, for
looking at windows of data which is what you have) and so on

(i did my example above in 9iR2 on linux but similar results should be found
when using the CBO on earlier releases)
Review & Followup
Rating: 4
Thanks! October 07, 2002
Reviewer: Brij Malkan from UK
Thanks Tom,

This has helped clarify things a lot.

The most common type of dql performed on the ladder tables, is to update
positions with new prices. And to get data for a number of positions over given
periods of time. Would the analytic functions you refer to, help here?

Stupid question - where can I find documentation on these?

Thanks,

Brij

Followup:
Well, the update will be impacted a little, not much tho.

ops$tkyte@ORA920.US.ORACLE.COM> update mark_price_ladder
  2     set bid_price = 55.55
  3   where (rfk_isec_id,value_spec_id,ladder_date) =
  4         ( select :x, :y, max(ladder_date)
  5             from mark_price_ladder
  6            where rfk_isec_id = :x
  7              and value_spec_id = :y )
  8  /

1 row updated.

Execution Plan
----------------------------------------------------------
   0      UPDATE STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=27)
   1    0   UPDATE OF 'MARK_PRICE_LADDER'
   2    1     INDEX (UNIQUE SCAN) OF 'MARK_PRICE_LADDER_PK' (UNIQUE) (Cost=1
Card=1 Bytes=27)
   3    2       SORT (AGGREGATE)
   4    3         FIRST ROW (Cost=2 Card=1 Bytes=14)
   5    4           INDEX (RANGE SCAN (MIN/MAX)) OF 'MARK_PRICE_LADDER_PK'
(UNIQUE) (Cost=2 Card=100000)

Statistics
----------------------------------------------------------
          0  recursive calls
          1  db block gets
          4  consistent gets
          0  physical reads

would update the current record

The other one "getting the data for a number of posititions over given periods
of time"  -- thats a simple index range scan (and would also benefit from having
ladder_date at the END of the key)

Analytics - -well, in my book I have a pretty good chapter on them and the data
warehousing guide does as well:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76994/analysis.htm#11237

Rating: 2
Read consistency October 08, 2002
Reviewer: A reader
Thanks Tom.

Also, is it possible to disable 'read-consistency' on particular objects?

Followup:
Not a chance.

October 08, 2002 Nathan -- Thanks for the question regarding "Database Transfer to high level Configuration Win NT", version 8.0.5.00
You Asked
Hi Mr.Tom,
  Specifications: Oracle 8.0.5 in Win NT.

  We are planning to upgrade Windows NT,what are all the OS/Oracle parameters to
be taken in account when we transfer the Database from existing WinNT((RAM 1GB,
P2(700MHZ)) to newmachine(RAM(2GB),P3(700MHZ) and tell me the best way of
database transfer (in archive log mode and no archivelog mode)
Expecting your great advice....

Regards

Nathan
and we said...
Well, DON'T upgrade NT -- you'll want to use the release of the Operating system
you are currently using OR if you decide to upgrade that, you'll need to
likewise upgrade the database.

Might be something to give SERIOUS consideration to in any case.  805 is not
supported.  If you hit issues on this new configuration, there will be large
support issues to overcome.

In short, you would:

o install on new machine
o use oradim on new machine to create services (see the windows NT admin guide)
o shutdown old
o copy data, init, redo, control files to new machine
o startup and test like crazy

But, my greatest advice to you -- couple this "upgrade" with a database upgrade
-- especially if you are changing the release of the operating system as you
would be using a totally untested (by Oracle) configuration (since the new OS
would post date the database software by years)
Review & Followup
Rating: 3
October 08, 2002
Reviewer: A reader
If you allowme and advice.
I agree with tom if you are upgrading of server you could be thinking in migrate
to 9.2, if this doesn´t implies problems to you.

If you are upgrading from nt 4 to nt 2000 you are going to get increase
performance, because nt 2000 autodetects hardware, and you don't have to
configure manually for example the usual 256k cache; but give a good test to
avoid surprises.

Check in she search/archives about the new database options
redo tablespace, tablespace autoadministration options, etc.

check the Database Administrator’s Guide for Windows A95491-01 for more tips

October 08, 2002 Rajiv -- Thanks for the question regarding "free text search", version 9.2
You Asked
can you please tell how to achieve the free text search in varchar2 fields

are there any new types of indexes for this?

i want to search like contains and search for an exact phrase for the strings
that were given with double quotes(")... can i leverage any oracle capabilities?

i am very new to context search or intermedia search.

thanks
and we said...
That is exactly what Oracle Text (aka interMedia, aka context) is all about.

Check out:
http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/nav/docindex.htm#index-TEX
Review
      & Followup

      Rating: 4
How big is your
      column?  October 08, 2002
Reviewer:  Adrian
      Billington  from UK
Tom

I know you hate rules of thumb but at what column/data size generally do you
start to think about Oracle Text ? To put this into context, we have a set of
reports that need to perform string matching within a column containing URLs
captured in our web logs. The average length of a URL in our system is
approximately 250 characters, so we are simply using instr at the moment...

Thanks

Adrian

          Followup:

it is not the width, it is the height.

If you find that using instr runs sufficiently fast (because of other predicates
perhaps) AND the height of the data (number of rows) you need to instr on will
remain more or less constant (not grow lots) -- then stick with it.

It is when you have to do the instr trick on a couple of thousand or more rows
that you'll start to realize "hey, this is slow".  That is when an indexing
technique such in Oracle Text starts to become appealing.

Just like scott.emp doesn't really need an index on EMPNO....  Until you put a
thousand rows in there anyway....

      Rating:
      4
  October 08, 2002
Reviewer:  Paul
       from UK
Tom,

Somehow I got into my head that Intermedia was used for searching documents (in
LOBs) rather than small columns.

I have a system where the user wants to be able to search for a name (could be
Company name or an individual) in a column (VARCHAR2(80)).  I know it's not an
ideal database design, but we've got huge amounts of legacy data from a
multitude of systems.

Would this be a suitable candidate for using Intermedia?

What are the disadvantages / overheads / performance issues with using
Intermedia?

Thanks,

Paul

          Followup:

It would be a good use -- Oracle text is useful for "skinny" as well as "fat"
data.  It gives you case insensitive (or case sensitive if you like) keyword
searching, stemming, wildcards, fuzzy, soundex, and more searches.  You can do
phrases or discrete words and use AND or OR and so on.

Oracle Text (aka intermedia/context) is just an indexing technique so the normal
overheads of "indexes" in general applies (takes storage for example, takes
time)

Disadvantages -- well, given that you want to search for words in a string and
unless you use Text you have to sort of "instr" and search on the fly, there
aren't any really.  Its like asking "what are the disadvantages of a b*tree
index on the EMPNO column of EMP".  the disadvantage of not have such an index
so outweighs anything else when emp has a couple of thousand rows and you
constantly "select * from emp where empno = :x"

check out the documentation -- plenty of examples on this site as well.  Its
something you can test out fairly quickly to see if it applies in your case.

      Rating: 4
Got
      it...  October 08, 2002
Reviewer:  Adrian
      Billington  from UK
Tom

Your followups (to mine and Paul from UK) have really helped. The height is
significant for me - we are talking about roughly 20K rows daily. Further to
this, some ad hoc queries run against a partition containing 2 million of these
URLs, so it looks like I should give Oracle Text a go...

Regards

Adrian

October 08, 2002 Ravi -- Thanks for the question regarding
      "Dynamic Selection of Table", version 8.1.5

      You Asked
Hi Tom,
I am developing a Pro*C program on Sun solaris.
I have 3 tables into my schema. let's say A, B and C.
Now I want to have a environment variable or some kind of flag which actually
tells the Pro*C code that update table A or B or C.
I do not want (if or else.

means update statement should be like

Update table  VARIABLE_NAME
set column1_price = 10;

This VARIABLE_NAME  should be table name passed by environment variable or some
kind of flag.

Please let me know if you need more info.
Thank You
Ravi

      and we said...
Use exec sql prepare and exec sql execute.  This is commonly refered to "method
2" dynamic sql.  See the Pro*c guide for all of the details.  Method 2 is in the
table of contents.

Here is an example:

Fri Aug 16 19:21:00 EDT 2002
(tkyte@aria) /export/home/tkyte/src/t
> cat t.pc
#include <stdio.h>
#include <string.h>
#include "t.h"

static char *     USERID = "/";

#define SQLCA_INIT
EXEC SQL INCLUDE sqlca;

static void sqlerror_hard( int lineno)
{
    EXEC SQL WHENEVER SQLERROR CONTINUE;

    printf("\nORACLE error detected (%d):", lineno);
    printf("\n% .70s \n", sqlca.sqlerrm.sqlerrmc);

    EXEC SQL ROLLBACK WORK RELEASE;
    exit(1);
}

static void process( int x )
{
EXEC SQL BEGIN DECLARE SECTION;
char        query[500];
EXEC SQL END DECLARE SECTION;

    EXEC SQL WHENEVER SQLERROR DO sqlerror_hard(__LINE__);
    sprintf( query, "update %s set x = :x", getenv("TNAME") );
    exec sql prepare S from :query;
    exec sql execute S using :x;
    printf( "%d rows updated via '%s'\n", sqlca.sqlerrd[2], query );
}

main( argc, argv )
int    argc;
char *    argv[];
{
EXEC SQL BEGIN DECLARE SECTION;
VARCHAR   oracleid[50];
EXEC SQL END DECLARE SECTION;

    strcpy( oracleid.arr, USERID );
    oracleid.len = strlen(oracleid.arr);

    EXEC SQL WHENEVER SQLERROR DO sqlerror_hard(__LINE__);
    EXEC SQL CONNECT :oracleid;
    printf("\nConnected to ORACLE as user: %s\n\n", oracleid.arr);

    process( atoi(argv[1]) );

    /* Disconnect from ORACLE. */
    EXEC SQL COMMIT WORK RELEASE;
    exit(0);
}

Fri Aug 16 19:21:00 EDT 2002
(tkyte@aria) /export/home/tkyte/src/t
> setenv TNAME T1

Fri Aug 16 19:21:00 EDT 2002
(tkyte@aria) /export/home/tkyte/src/t
> ./t 1032

Connected to ORACLE as user: /

1 rows updated via 'update T1 set x = :x'

Fri Aug 16 19:21:00 EDT 2002
(tkyte@aria) /export/home/tkyte/src/t
> setenv TNAME T2

Fri Aug 16 19:21:00 EDT 2002
(tkyte@aria) /export/home/tkyte/src/t
> ./t 23432

Connected to ORACLE as user: /

1 rows updated via 'update T2 set x = :x'

Fri Aug 16 19:21:00 EDT 2002
(tkyte@aria) /export/home/tkyte/src/t
> plus

SQL*Plus: Release 8.1.7.0.0 - Production on Fri Aug 16 19:25:25 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected to:
Oracle8i Enterprise Edition Release 8.1.7.4.0 - Production
With the Partitioning option
JServer Release 8.1.7.4.0 - Production

ops$tkyte@ORA8I.WORLD> select * from t1;

         X
----------
      1032

ops$tkyte@ORA8I.WORLD> select * from t2;

         X
----------
     23432

ops$tkyte@ORA8I.WORLD>
Review & Followup

      Rating: 5
how about
      wrapping it in pl/sq/ stored procedure ?  October 07, 2002

Reviewer:  A reader
Hi Tom,

  I am trying to create the stored procedure (Oracle 8.1.7.4 on Linux 7.3) (
which will be called from jdbc)

 In our schema, there is a table called

1.)
entity_type
-----------
Entity_code  varchar2(8)  (PK)
entity_name  varchar2(50)

only two fields are in this table.

another table
2.)

Institution
------------
Inst_code     char(11)   (PK)
Inst_name     varchar2(50)
inst_eff_dt   date
.......

3.) rating
-----------
R_entity_code  char(11) (PK)
R_rating_eff_dt date    (PK)
R_rating_agency code    (PK)
.....

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

 *** 1.)
      The entity_type contains values like
ent_cd ent_name
------ --------
isnt   Institution
prod   Product
....

  ***2.)
      The Rating.R_entity_code contains the actual entity_code if institution
'I093878'
            if product     'P09knlko'

r_ent_cd     r_eff_dt     r_ang_cd
'I093878'    sysdate       'GS'
'I088908'    sysdate       'BlM'
'P09knlk'    sysdate       'AMX'
....

now in the schema "INSTITUTION" is a table
when product are introduced there will be another table
called "PRODUCT" with id and product specific field.

  my problem is all the stored procedure must return the code and name of given
entity_type.

  now if I want to find the rating for some institution, or product

  I have to join them thro' entity_type table
but it will only  give me the type let's say institution
so at runtime I know that I have to see the institution table but I don't konw
which field to join on because I don't know the table spacific field name.
(ie if institution then inst_name but if product than may be prod_name or
product_name .
 also if the new table is created in the future and entity_type is added in the
intity_table

ent_cd ent_name
------ --------
isnt   Institution
prod   Product
bond   BOND

  then procedure should idendtify the appropriate filed where the name is stored
and retrun the compelete result set.

 Do you think this is possible ?

  or I sould hard code the  filed names and when a new entity is added, add
another if stmt make corrosponding changes and compile.

 what do you say ?

TIA

          Followup:

I cannot figure out what tables are to be joined to what.  I'm sure we can do
this with a view -- however, I cannot see how to join ANY of the above tables
together -- there are not any common columns.

This looks like a very poor data model, why would you do this to yourself?  If
you have the opportunity -- now would be an excellent time to review your
physical schema and ask "so, why are we doing it like this???"

      Rating: 5
I do not have
      the opportunity to redesign  October 07, 2002

Reviewer:  A reader
Thanks !! for replying back so soon !!

 how can we join it by view ?

 The tables I want to join are

1.) rating
    -------
r_ent_cd     r_eff_dt     r_ang_cd   r_ent_type
^^^^^^^^^   ---------    ----------  ~~~~~~~~~~
'I093878'    sysdate       'GS'        'inst'
'I088908'    sysdate       'BlM'       'inst'
'P09knlk'    sysdate       'AMX'       'prod'

2.) entity_type
    ------------
ent_cd ent_name
~~~~~~ --------
isnt   Institution
prod   Product
bond   BOND

3.) institution
    ------------

inst_code      inst_name
^^^^^^^^^     ----------
'I093878'       ORACLE
'I088908'       WHO

4.) Product
   ---------

prod_code   Prod_name
^^^^^^^^^   ---------
'P09knlk'   'Prod1'

/**************************************

  my questions is if I don't know the
following information till runtime
1.) name of the
2.) name of the fields of this table
3.) to which field  I have to join on

how can I create the dynamic stmt.

so in my example,

lets say user selected the this instance

1.) 'I093878'    sysdate       'GS'        'inst'

  from rating table I will know the entity_type
(here institute or product and It's code say,
 r_ent_cd = 'I088908' and
 r_ent_type = 'inst'
 )

so when from this I have to find that

1.) for this entity, the information  is stored in
    INSTITUTION table and go to institution table
3.) find out all the fields of the table
4.) find out INSTITUTION primary key
5.) match the r_ent_cd = 'I088908' code with
    institution primary key
6.) find the name/description field for the institution
    (here inst_name)
7.) return the result set

8.) if the user selects

'P09knlk'    sysdate       'AMX'       'prod'

 then

1.) for this entity, the information  is stored in
    PRODUCT table and go to PRODUCT table
3.) find out all the fields of the table
4.) find out PRODUCT primary key
5.) match the r_ent_cd = 'P09knlk' code with
    product primary key
6.) find the name/description field for the product
    (here product_description)
7.) return the result set

*****************************************/

TIA

          Followup:

I'm thinking you would use a VIEW and this VIEW would be updated WHENEVER you
made the major change of adding a table

Are you saying the tables themselves (institution, product, etc) may have
different NUMBERS and TYPES of columns or what?

(i'm telling you -- this model looks worse and worse to me)

      Rating:
      5
tables  October 08, 2002
Reviewer:
      A reader

 Yes, tables (institution,product have different number of columns and different
data types but, they are classified as
entity !!!)

  we have inherited this model and there is a lot of development done already on
this. Also the same schema is used by other groups. and I have to find the
solution of finding the method that will dynamicall recognize the new table, its
fields and use it as an enity and
query accordingly with the rating table..

  Thank you very much for your replies I know the model is not very clear and
may be not useful but I have to add the procedure or pkg or something and make
it work !!

          Followup:

I don't see any way to do this in a single query.  You'll have to pull the
entity meta data and then build yet another query to retrieve the attributes.

      Rating: 5
how to design
      the table in this case ?  October 10, 2002

Reviewer:  A reader
Hi tom,

   As now in our schema the (insitution,product,bond..)
are classified as entity

  but all the table have different columns based on the requirements

 and all the other tables has reference to entity_code and not to the individual
tables,
which will not identify the table name
(for eg which entity ? insittution or product  or bond ?)

 in this case how can I change the design ?
do you have any suggestions
the questions is

 if the hetrogenious objects(here, institution,product,bond)
with different "properties" (here,inst_code,prod_code...)
are grouped to gether in one object(here,entity(entiy_code))
how do design it ?     in the data model ?

( in my case an institution has its credit rating and
   some product also has the credit_rating
   with the same credit rating angency
   so how do I design this buss. req.
 so that I can keep it relational/normalized (If possible)
)

TIA

October 08, 2002 ls -- Thanks for the question regarding "dynamic
      function", version 8.1.7.2

      You Asked
Hi Tom

I wonder if it's possible using PL/SQL to create a function that calculates
average values by rceating dynamic queries, what I am trying to do is a function
that would accept a owner, table name, a groupping or more column/s and the
column name which avg should be calculated. For example

I have two tables

table_x
date deptno sal

table_y
date city invoice_amount

if I pass the parameters the function scott, table_x, sal, deptno

select deptno, avg(sal)
from table_x
group by deptno;

for table_y I would pass scott, table_y, invoice_amount, (date, city)

select date, city, avg(invoice_amount)
from table_y
group by date, city;

is this possible? Or it is too much complicated?
What I have problem is how can function generates queries based on parameters

cheers

      and we said...
see

http://osi.oracle.com/~tkyte/ResultSets/index.html
for more details on ref cursors but this is easy to do:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package dyn_query
  2  as
  3      type rc is ref cursor;
  4
  5      function  f( p_owner      in varchar2,
  6                   p_tname      in varchar2,
  7                   p_avg_col    in varchar2,
  8                   p_group_cols in varchar2 )
  9      return rc;
 10  end;
 11  /

Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package body dyn_query
  2  as
  3      function  f( p_owner      in varchar2,
  4                   p_tname      in varchar2,
  5                   p_avg_col    in varchar2,
  6                   p_group_cols in varchar2 )
  7      return rc
  8      is
  9          l_cursor rc;
 10      begin
 11          open l_cursor for
 12          'select ' || p_group_cols || ', avg(' || p_avg_col || ')
 13             from ' || p_owner || '.' || p_tname || '
 14            group by ' || p_group_cols;
 15
 16          return l_cursor;
 17      end;
 18  end;
 19  /

Package body created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> variable x refcursor
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autoprint on
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec :x := dyn_query.f( 'scott', 'emp',
'sal', 'deptno' );

PL/SQL procedure successfully completed.

    DEPTNO   AVG(SAL)
---------- ----------
        10 2916.66667
        20       2175
        30 1566.66667

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec :x := dyn_query.f( 'scott', 'emp',
'comm', 'job' );

PL/SQL procedure successfully completed.

JOB        AVG(COMM)
--------- ----------
ANALYST
CLERK
MANAGER
PRESIDENT
SALESMAN         550

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review &
      Followup

      Rating: 4
two
      parameters  February 24, 2002
Reviewer:
      A reader
Hi Tom

What can it be done if I have to group by two columns? i.e passing two columns
for last parameter

Using array? If so how do we generate query for arry?

cheers

          Followup:

it is set up such that you just pass a comma delimited list like this:

SQL> exec :x := dyn_query.f( 'scott', 'emp', 'sal', 'deptno, job' );

PL/SQL procedure successfully completed.

    DEPTNO JOB         AVG(SAL)
---------- --------- ----------
        10 CLERK           1300
        10 MANAGER         2450
        10 PRESIDENT       5000
        20 ANALYST         3000
        20 CLERK            950
        20 MANAGER         2975
        30 CLERK            950
        30 MANAGER         2850
        30 SALESMAN        1400

9 rows selected.

      Rating:
      5
  February 25, 2002
Reviewer:  Jurij
       from Croatia
hello

I had a similar function but to calculate the the sum but my situation were
different, instead of accepting > one parameter for groupping column I have to
accept one or more than one column name to calculate the the sum. Is this
possible?

something like

select deptno, sum(comm), sum(sal)
from emp
group by deptno

thanks

          Followup:

sure, ANYTHING is possible.  Look at the code, it is pretty straight forward.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package body dyn_query
  2  as
  3      function  f( p_owner      in varchar2,
  4                   p_tname      in varchar2,
  5                   p_avg_col    in varchar2,
  6                   p_group_cols in varchar2 )
  7      return rc
  8      is
  9          l_cursor rc;
 10      begin
 11          open l_cursor for
 12          'select ' || p_group_cols || ', avg(' ||
replace(p_avg_col,',','),avg(') || ')
 13             from ' || p_owner || '.' || p_tname || '
 14            group by ' || p_group_cols;
 15
 16          return l_cursor;
 17      end;
 18  end;
 19  /

Package body created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> variable x refcursor
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autoprint on
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec :x := dyn_query.f( 'scott', 'emp', 'sal,
comm', 'deptno, job' )

PL/SQL procedure successfully completed.

    DEPTNO JOB         AVG(SAL)  AVG(COMM)
---------- --------- ---------- ----------
        10 CLERK           1300
        10 MANAGER         2450
        10 PRESIDENT       5000
        20 ANALYST         3000
        20 CLERK            950
        20 MANAGER         2975
        30 CLERK            950
        30 MANAGER         2850
        30 SALESMAN        1400        550

9 rows selected.

Just take the concept presented and extend it to your needs -- once you know
about ref cursors and can see you can build a dynamic query -- building ANY
dynamic query should be straight forward.

      Rating: 4
Dynamic SQL,
      bind variables....  October 08, 2002
Reviewer:
      John  from Manhattan ;)
Hi Tom,

I know that on this site asking bind variables questions is almost like beating
a dead horse, but I have another one for you ;)  I need some clarification.  In
the dynamic sql example above, we need to concat the column names we want to
select in the dynamic statement because we cannot use bind variables for
identifiers, correct?  But, if we were including a where clause in the dynamic
statement we *must* use bind variables because othwerwise we would be doing a
parse every time we called the procedure with a different input for the where
clause...

ie:

'SELECT '||p_col_name||' FROM foo f WHERE f.id = :bv1' USING p_id;

would be the way to go?  Just want to make sure I fully understand the issue...

Thanks!

john

          Followup:

Well, you'll be doing a PARSE each time -- however it will be a SOFTer gentler
parse the 2cnd, 3rd and so on times that you use the same p_col_name value.

Yes, your code above is correct and the way to go.

October 08, 2002 Zsolt -- Thanks for the question regarding "Bulk
      collect with cursor variable", version 8.1.5 i

      You Asked
Hi Tom,

1.I have a cursor :

TYPE T_AltalanosKurzor IS REF CURSOR ;
Kurzor T_AltalanosKurzor ;

TYPE T_OszlopTomb IS Table OF VarChar2(80);
OszlopTomb T_OszlopTomb;

If I use this cursor with a bulk collect:

OPEN Kurzor FOR  'select '|| OszlopStr ||' from '|| TablaStr||'
Where '|| FeltetelStr ||' Order By '||RendezesStr;

FETCH Kurzor BULK COLLECT INTO OszlopTomb ;

Then I get the following error message:

Internal Server Error 500
Exception: EOracleError
Message: ORA-03113: end-of-file on communication channel

Is something wrong with it?

I can only resolve this with a
 LOOP
....
   EXIT WHEN Kurzor%NOTFOUND;
.....
 END LOOP;

2. How can I write a Pl/Sql procedure that always stays
in memory and automatically fires a session afer "x" minutes of
inactivity?

Thank you for your help

Zsolt Csillag

      and we said...
1) It is a combination of a server bug and a documenation bug.

The server bug is that the error message:

declare
*
ERROR at line 1:
ORA-01001: invalid cursor
ORA-06512: at line 11

should be returned instead of killing the session.  that is
corrected in 8.1.6

The documentation bug is that weakly typed ref cursors ( as you
are using ) are not supported with BULK COLLECT.  Unfortunately
for you, strongly typed ref cursors cannot be dynamically
opened.

So, a possible workaround for you is to use a package variable
and execute:

tkyte@ORACLE8I> create or replace package state_pkg
  2  as
  3      TYPE T_OszlopTomb IS Table OF VarChar2(80);
  4      OszlopTomb T_OszlopTomb;
  5  end;
  6  /

Package created.

tkyte@ORACLE8I>
tkyte@ORACLE8I>
tkyte@ORACLE8I> declare
  2
  3      TYPE T_AltalanosKurzor IS REF CURSOR ;
  4      Kurzor T_AltalanosKurzor ;
  5
  6  begin
  7
  8      execute immediate '
  9      begin
 10         select username
              bulk collect into state_pkg.OszlopTomb
 11           from all_users
 12          order by created;
 13      end;';
 14
 15      dbms_output.put_line( 'got ' ||
               state_pkg.OszlopTomb.count );
 16  end;
 17  /
got 16

PL/SQL procedure successfully completed.

So, instead of dynamically opening a cursor -- dynamically
execute a block that fetches into the global table.

2) see PROFILES in the admin guide.  You can set up a profile
that limits idle time.  Those sessions that exceed that idle
time will be killed.

Review &
      Followup

      Rating: 5
Invalid Cursor
      when using REF CURSOR and bulk collects  April 17, 2002

Reviewer:  Sunil  from Australia
Very Helpful as the documentation of Oracle is totally misleading...
I was stuck with this problem for days when I decided to search for Oracle
discussion groups.

Thanks a lot.

      Rating:
      4
Helped  June 28, 2002
Reviewer:
      Leonardo Graf  from Zurich, Switzerland
I spent one day trying to solve this problem, nice to see there is a workaround.

I use Oracle 8.1.6i, there the error message is correct (ORA-01001: invalid
cursor) but the documentation bug is still present.

      Rating: 5
Still a small
      problem!  October 08, 2002
Reviewer:  Rene
      A.  from Vienna, Austria, Europe
1)
The execute immediate workaround is nice, but I've a query that return an
unknown amount of data (0 - 200.000+). So I want to LIMIT the BULK each time and
work on 100 rows, and then on the next 100 rows -> how do I do that with execute
immediate?

eg:
        OPEN cur FOR select ...;

        LOOP
            FETCH cur BULK COLLECT INTO t_vc LIMIT 100;

            outer_runs := outer_runs + 1;

            FOR i IN 1 .. t_vc.COUNT
            LOOP
                DBMS_OUTPUT.put_line('[' || outer_runs || '][' || i || '] -> ['
|| t_vc(i) || ']');
            END LOOP;

            EXIT WHEN cur%NOTFOUND;
        END LOOP;
        CLOSE cur;

How do I convert this to execute immediate?

2)
" So, instead of dynamically opening a cursor -- dynamically execute a block
that fetches into the global table. "

WIll there be any problems, if this function is long running, and a other user
is executing the same function -> is he then writting to the same
package_variable or to his "own" ... ?

          Followup:

1) you won't, you'll use

scott@ORA9I.WORLD> declare
  2          type rc is ref cursor;
  3          l_cur rc;
  4
  5          type array is table of varchar2(255) index by binary_integer;
  6          l_data array;
  7  begin
  8          open l_cur for 'select ename from emp';
  9          loop
 10                  fetch l_cur BULK COLLECT into l_data limit 5;
 11
 12                  for i in 1 .. l_data.count
 13                  loop
 14                          dbms_output.put_line( l_data(i) );
 15                  end loop;
 16                  dbms_output.put_line( '--------------' );
 17                  exit when l_cur%notfound;
 18          end loop;
 19          close l_cur;
 20  end;
 21  /
SMITH
ALLEN
WARD
JONES
MARTIN
--------------
BLAKE
CLARK
SCOTT
KING
TURNER
--------------
ADAMS
JAMES
FORD
MILLER
--------------

PL/SQL procedure successfully completed.

In Oracle9i and up (bulk collect on a dynamically opened ref cursor wasn't
supported till then)

Prior to that, in 8i and before, you'll have to use DBMS_SQL to perform array
fetches.

2) every package has its own package state.  you cannot see anyone elses
variable values, they cannot see yours.

October 08, 2002 Shrek -- Thanks for the question regarding
      "Unindexed Foreign Keys, but using triggers ", version 8.1.7

      You Asked
Tom:

   Good day, once again, I need your help with a frustrating problem.

   There is a third party pl/sql package and related code (all wrapped), which
is painfully slow, even if there are no other users on the system. For example:
a single insert takes about 25 seconds to complete. Each delete takes about the
same time.

   Looking at the data model and the corresponding schema here's what I noticed:

a) Lots of Foreign Key dependencies (some of them self referencing), and a lot
of nested dependencies.
b) Only about 70% of the foreign keys are indexed
   (BTW, I am well aware of indexing foreign keys, I regularly
    read asktom, and I have your book too, but trust me,
    this situation is a little different)
c) FK Constraints were not created with delete_cascade option;
d) Each table has several triggers on it, and this is how RI
   is maintained.

Question for you:

   1) Do FK's still need to be indexed?
     (because triggers are used to enforce constraints)
   2) Customer support of the application asks me to rebuild indexes,
      I have done that many times, and the tables, indexes, are all
      fully analyzed after each change. still makes no difference.

Unrelated question (please feel free to disregard)
3) user_views.text sometimes doesn't show me the whole text
   but somehow TOAD shows me the entire text of a view, why is that?
4) Is there a way for me to kill a users session and yet commit their work so
they don't lose any work?

   I tried: Alter System disconnect session '45,13' post transaction;
   but the user has already gone home without doing a commit.
   But the tables are locked and I cannot do any DML on them.

-- Shrek

      and we said...
1) do fkeys need to be indexed?  Well, if you delete a parent record -- or
update the key of the parent, we will do a FULL SCAN of the child in order to
see if there are any pointers to the parent (full scan of every child table).

So yes, there should be indexes if you DELETE from parent or update parents key
(which many "tools" do by default -- they update every column regardless of
whether it changed or not)

2) do a TKPROF and analyze the results -- you'll see all of their sql and how
badly it is performing,  you can give that to them.

rebuild indexes, hah, what a laugh.  Like that would have any material affect.

3)

SQL> set long 50000

the default LONG display value in sqlplus is 80 or something small.  set it
bigger.

4) no, the user is the only one that can do that.  only they know if they have
done a transaction.  committing their work could leave the database in a "half
baked" situation.

Review &
      Followup

      Rating: 5
Perfect,
      thanks, and also a suggestion for asktom  October 09, 2002

Reviewer:  A reader
I got answers to all 4 of my questions.

about asktom:
When I posted the question, I didn't get a confirmation that my question was
posted. I used netscape communicator 4.76 on Linux. But I did get redirected to
the main asktom page.

However, I think when I will POST this "submit review", I will get some kind of
confirmation back, as I remember. I will find out now.

Thanks,

Shrek

October 09, 2002 -- Thanks for the question regarding "Segregating
      procedures/functions in large packages automatically", version 8.0.6

      You Asked
is there a better way to dump/or spool a file segregating all the
procedures/functions in large packages and any sub-procedures that these
procedures call other than manually going through the codes of individual large
packages one by one and noting them down.

Thanks for any hint or help in advance...

      and we said...
I cannot imagine the usefulness of segregating the procedures or functions of a
package into separate files.  I cannot even imagine.

No, I don't know of anyway to do it other then by writing your own plsql parser
to recognize when you go into and out of a procedure or function -- which will
be very difficult.
Review & Followup

      Rating:
      1
  October 08, 2002
Reviewer:
      A reader  from IL
There are thousands of packages, each of which contains innumerable
procedures/functions. The whole thing constitute an automatized LAB information
system (LIS) entirely controlled by PL/SQL.

I want to document how control passes through each package.

I was looking for any other ideas other than have to do manually which i am sure
will not complete in 3-4 months even.

          Followup:

and how exactly would writing each prodecure or function to a separate file help
you do that -- all that would accomplish would be to give you 10's of thousands
of little files with no information.

You will want to look into a tool such as Oracle Designer or some other case
tool to help you document that which has evolved.  You want to take something
that obviously grew up as a patchwork quilt of software over many years (given
thousands of packages with innumerable procedures/functions).  it is going to
take some amount of time to digest that and you'll want a "case" tool to help
you.

October 10, 2002 Venu -- Thanks for the question regarding
      "Execute Immediate does not work in forms.", version 8.1.7.3.0

      You Asked
Hi Tom,

   We have forms 6.0.8.15.0 version installed. And i am trying to bulid a
dynamic sql of the last executed query by storing the where clause. And when i
do
  execute immediate 'select col1, col2 from table where '||whereClause;

does not work. I am getting compilation errors. And neither this method is
working
OPEN CURSOR FOR 'select col1, col2 from table where '||whereClause;

 But both work if i write this in a DB procedure.

I know i can use dbms_sql to do this. But i want to know why this works only in
the back end.

Thanks,
Venu

      and we said...
Because forms 6.0 is old software with an older, pre oracle8i version of plsql.
Hence the constructs supported by your database version of the plsql language
may be wider and more varied then forms plsql.

You can use

forms_ddl( 'update/insert/delete/DDL statment')

for dynamic SELECT sql, you would be best off using

create_group_from_query()
populate_group()

and accesing the record group (it has the smallest number of server roundtrips
and does array fetching of 15 rows at a time -- efficient)

Review &
      Followup

      Rating:
      3
Create_Group_From_Query and Bind Variables  October
      07, 2002
Reviewer:  Jan  from SG
We have a procedure for dynamic populating of List Items, something like
---------------
Populate_Codes(p_code_category IN VARCHAR2,
            p_item_name IN VARCHAR2,
               p_lang IN VARCHAR2)

...
...
v_ret:=Populate_Group_With_Query( rg_id,
  'SELECT CODE_DESC,CODE FROM AHC_V0_CODES '||
  'WHERE CATEGORY= '||''''||p_code_category||''''||
  ' AND LANG= '||''''||p_lang||''''||
  ' ORDER BY SORT_ORDER');

v_ret:= POPULATE_GROUP(rg_id);
POPULATE_LIST(p_item_name,rg_id);

END Populate_Codes;
------------------------------

We put it in PL/SQL Library - PLL. How we can avoid of NOT USING BIND VARIABLES
here?

I can imagine it only by setting CURSOR_SHARING=FORCE, but  I found it doesn`t
work properly for Oracle 8.1.7 - there are some bugs with this parameter.

          Followup:

I haven't had any issue I could not workaround with cursor_sharing=force in any
release for a given query.

I would

  forms_ddl( 'alter session set cursor_sharing=force' );
  populate group with query
  forms_ddl( 'alter session set cursor_sharing=exact' );

that will do it for you.  If you have an issue with a specific query, we can
work around that....

      Rating: 2
Cursor
      Sharing  October 08, 2002
Reviewer:  Jan
       from SG
Database:
 Oracle 8i EE Release 8.1.7.0.0
for Windows (and it happends for Unix version too)

I have an application composed from 2-3 forms, so my idea was to issue
CURSOR_SHARING=FORCE on the beginning and put it back when leaving the
application

In my Form_1, I have

forms_ddl( 'alter session set cursor_sharing=force');

From this form, if I call Form_2, it will freeze  on this query in Form_2 (taken
from SQLAREA):

SELECT   code_desc, code
 FROM ahc_t6x_config
   WHERE CATEGORY = :"SYS_B_0" || :1
  ORDER BY code DESC

Secondly, I found in V_$SQLAREA that it has no effect for server stored
procedures. Browsing thru Metalink, you can see there several bug reports about
this parameter so this is why I give up to use this parameter.

          Followup:

So, use it in the manner i prescribed above -- FOR THE QUERIES THAT NEED IT.

You don't want to use it for all queries, you want to use it for the queries
that need it.

      Rating: 3
20 times
      altered session  October 09, 2002
Reviewer:
      Jan  from SG
If I use your solution, is there any overwork of frequent ALTER SESSION? If I
call this procedure 10 times, what is in fact 20 times 'ALTER SESSION..'

Thanks

          Followup:

which is infinitely better then 10 hard parses of unique queries.

In this case, it is the lesser of two evils.  We will take the small runtime hit
in order to utilize the shared pool facility.  More scalable

October 10, 2002 Bernard -- Thanks for the question regarding "SQL
      Loader Problem", version 8.1.6

      You Asked
Some data that I need to load using SQL Loader contains many
"\n" characters. For columns that contain this special
character, SQL Loader gives me errors similar to this:

Record 4369: Rejected - Error on table A_PRJ_HEADER61.
ORA-01401: inserted value too large for column

If the "\n" appears at the very end of the column, the row loads
okay. However, if there are multiple "\n"'s in the column, this
error occurs. The columns in question are CHAR datatypes and
they are delimited by "," character.

It is possible for me to get a copy of the datafiles with the
"\n" replaced by something else (i.e. XXXYYY). If I did it this
way, I would have to go back to the tables and replace the
XXXYYY with "\n". Is there an easy way to do this? Or is there
another approach?

Also, the strings with the newlines are less than 4000 bytes,
and they will be loaded into CHAR fields.

Thanks for your help,
Bernard

      and we said...
Great, what you will do then is code:

...
thatField   char(4000) "replace(:thatField,'XXXYYY',chr(10))",
...

in the ctl file for that field.  That'll

o CHAR(4000)
  let sqlldr know the string is >255 (default size of a string
  in sqlldr)

o "replace(:thatField,'XXXYYY',chr(10))"
   perform that sql function on the data on the way in --
   it will replace XXXYYY with \n.  If you really need \r\n,
just
   use chr(13)||chr(10) in place of chr(10).

Review &
      Followup

      Rating: 3
Can we do
      direct change in the data while inserting data into actual table using Sql
      loader ?  October 09, 2002
Reviewer:  M. Ibrahim
      Memon  from Karachi, Pakistan
Hi Tom,

Hope u r well.

I have similar problem. I have flat file which have data for multiple tables.
':' is being used as column separator in each record. In case, if the ':' is
appeared as columns data then it appears like this '?:'.

Some sample data is appended here for your reference.

44:TEXLON:SDY 75/72 SD:LOT NO ?: 853:MADE IN KOREA:

Table structure is as follows:

col1 number
col2 varchar2(20)
col3 varchar2(20)
col4 varchar2(20)
col5 varchar2(20)

What do u suggest to solve the above problem

Thanks in advance for your support & guidance.

Regards

M. Ibrahim Memon
M. Ibrahim Memon
mimemon@hotmail.com

          Followup:

a quick look, given

: = field separate

?: = ignore the :

leads me to think you'll be parsing this string yourself.  See

http://asktom.oracle.com/~tkyte/SkipCols/index.html
for a technique that you might find useful.

      Rating: 5
SQL Loader =
      same Oracle Version - But same Hardware ?  October 10, 2002

Reviewer:  pasko  from HH,Germany
Hi Tom ,

Thanks for your reply to the question above.

I tried to use sqlldr yesterday , but it crashes without even starting.

I was running sqlldr in Windows 2000 Client  , but the Database servers(8.1.7)
are on Solaris/Linux Boxes .
I am using 8.1.7 Sqlldr.

Do Client and Server Machines have to be of the same Hardware Type  in order for
SQLLDR  to work ?

Thanks in advance.

          Followup:

No they do not.  You can easily use sqlldr over a network with hetergenous
operating systems.

define "crashes".

      Rating: 4
Strange
      characters in flat file ? direct load  January 20, 2003

Reviewer:  Ivan Korac  from Ulm, Germany
Hi,
I have similar problem as Bernard had, but more extended.
DB character set is UTF8. In file we load via direct path: 0D hex presentation
is used for carriage return. There are also some other strange characters (F3
presentation, etc) that SQL*Loader rejects. Could something other, instead of
sql functions, be used (for direct load) to avoid this?
Regards

October 11, 2002 steve -- Thanks for the question regarding
      "oracle.exe modes", version 8.1.6

      You Asked
Hi Tom,

re: 8.1.6 on solaris

We recently had a problem where (upon login attempt)
any user not in the dba group received "Database not available"
(ora-1034) but any user in the dba group could connect just
fine.

It turned out to be the modes defined on oracle.exe -
an enthusiastic administrator had removed the suid properties.
It should be:
-rwsr-s--x   1 oracle   dba
But it was:
-rwxr-x--x   1 oracle   dba

My question is one of curiousity really, what exactly happens
that requires the suid bits and how is the behavior accomplished
on NT where suid is not available?

Thanks
Steve

      and we said...
That is the Oracle Server process.  It must attach to the SGA
(shared global area) upon startup.  If the process runs as the
user starting it or as the userid of the listener process.  If
that userid cannot attach to the SGA (the sga being treated much
like a file in the OS with owner, group and world permissions)
-- you'll get the ORA-1034.

Hence, the oracle process is setuid so that no matter who starts
it -- it runs as oracle and does the right thing.

On NT, we are a single process with many threads.  Since we do
not create a process -- we only create a new thread within a
process -- this nuance does not exist
Review &
      Followup

      Rating: 4
setuid
      root/intelligent agent  October 10, 2002
Reviewer:
      Sean  from TX
We are helping the client to setup the intelligent agents on the systems. They
concern the security issues related to the setuid root of dbsnmp:

     " The dbsnmp executable runs as setuid root to be effective. Anyone gaining
access to the server can then perform "privileged activities" that we may find
undesirable.
     Oracle Corp recommends not running the Intelligent Agent unless you need it
for a specific reason due to this risk. The DBSNMP  user in the CSU's is
normally disabled. "

Please advise.  What would be the recommended course?

          Followup:

recommended course -- I don't have one here.

do you know that you need to use SNMP in your environment?  If not, don't run
it.  If you do, understand the risk and properly secure your system so that you
cannot get access to it.

      Rating: 3
Security Issues
      OEM?  October 03, 2003
Reviewer:
      A reader  from NY, NY
Tom - I'm not following.  Are you saying there are security issues with running
the Agent?  Do you feel there are security issues with giving OEM access?

October 11, 2002 Shruti -- Thanks for the question regarding
      "PL/SQL WAS script -- generate a table with links to update rows..",
      version 4.0.7

      You Asked
Hi Tom,

I am trying to write a program in PL/sql using Owa utilities.
I want to write a program which would generate a table based on
simple select
statement. But I wanr these data to be enchor to the form which
would ask me
enter the new value and that will change the value if I submit
that form.

Thanks in advance. I am using all the code tips you have given
me in the past.

Shruti

      and we said...
The way I approach this is to create a view that selects the
data out in a link.  The link is a link to another procedure
that gets the primary key and selects out the data and displays
it for update.  Here is an example against the scott/tiger DEPT
table:

create or replace view dept_view
as
select '<a href=dept_pkg.update_form?p_deptno=' ||
         deptno || '>' || deptno || '</a>' deptno, dname, loc
from dept
/

There is a view that selects out a link.  I find it easier to
create a view then try to do it in the owa_util.cellsprint call
I use below.  Less "quotes in quotes" problems.  The view above
makes a link for each primary key that goes for a "update_form"
procedure

create or replace package dept_pkg
as
    procedure show_data;

    procedure update_form( p_deptno in varchar2 );

    procedure do_update( p_deptno in varchar2,
                         p_old_dname in varchar2,
                         p_old_loc   in varchar2,
                         p_dname     in varchar2,
                         p_loc       in varchar2 );

end;
/

Our sample package has 3 entry points.  Show data is the main
one, it prints out the table of data with the links.
Update_Form is the one the view links to -- it'll show a for
with the data to be updated.  Do_Update "safely" does the
update

create or replace package body dept_pkg
as
/*
show data just prints out the table.  I find owa_util.cellsprint
an easy way to do that.
*/
procedure show_data
is
begin
    htp.tableOpen( 'border=1' );
    owa_util.cellsprint( 'select * from ask_tom.dept_view');
    htp.tableClose;
end;

/*
Update_Form builds an update screen.  It does 2 main things

1) it builds and populates a form with all of the existing
values of the row in question

2) it saves in hidden fields all of the "before" images.  We'll
use these in the update routine to prevent a Lost Update from
occurring
*/

procedure update_form( p_deptno in varchar2 )
is
    l_rec    dept%rowtype;
begin

    select * into l_rec from dept where deptno = p_deptno;

    htp.formOpen( 'dept_pkg.do_update' );
    htp.p( 'Deptno: ' || p_deptno );
    htp.p( '<br>Dept Name:' );
    htp.formText( 'p_dname',
                   cvalue => htf.escape_sc(l_rec.dname) );
    htp.p( '<br>Location:' );
    htp.formText( 'p_loc',
                   cvalue => htf.escape_sc(l_rec.loc) );
    htp.formHidden( 'p_deptno',
                     htf.escape_sc(p_deptno));
    htp.formHidden( 'p_old_dname',
                     htf.escape_sc(l_rec.dname));
    htp.formHidden( 'p_old_loc',
                     htf.escape_sc(l_rec.loc));
    htp.br;
    htp.formSubmit;
    htp.formClose;
end;

/*
Do Update does a 'safe' update.  It updates the row with the
primary key value we are working with IF and ONLY IF the current
values in the database are the same as they were when we read
them out (if they are not -- someone else has updated this row).
 It then either shows the update form again (if our update did
not work) or it goes back to the show data screen to let you
pick another row....
*/

procedure do_update( p_deptno in varchar2,
                     p_old_dname in varchar2,
                     p_old_loc   in varchar2,
                     p_dname     in varchar2,
                     p_loc       in varchar2 )
is
begin
    update dept
       set dname = p_dname, loc = p_loc
     where deptno = p_deptno
       and dname = p_old_dname
       and loc = p_old_loc;

    if ( SQL%rowcount = 0 )
    then
        htp.bold( 'Sorry, data was changed, try again' );
        update_form( p_deptno );
    else
        htp.bold( 'Ok, ' || sql%rowcount || ' rows updated');
        show_data;
    end if;
end;

end;
/

I should mention -- WebDB will automatically generate a screen
of this type, with the same lost update logic, for you.  No code.
Review &
      Followup

      Rating: 5
Great tip for
      web developers  September 06, 2002
Reviewer:
      A.P.BALASUBRAMANIAN  from MANGALORE,INDIA
Hi tom..your tip is great..
But when I am using as
owa_util.cellsprint( 'select * from table1@dblink');
it says
Error 30584: DBMS_SQL has raised an unhandled exception. ORA-02019: connection
description for remote database not found.
But when I am querying the same table1@dblink,I am able to
get the rows from SQL*Plus.
What mistake am I doing?

          Followup:

who owns the procedure owa_util.cellsprint.
what version of the toolkit.

can THEY see the dblink.

      Rating:
      4
  October 10, 2002
Reviewer:  Sagi
       from India
Hi Tom,

It was really great example.

Can you provide the link to learn more about htp and htf packages.

Also where can I get some examples of OWA.util....

Regards,
Sagi

          Followup:

http://otn.oracle.com/docs/products/ias/doc_library/90200doc_otn/web.902/a90101/toc.htm

October 11, 2002 ragupathi -- Thanks for the question regarding
      "Need to finde the count", version 8.1.6

      You Asked
Hi Tom

I have work_order table the table have the following information.
I need to count the total no of reason code.How can i find the
total no of reason code.

Work Order No     Reason Code Reason Desc

100001            100         Repair
100002            100         Repair
100003            101         Reject
100004            100         Repair
100005            102         Scrab
100006            102         Scrab

The result i need into the following  :-

Work Order No     Reason Code Reason Desc  Total Reason No
100001            100         Repair       3
100002            100         Repair
100003            101         Reject       1
100004            100         Repair
100005            102         Scrab        2
100006            102         Scrab

Thanks
Raghu.K

      and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> update emp set job = 'CLERK' where job =
'SALESMAN';

4 rows updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select ename, deptno, job,
  2  count( distinct job ) over ( partition by deptno ) cnt
  3  from emp
  4  order by deptno
  5  /

ENAME          DEPTNO JOB              CNT
---------- ---------- --------- ----------
MILLER             10 CLERK              3
CLARK              10 MANAGER            3
KING               10 PRESIDENT          3
SCOTT              20 ANALYST            3
FORD               20 ANALYST            3
SMITH              20 CLERK              3
ADAMS              20 CLERK              3
JONES              20 MANAGER            3
ALLEN              30 CLERK              2
MARTIN             30 CLERK              2
WARD               30 CLERK              2
JAMES              30 CLERK              2
TURNER             30 CLERK              2
BLAKE              30 MANAGER            2

14 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> rollback;

Rollback complete.

Analytics functions once again *rule*

Review &
      Followup

      Rating: 2
Need To
      Count  October 11, 2002
Reviewer:  RAGUPATHI
      KUPPANNAN  from USA
In The Above Rsult. The count showing based on which column
either deptno or job?

I am expecting If it's department. I need the following result.No of department
(ex.10Th department count is 10,
20Th department is 5 and 30 Th department is 6)

ENAME          DEPTNO JOB              CNT
---------- ---------- --------- ----------
MILLER             10 CLERK              3
CLARK              10 MANAGER            3
KING               10 PRESIDENT          3
SCOTT              20 ANALYST            5
FORD               20 ANALYST            5
SMITH              20 CLERK              5
ADAMS              20 CLERK              5
JONES              20 MANAGER            5
ALLEN              30 CLERK              6
MARTIN             30 CLERK              6
WARD               30 CLERK              6
JAMES              30 CLERK              6
TURNER             30 CLERK              6
BLAKE              30 MANAGER            6

If it's Job. I need the following result.

ENAME          DEPTNO JOB              CNT
---------- ---------- --------- ----------
MILLER             10 CLERK              8
CLARK              10 MANAGER            3
KING               10 PRESIDENT          1
SCOTT              20 ANALYST            2
FORD               20 ANALYST            2
SMITH              20 CLERK              8
ADAMS              20 CLERK              8
JONES              20 MANAGER            3
ALLEN              30 CLERK              8
MARTIN             30 CLERK              8
WARD               30 CLERK              8
JAMES              30 CLERK              8
TURNER             30 CLERK              8
BLAKE              30 MANAGER            3

Thanks
Raghu.K

          Followup:

ops$tkyte@ORA920.US.ORACLE.COM> select deptno, job, ename,
  2         count(*) over ( partition by deptno ) count_in_dept,
  3             count(*) over ( partition by job ) count_in_job
  4    from emp
  5   order by deptno, job, ename
  6  /

    DEPTNO JOB       ENAME      COUNT_IN_DEPT COUNT_IN_JOB
---------- --------- ---------- ------------- ------------
        10 CLERK     miller                 3            4
        10 MANAGER   clark                  3            3
        10 PRESIDENT king                   3            1
        20 ANALYST   ford                   5            2
        20 ANALYST   scott                  5            2
        20 CLERK     adams                  5            4
        20 CLERK     smith                  5            4
        20 MANAGER   jones                  5            3
        30 CLERK     james                  6            4
        30 MANAGER   blake                  6            3
        30 SALESMAN  allen                  6            4
        30 SALESMAN  martin                 6            4
        30 SALESMAN  turner                 6            4
        30 SALESMAN  ward                   6            4

14 rows selected.

Analytic functions are alot like "Alice's restaurant" where "you can get most
anything you want...."

(sorry for the obscure, probably american only 1970's reference...)

October 11, 2002 Patricia -- Thanks for the question regarding
      "using application context and also statspack", version 8.1.7

      You Asked
Hi Tom,

I think there might be -- not really a mistake, but it might cause confusion in
some cases -- in your book on page 717 about use of sys_context. You defined the
query before using set_context to set those variables in the namespace. This
might be ok in your example since you are looping through to get "ename like
'%A%', but I found it to be essential to set_context before the query when you
are taking user input from web page, because otherwise that part of the user
input would be blank the first time the request is sent while take the 1st user
input value the second time(since it is actually taking the value set in the
namespace last time).

I learned the lesson the hard way and hope it will be helpful for others.

Another question about performance tuning: I have the following result from
statspack report(forgot to turn timed_statistics on so some are blank):

Instance Efficiency Percentages (Target 100%)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            Buffer Nowait %:  100.00       Redo NoWait %:  100.00
            Buffer  Hit   %:   98.99    In-memory Sort %:   99.99
            Library Hit   %:   92.57        Soft Parse %:   32.76
         Execute to Parse %:   88.33         Latch Hit %:  100.00
Parse CPU to Parse Elapsd %:             % Non-Parse CPU:

 Shared Pool Statistics        Begin   End
                               ------  ------
             Memory Usage %:   86.40   85.40
    % SQL with executions>1:    7.86    8.23
  % Memory for SQL w/exec>1:    2.69    2.89

Library Cache Activity for DB: ORA8  Instance: ora8  Snaps: 13 -14
->"Pct Misses"  should be very low

                    Get       Pct        Pin        Pct               Invali-
Namespace         Requests    Miss     Requests     Miss     Reloads  dations
--------------- ------------ ------ -------------- ------ ---------- --------
BODY                   9,522    0.0          9,521    0.1          4        0
CLUSTER                1,018    0.7          1,249    1.1          0        0
INDEX                     13   53.8             13   53.8          0        0
OBJECT                     0                     0                 0        0
PIPE                       0                     0                 0        0
SQL AREA             711,671   68.0      7,567,596   12.8      1,100    5,441
TABLE/PROCEDURE    1,172,702    0.1      5,505,851    0.1      1,262        0
TRIGGER                    0                     0                 0        0
          -------------------------------------------------------------

I know a big issue is lack of use of bind variables and I am working to improve
that. But other than that, is there any other thing I need to take care of? For
example, should I make SGA a bit larger, since memory usage is at least 85% and
sometimes reach 95%? Why I also have such a big pct miss on INDEX? ALso
sometimes I have negative value in # of rows processed and rows per exec in "SQL
ordered by executions" part. What does that mean?

FYI, our system has limited users now, but is processing live updates from a
mainframe server, and has several web applications(written in pl/sql using
webDB) doing relatively big transactions.

Thanks beforehand for sharing your time and expertise.

Patricia Hu

      and we said...
Are you talking about the top of page 717?  If so -- that was just an example
that says:

instead of building this:
select * from emp where ename like :bv;

build this:
select * from emp where ename like sys_context( 'namespace', 'ename' );

It was just stating the concept.  The real example followed immediately
thereafter and that sets the context as it builds the query -- BEFORE it opens
the cursor (as soon as you open the cursor -- the sys_context values are
evaluated)

That is among the WORST soft parse ratios I've ever seen.  That is hugely bad.

Your memory usage will change drastically once you fix this.  I'm a believer in
fix the problem then tune the other stuff.  Conventional wisdom says to increase
the shared pool but that could actualy make things much much much worse.  You
might want to make the shared pool smaller in a situation where you don't use
bind variables properly.  We can manage the paging in a small shared pool better
then a big one.

Don't worry about the pct mist in the index.  Look at the other numbers -- 13
gets.

The negative values are signed integers rolling over.

You NEED to fix your bind issue right away.  That is the only thing I would even
be looking at right now.
Review & Followup

      Rating: 4
Found out the
      cause of bad soft parse %  April 04, 2002

Reviewer:  Patricia Hu  from MD USA
Thank you very much for your answer.

For the application context, I meant the bottom part on page 717 of your book: I
feel in case of building dynamic sql query string based on user input from web
pages, it is important that the context variable be set before query be defined.
In my case e.g.:

IF (p_queue IS NOT NULL) THEN
                dbms_session.set_context('pull_context', 'queue', p_queue);
                l_query := l_query || ' and queue = ''' ||
sys_context('pull_context', 'queue') || '''';
            END IF;

Originally I had l_query part before set_context part, so the query string was
not built correctly. It seemed really obvious now, but when I first followed the
example and did not think much, it caused me problem.

For the soft parse issue, I used your script on page 449 to see what on earth is
going on, since I had already tried my best in bind variable use and I know
there won't be too many other people using sql*plus to issue query: we either
use pl/sql or have web pages for end users, even the dynamic sql part is using
sys_context! SO big surprise: turned out the culprit is the third-party
application we use for migrating and almost real-time updating info from
mainframe. One continuous update happened 4682 times in around 1 day w/o using
bind variables! So in this case is there anything we can do ourselves? I know it
might not be possible but was hoping you would have some magic:)

Thank you so much again. I learned so much from both your book and your forum.

Best regards,

          Followup:

No its not.  You can do it BEFORE or AFTER.  You only need to set the values
BEFORE yOU OPEN the cursor.  While your building -- the order of operation is
not relevant.

Its not a query till you parse it.  Its just a string before that!

But -- I see what you are doing and you are doing it wrong.  Your code should
be:

l_query := l_query || ' and queue = sys_context(''pull_context'', ''queue'')';

As you are doing it -- you are HARD CODING the value in there -- doing exactly
the opposite of what page 717 was trying to do!!!

      Rating: 5
Great
      discussion, but Soft Parse on SYS_CONTEXT?  October 11, 2002

Reviewer:  John  from Manhattan
Hi Tom,

I was reading this thread down to where you helped that reader with her
SYS_CONTEXT issue...  I have been trying to use SYS_CONTEXT myself with dynamic
sql, but when I check TKPROF, it appears to be doing *soft* parses...  I thought
this was a way to get around parsing, so I must be doing something incorrect...
Would you mind taking a look?

PROCEDURE LIST_STUFF
   ( p_prod_own    IN VARCHAR2,
     p_depth       IN NUMBER,
     p_rep_cursor  OUT types_pkg.reps_cursor_typ
   )
   IS
   l_query         LONG           := NULL;
   l_samples       VARCHAR2(32)   := '';
BEGIN
    l_query := 'SELECT *
       FROM
       (SELECT
       rep.id
       , rep.title
       , rep.subtitle
       , rep.type
       , rep.sample
       , to_char(rep.publish, ''FMMonth YYYY'') as publish
       , con.name
       , con.id as container_id
       , RANK()
          OVER ( ';
   IF (p_prod_own IS NULL) THEN
       l_samples := ' PARTITION BY rep.sample ';
       l_query := l_query||l_samples;
   END IF;
       l_query:=l_query||'ORDER BY rep.publish DESC) rn
       FROM
       reports rep
       , report_containers repcon
       , containers con
       , product_reports prodrep
       WHERE
       rep.id = repcon.report_id
       AND
       repcon.primary = 1
       AND
       repcon.container_id = con.id
       AND
       rep.id = prodrep.report_id';
    IF (p_prod_own IS NOT NULL) THEN
        DBMS_SESSION.SET_CONTEXT('MYWORKSPACE_CONTEXT','PROD',p_prod_own);
        l_query := l_query||' AND prodrep.product_id IN (SELECT * from THE
(SELECT CAST(STR2TBL(SYS_CONTEXT( ''MYWORKSPACE_CONTEXT'', ''PROD'' )) AS
in_tab_typ) from dual)) ';
    END IF;
        l_query := l_query||') reprank WHERE 1=1';
    IF (p_depth IS NOT NULL) THEN
         DBMS_SESSION.SET_CONTEXT('MYWORKSPACE_CONTEXT','DEPTH',p_depth);
         l_query := l_query||'  AND reprank.rn <= SYS_CONTEXT(
''MYWORKSPACE_CONTEXT'', ''DEPTH'' ) ';
    END IF;
    l_query := l_query||' ORDER BY reprank.sample';
    OPEN p_rep_cursor FOR l_query;
END;

When I check tkprok on the trace I see (having executed the procedure 7 times):

SELECT *
       FROM
       (SELECT
       rep.id
       , rep.title
       , rep.subtitle
       , rep.type
       , rep.sample
       , to_char(rep.publish, 'FMMonth YYYY') as publish
       , con.name
       , con.id as container_id
       , RANK()
          OVER ( ORDER BY rep.publish DESC) rn
       FROM
       reports rep
       , report_containers repcon
       , containers con
       , product_reports prodrep
       WHERE
       rep.id = repcon.report_id
       AND
       repcon.primary = 1
       AND
       repcon.container_id = con.id
       AND
       rep.id = prodrep.report_id AND prodrep.product_id IN (SELECT * from THE (
SELECT CAST(STR2TBL(SYS_CONTEXT( 'MYWORKSPACE_CONTEXT', 'PROD' )) AS in_tab_typ)
 from dual)) ) reprank WHERE 1=1  AND reprank.rn <= SYS_CONTEXT( 'MYWORKSPACE_CO
NTEXT', 'DEPTH' )  ORDER BY reprank.sample

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        7      0.03       0.02          0          0          0           0
Execute      7      0.00       0.02          0          0          0           0
Fetch       18      0.04       0.02          0        161         84          11
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       32      0.07       0.06          0        161         84          11

Thanks for any help you can offer on this one!  Driving me crazy ;)

Best,
john

          Followup:

its a way to avoid the EVIL HARD PARSE and make it a SOFT PARSE.

There is no way to avoid the parse with a ref cursor, none.  You can (and
should) avoid the HARD parse.  They are the "evil" things.

Setting session_cached_cursors to, oh say, 100 will make things nicer as well.

October 12, 2002 Ricky -- Thanks for the question regarding
      "Updating a Table Row from a %RowType Record (Collection)", version 8.1.7

      You Asked
Dear Mr. Ask Tom:

I'm not sure what category this question comes under, but I've submitted it
under SQL*Plus.  It is a PL/SQL question, but PL/SQL is not a member of the
category list.  Please help me if you can.

I would like to select a row into a record, change the record and insert it back
into the table from where it came without having to do individual column
assignments from the record. How is this done?

Data creation code and the simplified example procedure that won’t compile
follow.  I get an error at the line where I’m trying to do the insert.  I'd like
to use the same technique on update statements too.

Create Table Claims
(
ClaimNumber       Number,
PatientName       VarChar2(30),
ProviderName      Varchar2(30),
ClaimType         VarChar2(1)
);

Insert Into Claims Values(1, 'Jackson, Allen', 'Morgan, Pitts',   'A');
Insert Into Claims Values(2, 'Davis, Marsha',  'Stovall, Howard', 'R');
Insert Into Claims Values(3, 'King, Edward',   'Morgan, Pitts',   'D');

Create Or Replace Procedure InsertTest As

   Cursor ClaimCursor Is
      Select
         *
      From
         Claims
      For Update
   ;

   NextClaimNumber      Number;

   Begin
   NextClaimNumber := 4;

   For ClaimRecord In ClaimCursor Loop

      If ClaimRecord.ClaimType = 'R' Then

         NextClaimNumber := NextClaimNumber + 1;

         ClaimRecord.ClaimNumber := NextClaimNumber;
         ClaimRecord.ClaimType := 'F';

         -- This statement is giving me trouble
         Insert Into Claims
            Select
               *
            From
               ClaimRecord
         ;
         -- End of troublesome statment
      End If;

   End Loop;

   Commit;

End InsertTest;
Sincerely,

Ricky Mitchell

      and we said...
I would simply execute:

  insert into claims
  select function_to_get_next_claim_number_preferablly_a_sequence,
         patient_name,
         provider_name,
         'F'
    from claims
   where claim_type = 'R';

never never never do procedurally that which is done easily (faster too) in a
single statement.

What you are looking for is a new 9iR2 feature, not available prior to that
release.

In 8i, you must

insert into claims ( c1, c2, c3, ... ) values ( claimrecord.c1, claimrecord.c2,
.... )

See also:

http://asktom.oracle.com/~tkyte/9iR2NewFeaturesForDevelopers/img27.html

demo@ORA920.US.ORACLE.COM> declare
  2     l_rec  emp%rowtype;
  3  begin
  4     select * into l_rec from emp where rownum=1;
  5     l_rec.ename := lower(l_rec.ename);
  6     update emp set ROW = l_rec where empno = l_rec.empno;
  7     l_rec.empno := 5678;
  8     l_rec.ename := initCap( l_rec.ename );
  9     insert into emp values L_REC;
 10  end;
 11  /

PL/SQL procedure successfully completed.

demo@ORA920.US.ORACLE.COM> select empno, ename from emp;

     EMPNO ENAME
---------- ----------
      7369 smith
      7499 allen
      7521 ward
      7566 jones
      7654 martin
      7698 blake
      7782 clark
      7788 scott
      7839 king
      7844 turner
      7876 adams
      7900 james
      7902 ford
      7934 miller
      5678 Smith

15 rows selected.

demo@ORA920.US.ORACLE.COM>
October 12, 2002 Juan Carlos -- Thanks for the question regarding
      "spfile autoconfiguration", version 9.2

      You Asked
Hi Tom
After reading  documentation
There is a phrase that indicates, as I understand, that one of the advantages of
using spfile is that autotuning of parameters will be saved.

This autotuning is because the use of enterprise tunning packages.
or memory management or what?

Thank you

      and we said...
It is because of the use of the

ALTER SYSTEM SET <parameter> = <value> SCOPE = <spfile|both>;

That is, when using the spfile, we can SAVE the changes to the configuration
file for you -- you need not remember to also edit the init.ora file.  So, tools
that do the alter system command can not only change the current instance, but
make the change permanent so the next restart won't lose that change.

Using a parameter file -- you would have to remember to update all of the
init.ora files that might ever be used to start that instance.

I HIGHLY recommend using an spfile, and periodically issuing "create pfile from
spfile" to have a "text" backup of it (sort of like backup controlfile to trace)
Review &
      Followup

      Rating:
      3
  October 12, 2002
Reviewer:  Juan
      Reyes  from Bolivia
Thank you Tom

But the question was in other way.
Reading documentation I got the idea that other process could change some
parameters automatically in the spfile, maybe SMON or enterprise manager.

I am wrong or not?

P.D.
Could you add in the review page a cookie for saving the name, email and where
are you, pleasee, to avoid retyping every time.

          Followup:

give me a link onto OTN where you read what you read and I'll see what they were
trying to say.

      Rating:
      3
  October 14, 2002
Reviewer:
      A reader
Thank you
I read it while I was studying all the manuals, tbts and other documentation in
internet, so it is going to take a time if I can found it. (I gave the first
three exams for OCP)

October 12, 2002 Suresh -- Thanks for the question regarding
      "PDML", version 8.1.7

      You Asked
Tom,
   I have created a table with Parallel degree 4 as below and tried updating the
table. The UPDATE used Parallel option to my surprise. It was my understanding
that PDML would work only with Partitioned table, is it so?

SQL> create table t (a int) parallel 4;
Table created.

SQL>  insert into t select rownum from all_objects where rownum < 50;
49 rows created.

SQL> commit;
Commit complete.

SQL> analyze table t compute statistics;
Table analyzed.

SQL> set autotrace on
SQL> update t set a = 100;

49 rows updated.

Execution Plan
----------------------------------------------------------
   0      UPDATE STATEMENT Optimizer=CHOOSE (Cost=1 Card=49 Bytes=98)
   1    0   UPDATE OF 'T'
   2    1     TABLE ACCESS* (FULL) OF 'T' (Cost=1 Card=49 Bytes=98)    :Q64000

   2 PARALLEL_TO_SERIAL            SELECT /*+ Q64000 NO_EXPAND ROWID(A1) */ A1.
                                   ROWID,A1."A" FROM "T" PX_GRANULE(0,

Statistics
----------------------------------------------------------
         41  recursive calls
         64  db block gets
          5  consistent gets
          2  physical reads
      12520  redo size
        853  bytes sent via SQL*Net to client
        546  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          4  sorts (memory)
          0  sorts (disk)
         49  rows processed

Thanks,
Suresh

      and we said...
It 9i release 1 and below -- it is well documented that PDML works against
partitioned objects only.

The FTS was done in parallel -- the update -- serially.
Review &
      Followup

      Rating: 4
Parallel
      update?  October 10, 2002
Reviewer:
      A reader
Dear Tom,
For the following query there is NO PARALLEL UPDATE.

15:31:48 SQL> ed
   update /*+ parallel(main,  2)  */
   ( select /*+ noparallel */
    a.amnt_pndng_trnsfr lg_pndng,a.amnt_trnsfrd lg_trnsfrd,
    a.amnt_in_err lg_err, b.amnt_pndng_trnsfr sm_pndng,
    b.amnt_trnsfrd sm_trnsfrd, b.amnt_in_err sm_err,
decode(a.amnt_pndng_trnsfr,0,a.amnt_in_err,
       a.amnt_pndng_trnsfr) to_trnsfr
   from trnsctn_accntng_lg3 a, trnsctn_accntng_smmry3 b
   where a.dt = b.dt
   and a.trnsctn_accntng_cd=b.trnsctn_accntng_cd
   and a.trnsctn_dt >=to_date('01-NOV-2002','DD-MON-YYYY'))
main
  set lg_pndng=decode(sm_pndng,0,0,to_trnsfr),
      lg_trnsfrd=decode(sm_trnsfrd,0,0,to_trnsfr),
      lg_err=decode(sm_err,0,0,to_trnsfr)
15:31:57 SQL> /

165559 rows updated.

15:33:03 SQL> select count(*) from trnsctn_accntng_lg3 where
rownum=1;

 COUNT(*)
---------
        1

15:33:09 SQL> commit;

Commit complete.

Adding ROWNUM < 100000000, in the where clause it is a PARALLEL
UPDATE.

15:33:26 SQL> update /*+ parallel(main,  2)  */
15:33:32   2   ( select /*+ noparallel */
15:33:32   3             a.amnt_pndng_trnsfr lg_pndng,a.amnt_trnsfrd
lg_trnsfrd,a.amnt_in_err lg_err
,
15:33:32   4              b.amnt_pndng_trnsfr
sm_pndng,b.amnt_trnsfrd
sm_trnsfrd,b.amnt_in_err s
m_err,
15:33:32   5
decode(a.amnt_pndng_trnsfr,0,a.amnt_in_err,a.amnt_pndng_trnsfr) to_trn
sfr
15:33:32   6           from trnsctn_accntng_lg3 a,
trnsctn_accntng_smmry3 b
15:33:32   7           where a.dt = b.dt
15:33:32   8           and a.trnsctn_accntng_cd=b.trnsctn_accntng_cd
15:33:32   9           and a.trnsctn_dt >=
to_date('01-NOV-2002','DD-MON-YYYY') ) main
15:33:32  10  set lg_pndng=decode(sm_pndng,0,0,to_trnsfr),
15:33:32  11  lg_trnsfrd=decode(sm_trnsfrd,0,0,to_trnsfr),
15:33:32  12  lg_err=decode(sm_err,0,0,to_trnsfr)
15:33:34  13  where rownum < 100000000;

165559 rows updated.

15:34:53 SQL> select count(*) from trnsctn_accntng_lg3 where
rownum=1;
select count(*) from trnsctn_accntng_lg3 where rownum=1
*
ERROR at line 1:
ORA-12838: cannot read/modify an object after modifying it in parallel

Please explain this behaviour.

This is on Oracle 8.1.5, the parameters are
cpu_count                                                        4
parallel_server                                                  FALSE
parallel_server_instances                                        1
recovery_parallelism                                             0
fast_start_parallel_rollback                                     LOW
parallel_broadcast_enabled                                       FALSE
parallel_adaptive_multi_user                                     FALSE
parallel_threads_per_cpu                                         2
parallel_automatic_tuning                                        FALSE
optimizer_percent_parallel                                       0
parallel_min_percent                                             0
parallel_min_servers                                             2
parallel_max_servers                                             2
parallel_instance_group
parallel_execution_message_size                                  2148

          Followup:

lose the noparallel hint.  what is its intended purpose anyway (other then to
turn off the parallel stuff you seem to desire)

      Rating: 4
Saying
      conclusively  October 12, 2002
Reviewer:
      A reader
Dear Tom,

1. The hint at the top (for update) takes precedence.
2. I removed the hint inside, the result still remains same.

Other than trying to query the table after PDML and getting ORA-12838 to confirm
PDML, is there any other way to know PDML has occured.

I know explain plan won't tell. Is it that the plan says Parallel Execution
would occur, still, say if there are no parallel query slaves available, the
query would be executed serially (inspite of plan being in parallel?)

Is there a SET EVENT that would tell whether a PDML/Parallel query has exeucted
in serial, despite the plan being in parllel, and the reason thereof?

Sorry, for sounding totally confused, but I sure am missing something big here.
Please help me or point me to some relevant material to clear this, mess.

Thanks and regards,

          Followup:

1) does it, how do you know that? (actually, hmm, it doesn't)  The hint closest
to the statement is the one that'll be used for optimization that part.

2) The method you are using (try to query the table) is a sound one and as good,
if not better then any other approach possible.  I would stick with it (and
since we have a very viable, good way to do this -- I'm not going to hunt around
for another)

But anyway -- adding the rownum to it can radically affect a query plan -- that
is the reason behind the change.  The optimizer did it in parallel one time, not
so the other.  you didn't show us the plans or anything so its not really
possible to comment.   So -- which one was actually *FASTER*, that would be
interesting, no?

October 12, 2002 Abhinay -- Thanks for the question regarding
      "Problem of the rows getting updated while they are being currently
      displayed on the online screen", version 8.1.7

      You Asked
Hi,
We  are developing an online application and for this there is search button.If
it is pressed it brings some 100 records but on the screen only first 10 records
are shown.When next page button is pressed it bring next 10 records.My problem
is that we decide the number of pages
before opening the cursor, so for 100 records 10 pages(screen) are required.But
in the mean time if some records are deleted say 15 records, now the updated
page count should be 9 instead of 10.But for me its still 10 and when the next
page button after the page 9 is pressed, it gives error beacuse cursor does not
have any record.

Is there a way out to get the latest updated row count of the cursor.

Thanks and Regards
Abhinay Kumar

      and we said...
Here is my answer on this:

Say "Here is page 1 of about 10 or so pages (maybe more, maybe less)"

Approximate the number pages, don't be definitive about it and you CANNOT GO
WRONG.

I do it here.

Google does it.

All of the search engines do it.  It is not only acceptable -- its the only way
to do it

a) quickly
b) in a scalable fashion
c) without wasting (utterly and totally wasting) a TON of resources

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3489618933902
and if you search for google on there -- click on that search i have and goto
page 3 - see what happens (and then hit previous page and see what happens)

If google can do it -- so can we.

I find people want access to their data.  If you let them paginate, give
them their data, they get over that you don't know exactly how much data their
search will return (the first 10 hits are the most IMPORTANT anyway -- have you
ever actually paged through 10 pages on a google search?  I haven't.)

October 14, 2002 -- Thanks for the question regarding "Design of
      copying data", version 8.1.7

      You Asked
Hi Tom,

We are trying to design our database to support copying the data from A to B
i.e.one customer to another customer if we found at later stage that both the
customers are same.

Example: Customer A:
         -----------
cust_id       :  100
cust_name     :  Capital Inc.
cust_address  :  #123, washington street, san diego
cust_contact  :  sebastien
cust_type     :  AB
price_code    :  REG
pay_terms     :

Customer B:
-----------

cust_id       :  200
cust_name     :  Capital Inc.
cust_address  :  #123, washington street, san diego
cust_contact  :  sebastien
cust_type     :  MN
price_code    :  NON-REG
pay_terms     :  C.O.D

sales for customer A
--------------------

sale_id     cust_id     salecode   saledate         units
-------     -------     --------   --------         -----
1           100         W1         01-JUL-2002      5.00
2           100         D2         01-AUG-2002     10.00

sales for customer B
--------------------

sale_id     cust_id     salecode   saledate         units
-------     -------     --------   --------         -----
3           200         W1         01-JUL-2002      3.00
4           200         W2         01-SEP-2002     12.00

we found that customer A and customer B are same and decided to keep as customer
B updated so need to copy the copy data from A to B.

we have designed already to copy the data for example in this case from customer
A to Customer B with following rules.

1. cust_type AB has highest precedence over MN
2. price_code REG has highest precedence over NON-REG
3. if the customer A has pay_terms and B has blank, then copy

After copying data from customer A to B, the data will be

Customer B:
-----------

cust_id       :  200
cust_name     :  Capital Inc.
cust_address  :  #123, washington street, san diego
cust_contact  :  sebastien
cust_type     :  AB
price_code    :  REG
pay_terms     :  C.O.D

sales for customer A
--------------------

sale_id     cust_id     salecode   saledate         units
-------     -------     --------   --------         -----
1           100         W1         01-JUL-2002      0.00
2           100         D2         01-AUG-2002      0.00

sales for customer B
--------------------

sale_id     cust_id     salecode   saledate         units
-------     -------     --------   --------         -----
3           200         W1         01-JUL-2002      8.00
4           200         W2         01-SEP-2002     12.00
5           100         D2         01-AUG-2002     10.00

Suppose after few days, we decided to undo these above transaction i.e. decided
to treat these two customers as separate customer.

what mechanism/design would you suggest to undo the above (copy data from one
customer to another customer) transaction which was commited already.

Thanks in advance,
Jaya

      and we said...
You would not update the record, nor would you ever delete data -- but rather
you would "end date" records and create a new record.

In oracle9i and up, this is done automagically for you if you like (see:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4529781014729
for examples)

before that, you would have to code it.  An update would be an update to a
END_DATE field and an insert of a new record with an END_DATE of (for example)
01-jan-3000 (some date way in the future, after you are dead, so you won't have
to fix it ;)

A view "select * from table where end_date = to_date('01-jan-3000')" is what you
would program against.

You would then have the original data in the table to "restore" whenever you
wanted it.

Review &
      Followup

      Rating: 4
How to disable
      versioning?  October 05, 2002
Reviewer:
      tony  from India
Hi Tom,
How to disable versioning?.
If versioning is enabled, I'm unable to do any DDL opration on the table. Why?

          Followup:

Lets see -- you figured out how to enable versioning with workspace mgmt, but
you cannot figure out how to turn it off???  Interesting.

In 9iR2, DDL is supported:

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/appdev.920/a96628/long_new.htm#969131
against versioned tables (as well as a ton of other new features).

But, in 9iR1, the function you are looking for is aptly named
"disableVersioning"

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/appdev.901/a88806/long_ref.htm#78996

      Rating: 2
Design of
      copying data  October 13, 2002
Reviewer:
      Jaya  from USA
Hi Tom,

Other application also uses these tables data and setting end_date in the way
you suggested required other system to change their logic to retrieve the data
like where end_date = to_date('01-jan-3000').

So I would like to make this with less impact on other systems by creating one
log table for customer and one for sales and store source and target data [the
data that will change by copying] before copying into log table. so whenever we
want to undo this copy, we can get it from the log table and restore it back.

What is your suggestion about this way of doing it ?

Thanks in advance,
Jaya

          Followup:

No, a VIEW would hide that from them.

rename table T to T_Table;
alter table T_Table add end_date date;
create view v as select <all columns BUT end_date> from T;

and use instead of triggers to turn an UPDATE of T into an update to T_table to
end date the record and an insert into T_Table to create the new record.

No impact on the other systems.

October 14, 2002 Piotr -- Thanks for the question regarding
      "External table (again)", version 9.2.0.1.0

      You Asked
Hello,

I have a file with listing of ls -l on a directory in the format like this
(fields tab separated, records newline separated):

-rwxr-xr-x    1 root     root         6915 Oct  3 05:01 tzselect

and I want to create an external table like this:

create table files(fmask char(10),links number,owner varchar2(30), agroup
varchar(30), asize number,
atime date, name varchar2(255))
organization external (
  TYPE oracle_loader
  DEFAULT DIRECTORY test_dir
  ACCESS PARAMETERS
  ( RECORDS DELIMITED BY NEWLINE
     BADFILE test_dir:'bad_list_ext'
     LOGFILE test_dir:'log_list_ext'
     FIELDS TERMINATED BY 0x'09'
     MISSING FIELD VALUES ARE NULL
     (fmask, links, owner, agroup, asize,
--       ???????
--       atime char date_format date mask "Mon-DD-HH24:MI",
--
      name)
   )
LOCATION ('file_list.csv')
)

Is it possible to tell Oracle that several fields in the CSV file should map to
one table field? For instance the following 3 fields (month, day, time):
... Oct  3 05:01 ...

should be "seen" as one field of type date "atime".
A mapping like this:

atime <- (month, day, time, "Mon DD HH24:MI");

Thank you!

      and we said...
The EASIEST way to do this is a view

Just create a view that puts them together for you based on the external table
and wah-lah, you are done.

Since that is soo easy, I'm not going to agonize over the control file (yes, you
could do it but it is really hard)

This is why I dig external tables so much  - make the simplest control file
possible and then just use sql to do the hard stuff!
Review &
      Followup

      Rating: 5
How
      simple  October 13, 2002
Reviewer:  Piotr
      Jarmuz  from Braunschweig, Germany
I always knew the simplest solutions are the best!
By the way could you, please, once and for all dispel or confirm the myth that
views add significant overhead to the SQL queries at least during query parse
stage?

And the last question that has been bothering me for a long time:
There is one Oracle DB instance running on one machine and many instances of
Apache WS running on many, say 4 machines additionally each Apache instance runs
many virtual servers and all of them use the same DB. Each virtual server runs
its own application and on average there are 25 Apache kids in the instance that
makes ~100 connections to DB which is doable and works fine. This is true if we
use "big database user" model each apache process in an instance has one
connection. But it'd have many more if we wanted to assign each application one
Oracle account e.g. one Apache instance running ~25 kids and ~10 virtual servers
i.e 10 applications this would give us 250 DB connections per apache instance
and +1000 per whole site this is too much unless we have Oracle on Sun E10000
server...

Yes, Apache has connection pooling but this is not an independent pool of
connections and pool of threads like Websphere or Weblogic has but rather one
connection per thread (process) if it's shared accross all the apps and 10 if
each app has its own.

With all this in mind I wonder about the optimal solution:
1. Use MTS and let each application have its own account (tried this but
response times were much worse especially if somebody else was doing lots of
stuff hogging shared servers from sweet web transactions).
2. Use shared connection and FGAC (have no much experience probably lots of code
rewrite).
3. Use shared connection and something like switching from user to user before
serving a request for particular application (is it possible?).
4. Any other idea???

And if I opted for shared connection: Should this user be powerful (SELECT ANY
TABLE, INSERT ANY TABLE...) for convenience or just nobody with selective grants
(CREATE SESSION and EXECUTE ON 'some_package' etc. lots of stuff) for security?

Could you, please, comment on this?

Thank you!

          Followup:

Significant is in the eye of the beholder -- is it not?

During a hard parse, there is extra work for the view.  HOWEVER, this extra work
makes everything else soooo much easier that I say "so what".  This example
shows it added 0.00084 seconds give or take to the parse time for a HARD PARSE:

ops$tkyte@ORA920.US.ORACLE.COM> create or replace view emp_view as select * from
emp;

View created.

ops$tkyte@ORA920.US.ORACLE.COM> alter system flush shared_pool;
System altered.

ops$tkyte@ORA920.US.ORACLE.COM> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5
  6      type rc is ref cursor;
  7      l_cur rc;
  8  begin
  9      insert into run_stats select 'before', stats.* from stats;
 10
 11      l_start := dbms_utility.get_time;
 12      for i in 1 .. 1000
 13      loop
 14          open l_cur for 'select * from emp where empno = ' || i;
 15          close l_cur;
 16      end loop;
 17      l_run1 := (dbms_utility.get_time-l_start);
 18      dbms_output.put_line( l_run1 || ' hsecs' );
 19
 20      insert into run_stats select 'after 1', stats.* from stats;
 21      l_start := dbms_utility.get_time;
 22      for i in 1 .. 1000
 23      loop
 24          open l_cur for 'select * from emp_view where empno = ' || i;
 25          close l_cur;
 26      end loop;
 27      l_run2 := (dbms_utility.get_time-l_start);
 28      dbms_output.put_line( l_run2 || ' hsecs' );
 29      dbms_output.put_line
 30      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 31
 32      insert into run_stats select 'after 2', stats.* from stats;
 33  end;
 34  /
131 hsecs
215 hsecs
run 1 ran in 60.93% of the time

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> select a.name, b.value-a.value run1,
c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11   order by abs( (c.value-b.value)-(b.value-a.value))
 12  /

NAME                                 RUN1       RUN2       DIFF
------------------------------ ---------- ---------- ----------
...
LATCH.session allocation                1        826        825
STAT...parse count (total)           1071       2017        946
STAT...calls to get snapshot s       1104       2051        947
cn: kcmgss

STAT...execute count                 1091       2040        949
STAT...opened cursors cumulati       1052       2016        964
ve

STAT...consistent gets                420       2140       1720
STAT...session logical reads          464       2214       1750
STAT...table fetch by rowid           144       2028       1884
STAT...no work - consistent re        165       2056       1891
ad gets

LATCH.row cache objects              4137       6147       2010
LATCH.row cache enqueue latch        4114       6126       2012
STAT...redo size                    27352      29924       2572
STAT...buffer is not pinned co        321       4087       3766
unt

LATCH.cache buffers chains            730       4525       3795
LATCH.library cache pin alloca       6340      12192       5852
tion

STAT...recursive calls               4457      10630       6173
LATCH.library cache pin              7640      15343       7703
LATCH.shared pool                   25412      33695       8283
LATCH.library cache                 19097      35622      16525

69 rows selected.

Now, it does more latching, however, you only hard parse a query once and on a
properly done system, it'll really only ever be ONCE

ops$tkyte@ORA920.US.ORACLE.COM> delete from run_stats;
1509 rows deleted.

ops$tkyte@ORA920.US.ORACLE.COM> commit;
Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5
  6      type rc is ref cursor;
  7      l_cur rc;
  8  begin
  9      insert into run_stats select 'before', stats.* from stats;
 10
 11      l_start := dbms_utility.get_time;
 12      for i in 1 .. 1000
 13      loop
 14          open l_cur for 'select * from emp where empno = ' || i;
 15          close l_cur;
 16      end loop;
 17      l_run1 := (dbms_utility.get_time-l_start);
 18      dbms_output.put_line( l_run1 || ' hsecs' );
 19
 20      insert into run_stats select 'after 1', stats.* from stats;
 21      l_start := dbms_utility.get_time;
 22      for i in 1 .. 1000
 23      loop
 24          open l_cur for 'select * from emp_view where empno = ' || i;
 25          close l_cur;
 26      end loop;
 27      l_run2 := (dbms_utility.get_time-l_start);
 28      dbms_output.put_line( l_run2 || ' hsecs' );
 29      dbms_output.put_line
 30      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 31
 32      insert into run_stats select 'after 2', stats.* from stats;
 33  end;
 34  /
32 hsecs
32 hsecs
run 1 ran in 100% of the time

PL/SQL procedure successfully completed.

same wall clock time on the soft parse

ops$tkyte@ORA920.US.ORACLE.COM> select a.name, b.value-a.value run1,
c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11   order by abs( (c.value-b.value)-(b.value-a.value))
 12  /

NAME                                 RUN1       RUN2       DIFF
------------------------------ ---------- ---------- ----------
...
LATCH.shared pool                    9017      10017       1000
LATCH.library cache pin              6026       8020       1994
LATCH.library cache pin alloca       6018       8018       2000
tion

LATCH.library cache                 14063      20057       5994

22 rows selected.

ops$tkyte@ORA920.US.ORACLE.COM>

and those are way way down.    So, it has an impact, BUT the impact of NOT
using a view sooooo outweighs using them.  USE VIEWS.

As for the connection pool stuff....

1) I would never let anyone use a webserver for something that takes more then a
couple of seconds.  If you have longer running transactions, they should not be
using the browser and waiting.  Sounds more like something for AQ (advanced
queues).

2) the point of FGAC is to NOT have to rewrite any code whatsoever.  It is
doable.

You should ALWAYS ALWAYS ALWAYS use the concept of "least privileges".  SELECT
ANY TABLE -- way too powerful.

October 14, 2002 Prince -- Thanks for the question regarding "is
      there anyway to exclude tables while importing?", version 8.1.7

      You Asked
Tom,

IS there anyway, I can exclude a few tables while doing an import?

I have couple of hundred tables in the export dump and I want to import
everything except two (These two tables are very large and only have some blob
data, which I don't need to restore)

I would apprecitae any of your suggestion.

Thanks,

      and we said...
You can either

o do table level import, list the tables you WANT on the import.

o cause the big tables to "fail" for some reason.  IMP will skip them.

for example, say I have an exp:

exp userid=/ owner=scott

Export: Release 8.1.7.2.0 - Production on Fri Mar 22 08:27:44 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.7.2.0 - Production
With the Partitioning option
JServer Release 8.1.7.2.0 - Production
Export done in US7ASCII character set and WE8ISO8859P1 NCHAR character set
server uses WE8ISO8859P1 character set (possible charset conversion)

About to export specified users ...
. exporting pre-schema procedural objects and actions
. exporting foreign function library names for user SCOTT
. exporting object type definitions for user SCOTT
About to export SCOTT's objects ...
. exporting database links
. exporting sequence numbers
. exporting cluster definitions
. about to export SCOTT's tables via Conventional Path ...
. . exporting table                          BONUS          0 rows exported
. . exporting table                           DEPT          4 rows exported
. . exporting table                          DUMMY          1 rows exported
. . exporting table                            EMP         14 rows exported
. . exporting table                       SALGRADE          5 rows exported
. exporting synonyms
. exporting views
. exporting stored procedures
. exporting operators
. exporting referential integrity constraints
. exporting triggers
. exporting indextypes
. exporting bitmap, functional and extensible indexes
. exporting posttables actions
. exporting snapshots
. exporting snapshot logs
. exporting job queues
. exporting refresh groups and children
. exporting dimensions
. exporting post-schema procedural objects and actions
. exporting statistics
Export terminated successfully without warnings.

and I wanted all of the data but EMP imported.  I could:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table emp ( x int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exit
Disconnected from Oracle8i Enterprise Edition Release 8.1.7.2.0 - Production
With the Partitioning option
JServer Release 8.1.7.2.0 - Production

$ imp userid=/ fromuser=scott 'touser=ops$tkyte'

Import: Release 8.1.7.2.0 - Production on Fri Mar 22 08:28:28 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.7.2.0 - Production
With the Partitioning option
JServer Release 8.1.7.2.0 - Production

Export file created by EXPORT:V08.01.07 via conventional path
import done in US7ASCII character set and WE8ISO8859P1 NCHAR character set
import server uses WE8ISO8859P1 character set (possible charset conversion)
. . importing table                        "BONUS"          0 rows imported
. . importing table                         "DEPT"          4 rows imported
. . importing table                        "DUMMY"          1 rows imported
IMP-00015: following statement failed because the object already exists:
 "CREATE TABLE "EMP" ("EMPNO" NUMBER(4, 0) NOT NULL ENABLE, "ENAME" VARCHAR2("
 "10), "JOB" VARCHAR2(9), "MGR" NUMBER(4, 0), "HIREDATE" DATE, "SAL" NUMBER(7"
 ", 2), "COMM" NUMBER(7, 2), "DEPTNO" NUMBER(2, 0))  PCTFREE 10 PCTUSED 40 IN"
 "ITRANS 1 MAXTRANS 255 NOLOGGING STORAGE(INITIAL 524288)                   "
. . importing table                     "SALGRADE"          5 rows imported
Import terminated successfully with warnings.

so I got everything but EMP
Review &
      Followup

      Rating: 4
I was thinking
      of altering the structure of unwanted tables.  March 22,
      2002
Reviewer:  Prince
Thanks Tom,

Since, I want to have the table structure for these tables to be there, I was
thinking of importing all the tables without the data first. Then alter the
structure of the tables, I don't want to have data. (say adding a NOT NULLable
column). Then import the data. After the import is done, I will drop the column.
But, here the problem is it spews lot of error messages, for every row.

Now, (after reading ur suggestion), I will import only those tables (without
data) for which the data is not needed. Then import the export dump (the
existing ones will fail).

      Rating: 5
Importing Just
      the data  October 11, 2002
Reviewer:  sm
       from usa
Tom,
I am moving a database from windows to UNIX..and changing the tablespaces from
DMT to LMT...
1.The day we go live...I want to refresh the data of the UNIX database with the
WINDOWS (current production database)..
2.Currently, I am at the point where I am moving my TEST database (on UNIX) to
another UNIX machine (PROD)...
I want to take the structure only...but get the data from current production
(windows) database.  How can this be done....
My approach..was to do an export with rows = n compress = n direct = y full=y of
the test database...
then do an export of current production data
full=y rows=y constraints=n indexes=n compress=n

and then do the imports...
what do you think?

          Followup:

I think that would be bad -- the indexes would be all in place when you did the
final import -- making it take many times longer then it should.

What is the advantage you see in doing it in a two step?

      Rating: 5
Why two
      steps?  October 13, 2002
Reviewer:  sm
       from USA
As we are moving from Windows to UNIX..we are also making another minor change
:) Changing Dictionary Managed Tablespaces to Locally Managed tablespaces....so
I took the Current Production (WINDOWS) database to a TEST database...changed
all the tables and indexes to small, medium, large tablespaces...
Now we need to build a PROD database in UNIX...so I can:
1. Take a structure export of the TEST (UNIX) database...
2. Import structure into PROD (UNIX) database
3. Take an Export of just the data of Current PROD (WINDOWS) database
4. Disable all the constraints in PROD(UNIX)
5. Truncate all the tables in PROD(UNIX)
5. Do an import of current Production (WINDOWS) data into PROD (UNIX).
6. Enable all the constraints

Steps 1 and 2 does not have to take place the evening we are making the
switch...bcause we do want to test the PROD database for couple of days for all
the applications
Step 3,4, and 5 will take place the evening we make the switch... i.e just
refreshing the data...

Export TEST structure:
rows = n compress = n direct = y full=y

Export PROD (WINDOWS) data:
full=y rows=y constraints=n indexes=n compress=n triggers=n

What do you think?
is there a better way..

Thanks a bunch....

          Followup:

I would drop the indexes before the load, put the indexes back on after the load
-- it'll be slightly faster that way (slightly meaning a whole bunch)

      Rating: 5
Drop all the
      indexes....  October 14, 2002
Reviewer:  sm

Tom,
Thanks for the reply...
Database is only 2.5 gig.
Do you think the speed gsin in this case will be significant to drop the indexes
and recreate them...

Thanks...

          Followup:

2.5 gig, probably not.

Must mean you really only have but one tablespace "small" ;)

October 14, 2002 Bala -- Thanks for the question regarding "Eg for
      DESEncryption and DESDecryption ", version 8.1.6

      You Asked
Tom

Can you give me an example to use DESEncryption and
DESDecryption.

I would like to have a table with 3 cols, one col being cr card
and i wanted to encrypt cr card and store it in the db, then I
would want to decrypt and see if I get the proper result.

How will I update if i encrypt cr card info.

Thanks

      and we said...
Here is an example:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:791026226790
As for how to update it -- you will have to write a procedure
that takes as input the primary key and the credit card number.
It will crypt the credit card number using your key.  It can
then update the column in the database.  Likewise on the way
"out" when you select out the credit card number and need to see
it -- you'll need a procedure to decrypt the card as well.  The
key management is up to you.
Review &
      Followup

      Rating: 5
credit
      card  October 14, 2002
Reviewer:
      A reader
hi tom,

   Can we process the credit card
1.) varification
2.) get the authentication

 from pl/sql stored procedure ? Or we have to always use the 3-rd party
softwares ?

 Can you provide me some information how do I process the
credit card from the oracle developer( forms)

  I have to add this functionality and I am not in position to use the 3-rd
party software ?

is it possible to
1.) send the request with ( crard#) to corrosponding bank
2.) varify/receive the authentication

thro' stored procedure ?

 if yes can you give me sample implementation ?

TIA

          Followup:

You'll really need to contact a "credit card" company or whatever to find out
what options are available.  Typically -- a 3rd party verfication/payment
service will be involved.  Yes, you can do it via PLSQL -- you need to get the
*api* that is used to do it first.

October 14, 2002 Rahul -- Thanks for the question regarding
      "Tables that have been accessed...", version 8.1.7

      You Asked
Hi Tom,

In my database i have lot of tables which i want to get rid off.I want to find
out which tables have been accessed and how many times let say for past 15
days...

Your help is greatly appreciated...

Rahul M.

      and we said...
In 15 days we can answer this.

Enable auditing (audit_trail=true)
Audit select on those objects
Sit back
And in 15 days, you'll know.
October
      14, 2002 Vinod -- Thanks for the
      question regarding "Storing Decimals in Number datatype", version 8.1.7

      You Asked
Hi Tom,

I am trying to store decimal values (i.e. currency exchange rate) in a number
datatype, but i found that the decimal values are being rounded off.

Following are the statements executed :

Create Table Temp (Col1 Number)
/

Insert Into Temp Values (0.0208636606911768)
/

Select * From Temp
/

I get .02086366 instead of .0208636606911768. I also tried FLOAT instead of
NUMBER, but got the same results as above.

What would be the problem?. Are there any parameter settings i need to be aware
of?. Appreciate if you could give me a solution.

Thanks in advance.

Regards
Vinod

      and we said...
Not to worry -- the data is OK.  sqlplus is just a reporting tool and uses a
default number format.  Consider:

ops$tkyte@ORA920.US.ORACLE.COM> Create Table Temp (Col1 Number)
  2  /

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> Insert Into Temp Values (0.0208636606911768)
  2  /

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> Select * From Temp
  2  /

      COL1
----------
.020863661

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> set numformat 99.99999999999999999999999999999
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> Select * From Temp
  2  /

                             COL1
---------------------------------
   .02086366069117680000000000000

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>

Review &
      Followup

      Rating: 4
Storing
      Decimals in Number datatype  October 14, 2002

Reviewer:  Vinod  from Sydney, Australia
Thanks for your quick response. I totally forgot about the formatting in
SQLPlus.


Subject | Article | Last Updated (East coast time)
Standby hot database backup scrips | article only | Oct 15, 2002 07:38 am
Recommended Use of Partition Views | article only | Oct 15, 2002 07:41 am
Trigger an event from a select | article only | Oct 15, 2002 08:38 am
How to Force the Index in the date column | article only | Oct 15, 2002 08:57 am
XML!!! | article only | Oct 15, 2002 09:08 am
Efficient ways to retrieve Records | article only | Oct 15, 2002 09:26 am
Havign auditing objects being added continously in the database | article only | Oct 15, 2002 09:47 am
Hot Backup script | article only | Oct 15, 2002 12:17 pm
utl_file, get contents of os file | article only | Oct 15, 2002 12:18 pm
Mass Insert | article only | Oct 15, 2002 12:43 pm
Insert /+ append +/ statement | article only | Oct 15, 2002 03:29 pm
Question on Sqlldr ... | article only | Oct 16, 2002 08:20 am
processes, open_cursors, sessions | article only | Oct 16, 2002 10:04 am
Securing the Database | article only | Oct 16, 2002 12:58 pm
deadlock detected | article only | Oct 16, 2002 01:07 pm
Oracle 9i Feature Explanation | article only | Oct 16, 2002 02:52 pm
SQL Loader 00306 | article only | Oct 16, 2002 06:47 pm
CREATE ANY CONTEXT sys. priviledge --- "Big Deal" ? | article only | Oct 16, 2002 07:12 pm
Invoker's Rights | article only | Oct 16, 2002 07:20 pm
Usage of Role Password | article only | Oct 17, 2002 07:39 am
Statspack report: which sqls to tune ? | article only | Oct 17, 2002 01:56 pm
Tuning environment | article only | Oct 17, 2002 01:59 pm
Public DB link or not? | article only | Oct 17, 2002 02:04 pm
Client side SQL & Server side SQL | article only | Oct 17, 2002 02:48 pm
Manipulating Sort Area Size | article only | Oct 17, 2002 03:13 pm
XML parser usign native PL/SQL code. | article only | Oct 17, 2002 04:31 pm
loading single record datafile into multi records table | article only | Oct 17, 2002 07:32 pm
ULT_FILE | article only | Oct 17, 2002 07:37 pm
Packages | article only | Oct 18, 2002 10:52 am
Client vs Server Code | article only | Oct 18, 2002 11:00 am
How to tune this one | article only | Oct 18, 2002 11:58 am
javasyspriv - a security vulnerability? | article only | Oct 18, 2002 04:26 pm
Serializing Java Objects into the database (and getting them back out) | article only | Oct 18, 2002 08:01 pm
Use of functions within a procedure... | article only | Oct 18, 2002 08:17 pm
Connection to MS SQL through java stored Procedure using JDBC | article only | Oct 18, 2002 08:19 pm
INITRANS Cause of deadlock | article only | Oct 19, 2002 10:56 am
how to run OS commands in pl/sql block | article only | Oct 19, 2002 11:19 am
How can we design the big database. e.g. Tera byte Database | article only | Oct 19, 2002 11:35 am
automatically revoking permission from objects | article only | Oct 19, 2002 11:46 am
message from sales to store department | article only | Oct 19, 2002 11:47 am
To check performance issue | article only | Oct 20, 2002 09:37 am
Commit someone's session | article only | Oct 20, 2002 09:39 am
When you can say a Database is VLDB in Oracle ?? any standards ? | article only | Oct 20, 2002 09:46 am
how to rebuild a table in 8.0.6.1.0 EE | article only | Oct 20, 2002 09:49 am
R-trees | article only | Oct 20, 2002 09:53 am
commit | article only | Oct 20, 2002 10:07 am
what does a DBA need? | article only | Oct 20, 2002 02:10 pm
performance on index and view | article only | Oct 20, 2002 05:59 pm
Which is faster - Database or a programming language like C++ | article only | Oct 20, 2002 09:20 pm
oracle8i database full EXP/IMP | article only | Oct 21, 2002 07:39 am
Inserting Unknown values | article only | Oct 21, 2002 08:28 am
queue tablespace nologging | article only | Oct 21, 2002 08:45 am
Concatenation problem | article only | Oct 21, 2002 09:07 am
enabling Function-Based Indexing | article only | Oct 21, 2002 09:18 am
Snapshot too old | article only | Oct 21, 2002 10:39 am
Lock and performance | article only | Oct 21, 2002 12:58 pm
row(s) 1 - 56 of 56
October 15, 2002 Partha -- Thanks for the question regarding "Standby hot database backup scrips", version 8.1.7.3
You Asked
Hi, Tom

Can u provide me the complete script for hot database backup for standby
database.

I need step by step process to create standby database using hot backup scripts.

Thanks in advance
and we said...
It is really cool that we actually document stuff like this, including "Step by
Step" directions!!!
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76995/toc.htm
is the book you need to read from start to finish. Amazing but true: it has all
of the information you actually need to

a) understand the standby architecture
b) implement a standby
c) manage a standby
d) failover to a standby when needed!
Review & Followup
Rating: 3
Stby Review October 14, 2002
Reviewer: A reader

Rating: 4
October 14, 2002
Reviewer: A reader
Tom,

When modifying the parameter file for standby database, does the sid of standby
has to be the same as primary..

Thanks..

Followup:
No, it does not

October 15, 2002 Matt -- Thanks for the question regarding "Recommended Use of Partition Views", version 9.0.1.4
You Asked
I am looking to improve the performance of an existing  DB that relies heavily
on an Oracle feature called Partition Views.

What we have many pairs of large tables (approx 10Gb each) that store interval
based data on a monthly basis. Each table stores data according to a different
interval value. The interval data is assocatited with an sequenced ID. We have
enough of these tables to store data for several years online. Queries must be
able to access interval data for a single id or set of id's for the entire range
of data.

Currently we have a cluster index defined on each pair of monthly tables (as
these tables are regularly joined together).

To allow an efficient query accross all the years of data the parameter
PARTITION_VIEWS_ENABLED is set to true in the init.ora and several views are
used to UNION ALL the various tables.

Can you please explain to me how the partition view functionality works? I have
gathered some trace files and expected to see tables excluded from queries
depending on the range of data requested. However the trace just reveals the
view query.

Secondly can you briefly highlight a solution that is based on 9i functionality.
I would like to introduce table partitions but am aware that clusters cannot be
used here. I am also concerned about the volume of data that must be converted.

Finally can you summarise any concerns or potential issues that you might have
with this approach?

Many Thanks.
and we said...
Partitioned views were used briefly in version 7.3 before partitioning was
available.

Post 7.3, partitioned views are

o supported
o not recommended
o not being enhanced at all

Partitioning would be the appropriate technique here.

Now, with the partition view query plans -- you would expect to see ALL of the
tables referenced in the plan (just like in most partitioned query plans as
well) -- but at runtime, certain tables would be queried or not queried
depending on the inputs (binds) to the query plan itself.  You can observe this
by offlining a tablespace for example that has some of the tables contributing
to the view -- and then running a query you know should not need to query that
table.  If it runs, well, then that table was eliminated from consideration at
runtime (but it'll appear in the plan, it has to -- we might need to query it
given different inputs)

Partitioned tables would be the way to go for further enhancements and
development.  Converting a 10gig table into a partition should only take a
couple of minutes on a reasonably fast system these days.

My concern with the "partitioned view" is that it might go away in the near
future, development on it stopped in about 1995, and partitioning is our future
direction.
Review & Followup
Rating: 3
Followup October 13, 2002
Reviewer: Matt from Australia
Yep, we recently upgraded from 7.3 all the way up to 9.0.1.4.0 which is why this
functionality is in place.

I think that Oracle has already identified that this functionality will be
de-supported / deprecated in thenext release after 9.2.

Can you reference any Oracle documentation on the limitations of Partitioned
tables? For instance how many partitions may such a table have?

Say I partition the data in a different way - by week say - I would have 364
partitions for each table type (or over 700 if I decide to stick all the (7
years of) data together). Is this possible?

(obviously we would need to test each of these approaches).

Oh, we are currently RBO and on the way to CBO. From memory I believe that we
need to get to CBO before we can really take advantage of partitions. Can you
confirm this?

One final question. I have a requirement to summarise(aggregate) this data for
additional analysis. That is all the 7 years of data. Although most of the
queries would run accross the first year of data (this is where all the data
changes). The requirement is to query the summarised data and then drill into
the underlying raw data according to the results of the initial analysis. I am
intending to implement this in a seperate schema in the same instance (depending
on whether the machine can handle this) but am curious to find out what is the
best way to summarise this huge dataset. I am thinking that the older data would
be summarised in a materialised view and the newer data would be summarised at
runtime (to always pick up the latest data), I would need a 'rolling window'
mechanism so that these two query types are altered according to the dates
associated with the data.

Is there some way that I can partition materialised views?

Could you please comment on my proposed approach and highlight any other
functionality that might be better suited to my explanation of the requirements?

Best Regards.

Followup:
See appendix C in your 7.3 "Oracle7 Server Tuning" guide.  There are no
physical, only practical (the view statement gets rather large, parsing will
increase over time as you have hundreds of referenced objects/indexes and so on)

As soon as you access a partitioned table (truly partitioned) you will be using
CBO.

Hundreds, thousands of partitions are doable in a truly partitioned table.

MV's can be partitioned yes, it just part of the syntax (see the CREATE
MATERIALIZED view).  to support incremental (fast) refreshs, you would create MV
logs on the base detail data is all.

Rating: 4
Many thanks. October 14, 2002
Reviewer: Matt Butler from Australia
Thanks for the reference. This covers all the information I had wanted to know
(but could not find).

The 7.3 database reference lists the limit of SQL statement size as 64K (same as
9i) which, I guess will be the limiting factor on the views (alongside those you
mention).

The 9i Reference indicates that the limit on partitions per table is 64K - 1.
ie: alot.

"As soon as you access a partitioned table (truly partitioned) you will be using
 CBO."  -

My undertanding is then that we *could* run RBO and still get benefits from
partitioning (provided we have sufficiently up to date stats for the CBO)?

Thanks for the information about the MV's. I think that these will be very
useful - particulary the fast refresh.

Followup:
the 64k "limit" is not really  a limit, it can go much much much larger.
However, the parse time will get much much much larger as the number of objects
referenced in the query gets really big.

If you reference a partitioned table in a query  the CBO will be used, the RBO
cannot in any way shape or form be use for that query.  Other queries could
use RBO but anything that touched that table -- CBO.

Rating: 4
Agreed October 15, 2002
Reviewer: Matt from Australia
Yep, this confirms my understanding.

Many Thanks

October 15, 2002 Bruno -- Thanks for the question regarding "Trigger an event from a select", version v 7.3.4 and v.8.1.6
You Asked
The options for a trigger are insert, update or delete.  But I want to trigger
an event when someone SELECTS or updates a certain person_id from the person
table.

Can this be done through a trigger? I can't get it to work and someone mentioned
alternate triggers and I can't seem to find anything about that
subject...........

Thanks

Bruno-
and we said...
There are no select triggers available in any release.

In Oracle8i, release 8.1 and up -- there is a facility called fine grained
access control which might help you (see
http://osi.oracle.com/~tkyte/article2/index.html
for info).

Another possibility is to perform queries via stored procedures (using ref
cursors see
http://osi.oracle.com/~tkyte/ResultSets/index.html
for info) and perform any sort of processing you want in there.
Review & Followup
Rating: 5
who executes event trigger? October 15, 2002
Reviewer: A reader
hi

if I have an event trigger such as after LOGON database, when trigger runs in
background under what user is run? The owner of Trigger?

Followup:
ops$tkyte@ORA920.US.ORACLE.COM> create table on_logon_table
  2  ( current_user      varchar2(30),
  3    current_schema varchar2(30),
  4    session_user      varchar2(30)
  5  )
  6  /

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create or replace trigger On_Logon
  2  after logon on database
  3  begin
  4          insert into on_logon_table
  5          ( current_user, current_schema, session_user )
  6          values
  7          ( sys_context( 'userenv', 'current_user' ),
  8            sys_context( 'userenv', 'current_schema' ),
  9            sys_context( 'userenv', 'session_user' ) );
 10  end;
 11  /

Trigger created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> connect scott/tiger
Connected.
ops$tkyte@ORA920.US.ORACLE.COM> connect /
Connected.
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> column current_user format a10
ops$tkyte@ORA920.US.ORACLE.COM> column current_schema format a10
ops$tkyte@ORA920.US.ORACLE.COM> column session_user format a10
ops$tkyte@ORA920.US.ORACLE.COM> select * from on_logon_table;

CURRENT_US CURRENT_SC SESSION_US
---------- ---------- ----------
OPS$TKYTE  OPS$TKYTE  SCOTT
OPS$TKYTE  OPS$TKYTE  OPS$TKYTE

ops$tkyte@ORA920.US.ORACLE.COM>

The privs will be that of the CURRENT_SCHEMA (as is the case with ALL definers
rights code and triggers are ALWAYS definers rights).

So, this trigger, created by ops$tkyte, runs with the set of privs granted
DIRECTLY to ops$tkyte (no roles)

October 15, 2002 ragupathi -- Thanks for the question regarding "How to Force the Index in the date column", version 8.1.6
You Asked
Hi Tom,

I am joining two tables based on date column.
The date column have including time. so i used trunc function to avoide the
time. If i am using trunc  the date column not using index.
so let me know what is the workaround to use index.

Ex:-
First Table 1:-
  1* select order_no,to_char(order_date,'dd-mon-yyyy hh24:mi:ss') from temp1

  ORDER_NO TO_CHAR(ORDER_DATE,'
---------- --------------------
      1001 01-jan-2001 23:12:12
      1002 01-jan-2001 23:12:12
      1003 01-jan-2001 23:12:12
      1004 01-jan-2001 23:12:12
      1005 01-jan-2001 23:12:12
      1006 01-jan-2001 23:12:12

First Table 2:-

SQL> select proces_no,to_char(process_date,'dd-mon-yyyy hh24:mi:ss') from temp2;

 PROCES_NO TO_CHAR(PROCESS_DATE
---------- --------------------
      2001 01-jan-2001 11:12:12
      2002 01-jan-2001 11:12:12
      2003 01-jan-2001 11:12:12
      2004 01-jan-2001 11:12:12
      2005 01-jan-2001 11:12:12

The above table  joined based on order_date and process_date.
select * from temp1,temp2 where trunc(order_date) = trunc(process_date);

The table temp1 column order_date and table temp2 column process_date
have index.

when i am joing this two table based on order_date and process_date
and i am using turnc function for terminating time.

when i am using trunc function the execute plan not using index.

the performance is very slow.
pls give some workaround for improve the perfomance of the above problem.

Thanks
Raghu.K
and we said...
Well, first and foremost would be to STORE the process date without time if that
is what you really meant to do (eg: a simple trigger could

   :new.process_date := trunc(:new.process_date)

and a simple update of the existing data would fix it.

Short of that, you'll be looking at a function based index on the
trunc(process_date).  read
http://asktom.oracle.com/~tkyte/article1/index.html
October 15, 2002 OCP -- Thanks for the question regarding "XML!!!", version 8.1.7
You Asked
Tom,

As I am new to XML,can you please give some examples of how to use them in my
database & their advantages, so that i could get started.

Thanks for all your help.

Justin.
and we said...
see
http://otn.oracle.com/tech/xml/content.html
download the XDK (installs in 8i as well as 9i)

Tons of stuff there, samples, examples, documentation and source.
October 15, 2002 -- Thanks for the question regarding "Efficient ways to retrieve Records", version 9.x
You Asked
Tom,
We are developing a web based application which will be using packages to select
from tables ie there will be no select sql in the application at all only calls
to the packages. One school of thought is leaning towards doing only select *
from the table. The rationale is that oracle is reading the data block so it
makes sense to get all the column and one can take advantage of the multi-block
read. The other school of thoughts says that pass the column names as in
parameters instead of doing "select *".
My concern is that a)some of the tables have outline CLOBs (not inline) so doing
select * can be an issue and these tables will be heavily used and b)doing
select * will generate extra sqlnet traffic which one can do without.
Appreciate your thoughts on this.
PS: we are using oracle 9i and the block size for the tablespaces is 8K and are
looking at using multi blocksize capabilities.
Thanks
and we said...
School of thought people need to go back to school ;)

A "select *" versus "select c1,c2,c33" will not have any impact on multi-block
reads.  Multi-block reads are used when we FULL SCAN.  If you use index access
-- we will NOT be using multi-block reads.

A "select *" versus "select <the columns you really want>" boils down to this:

o select * will transmit all of the columns over the network (increased network
bandwidth)

o select * will AFFECT the query plans in a bad way.  There are times when a
select <columns you want> is incredibly efficient as compared to select *.  a
SELECT * will have to goto the table.  A select <columns you want> -- might
not have to.

o clobs won't affect this -- inline or out of line.  What gets transmitted to
the client is ALWAYS a lob locator -- not the contents.  So, regardless of the
size of the clob, it won't affect this.   A select * on these will not be
affected by out of line or in line clobs.

But the bottom line is: use SELECT <columns you need>, do not use SELECT *.
SELECT * will be

o inefficient
o error prone (what order will the columns come back in.  Is the column YOU NEED
going to be there?  Are extra columns you were not anticipating going to get in
the way??)

Try to avoid tons of dynamic sql (passing in column lists).  Set up a QUERY
(static is best) for your application that satisfies its needs.  Do not go down
the "oh, we'll just dynamically execute all of our sql" path.  Use static sql as
much as possible (until you cannot for a very good reason).  You'll not be sorry
you did.  It is

o easier to tune
o easier to managed
o sets up the hard dedpendency between your packages and the tables -- change
management is easier
Review & Followup
Rating: 5
say it again October 15, 2002
Reviewer: Chris McCaffrey from King Of Prussia Pa. USA
I have been a DBA for a long time.  You should put this response in every search
that has anything like SQL, efficient, performance, development.

Say it again and again.

Sooner or later a developer might read it.

October 15, 2002 Nihal -- Thanks for the question regarding "Havign auditing objects being added continously in the database", version 9.1.1
You Asked
Hi Tom,
IWe are having a phase wise deployment of a project, where in some modules are
developed,deployed and thrown live into production.
We also need to have a ability to audit the changes made by the users (oracle
User) apart from the Application user.
Is there a way to audit all objects in the database ? or do we have to write the
audit command for each and every objects.

Thanks

Nihal
and we said...
Wow, "where some modules are developed, deployed and "thrown" live into
production"

Must be exciting times for you.  Probably lots of yelling, sleepless nights
going on there.

You can

audit select table, insert table, update table;

it'll get everything (literally).  Might want to TEST this in a TEST machine.
October 15, 2002 Marc -- Thanks for the question regarding "Hot Backup script", version 8.1.7
You Asked
Hello Tom,

I have seen a hot backup script which works like that :

Foreach DATAFILE
  alter tablespace (DATAFILE) begin backup
  backup DATAFILE
  alter tablespace (DATAFILE) end backup
end loop

and then switch logfile ...

What do you think of this kind of loop ?
I thank we had to loop over the tablespaces, and then the datafiles...

Thanks for your opinion,

Marc
and we said...
That would be a neat script -- since the commands referenced in it do not
exists.

You can begin/end backup on a TABLESPACE.
You can END backup by datafile OR tablespace.

It would be:

   for each tablespace
        alter tablespace begin backup
        for each datafile IN tablespace
              copy the datafile
              <alter database datafile 'filename' end backup>
        end
        <alter tablespace end backup>
   end

where <> denotes you would do either or of these commands (alter tablespace is
more straight forward I think)......
Review & Followup
Rating: 1
Precision... October 15, 2002
Reviewer: marc from Belgium
Thanks Tom, but I am not sure my question was precise enough...

For each datafile
  XXX := (the tablespace the datafile resides on)
  alter tablespace XXX begin backup
  copy datafile
  alter tablespace XXX end backup
end loop

If there are many datafiles in a tablespace, the tablespace is put many times in
backup mode...

Thanks for your help

Followup:
That is not precision.

That is apples
Compared to flying toaster ovens.

So -- one star to you for correctness.  You had:

Foreach DATAFILE
  alter tablespace (DATAFILE) begin backup
  backup DATAFILE
  alter tablespace (DATAFILE) end backup
end loop

but now I see what you meant.

It works, it is OK.  It won't add much if any time to the backup at all.

October 15, 2002 Rick -- Thanks for the question regarding "utl_file, get contents of os file", version 8.1.7 - RH linux 7.2
You Asked
Hi again Tom!

I am trying to use utl_file to read the contents of a .txt file on my server.
What happens is:

i. I (automatically) send a query to the shell, doing a whois lookup on a
specified IP. It returns the value to a text file:
$attacking_ip=xxx.xxx.xxx.xxx
$whois -h whois.geektools.com $attacking_ip > get_whois.txt
(This part all works fine)

ii. I need to get the contents of that file, so I can automatically display it
in an email. I am trying (futilely!!) to create a function that reads that whole
file (line by line is how I am currently doing it) and will display it in my
email. btw, I am using your mail_pkg package to send my emails etc.

Could you show me a function that does what I'm looking for? (I've looked in
many places, incl. your site, and still couldn't make any of it quite do what I
wanted). Also, does what I am trying to do sound reasonable and is the right way
to approach the situation?

Cheers,

Rick
and we said...
Even easier I think would be:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace directory my_dir as '/tmp'
  2  /

Directory created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> !uptime > /tmp/uptime.dat

ops$tkyte@ORA817DEV.US.ORACLE.COM> !date >> /tmp/uptime.dat

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2          l_file bfile := bfilename( 'MY_DIR', 'uptime.dat' );
  3          l_txt  long;
  4  begin
  5          dbms_lob.fileopen( l_file );
  6          l_txt := utl_raw.cast_to_varchar2( dbms_lob.substr( l_file, 32000,
1 ) );
  7          dbms_lob.fileclose( l_file );
  8
  9          dbms_output.put_line( substr( l_txt, 1, 250 ) );
 10  end;
 11  /
10:55am  up 141 day(s),  6:54,  4 users,  load average: 0.00, 0.01, 0.02
Tue Oct 15 10:55:47 EDT 2002

PL/SQL procedure successfully completed.

that'll let you very easily read 32k of the file in one fell swoop.  You can
use dbms_lob.getlength to see if you need to do more then one read

No need to bounce to setup utl_file_dir either!
Review & Followup
Rating: 4
Works well up to a certain point... October 15, 2002
Reviewer: Rick from BC, Canada
(Neat idea btw!)

It seems to read the file, but only up to around 240 or so characters before it
ends.
What is returned:
<quoted>
[whois.geektools.com]
Query:     xxx.xxx.xxx.xxx
Registry:  whois.apnic.net
Results:
% [whois.apnic.net node-1]
% How to use this server
http://www.apnic.net/db/
% Whois data copyright terms
http://www.apnic.net/db/dbcopyright.html
inetnum:
</quoted>

what is actually there:
<quoted>
[whois.geektools.com]
Query:     xxx.xxx.xxx.xxx
Registry:  whois.apnic.net
Results:
% [whois.apnic.net node-1]
% How to use this server
http://www.apnic.net/db/
% Whois data copyright terms
http://www.apnic.net/db/dbcopyright.html
inetnum       xxx.xxx.xxx.xxx
netname:      xxxx
descr:        xxxxx xxx xxx x x x x
country:      CN
admin-c:      WF58-AP
tech-c:       WF58-AP
mnt-by:       MAINT-CNNIC-AP
changed:      xxxxxxxxx
status:       ALLOCATED PORTABLE
source:       APNIC
...
</quoted>

Any way of getting around that?

Thanks again,

Rick

Followup:
it read the whole thing, i have:

 9          dbms_output.put_line( substr( l_txt, 1, 250 ) );

in there to print out just the first 250 characters -- since DBMS_OUTPUT will
complain really LOUD if you try for more then 255 at a time.

Its all there, I just didn't print it!

Rating: 4
An alternative October 15, 2002
Reviewer: Rick from BC, Canada
I created a simple function that seems to do what you described (using your
previous code, of course):

CREATE OR REPLACE FUNCTION get_whois_file
RETURN VARCHAR2
IS
 l_file BFILE := BFILENAME( 'GET_WHOIS_DIR', 'get_whois.txt' );
 l_txt  LONG;
BEGIN
   DBMS_LOB.FILEOPEN( l_file );
   l_txt := UTL_RAW.CAST_TO_VARCHAR2( DBMS_LOB.SUBSTR( l_file, 32000,1 ) );
   DBMS_LOB.FILECLOSE( l_file );
RETURN l_txt;
END;
/
show errors

Thanks again, Tom!

Followup:
that'll work in SQL upto 4000 characters.

It'll work in PLSQL upto 32k.

October 15, 2002 Nag -- Thanks for the question regarding "Mass Insert", version 8.1.7
You Asked
Tom

An interview question..

I was asked..

A table has 100,000 rows.

What is the best optimized way to insert these says rows into the same table
untill the table size is  million and a half rows.

My answer: Load the rows of the table into a flat file.
           Disable the constraints on the table.
           use sqlloader direct path load
           set the no logging/unrecoverable

Is my answer appropriate.

Thanks
and we said...
No way.

INSERT /*+ append */ into T select * from T;
commit; -- 200k

INSERT /*+ append */ into T select * from T;
commit; -- 400k

INSERT /*+ append */ into T select * from T;
commit; -- 800k

INSERT /*+ append */ into T select * from T;
commit; -- 1600k, close enought to 1.5 mill for me.

Might -- depending on number/size of indexes -- alter index iname unusable and
set skip_unusable_indexes=true and then rebuild them in parallel/nologging
afterwards.  If in archivelog mode, I might have set the table to nologging
first but it depends on the situation.

Unloading and reloading -- way way way too hard.  Once data gets into my
database, I never let it leave again ;)
Review & Followup
Rating: 5
Interesting October 15, 2002
Reviewer: Nag
Why not Sql loader?
The method you suggested is the most basic. Dont you think if my table is a fat
table i.e. with a lot of columns and a huge row size, "Insert into" might be
tedious and might take for ever.

I'm surprised that you didnt suggest sql loader.

Please discuss.

Followup:
INSERT /*+ APPEND */

is a high speed, DIRECT PATH loader.

tell me, should we:

a) read all of the data
b) write it to the operating system (what are you using for that???)
c) create a control file (eg: could be hard, if table is "fat" eh?)
d) run sqlldr in direct mode to READ the OS file, WRITE the database files
e) repeat D 13 more times

or,

a)

SQL> begin for i in 1 .. 4 loop
        insert /*+ append */ into t select * from t;
        commit;
     end loop;
     /

read:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c21dlins.htm#365
to learn more about insert appends.

I will race you -- I will win.  My one little loop "tedious"?  No, I don't think
so.

Your approach is hard, involves lots of custom code, is error prone and would
take much longer then insert /*+ append */ just reading/writing the data.  You
generate gobs more IO, write lots of code.  You took the hard way, I took the
easy way.

I wouldn't have given sqlldr a moments thought.  I love 9i where I never ever
need to use sqlldr again (external tables = no more sqlldr)

Rating: 4
But what's in 't'? October 15, 2002
Reviewer: Randy from Austin
I've used this many times, and it is extremely fast.  But it only works if you
have something to SELECT from, as in "select * from t".  So that begs the
question, how do you get your data into t?  Without external tables, we have to
get the data into Oracle in the first place.

Followup:
An interview question..

I was asked..

A table has 100,000 rows.
.....

table T already has data in it....  didn't need to prime the pump, it was part
of the setup. (hence makes sqlldr look really bad as an answer)

Rating: 3
mass inserts of data October 15, 2002
Reviewer: Kumar from San Diego
I have the same situation(s) faced like RANDY explained. How you bring data to
table "t" these direct inserts will help only the syntax as select from "t".

Well in such cases the only option left here is SQLLoader ( if the Oracle is not
9i version).

May be Our guru (Tom) can suggest any mantra in such cased to avoid this Loader.

Followup:
if t is empty -- get the data in anyway you want. (sqlldr)

after that, if you need to multiply it, insert /*+ append */ to make it have as
many rows as you like -- or run sqlldr N times.

(seems like common sense at this point doesn't it???  I mean "my table is empty,
I have a file of data, what do i do")

but, it misses the point of this question which was

o you HAVE a table with 100k rows
o you WANT it to have 1.5m rows
o HOW DO YOU DO THAT

Rating: 5
How do they do it? October 15, 2002
Reviewer: Steve from UK
Reading some of the comments on here begs the question how some of these guys
manage to pass interviews and get jobs!!!  ;-))

Rating: 5
They do just fine, thank you. October 15, 2002
Reviewer: Randy from Austin
It's a "duh" to figure out how to get data into the database.  But every time I
read an answer to this particular question, the implication is that a direct
"insert ... select from ..." is the answer to every mass insert.  People tend to
think there must be a way to use it for external data and avoid sqlldr.

I find a nugget or two every time I visit this site, but I guess I have a lot to
learn.  Why do you even visit?  If you notice, Tom generally doesn't respond to
postings like yours.

Tom, the answer was great--just trying to dig a little.

Rating: 5
Re: How do they do it? October 15, 2002
Reviewer: CJ from Albany, NY
>Reading some of the comments on here begs the question
>how some of these guys
>manage to pass interviews and get jobs!!!  ;-))

Because oft times the interviewer knows less than the interviewee. In the past
10 interviews I've had, only 1 technically literate person interviewed me. The
rest had no clue about Oracle (and didn't want to have a clue).

Rating: 1
For Steve October 15, 2002
Reviewer: A reader
The way you get it.

Are you smart just by writing this.

Rating: 5
Squeezing out the last drops.... October 16, 2002
Reviewer: Connor from UK
I'm just being a prat here...

SQL> create table t tablespace users nologging as
  2  select rownum r from x$ksmmem
  3  where rownum < 100000;

Table created.

Elapsed: 00:00:00.50
SQL> create table t1 tablespace users nologging as
  2  select rownum r from x$ksmmem
  3  where rownum < 100000;

Table created.

Elapsed: 00:00:00.48
SQL> begin
  2    insert /*+ append */ into t1
  3    select t1.* from t1, ( select 1 from x$ksmmem where rownum < 16);
  4    commit;
  5  end;
  6  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:04.99
SQL> begin
  2  for i in 1 .. 4 loop
  3     insert /*+ append */ into t select * from t;
  4     commit;
  5  end loop;
  6  end;       <=== Don't forget this
  7  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:05.59

Don't worry Tom - I think you'd still have a pretty good chance at getting a job
here :-)

October 15, 2002 Om -- Thanks for the question regarding "Insert /+ append +/ statement", version 8.1.7
You Asked
Hi Tom,

It's really a pleasure to see you back.

I recently made use of Insert /+ append +/ hint to copy some
70,0000 records from the table in one database to a table
(empty table with same structure) into another database.

I found that this command is quite fast and it took me less then
30 minutes to transfer these records which I think is good
as compared to exp/imp or any other method.

Q1) Is there any drawback of using INSERT /+append + / Hint
statement ?

Q2) Can we consider this as similar to insert statement as
far as considering factors like triggers etc

Please comment.

Best regards,
Om
and we said...
q1) if you had a BIG table, deleted (not truncated) all of the rows, and then
insert append -- none of the free space that was already there would be reused.
append only goes ABOVE the hwm.  so appending will not reuse space on the
freelists, it'll always grab space above the HWM

q2) see
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c21dlins.htm#3321
or
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c21dlins.htm#365
there is a whole chapter on it!
Review & Followup
Rating: 5
Insert /+ append +/ statement October 16, 2002
Reviewer: Om from INDIA
Thanks very much for the feedback and the useful links.It's very handy and
useful.Aaagin a great tip as always from the site.

October 16, 2002 Dinesh -- Thanks for the question regarding "Question on Sqlldr ...", version 7.3
You Asked
Hi Tom,

LOAD DATA
INFILE *
REPLACE
INTO TABLE emp
when (empno != "2")
(
empno
,ename
)
BEGINDATA
7370 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
7698 BLAKE

Is it possible to use functions in the when clause like decode or some user
functions to do the validations check to load the data into the table?  If not,
are there any alternatives?

Thanks,
dinesh ...
and we said...
In 9iR1&2 -- yes, definitely.  We have external tables (the ability to query a
file like it was a table -- full where clause support -- 100%)

In 7.3 -- software that was written before Netscape was a browser -- no, sqlldr
is what it is.  You would have to load into a staging table and then use "insert
into select * from stage where ....."
Review & Followup
Rating: 4
That's a good one. October 15, 2002
Reviewer: Dinesh from St. Petersburg
Thanks.

Rating: 5
what are external tables October 15, 2002
Reviewer: A reader
tom,

 can you brief us on external tables of Oracle 9i release 2

is there an option to
trim blank spaces in the fields of a delimited file.

Followup:
create table external_table
(EMPNO NUMBER(4) ,
 ENAME VARCHAR2(10),
 JOB VARCHAR2(9),
 MGR NUMBER(4),
 HIREDATE DATE,
 SAL NUMBER(7, 2),
 COMM NUMBER(7, 2),
 DEPTNO NUMBER(2)
)
ORGANIZATION EXTERNAL
( type oracle_loader
  default directory data_dir
  access parameters
  ( fields terminated by ',' lrtrim )
  location ('emp.dat')
)
/

documentation available for free at:
http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96652/part3.htm#436392

October 16, 2002 Susan -- Thanks for the question regarding "processes, open_cursors, sessions", version 8i
You Asked
Hi Tom,

Could you explain, generally, processes, open_cursors and sessions. During
development, I'm often asked to do a database check on these items to see if the
db is choking on these parameters.  I think really that it's the open_cursors
that is the problem but I'm not sure.  Thanks.

Susan
and we said...
processes controls the maximum number of physical OS processes (or on NT/Netware
OS threads) we will create.

In a dedicated server mode (most common mode), there is a process per session
typically and you must count the background/slave processes (DBWR, LGWR, ARCH,
CKPT and so on).

A peek at v$process will tell you how many there are.

A session is a session -- for each session you'll find a row in V$session.  A
session will be mapped to a single process ( and a single process might have
more then one session ).

Open_cursors dictates how many concurrent open cursors a single session can
have.  The default of 50 is way too small.  I have mine set to 1000.
Review & Followup
Rating: 5
excellent explaination February 26, 2002
Reviewer: David from Irving, TX
I spend a quite bit of time searching Oracle books to find out how the processes
in the init.ora file related to your OS processes and your Oracle sessions. No
one care about talking about this.

Thanks Tom

Rating: 3
To set up parameter values October 16, 2002
Reviewer: Gururaj Kulkarni from Bangalore, Karnataka India
Tom,

What if I set up open_cursors value to 3000? Will it affect our OLTP application
?

We have D2K built OLTP application and DB 8.1.5 is running on Linux.

I am working on ideal parameter file for our product development environment. We
are having muliple instances running on same server for different
versions.Currently we can have only 4 instances up.We have dual processer with 2
GB RAM and 700 GB H/D server for developement activities. We have around 20
developers.

Now, my assignement is reducing un-neccessary parameter values and coming out
with ideal parameter file for all the instances, so that I can have at least
another instance added(This is the Objective of the exercise). Right now the
parameter file is almost with defualt values.

Could you tell me the parameters to consider for this exercise ? Will it be
helpful if we go for multi threaded server?

I appreciate your inputs.

Thanks
-Gururaj

Followup:
that'll allocate an array of 3000 "cursor pointers".

we'll allocate 64 cursor areas at a time as needed....

so having 3000 cursors is OK, we'll use them 64 at a time...

October 16, 2002 Doug -- Thanks for the question regarding "Securing the Database ", version 9.2.0
You Asked
Tom,
We are building a Web-based Java app and using the JDBC thin-client for DB
connections.  (The app will reside on a client machine and access the data on a
separate DB server.)  We need to secure that connection string as it contains
the Oracle User ID and password. In our development environment that User ID is
the creator/owner of all the Oracle objects. We are thinking that for production
we want to have the User ID and password in the JDBC database connect string to
be some generic ID that has only 'connect' priviledges and then the first thing
the app will do is 'alter session set current_schema = <prod>'.

So,
1. What are good techniques for securing that db connection string?

2. Is that approach of using a generic User ID and altering the session to set
current schema to <prod> seem reasonable? Better techniques?

Thanks for this valuable service...
and we said...
Ok, that is the worst of all possible cases.

Here I have zero, zippo -- no good solution for you at all.  Basically, you have
an account which NO ONE NEEDS TO PROVE THEY CAN USE.  So, why bother even trying
to hide the password?  Anyone can use it, there is no security, it is a public
account.

You do understand that "alter session set current_schema=<prod>" will not give
you any extra privs, this generic account will need access to all of the prod
stuff.

I would give serious reconsideration to your environment.  This environment by
defintion has NO security whatsoever.

You will have to install the password on each client.
You will never be able to really change this password (without updating every
client)
You will have no security, no audit trail, no nothing.

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

Is this JDBC app resident on

o a clients machine
o an app server without the database
o an app server that also hosts the database

Using the generic userid is something I personally despise -- but answer the
above and maybe I'll be able to give you a much better answer.
Review & Followup
Rating: 3
More info on the environment October 15, 2002
Reviewer: Doug Jensen from Salt Lake City, Utah
You've got my attention, Tom. More info on our environment: We need the
flexibility of deploying current client software to any number of client
machines. We are doing this with Java WebStart (at login it will check with App
Server to see if you have the latest version and download it if not current.)

The JDBC connect string will be encrypted in an XML file which the app would
decrypt at DB Connect time.

The 'generic' UserID will have only 'connect' and 'execute' authority on the
stored procedures in the <prod> schema (hence the '...set current schema...). If
someone hacked the generic User Id and password they couldn't directly access
any of the data..

Does that sound any more secure?

Followup:
NO.

(DO NOT use connect, look at all of the stuff CONNECT conveys!!! too many privs,
just CREATE SESSION is what you want)

Well, tell me -- why bother trying to hide the password at all?  If all they can
do is

o create session
o execute your procedures with validated inputs

why do you care if they know the password or NOT?

You do not need "set current schema" as the procedures will access what they
access regardless of who is running them.

I *hate* this common username password.

can you audit?  (no)
can you have different types of users with different privs? (no)

Why not actually have the people AUTHENTICATE???

Rating: 3
Your 2c... October 16, 2002
Reviewer: Kishan from Singapore
Hi Tom:

Thanks to you I understand how not to do such a thing. But could you give us
your thought on how to implement such a thing?

The trend I'm seeing here in my organisation, is to have a LDAP server
(non-oracle) to authenticate the application users, and a generic user to
connect to the database from a web-server. I'm not familiar with multi-tier
approach to application development. It would be helpful if you could direct me
to some generic document on multi-tier/web based database application
development.

Thanks for your time.

Regards,
Kishan.

Followup:
see
http://otn.oracle.com/docs/products/ias/doc_library/90200doc_otn/core.902/a90146/fundamen.htm#1006392

October 16, 2002 ds -- Thanks for the question regarding "deadlock detected", version 8.1.7
You Asked
i found ora-00060 deadlock detected error in alert log file .
what should i do as i m getting these errors very frequently which leads to
database hanging for some  time plz help me as i m a new dba in this company n
does not know much about the applicationn at this moment.
and we said...
deadlocks are most commonly related to unindexed foreign keys.

see
http://asktom.oracle.com/~tkyte/unindex/index.html
for a script that can help you there.

That is where I would start.  Ultimately, if it is not unindexed foreign keys --
you'll need to review the trace files, find out WHAT application is causing it
and correct the application code.
Review & Followup
Rating: 2
September 30, 2002
Reviewer: Preeti from INDIA
Tom,

We have checked the database stuff but could not find any discrepancies wrt to
Unindexed Foreign Keys. How should we proceed about seeing the application for
Deadlock detections ?

Feedbacks/valuable inputs required.

Followup:
review the trace files.

track down the applications running the sql you see in the trace files.

review their processing -- correct their logic.

Rating: 4
How to run as sys user on all schemas? October 16, 2002
Reviewer: A reader
As DBA, I wish to run all schemas.

Followup:
does not compute....

Rating: 4
How to run as sys user on all schemas? October 16, 2002
Reviewer: A reader
As DBA, I wish to run all schemas.

October 16, 2002 Riju -- Thanks for the question regarding "Oracle 9i Feature Explanation", version 9i
You Asked
Could you please provide an explanation of the new 9i feature: "Automatic
segment- space management". Also, when to use it and when to avoid it?

Thanks,

Riju Saini
and we said...
Ok, we'll have to hypothesize here a little and I won't be able to concretely
answer the last part.  That'll come with experience (and right now, I don't have
lots of empirical knowledge of this feature).

This does for free space management in a segment what locally managed
tablespaces did for dictionary managed tablespaces.

The goal of this feature is to remove the need to set PCT_USED, FREELISTS and
FREELIST GROUPS.  In fact, if you set them on the create table, they are
ignored.

The free space for a segment is managed in a bit map and managed in such a way
to permit highly concurrent access (like manually tuning freelists and freelist
groups would) and provide for efficient space usage (like pct used would).  It
is to do this without manual intervention (no tuning of pct_used, freelists or
freelist groups)
Review & Followup
Rating: 4
then what this means? October 16, 2002
Reviewer: Natalia from Pakistan
As Automatic Segment Space Management replace freelists effectively and
effeciently eleminates the use of pctused and etc. the why it is said that "for
automatic managed segments, formatted blocks and blocks below High Water Mark
are not always same?
Thanks

Followup:
because there could be blocks that were never used below the HWM with ASSM now.
It manages groups of blocks and they may or may not be formatted (used)

Rating: 5
thanks October 17, 2002
Reviewer: A reader
right. now i understand it
thanks

October 16, 2002 Kasthuri -- Thanks for the question regarding "SQL Loader 00306", version 8i
You Asked
Hi Tom,

 I have a amt column in my table which is NUMBER(11,2). The
input data contains a trailing negative sign and doesnt contain
decimal points. i have something like this in my control file.
But i get an error saying that the token is longer than
allowable 258 char. I dont know how i can further reduce it. Is
there any other way of doing it?
Please suggest.

Control file:

 contrib_amt     POSITION(21:35)         DECIMAL EXTERNAL
"decode
(substr(:contrib_amt,length(:contrib_amt)),'-',-to_number(substr(
:contrib_amt,1,length(:contrib_amt)-3)||'.'||substr(:contrib_amt,
length(:contrib_amt)-3,2)),to_number(
substr(:contrib_amt,1,length(:contrib_amt)-3)||'.'||substr(:contr
ib_amt,length(:contrib_amt)-3,2)))"

- I tried with Zoned also. it didnt work. gave me an 'invalid
zoned decimal nibble'

Thanks & Regards
J.Kasthuri
and we said...
The limit on a sql function is 258 bytes -- yours is 277 bytes.
Thats the error.  We just need a smaller function is all. Try
this:

LOAD DATA
INFILE *
REPLACE
INTO TABLE T
(contrib_amt position(21:35) char
"decode(rtrim(:contrib_amt,'-'),:contrib_amt,:contrib_amt,-1*rtri
m(:contrib_amt,'-'))"
)
BEGINDATA
                    1234-          xxx
                    1234          -xxx
                    1234           xxx

That function relies on the implicit conversion that'll take
place.  I didn't know if the - was in 35 or at the end of the
number so i tried both.  This works fine.

ops$tkyte@8i> select * from t;

CONTRIB_AMT
-----------
      -1234
      -1234
       1234
Review & Followup
Rating: 4
Token longer than max allowable length of 258 chars October 14, 2002
Reviewer: Ravi from Peoria, IL
Tom
When I run sqlldr I get following error.

SQL*Loader: Release 8.1.7.2.0 - Production on Mon Oct 14 13:24:40 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

SQL*Loader-350: Syntax error at line 27.
Token longer than max allowable length of 258 chars
TTL_MDFR :ttl_mdfr

Here is my ctl file

LOAD DATA
INFILE 'my.dat'
INTO TABLE tt
replace
FIELDS TERMINATED BY '\'
(MNO,
MREV,
TTL_MDFR,
RPL_BY)
begindata
AB47\01\LEGACY INFO NOT TO BE CONVERTED\SNR\
AC23\02\TO BE CONVERTED\\

How can I avoid this error?  Should I be using trim function in the ctl file
itself.
Something like this
load data
infile 'my.data'
.. ..
.. ..
TTL_MDFR trim(:ttl_mdfr),
.. ..
.. ..

I am not sure whether this is correct syntax or not.

Thanks for all your help.
Ravi

PS:  Just tried with trim inside the ctl file, still gave me the same error.
Please help.
Thanks

Followup:
infile *

not infile 'filename'. It was not thinking it would find data in this file -- it
was reading the DATA as if it were CONTROL FILE stuff.

Rating: 4
Problem was '\' October 15, 2002
Reviewer: Ravi from Peoria, IL
Tom
The problem I was getting "Token longer than max allowable length of 258 chars"
was because (I guess) sqlldr is unable to interpret '\',  when I added one more
\ in the control file, everthing went ok.
Now the control file looks like this

load data
infile 'mydata.dat'
replace
into tt
fields terminated by '\\'
....
....
...

Thanks for your help.

Rating: 5
triming the fields October 16, 2002
Reviewer: A reader
in the data file that is going be loaded to our table has all the fields white
spaced. how do we trim them

this is control file we are using

LOAD data

INFILE 'H:\PS_EM_PERSONAL.DAT '

TRUNCATE

INTO TABLE PS_EM_PERSONAL

FIELDS TERMINATED BY '|' trailing nullcols
(

EMPLID                         CHAR,
ADDRESS1                       CHAR,
ADDRESS2                       CHAR,
ADDRESS3                       CHAR,
CITY                           CHAR,
STATE                          CHAR,
ZIP                            CHAR,
COUNTRY                        CHAR,
PHONE                          CHAR,
BIRTHDATE                      DATE "MM-DD-YYYY HH24:MI:SS",
MAR_STATUS                     CHAR
)

Followup:
read
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76955/ch05.htm#5817

October 16, 2002 Robert -- Thanks for the question regarding "CREATE ANY CONTEXT sys. priviledge --- "Big Deal" ?", version 8.1.7
You Asked
Tom,

I really want to use SYS_CONTEXT in NDS and Parameterized View like you showed
all over.

We developers here have quite extensive priviledges
(we share one account) but CREATE ANY CONTEXT is NOT one.
I think this is due to ignorance.

My question is:
1> Is CREATE ANY CONTEXT  a big deal of a priviledge (for developer) to have ?

I want your input before I approach db-manager/dba and ask for something I doubt
ANYONE EVER asked for.
2> Is there anything about CONTEXT that'll make them nervous?

And they are surely going to ask me like :
"what's this for ?"
"why do you need it to do your work ? no one ever asked for it..."
3> How would you response to questions like those ?

Thanks !
and we said...
1) Well, if you use contexts for security (fine grained access control)
http://asktom.oracle.com/~tkyte/article2/index.html
a person with "create any context" could "kidnap" a context used in a security
policy and set the context values to any value they wanted -- allowing them
access to data they should not otherwise see.

See, if developer A creates context "foo" and "foo" is assigned to
"security_procedure" -- you as developer B could "create or replace foo using
trojan_horse" -- you would be able to set the context foo now (and they would
not)

You might ask the DBA to create the context FOR YOU (much like they would create
a public synonym)

2) see #1, other then that -- I do not forsee any other "bad" issues.

To answer the questions, print out my cursor example and the paper on fine
grained access control.

3) by answering it?  ;)
October 16, 2002 Pushparaj -- Thanks for the question regarding "Invoker's Rights", version 8.1.7
You Asked
Tom,

Our application currently has two set of users namely
domestic users and international users.

Both of these users share many common tables but few tables
very specific to them.

So we created three schemas in the database. TABSCH, DOMSCH, INTSCH.

The TABSCH schema has all the tables that are commonly referred by
both set of users. PUBLIC synonyms are created for these tables
and required privileges are granted to DOMSCH and INTSCH.

The DOMSCH has 10 local tables that are only specific to DOMSCH schema.
The INTSCH has 10 local tables that are only specific to INTSCH schema.

The structure and the name of these 10 tables are exactly the same
and the data is the only difference.

In order to bind the stored PL/SQL programs to their specific local
tables we compile the same set of programs in each of these two
schemas DOMSCH and INTSCH seperately.

The stored PL/SQL programs are exactly the same for both of these
schema.

The international users connect to INTSCH schema and executes the stored
procedure and the domestic users connect to DOMSCH schema
and executes the stored procedure.

USING invoker's-rights feature:
-------------------------------
Because the same exact programs and same exact tables are referenced, Instead of
compiling the programs seperately in each schema, we
thought of using the invoker's rights feature, so that we only
compile the programs in only one schema DOMSCH and give execute privilege to the
other schema INTSCH. The other schema INTSCH will create private synomyms for
all these programs.

CREATE OR REPLACE procedure regdat (...)
AUTHID CURRENT_USER AS
BEGIN
...
END;
/

Since the procedure executes with the privileges of its current user
and binding to the local table happens at the run time, is there any
performance degradation using this feature.

Also please let me know if I over looked anything using this feature for this
situation.

Thanks
Pushparaj
and we said...
Here is how I would do this (and it would not involve INVOKERS rights at all --
i use that for utilities only, like "print a table", "dump a query to a flat
file" type stuff)

I would use ONE schema.
I would use NO public synonyms.
All tables would be in one schema.
The 10 special tables would use either

   - a view that lets domestic see domestic data, international see
international
   - VPD (more flexible over time)
http://asktom.oracle.com/~tkyte/article2/index.html

If you have my book "Expert one on one Oracle" -- you'll know what I would frown
on invokers rights (you'll flood the shared pool with lots of extra queries you
don't need to) and why VPD (fine grained access control) is a good way to
implement this.
October 17, 2002 Muthukumaran -- Thanks for the question regarding "Usage of Role Password", version 817
You Asked
How can i utilize password option in "create role" statement(create role
<role_name> identified by <passwd>/globally/externally;)

NIRMAL@ORANS.QTEL.COM.QA> create role nirm identified by kumar;
Role created.

NIRMAL@ORANS.QTEL.COM.QA> grant all on demo to nirm;
Grant succeeded.

NIRMAL@ORANS.QTEL.COM.QA> grant nirm to scott;
Grant succeeded.

NIRMAL@ORANS.QTEL.COM.QA>@c scott/tiger
Connected.

SCOTT@ORANS.QTEL.COM.QA> select * from nirmal.demo;

       ID THECLOB
--------- -------------------------------------------------------------------
        1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
 <html>          <head>
        2 -------------------------------------------------------------------

SCOTT@ORANS.QTEL.COM.QA>

Here user scott using the role by selecting the demo table, However in which
place the roles's password is used?...

Can you please explain.

Rgds,
Nirmal.
and we said...
you made it a default role of scotts.  make it a non-default role, he'll have to
provide the password then

ops$tkyte@ORA8I.WORLD> create table t  ( x int );
Table created.

ops$tkyte@ORA8I.WORLD> create role toms_role identified by some_pw;
Role created.

ops$tkyte@ORA8I.WORLD> grant select on t to toms_role;
Grant succeeded.

ops$tkyte@ORA8I.WORLD> grant toms_role to scott;
Grant succeeded.

ops$tkyte@ORA8I.WORLD> alter user scott default role all except toms_role;
User altered.

ops$tkyte@ORA8I.WORLD> connect scott/tiger

scott@ORA8I.WORLD> desc ops$tkyte.t
ERROR:
ORA-04043: object ops$tkyte.t does not exist

scott@ORA8I.WORLD> set role toms_role identified by some_pw;
Role set.

scott@ORA8I.WORLD> desc ops$tkyte.t
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 X                                            NUMBER(38)
Review & Followup
Rating: 5
default role July 12, 2002
Reviewer: O user from LA
hi tom
why everytime I grant a role to a user it becomes a default role for that users
?
how many default roles can a user have?

SQL> create user a identified by a;

User created.

SQL> create role ra identified by a;

Role created.

SQL> grant ra to a;

Grant succeeded.

SQL> ed
Wrote file afiedt.buf

  1* select * from dba_role_privs where grantee = 'A'
SQL> /

GRANTEE                        GRANTED_ROLE                   ADM DEF
------------------------------ ------------------------------ --- ---
A                              RA                             NO  YES

Followup:
because the default behavior is to make it a default role.

you can use the ALTER USER command to change that, to make it non-default (be
default, a role is a default role).

You control the max number of default roles via the init.ora parameter
max_enabled_roles.  You cannot have more then that many default roles.

Rating: 5
dba_role_privs default_role October 16, 2002
Reviewer: sm from usa
Tom,
I am moving a database from WINDOWS to UNIX...
I built a test database first...no problems..
repeating same steps for PROD database now...
I did:
users,
roles..
grants...
structure..
data..

Strangely enough I saw many users that were granted roles with DEFAULT_ROLE as
'NO'..
As a result...although a user was granted a role..and that role had object
privilege been granted..
but when I logged in as that user...it will say that I don't have some of the
object privileges (privileges that were granted to me through a role that had
default_role as 'YES'...that's when I started looking at DBA_ROLE_PRIVS..
Question:
1. Why didn't it do some roles as default_roles
2. is the error I was getting (that I don't have updates on a table) was because
of this....

Thanks..

Followup:
I don't know, you didn't give me a reproducible example with which to see what
you actually executed.

Rating: 5
alter user scott default role all October 16, 2002
Reviewer: sm from usa
doing alter user scott default role all..
took care of the issue...
but I am still interested in your answer..
and if you can shed some light on DEFAULT_ROLE...

Thanks

Rating: 3
October 17, 2002
Reviewer: Sagi from India
Hi Tom,

Can we set and unset roles in the procedures instead of set command at SQL
prompt.

Something like below

CREATE OR REPLACE PROCEDURE
AS
<Now Set the ROLE toms_role>
. . . . . . . . . .
do some job here
. . . . . . . . . .
<Unset the ROLE toms_role>
END ;

Regards,
Sagi

Followup:
You do understand that

a) roles not active in a definer rights (normal) procedure
b) procedures run with the privileges of the definer (normally)

so, the above doesn't really "make sense".  When using an invoker rights routine
(rare, used for utilities like "print_table", "dump_csv", "show_space" -- and
others I have here), it will run with the privs AND roles of the invoker (person
running it).  If you have my book "Expert one on one Oracle" -- read the chapter
on this topic (invoker vs definer rights).

Consider the differences below:

ops$tkyte@ORA920.US.ORACLE.COM> create or replace procedure dr_demo( p_role in
varchar2 default null )
  2  as
  3  begin
  4          if ( p_role is not null )
  5          then
  6                  dbms_session.set_role( p_role );
  7          end if;
  8
  9          dbms_output.put_line( '---------------' );
 10          for x in ( select * from session_roles )
 11          loop
 12                  dbms_output.put_line( x.role );
 13          end loop;
 14          dbms_output.put_line( '---------------' );
 15  end;
 16  /

Procedure created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create or replace procedure ir_demo( p_role in
varchar2 default null )
  2  authid current_user
  3  as
  4  begin
  5          if ( p_role is not null )
  6          then
  7                  dbms_session.set_role( p_role );
  8          end if;
  9
 10          dbms_output.put_line( '---------------' );
 11          for x in ( select * from session_roles )
 12          loop
 13                  dbms_output.put_line( x.role );
 14          end loop;
 15          dbms_output.put_line( '---------------' );
 16  end;
 17  /

Procedure created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> exec ir_demo
---------------
CONNECT
DBA
SELECT_CATALOG_ROLE
HS_ADMIN_ROLE
EXECUTE_CATALOG_ROLE
DELETE_CATALOG_ROLE
EXP_FULL_DATABASE
IMP_FULL_DATABASE
GATHER_SYSTEM_STATISTICS
WM_ADMIN_ROLE
JAVA_ADMIN
JAVA_DEPLOY
XDBADMIN
OLAP_DBA
---------------

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> exec dr_demo
---------------
---------------

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> set role none;

Role set.

ops$tkyte@ORA920.US.ORACLE.COM> exec ir_demo
---------------
---------------

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> exec ir_demo('DBA')
---------------
DBA
SELECT_CATALOG_ROLE
HS_ADMIN_ROLE
EXECUTE_CATALOG_ROLE
DELETE_CATALOG_ROLE
EXP_FULL_DATABASE
IMP_FULL_DATABASE
GATHER_SYSTEM_STATISTICS
WM_ADMIN_ROLE
JAVA_ADMIN
JAVA_DEPLOY
XDBADMIN
OLAP_DBA
---------------

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> exec ir_demo('ALL')
---------------
CONNECT
DBA
SELECT_CATALOG_ROLE
HS_ADMIN_ROLE
EXECUTE_CATALOG_ROLE
DELETE_CATALOG_ROLE
EXP_FULL_DATABASE
IMP_FULL_DATABASE
GATHER_SYSTEM_STATISTICS
WM_ADMIN_ROLE
JAVA_ADMIN
JAVA_DEPLOY
XDBADMIN
OLAP_DBA
---------------

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> exec dr_demo
---------------
---------------

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>

but before you run off and just do everything with invokers rights which would
be a hugely bad mistake, make sure you understand the ramifications.  If you
want it spelled out -- I'll have to refer you to the book at that point -- tons
of examples, lots of hours spent putting it together.

Rating: 4
October 17, 2002
Reviewer: Sagi from India
Thanx Tom.

Kind Regards,
Sagi

October 17, 2002 Arun -- Thanks for the question regarding "Statspack report: which sqls to tune ?", version 8.1.7.3.0
You Asked
Tom
From the statspack report, which sqls should I focus on tuning, the one with
highest buffer gets over a period of 8 hours or the ones with very high buffer
gets per execution ? My thought is that though a query might be having 30-40
buffers gets/execution, since it is called 100,000 times, the buffer gets would
be high, whereas a query which does 300 buffer gets/execution might be doing
something worth looking into.

Is it alright to run statspack for 9 hours a day on a production database ?

Thanks
Arun
and we said...
I would personally look at two things:

a) queries that are executed 100,000 times -- maybe they shouldn't be.  why are
they.  verify that this is the best you can do (maybe you are doing a lookup in
a loop and we could move the lookup outside the loop or otherwise change the
algorithm)

b) queries with HIGH buffer gets.  These could stand some tuning perhaps to get
the answer with less gets.

I find a snapshot interval of 1/4 to 1/2 hour is best.  A snapshot over a 9 hour
period isn't granular enough.  What averages out "fine" over 9hours might not be
so good looking if you discover 99% of it happened in a 1 hour window in the 9
hours!  Find the times of the days when you want to measure (eg: peak times).
Say you determine that 9am, 1pm, 4pm are your "peak times" (morning login, right
after lunch break, right before you go home).  Take a snapshot at 8:50, 9:10,
12:50, 13:10, 15:50 and 16:10.  Those 20 minute windows are what you want to
look at then.
Review & Followup
Rating: 5
Sampling frequency October 17, 2002
Reviewer: Mohsin Jessa from Canada
Hi Tom,
I have always performed sampling frequency at a max of 5 mins and sometimes more
frequently (i.e snapshots every 5min interval) over the course of the day or
time interval of several hours.

My theory is that if there is a performance issue in an OLTP system then it will
manifest itself in a poor response time at the user level and therefore with
such granular data collection I should be able to pin point the culprit.

However, my question is: What is the overhead of collecting each snapshot ? Is
it something I should be concerned with besides the growth of the tables to
store this info.

Thanks for your response.

Followup:
It is a process that takes a couple of seconds to run (introduces additional
processing).

It heavily queries v$ tables which are mostly memory structures and to do so,
might "latch" out other processes for a bit of time.

It puts lots of data into tables, incurring storage and processing overhead.

October 17, 2002 Arun -- Thanks for the question regarding "Tuning environment", version 8.1.7.3.0
You Asked
Hi Tom,
I want to setup an environment to tune the queries and experiment with other
database changes before moving changes to production. How close should my test
environment be to the production in terms of Oracle initialization parameters,
user load, data volume or any other parameter that I need to consider.
Thanks very much.
Arun
and we said...
100% would be best.  As close as you can get it.

Tuning in a dust free laboratory environment never works.  Tune in the dirt.
Review & Followup
Rating: 3
If data volume is a problem... October 17, 2002
Reviewer: Tim from Harrogate,North Yorkshire
If you can't scale your environment up to a size representative of typical use,
don't forget to look at using DBMS_STATS to import real world stats, and the
stored outlines feature . This will afford you a degree of stability in terms of
execution plans between environments.

Followup:
I've always questioned the usefulness of this.

I mean -- yeah, so I have a plan that would be generated against that 1 million
row table and it works great on my 1,000 row sample -- does that mean it'll work
great against the 1,000,000 row table????? No (you really do need to test
against scale, just having the stats doesn't mean the query will perform in the
real world.  In fact -- the premise has always seemed slightly backwards to me.
A query that performs well against small data won't be the query I want against
large and vice versa.  So, as I tune the query to work well against SMALL *but
I've tricked the optimizer into thinking its big*, will it work good against
BIG?)

Rating: 4
Tuning info is always good! October 17, 2002
Reviewer: Robert from Memphis, USA

October 17, 2002 gs -- Thanks for the question regarding " Public DB link or not?", version 8.1.7.4
You Asked
Tom,

Question: 1
------------
I will have few instances running on different servers. One of the server (say,
MSTR server) will have some tables which will be accessed (only select) by all
other srvers once in a while.

The tables with same structure and name is there in all the other servers also.
And all the other servers may have more than one user (schema) with the same
tables.

I am thinking of creating a user on the MSTR server with a schemaname say
RDONLY, who can only select from the tables. (the tables are owned by another
schema)

Now, I will create public DB Link on all the servers with fixed user RDONLY
pointing to the MSTR schema.

Is there any better way to do this? The current implementation has some
limitation as anyone logged in to one of the N servers can select from
RDONLY@MSTR.

Another somewhat related question: Is there anyway, I can pass the userid and/or
password to the DB link?

Question: 2
-----------
Soon I will be having more servers and will have one instance per server.
Currenly all my instances are called the same name.

Is there any advantage of having different name to different instances? What are
all the utilities/products (such as OEM) mandates that the instance/dbname has
to be different on each server?

Note: Each of my instances will always be on differnt servers.

Thanks.
and we said...
1) the RDONLY is good -- but you can use a private db link and views to restrict
WHO can access it from the remote sites, consider:

b@ORA815DEV.FOO> @connect /

ops$tkyte@ORA815DEV.FOO> drop user a cascade;
User dropped.

ops$tkyte@ORA815DEV.FOO> drop user b cascade;
User dropped.

ops$tkyte@ORA815DEV.FOO> grant create session to a identified by a;
Grant succeeded.

ops$tkyte@ORA815DEV.FOO> grant create session to b identified by b;
Grant succeeded.

ops$tkyte@ORA815DEV.FOO> @connect scott/tiger

scott@ORA815DEV.FOO> drop database link ORA817DEV.US.ORACLE.COM;
Database link dropped.

scott@ORA815DEV.FOO> create database link ORA817DEV.US.ORACLE.COM
  2  connect to scott
  3  identified by tiger
  4  using 'ORA817DEV.US.ORACLE.COM';

Database link created.

scott@ORA815DEV.FOO> select * from dual@ORA817DEV.US.ORACLE.COM;

D
-
X

scott@ORA815DEV.FOO> create view remote_table
  2  as
  3  select * from emp@ORA817DEV.US.ORACLE.COM
  4  /
View Created.

scott@ORA815DEV.FOO> grant select on remote_table to a
  2  /
Grant succeeded.

scott@ORA815DEV.FOO> @connect a/a

a@ORA815DEV.FOO> select ename, empno from scott.remote_table where rownum = 1;

ENAME           EMPNO
---------- ----------
SMITH            7369

a@ORA815DEV.FOO> @connect b/b

b@ORA815DEV.FOO> select ename, empno from scott.remote_table where rownum = 1;
select ename, empno from scott.remote_table where rownum = 1
                               *
ERROR at line 1:
ORA-00942: table or view does not exist

so, using that technique, you can "reduce the exposure" to the RDONLY account
easily

To "pass" a username/password to a dblink -- you use the technique above (i like
views much better then synonyms to hide the dblink too -- imports the meta data
locally, easier all around -- better security choices)

2) global_name should be unique (easy to alter database).  they can have all of
the same sid and oracle home.  dbname is OK to be the same as well.
Review & Followup
Rating: 4
I thought about it. But the issues are October 16, 2002
Reviewer: gs
I have my application do "set current_schema=".

Say, I have application user as appl_user1, appl_user2 etc
     The corresponding db schema are db_user1, db_user2 etc

db_userX has the database objects and appl_userX has the select, insert etc
privileges.

So, if appl_user1 set the current_schema to db_user1, it couldn't see the
DB_LINK owned by the db_user1. The other was is to create the db_link probably
under appl_userX. So, in this case what could be the preferred way?

The issue with views are, I have the same table on both side with the same name.

Regarding Question 2, I was thinking of having different global names for each
instances. Then I was not sure whether the instance/db names also need to be
unique. But your answer clarifies that very well. I didn't have two instances
installed to test that.

Followup:
so, name the view something DIFFERENT then.  Use the view.

Rating: 3
again October 16, 2002
Reviewer: gs
BTW, is there anyway to access the objects thru the db_link defined on another
schema (not public)? I guess this is not possible! (not by creating a procedure
in the other schema and acessing)

Thanks!

Followup:
sure you can -- just like you would on a single database.  You "grant select" on
the object and you have access to it.

Rating: 5
October 17, 2002
Reviewer: A reader
Hi Tom,

I have two test databases on two servers.One is 9.0.1. and the other database is
8.1.7. The name of both the database is test and the domain name is oradom.com.
I want to create a link from 9i to 8i. but when i try to create link it gives me
this error..

system@TEST.ORADOM.COM>  create database link test.oradom.COM
  2                        connect to scott
  3              identified by tiger
  4             using 'test.oradom.COM';
                      connect to scott

ERROR at line 2:
ORA-02082: a loopback database link must have a connection qualifier

Followup:
if they both have the same name.... you are creating a loopback link -- this
database connects back to itself.

Two pieces of advice

a) first and foremost, DON'T USE SYSTEM, thats our account, don't use it, don't
use sys, not even for testing.

b) use different names for them.  test8i, test9i perhaps.  change their global
names (alter database command)

Rating: 3
October 17, 2002
Reviewer: A reader
Hi,

That is because you are using the DB_LINK name and the connect string which are
same.

re-write it as

create database link test8i                           connect to scott
identified by tiger
using 'test.oradom.COM';

Am I correct Tom?

Regards,
Sagi.

Rating: 3
Sagi October 17, 2002
Reviewer: Sagi from India
Hi !

I forgot one more thing i.e. regarding global_names.

From the docs

"The name that you give to a link on the local database depends on whether the
remote database that you want to access enforces global naming. If the remote
database enforces global naming, then you must use the remote database's global
database name as the name of the link. For example, if you are connected to the
local hq server and want to create a link to the remote mfg database, and mfg
enforces global naming, then you must use mfg's global database name as the link
name."

Also please be aware that you can change the global_name using:

ALTER DATABASE RENAME old_global_name TO new_global_name ;

Tom please comment if i am wrong.

Regards,
Sagi

Rating: 4
October 17, 2002
Reviewer: A reader
thanks sagi for your answer...was using the wrong service name..got it now...

Rating: 3
October 17, 2002
Reviewer: A reader
Tom,

If the database enforces global names and both databases on different servers
have the same name, then how will one create the db link...

Rick.

Followup:
then you CANNOT BY DEFINITION be using global names!!!

global names is a method whereby we enforce uniqueness of database global names
in a collection of distributed databases.  they cannot have the same name by its
very purpose.

October 17, 2002 Nag -- Thanks for the question regarding "Client side SQL & Server side SQL", version Forms 6i
You Asked
Tom

Depending on a certain condition , I have to update a column for table t with a
constant, either 'T', OR 'F' dependingon some conditions. The update is on all
the rows. The number of rows in T is 200,000.

Now I have 3 routes which I can take.

1.Loop through the records of my oracle forms block(which is based on T) and
update the concerned column to 'T' OR 'F' depending on user action.

2.Write an update sql on the client side.

3.Write a procedure on the server side having the same sql as in 2.

I.Which one of the above is the best route and faste and why?
II.In case of 1 when so many rows have to be read, will forms create temporary
data files in the temp directory( it happened in my case), to hold the rows.

III.I want to display the number of rows updated to the user, by the update
statement, how can I do it in forms.

Thanks

Nag
and we said...
1) that would stink, totally.

2) that would work

3) just as good as #2

#2 and #3 are pretty darn much equivalent here -- the update utlimately is
ALWAYS server side.

#1 just rots, what, you want to bring 200k rows back to a client
(waaaaiiiitttting for that to happen) and then do 200k round trips back and
forth and back and forth.

#1 should not even be considered by any reasonable person.  Why in the world
would you want to write a ton of procedural code when a single, simple, easy to
code UPDATE would do all of the work for you?????  I've never understood that.

sql%rowcount after the update holds the number of rows affected.
Review & Followup
Rating: 5
how to October 17, 2002
Reviewer: Nag
How can we return the sql%rowcount back to the client after the update is
complete if I'm using #2 and #3 methods.

Followup:
You are in forms man -- just do it:

begin
   update t set x = y;
   Message( sql%rowcount || ' rows were updated!!!' );
end;

put that in a when button pressed trigger or something and just do it.

If you put it in a stored procedure -- have an OUT parameter that is the rows
affected.

October 17, 2002 'Abdur-Razzaaq -- Thanks for the question regarding "Manipulating Sort Area Size", version 8.1.6
You Asked
Hi Tom,

The value specified by SORT_AREA_SIZE is assigned on a per user basis.
Therefore, if it is set to 1M, and 25 users connect to the database, each user's
Server Process will consume 1M (25M in total).

The questions are:

1)  The memory consumed for the sort, is it within the SGA memory or the Server
memory (outside SGA)?

2)  What will be the case if 25 physical users (different sessions) are
connected using the same database user?

3)  Let's assume, SORT_AREA_SIZE is set to 1M and temporary tablespace is
locally managed with uniform size of 1M.  If SORT_AREA_RETAINED_SIZE is set to a
higher value other than 1M, what will be the impact on performance?

Thanks,
'Abdur-Razzaaq
and we said...
Well, your basic assumption is wrong.

sort_area_size is an upper bound on the memory we dynamically allocate for a
sort.

A session may have more then one sort going at the same time (more then one sort
area size)

sort_area_sizes are not allocated until they are needed (and GROW to be
sort_area_size bytes in size -- they don't start there) and are freed the second
the sort is over.

So, if it is 1m and there are 25 users connected to the database you will have
somewhere between 0 and many megs of sort space at any point in time (if all 25
do a single sort AT THE SAME exact instant -- you'll have 25m.  If they are not
sorting, you'll have 0 meg)

1) sort_area_size space is allocated in the PGA, outside of the SGA (retained
can be in the UGA with MTS)

2) it is done by the SESSION not by name and a single session can have more then
one going at any time

3) SORT_AREA_RETAINED_SIZE is bounded by sort_area_size, hence this hypothetical
situation won't happen.

See
http://www.ixora.com.au/newsletter/2000_11.htm
for a good discussion on this.

followup to comment one

It is definitely an inaccurate statement on their part (eg: its *wrong* plain
and simple)

See
http://www.sybex.com/erratatracking.nsf/weberrataform?OpenForm&ISBN=2684
there is an errata sheet for this book -- you might want to drop sybex a line
and have them add this to the sheet.

followup to comment two

Nope -- MTS won't affect this, sort_area_size is a per process thingy -- its in
the shared_server under MTS (so using MTS the max sort area memory would be a
function of the number of shared servers you have). The sort area retained comes
from the UGA in MTS which is in the SGA -- and sort area spaces are always freed
after the sort (sort area retained is freed right after the last row is
processed)

sort areas are transient, they come and go -- they are not always there.
Review & Followup
Rating: 4
Reason for wrong basic assumption August 15, 2001
Reviewer: 'Abdur-Razzaaq from Singapore
Tom,
In the book "OCP Oracle8i DBA Performance Tuning and Network Administration
Study Guide" by Joseph C. Johnson and Matthew Weishan from Sybex publishing, it
is quoted on page 263 as follows:
"... The value specified by SORT_AREA_SIZE is assigned on a per user basis.
Therefore, if SORT_AREA_SIZE is set to 1MB, and 20 users connect to the
database, each user's Server Process will consume 1MB of server memory, 20MB
total, whether they are actively sorting or not."  Is this a documentation bug?

Rating: 3
August 16, 2001
Reviewer: AB from London
I think the OCP book might be referring to MTS. It was my understanding that MTS
allocated all SORT_AREA_SIZE to a user at sign-on, whereas non-MTS provides the
SORT_AREA_SIZE as and when needed by a session up to the limit specified.

Rating: 5
Here is what the author of the book has to say August 16, 2001
Reviewer: 'Abdur-Razzaaq from Singapore
Tom Kyte is correct. The amount of memory specified by the
SORT_AREA_SIZE parameter is not allocated to a user's process until they perform
their first sort. This was an erroneous statement and I thank you for bringing
it to my attention.

However, they does seem to be some conflicting information regarding whether the
space is released *after* the user's sort is complete. I think this was the
cause of my own
confusion on this issue. Specifically, page 2-16 of the "Oracle8i Designing and
Tuning for Performance" reference manual states:

"...the user had set SORT_AREA_SIZE to 10MB on a machine with 64 MB of memory
serving 20 users. When users logged on to the system, the first time they
executed a sort, their
sessions were assigned to a sort area. Each session held the sort area for the
duration of the session."

Additionally, page 19-42 of that same manual also states:

"Large sort areas can be used effectively if you combine a large SORT_AREA_SIZE
with a minimal SORT_AREA_RETAINED_SIZE. If memory is not released until the
user disconnects from the database, then large sort work areas could cause
problems."

However, the Oracle8i Reference on page  1-112 seems to contradict the "sort
area is not released until the user disconnects" idea illustrated above by
stating:

"After the sort is complete, but before the rows are returned, Oracle releases
memory down to the size specified by the SORT_AREA_RETAINED_SIZE parameter.
After the last
row is returned, Oracle releases the remainder of the memory."

After further testing and research it appears that the last statement is
correct. Therefore, the corrected statement on page 263 on the Sybex review
guide, section "Make it Bigger", starting at the second sentence should be:

"Care must be taken when increasing this parameter, however. While the amount of
memory specified by SORT_AREA_SIZE is not allocated to a user's session until
they initiate a sort, the server's available memory must be sufficient to
accommodate the user's sort area while the sort is being processed. In
particular, if SORT_AREA_SIZE is set to a large value and many users are sorting
simultaneously, the demands placed on the server's memory may adversely impact
performance until the memory allocated to those sort areas is released when the
sorts are
completed."

Cordially,
Joe Johnson, OCP

Rating: 4
July 18, 2002
Reviewer: A reader
Can you show us some examples.  Used the script provided at
http://www.ixora.com.au/scripts/io_opt.htm#disk_sorts
to monitor disk sorts.

Steps.

1.  Restarted database with Sort_area_size = 64k.

connect as my application user in SQL*Plus and ran a query three times.

checked disk sorts with above script

DISK_SORTS AVERAGE_SIZE PEAK_CONCURRENT
---------- ------------ ---------------
         3          70K               1

did an alter session and set sort_area_size = 1M for my application user and ran
the query 3 more times

checked disk sorts with above script

DISK_SORTS AVERAGE_SIZE PEAK_CONCURRENT
---------- ------------ ---------------
         6          68K               1

I would think that if the user alters his sort area to 1M it would not have a
disk sort.  I tried with 10M sort with the same results.

Can you show an example of this using the alter session?

Thanks

Followup:
not knowing what you queried with -- not having your script from end to end -- I
cannot really comment.  I don't know what you are looking for exactly and how
you were testing.

Rating: 4
July 19, 2002
Reviewer: A reader
Ok,  Let me try to explain a little better what I am asking.
Database 8.1.7.3
DB_Block = 8192
sort_area_size = 65500
sort_area_retained_size = 65500

I spent some more time making up a better test script.

---------------------------------
create table t as (select 'SORTING OF LENGTH 20' TEST from all_objects);

DESC T;

show parameter sort_area

alter session set sort_area_size = 65500;

set autotrace on stat

select count(1) from (select TEST from t
where  rownum < 1331 order by 1);

select count(1) from (select TEST from t
where  rownum < 1332 order by 1);

alter session set sort_area_size = 1024000;

select count(1) from (select TEST from t
where  rownum < 1777 order by 1);

select count(1) from (select TEST from t
where  rownum < 1778 order by 1);

drop table t;

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

I used this query in sql*plus to find the break point

select count(1) from (select TEST from t
where  rownum < &rows order by 1);

Ok, I created a table of char(20) with fixed data.  I set my sort_area_size to
65500.  The most rows I can select and still do a memory sort is 1331.  1332
does a disk sort.

I then set my sort_area_size to 1024000 which is about 15 times larger that
before.  But the max number of rows I can sort in memory is 1777.  1778 does a
disk sort.

I also looked at the sort_area_size and increasing it above 73800 returned same
results and did not allow me to sort more that 1777 rows in memory.

So my questions would be?

1.  Given a fixed about of data, What goes into the sort_area?
2.  If you double your sort_area, shouldn't the amount of data you can sort
double or is there an upper limit of how much sort area you can use?
3.  Can you so an example showing how changing the sort area directly/indirectly
affects how much data will be sorted in memory

Thanks

Followup:
My question to you is -- well, not really a question, rather statement of fact
on my part - I cannot reproduce your findings.  Oh -- I can reproduce your
finding of 1331 to 1332 just fine, but not eh 1700 example.  In fact, I went to
10k rows and didn't do a disk sort:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t as (select 'SORTING OF LENGTH
20' TEST from all_objects);

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> DESC T;
 Name                                                                    Null?
 Type
 -----------------------------------------------------------------------
-------- -------------------------------------------------
 TEST
 CHAR(20)

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> show parameter sort_area

NAME                                 TYPE    VALUE
------------------------------------ ------- ------------------------------
sort_area_retained_size              integer 0
sort_area_size                       integer 1024000
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set sort_area_size = 65500;

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace on stat
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(1) from (select TEST from t
  2  where  rownum < 1331 order by 1);

  COUNT(1)
----------
      1330

Statistics
----------------------------------------------------------
          0  recursive calls
         12  db block gets
          9  consistent gets
         63  physical reads
          0  redo size
        368  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          1  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(1) from (select TEST from t
  2  where  rownum < 1332 order by 1);

  COUNT(1)
----------
      1331

Statistics
----------------------------------------------------------
          0  recursive calls
         14  db block gets
          9  consistent gets
          5  physical reads
          0  redo size
        368  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          1  sorts (disk)
          1  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set sort_area_size = 1024000;

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(1) from (select TEST from t
  2  where  rownum < 1777 order by 1);

  COUNT(1)
----------
      1776

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

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(1) from (select TEST from t
  2  where  rownum < 1778 order by 1);

  COUNT(1)
----------
      1777

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

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(1) from (select TEST from t
  2  where  rownum < 10000 order by 1);

  COUNT(1)
----------
      9999

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

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle8i Enterprise Edition Release 8.1.7.3.0 - Production
PL/SQL Release 8.1.7.3.0 - Production
CORE    8.1.7.0.0       Production
TNS for Solaris: Version 8.1.7.3.0 - Production
NLSRTL Version 3.4.1.0.0 - Productio

lets see a cut and paste just like that.

Rating: 4
July 19, 2002
Reviewer: A reader
Here you go.  What else would affect this?
-------------------------------

SQL> show parameter sort_area

NAME                                 TYPE    VALUE
------------------------------------ ------- ------------------------------
sort_area_retained_size              integer 65536
sort_area_size                       integer 1024000
SQL>
SQL> create table t as (select 'SORTING OF LENGTH 20' TEST from all_objects);

Table created.

SQL> DESC T
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 TEST                                               CHAR(20)

SQL>
SQL> show parameter sort_area

NAME                                 TYPE    VALUE
------------------------------------ ------- ------------------------------
sort_area_retained_size              integer 65536
sort_area_size                       integer 1024000
SQL>
SQL> alter session set sort_area_size = 65500;

Session altered.

SQL>
SQL> set autotrace on stat
SQL>
SQL> select count(1) from (select TEST from t
  2  where  rownum < 1331 order by 1);

  COUNT(1)
----------
      1330

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

SQL>
SQL>
SQL> select count(1) from (select TEST from t
  2  where  rownum < 1332 order by 1);

  COUNT(1)
----------
      1331

Statistics
----------------------------------------------------------
          0  recursive calls
         14  db block gets
          9  consistent gets
          5  physical reads
          0  redo size
        368  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          1  sorts (disk)
          1  rows processed

SQL>
SQL>
SQL> alter session set sort_area_size = 1024000;

Session altered.

SQL>
SQL>
SQL> select count(1) from (select TEST from t
  2  where  rownum < 1777 order by 1);

  COUNT(1)
----------
      1776

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

SQL>
SQL>
SQL> select count(1) from (select TEST from t
  2  where  rownum < 1778 order by 1);

  COUNT(1)
----------
      1777

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

SQL>
SQL>
SQL> drop table t;

Table dropped.

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

Thanks Tom for your time.

Followup:
Interesting,

what platform
what version of that platform
what does select * from v$version show
what is your init.ora (not v$parameter, just show me what you are setting)

Rating: 4
July 19, 2002
Reviewer: A reader
SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle8i Enterprise Edition Release 8.1.7.3.0 - Production
PL/SQL Release 8.1.7.3.0 - Production
CORE    8.1.7.2.1       Production
TNS for 32-bit Windows: Version 8.1.7.3.0 - Production
NLSRTL Version 3.4.1.0.0 - Production

OS is Win2000 Workstation SP2 256Meg Ram
Running Oracle in Dedicated Server Mode

init.ora has:

db_files = 1024
open_cursors = 500
max_enabled_roles = 30
db_file_multiblock_read_count = 32
db_block_buffers = 4000
shared_pool_size = 31457280
large_pool_size = 0
JAVA_POOL_SIZE =  0
log_checkpoint_interval = 10000
log_checkpoint_timeout = 1800
processes = 150
parallel_max_servers = 5
log_buffer = 32768
timed_statistics = true
max_dump_file_size = 10240
db_block_size = 8192
remote_login_passwordfile = exclusive
os_authent_prefix = ""
distributed_transactions = 500
compatible = 8.1.0
sort_area_size = 512000
sort_area_retained_size = 65536
job_queue_processes = 1

Thanks

Followup:
DOH -- don't know why I didn't think of that before.

It is your sort_area_retained_size.  It was not big enough.  There were two
things happening here -- the sort and the aggregation.  In between the two, you
give up your sort_area_size and shrink it down to sort_area_retained.  Once we
did that -- the intermediate result was bigger then fit in your
sort_area_retained and we put it to disk.  As soon as we put to disk -- there
you go, sort disk.

If you change your retained size, you'll see the sort disk goes away.  It didn't
really SORT on disk, it sorted and then stuff some results on disk cause they
wouldn't fit in the retained size.

Rating: 5
I spotted it! July 19, 2002
Reviewer: Mark J. Bobak from Ann Arbor, MI
Heh, I'm pretty proud of myself, as I spotted the problem
right away, when the reader posted the first full cut and
paste. ;-)

I just wanted to mention to the reader:

The link to IxOra that Tom has in the reply to the original
question, Steve Adams does a nice discussion of sort_area_size
and sort_area_retained_size, and how they are used.

-Mark

PS  Hey, I just noticed the spell checker icon, pretty cool.
Nice touch!

Rating: 5
Do i have these parameters incorrectly set? July 20, 2002
Reviewer: Yogeeraj from Mauritius
Hello,

Reading this post, i became very curious to see the behaviour of the above test
in my system. So i ran the same steps!

Oops! All these tests give me " 1  sorts (disk)" each time!!

Please advise on how i proceed in fixing the problem (if any).

Thank you for your precious time.

Best Regards
Yogeeraj
============================================================
SQL> show parameter sort_area

NAME                                 TYPE    VALUE
------------------------------------ ------- ------------------------------
sort_area_retained_size              integer 65536
sort_area_size                       integer 262144
SQL> create table t as (select 'SORTING OF LENGTH 20' TEST from all_objects);

Table created.

SQL> DESC T
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 TEST                                               CHAR(20)

SQL> alter session set sort_area_size = 65500;

Session altered.

SQL> set autotrace on stat

SQL> SQL> select count(1) from (select TEST from t
  2  where  rownum < 1331 order by 1);

  COUNT(1)
----------
      1330

Statistics
----------------------------------------------------------
          0  recursive calls
         14  db block gets
          9  consistent gets
         12  physical reads
          0  redo size
        296  bytes sent via SQL*Net to client
        359  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          1  sorts (disk)
          1  rows processed

SQL> select count(1) from (select TEST from t
  2  where  rownum < 1332 order by 1);

  COUNT(1)
----------
      1331

Statistics
----------------------------------------------------------
          0  recursive calls
         14  db block gets
          9  consistent gets
          5  physical reads
          0  redo size
        296  bytes sent via SQL*Net to client
        359  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          1  sorts (disk)
          1  rows processed

SQL>

SQL> alter session set sort_area_size = 1024000;

Session altered.

SQL> select count(1) from (select TEST from t
  2  where  rownum < 1777 order by 1);

  COUNT(1)
----------
      1776

Statistics
----------------------------------------------------------
          0  recursive calls
         14  db block gets
         11  consistent gets
         15  physical reads
          0  redo size
        296  bytes sent via SQL*Net to client
        359  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          1  sorts (disk)
          1  rows processed

SQL>

SQL> select count(1) from (select TEST from t
  2  where  rownum < 1778 order by 1);

  COUNT(1)
----------
      1777

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

SQL>

SQL> select * from v$version;

BANNER
------------------------------------------------------------
Oracle8i Enterprise Edition Release 8.1.7.4.0 - 64bit Production
PL/SQL Release 8.1.7.4.0 - Production
CORE    8.1.7.0.0       Production
TNS for HPUX: Version 8.1.7.4.0 - Production
NLSRTL Version 3.4.1.0.0 - Production

-------------------------
Parameters in my Init.ora
-------------------------
control_files = ("/d02/oradata/cmtdb/control01.ctl",
"/d02/oradata/cmtdb/control02.ctl", "/d02/oradata/cmtdb/control03.ctl")
open_cursors = 350
max_enabled_roles = 60
db_block_buffers = 10000
shared_pool_size = 157286400
large_pool_size = 20971520
java_pool_size = 20971520
log_checkpoint_interval = 100000
log_checkpoint_timeout = 1800
processes = 275
og_buffer = 262144
timed_statistics = true
log_archive_start = true
log_archive_dest_1 = "location=/d01/app/oracle/admin/cmtdb/arch"
log_archive_format = arch_%t_%s.arc
background_dump_dest = /d01/app/oracle/admin/cmtdb/bdump
core_dump_dest = /d01/app/oracle/admin/cmtdb/cdump
user_dump_dest = /d01/app/oracle/admin/cmtdb/udump
db_block_size = 8192
remote_login_passwordfile = exclusive
remote_os_authent=true
os_authent_prefix = "ops$"
job_queue_processes = 4
job_queue_interval = 60
distributed_transactions = 80
open_links = 4
local_listener = "listener_1"
compatible = "8.1.7.3"
sort_area_size = 262144
sort_area_retained_size = 65536
NLS_DATE_FORMAT=DD/MM/YYYY
utl_file_dir = *
sessions=400
session_cached_cursors=100
_trace_files_public=true
db_file_multiblock_read_count=32
resource_limit = true
============================================================

Followup:
play with it (list the original guy did)

find your "break even" points.

Rating: 4
October 17, 2002
Reviewer: A reader
Tom,
I tried to use the script disk_sorts.sql that is showed in the website that you
recommended, but I am not able to run it because I don't have the table
x_$kcctf,x_$ktfthc and sys.x_$kcftio.
How can I do in order to create these tables?
I checked all the scripts on /oracle/rdbms/admin (grep kcctf *)and none of them
use these objects.
Is there any script that I can use in order to create them?

Thanks

Followup:
that is the problem with x$ tables -- we change them (and they are
undocumented).

You cannot create these tables, they are burned into the kernel itself.  They
are compiled into Oracle -- they are not real tables, they are data structures.

Rating: 4
October 17, 2002
Reviewer: A reader
Tom,
Thanks for answering me.
My db is Oracle8i Enterprise Edition Release 8.1.7.3.0 - 64bit Production. I saw
that the "reader -07/19/02" who run the same script and also you had run the
script on a 8.1.7.3.0 version. So I should be able to run the script too, I mean
how can I do in order to create those table that script need.

Thanks for your help.

Rating: 4
Regarding missing x_$kcctf etc. October 17, 2002
Reviewer: Padders from UK
Regarding missing x_$kcctf etc. I believe Steve Adams typically creates a series
of views over x$ fixed tables and his scripts utilise these views. Script to
create so called 'x_$ views' is also available on his site.

Rating: 5
RE: ...in order to create those table... October 17, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
RE: ...in order to create those table...

PMFJI...

Review Tom's answer...

1) The "x$ tables" are not "real" tables, they are structures that are burned
into the Oracle binary.

2) There is no script to create them.

3) They are not publicly documented for a reason...

4) Here's a small hint:  They, in fact, are present, you just don't know how to
access them... BUT you don't need to access them.

5) Ask yourself if there is a better way to get the information needed to tune
disk sorts.  For example, Statspack? Maybe v$sysstat? Or maybe chapters 19 and
20 of the "Oracle8i Designing and Tuning for Perfomance" guide?  There are
sections in those chapters dedicated to this exact topic.

If you really need to tune the sort_area_* parameters, you can get the
information you need from sources other than the "mystical, magic, and
all-powerful x$ tables"...

Anyway, just throwing a suggestion or two out...

- Mark

October 17, 2002 Ramon -- Thanks for the question regarding "XML parser usign native PL/SQL code.", version 8.1.7
You Asked
Hi Tom,

   We have the following problem:
We would like to parse a XML stream into PL/SQL data types in order to map our
internal PL/SQL procedures interfaces. We've seen an Oracle utility called XDK
but the problem is that this utility is based in JSP (Java Stored
Procedures)technology and we want to avoid the use of shuch method to do it.
Please, is it possible to decode an PL/SQL stream or file using native PL/SQL
functions?.

Thanks.
and we said...
I asked Sean Dillon, our local XML Technologist, to take a look at this and
here's what he had to say:
----------------------------

Yes, the "PL/SQL-not-Java" request, I've seen this before ;-).

In Oracle8.1.7, if you use the XML Developer's Kits you're stuck with Java
Stored Procedures (the PLSQL packages are all built on top of Java Stored
Procs).  There is another utility you can use, although it's considerably dated
and it only supports some XSL and DOM functionality.  It's a utility written by
Steve Meunch and Co., which you can find on OTN at this URL:
http://otn.oracle.com/tech/xml/htdocs/plsxml/xml4plsql.htm
In Oracle9iR2, in XML DB, you'll find that the majority of the functionality
offered in the XDKs have now made their way into native supplied packages
(typically DBMS_* packages).  These are all built on top of the C libraries
under the covers, so you eliminate the Java Virtual Machine from the mix all
together.  That, of course, requires you be on 9i R2 :-).

I guess another option would be to write an external procedure to take the data
you're getting in PL/SQL and pass it to a C program hosted on the operating
system.  I don't know if C is an option for you but it's one more way you could
get done what you need to get done and avoid the Java side of it all.

Hope that helps!

_smd_

_____________________________________________________________________
Sean Dillon - Principal Technologist, Oracle Corporation
Author "Beginning Oracle Programming"
http://www.amazon.com/exec/obidos/ASIN/186100690X
October 17, 2002 Sudhanshu -- Thanks for the question regarding
      "loading single record datafile into multi records table", version 8.1.7

      You Asked
Hi ,

I have a datafile having sample data as follows :-

UNB+UNOA:2+TSTSCICON+EDI2450801+020815:1722+396++DEBSTA'UNH+39600001+DEBSTA:0:1:D
V:DVLA'BGM+DEB:DVM:DVL+H314'DTM+137:020815:101'DTM+205:020819:101'DTM+167:020809:
101'DTM+168:020815:101'NAD+MR+H314:DVP:DVL'MOA+9:155750'FII+BF+24478806:INSURANCE
 CO. ENQS+:::556120:25:UN'FII+PB+97603104:CHURCHILL INS
MOTO+:::600001:25:UN'ALC+C++++FPE:DVC:DVL'QTY+2:623'RTE+2:250:1'MOA+23:155750'UNT
+15+39600001'UNZ+1+396'

This is a single record.

I want to load this into database table using sql loader breaking the string
whenever I encounter single quote.

That means my table should have 17 records like this :-
UNB+UNOA:2+TSTSCICON+EDI2450801+020815:1722+396++DEBSTA
UNH+39600001+DEBSTA:0:1:DV:DVLA
BGM+DEB:DVM:DVL+H314
DTM+137:020815:101
DTM+205:020819:101
DTM+167:020809:101
DTM+168:020815:101
NAD+MR+H314:DVP:DVL
MOA+9:155750
FII+BF+24478806:INSURANCE CO. ENQS+:::556120:25:UN
FII+PB+97603104:CHURCHILL INS MOTO+:::600001:25:UN
ALC+C++++FPE:DVC:DVL
QTY+2:623
RTE+2:250:1
MOA+23:155750
UNT+15+39600001
UNZ+1+396

Could you specify how to go about this ?

      and we said...
Note that

select to_char( ascii(''''), 'XX' ) from dual

is x'27'!!!

ops$tkyte@ORA817DEV.US.ORACLE.COM> desc t
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 C                                            VARCHAR2(255)

ops$tkyte@ORA817DEV.US.ORACLE.COM> truncate table t;

Table truncated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> !cat test.dat; cat t.ctl; sqlldr / t
UNB+UNOA:2+TSTSCICON+EDI2450801+020815:1722+396++DEBSTA'UNH+39600001+DEBSTA:0:1:D
V:DVLA'BGM+DEB:DVM:DVL+H314'DTM+137:020815:101'DTM+205:020819:101'DTM+167:020809:
101'DTM+168:020815:101'NAD+MR+H314:DVP:DVL'MOA+9:155750'FII+BF+24478806:INSURANCE
CO. ENQS+:::556120:25:UN'FII+PB+97603104:CHURCHILL
INSMOTO+:::600001:25:UN'ALC+C++++FPE:DVC:DVL'QTY+2:623'RTE+2:250:1'MOA+23:155750'
UNT+15+39600001'UNZ+1+39
LOAD DATA
INFILE test.dat  "str X'27'"
INTO TABLE t
( c char(255) )

SQL*Loader: Release 8.1.7.4.0 - Production on Thu Oct 17 15:33:37 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Commit point reached - logical record count 16
Commit point reached - logical record count 17

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

C
----------------------------------------------------------------------
UNB+UNOA:2+TSTSCICON+EDI2450801+020815:1722+396++DEBSTA
UNH+39600001+DEBSTA:0:1:DV:DVLA
BGM+DEB:DVM:DVL+H314
DTM+137:020815:101
DTM+205:020819:101
DTM+167:020809:101
DTM+168:020815:101
NAD+MR+H314:DVP:DVL
MOA+9:155750
FII+BF+24478806:INSURANCECO. ENQS+:::556120:25:UN
FII+PB+97603104:CHURCHILL INSMOTO+:::600001:25:UN
ALC+C++++FPE:DVC:DVL
QTY+2:623
RTE+2:250:1
MOA+23:155750
UNT+15+39600001
UNZ+1+39

17 rows selected.

Review &
      Followup

      Rating: 3
Sql
      Output  October 17, 2002
Reviewer:  Reader
       from Charlotte,NC, USA
Hi Tom

Is that sql suppose to give output as ---x'27' OR 27.

When I execute the sql, I am getting 27

Thanks

          Followup:

27 -- hex two seven.

x'27' being a standard way (or 0x27) to write "hex two seven"

October 17, 2002 Kamini -- Thanks for the question regarding
      "ULT_FILE", version 8.1.7

      You Asked
Hi Tom

Is it possible to open a file from a path which is not specified in the
utl_file_dir parameter.  If the answer is no could u please tell an workaround
to do so.  Also can u please tell the reason why it is necessary to specify the
utl_file_dir.

Thanks
Kamini

      and we said...
well, it is needed to specify it in order for it TO WORK!!

consider this:

  utl_file.fopen( '/d01/oradata/system.dbf', 'w' );

hmmm, you really want to be able to do that???  I don't want you to, thats for
sure.

It is for security.

In 9iR2 we use directory objects -- meaning a properly privileged person can
create a directory and grant you READ or WRITE on it.  You do not need to set
the utl_file_dir init.ora setting before that.

In 9iR1 and before, you must set the utl_file_dir init.ora setting in order to
WRITE a file.  You can read files with BFILES (using utl_raw.cast_to_varchar2 to
convert the raw input into "text") and directory objects without using the
utl_file_dir
Review & Followup

      Rating: 4
How about
      UTL_FILE_DIR = *  October 17, 2002
Reviewer:
      Suresh  from Texas

          Followup:

how about following that with erasing your system.dbf file....

don't use *

Using * is a truly bad idea.

October 18, 2002 Mishka -- Thanks for the question regarding
      "Packages", version 9.1

      You Asked
Thanks to your "Gospel of the Bind Variable", we're in the process of modifing
our site to use them.  The project is going well but there is one point that I
was hoping you could clear up for me.

The package I'm using contains 10 different procedures that each pull data from
different tables or views. If view A is modified in such a way that the sql in
procedure A of the package becomes invalid, procedure B,C and D still work.
However, if view A is dropped, the entire package ceases to function.

My question is this: What is the best way for me to design this package to avoid
losing a huge chunk of functionality on my site if someone (in a fit of
incredible stupidity) drops or renames a table?  Should I give each set of
procedures that reference common tables their own packages?

Thanks again.

Mishka

      and we said...
Umm, I would not take that approach at all -- I would rather put into place
controls that make it impossible for the situation to arise.

If you have a situation where someone can drop or rename a table without going
thru proper procedures -- YOU NEED TO FIX THAT.

I could make it so this procedure is the only one affected however that would

o have performance implications
o not solve the problem
o make it IMPOSSIBLE TO detect the problem

we would remove the dependency between the package and the view -- meaning, you
wouldn't be able to tell you are USING the view -- meaning, the probability of
someone saying "hey, look at this thing, no one uses it -- get rid of it" go up.

Fix the problem, don't try to work around it.
October 18, 2002 Jan -- Thanks for the question regarding "Client
      vs Server Code", version 8.1.7.

      You Asked
Dear Tom,

could you please explain why is better to put all SQL and PL/SQL codes into
Server instead of Client (Forms) from performance point of view?

1. Is the query on Client taking all data from queried tables from Server to
Client and after filtering on Client?

SELECT a1,b1,c1
  FROM a,b,c
   WHERE a.a=b.b
     AND a.a=c.c
  ORDER BY a1

2. If the Client PC is powerfull, is there some advantages to perform complex
query on the Client?

3. Which memory (Client or Server) is data of PL/SQL tables occupied?

Thanks,

      and we said...
it is not that ALL -- but that the code that SHOULD be in the DB should be in
the database.

Lets say you do this:

begin
   for x in ( select * from t)
   loop
      some_funky_process(x);
      insert into t2 values ( x.c1, x.c2, ... );
   end loop;
end;

say T has 1000 rows, some_funky_process tends to query 50 records PER call
(50*1000 records) and you insert all 1000 rows.

Suppose that is in the client -- we have

o 1000 round trips to get the rows of t
o 50*1000 round trips to get the rows of some_funky_process
o 1000 round trips to insert the data

Lots of back and forth and back and forth.  Now, suppose you put that in the
database.

We now have 1 round trip for the entire thing.

So, it is a simple matter of "math" -- should the network be involved or NOT?

2) the query is ALWAYS ALWAYS ALWAYS done on the server -- period.  You have to
ask yourself -- do I need to pull these rows over the network back to the client
or not.  If not, put it in the database.  If so, put it in the client.

3) depends -- a plsql package in a form -- client.  plsql package compiled into
the database -- server.
October 18,
      2002 Arun -- Thanks for the question
      regarding "How to tune this one", version 8.1.7.3.0

      You Asked
I see lot of SQL like this in my v$sqlarea. What does this mean and how to tune
this since this shows as top consumer of memory (buffer_gets).

BEGIN
   pkg_refl_srch.usp_do_refl_counts (
      p_nbr_seq_search          => :v00001,
      p_ind_include_reslts      => :v00002,
      p_cde_county              => :v00003,
      p_cde_munic               => :v00004,
      p_cde_type_transp         => :v00005,
      p_cde_school_dist         => :v00006,
      p_cde_school              => :v00007,
      p_cde_type_provr          => :v00008,
      p_cde_level_care          => :v00009,
      p_cde_lang                => :v00010,
      p_cde_prog_cc_early       => :v00011,
      p_cde_need_spec           => :v00012,
      p_cde_envmt               => :v00013,
      p_cde_shift               => :v00014,
      p_cde_unit_of_care        => :v00015,
      p_cde_pmt_option          => :v00016,
      p_cde_type_fee            => :v00017,
      p_cde_meal                => :v00018,
      p_cde_crclm               => :v00019,
      p_cde_acredn              => :v00020,
      p_cde_time                => :v00021,
      p_day_of_week             => :v00022,
      p_address1                => :v00023,
      p_case_id                 => :v00024,
      p_userid                  => :v00025,
      p_radial                  => :v00026,
      p_dist_school             => :v00027,
      ref_cursor                => :r000c000
   );
END;

Thanks
Arun

      and we said...
buffer gets does not mean "i use lots of memory" at all....  buffer gets means I
did lots of logical IO.  It is zero correlation to the amount of memory you
used.

Now, a PLSQL block doesn't do any buffer gets really.

A plsql block does run SQL which DOES DO buffer gets.

We report to you the sum of all buffer gets done by all of the SQL a plsql block
executes.  Hence, all this means is that your procedure runs lots of sql that
does buffer gets and when you add them all up -- it is a big number.

So, you cannot look at that and say "hey, gotta tune this".  You need to look at
the plsql routine, determine is the algorithm is optimal and tune the individual
SQL statements therein.

You can use the dbms_profiler package to do this (tuning of the algorithm).  If
you have my book "Expert one on one Oracle" -- see the appendix, i go over it
there or search this site for dbms_profiler for various examples.  Use
sql_trace+tkprof to tune the sql.
Review &
      Followup

      Rating:
      5
  October 18, 2002
Reviewer:  Arun
      Gupta  from Harrisburg, PA USA
But why this happens only for two procedures ? For all other 300 procedures, I
see individual queries which are performing good or bad. Only for two procedures
I see procedure call at the top of buffer gets list. These procedures contain
some pl/sql code and some sql statements and are no different than others. Are
there some conditions in which Oracle will report buffer gets for individual
queries out of a procedure and in other cases the sum of buffer gets by all the
queries in procedure ?

Apologies for equating buffer gets with memory consumption. That was obviously
incorrect.
Thanks
Arun

          Followup:

it is the sum of the stats for the sql executed by the procedure - like I
said,use SQL_TRACE+TKPROF to run this procedure and see what it is doing

October 18, 2002 Salim -- Thanks for the question regarding
      "javasyspriv - a security vulnerability?", version 9.2.0

      You Asked
Tom,

I am new to the world of JAVA, and I just realized that by assigning JAVASYSPRIV
role to an unprivileged database userid lets it execute a java stored proc that
can write to the software owner's (oracle) directories with software owner's
permissions. This can allow such a database id to wipe out everything owned by
Oracle.
I followed steps given at

http://www.oracle.com/oramag/oracle/00-Mar/index.html?o20java_2.html
to create a simple java stored proc to issue OS commands. I am on Solaris.

Is this not a security vulnerability?

thanks

Salim

      and we said...
Yes, you should use DBMS_JAVA to grant very granular fine grained privileges.
That article was written for Oracle8i release 1 (version 815) whereby the
dbms_java.grant_privilege package did not exist.

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:952229840241
for example (use the SECOND version of the routine there for 9i)
Review &
      Followup

      Rating: 2
Still have
      questions  October 18, 2002
Reviewer:  Salim
       from NJ, USA
So, is there any way to prevent the common, un-privileged database user (who
wants to run OS commands using this java stored proc) from creating files owned
by the unix id 'oracle' (the software owner)?

thanks

Salim

          Followup:

well, first, i would never just have a "run anything you want"

It would run only very very very specific, controlled and controllable commands.

Second, the java runs as the database user account (os account), so no.

You can use an EXTERNAL procedure in C, that can run as a different user, say
the user "nobody".

But -- don't let them just run "anything", that would be horrible.

October 18, 2002 venkat -- Thanks for the question regarding
      "Serializing Java Objects into the database (and getting them back out)",
      version version 8.1.6

      You Asked
Hi,

How do I serialize Java objects into Oracle database and
vice-versa.

thanks & regards
Venkat Vudithyala

      and we said...
For this one, I asked my resident java guru Mark Piermarini
(mbpierma@us.oracle.com) to come up with an example.  Below are
two examples he came up with -- the first with minimal error
checking and code shows an example of

o creating an object
o serializing it into the database
o reading it back out
o printing it.

The second, more robust example with comments is a little more
flexible.  It will serialize a java instance into the database
if passed no inputs.  After it puts it in -- it'll print out the
"id" for that object instance.  Later, you can run this same
java example and pass it that id -- it'll read out that object
instance and print it out, showing how to make this work across
invocations.

Things you will need to run this example in this case are:

create sequence java_obj_seq;

create table java_objects(
   id number,
   classname varchar2(2048),
   bytes blob default empty_blob()
)

We use the sequence to create a "primary key" for out object
instances.  We need this number later to retrieve a specific
instance.  The table is what we use to store the object
instance.  Here we store the classname of the original java
instance in a varchar2 field and in the BLOB we'll store the
bytes of the serialized object.

Here is the first small example:

import java.lang.*;
import java.sql.*;
import java.util.*;
import oracle.jdbc.driver.*;
import oracle.sql.*;

public class serial2 {
   static String genID =
         "select java_obj_seq.nextval from dual";

   static String writeSQL =
       "begin insert into java_objects(id,classname,bytes) "+
             " values (?,?,empty_blob()) " +
             " return bytes into ?; end;";

   static String readSQL =
       "select bytes from java_objects where id = ?";

   public static long write(Connection conn, Object o)
   throws Exception
   {
      long id = nextval(conn);
      String className = o.getClass().getName();
      CallableStatement stmt = conn.prepareCall(writeSQL);
      stmt.setLong(1, id);
      stmt.setString(2, className);
      stmt.registerOutParameter(3, java.sql.Types.BLOB);
      stmt.executeUpdate();
      BLOB blob = (BLOB) stmt.getBlob(3);
      OutputStream os = blob.getBinaryOutputStream();
      ObjectOutputStream oop = new ObjectOutputStream(os);
      oop.writeObject(o);
      oop.flush();
      oop.close();
      os.close();
      stmt.close();
      System.out.println("Done serializing " + className);
      return id;
   }

   public static Object read(Connection conn, long id)
   throws Exception
   {
      PreparedStatement stmt = conn.prepareStatement(readSQL);
      stmt.setLong(1, id);
      ResultSet rs = stmt.executeQuery();
      rs.next();
      InputStream is = rs.getBlob(1).getBinaryStream();
      ObjectInputStream oip = new ObjectInputStream(is);
      Object o = oip.readObject();
      String className = o.getClass().getName();
      oip.close();
      is.close();
      stmt.close();
      System.out.println("Done de-serializing " + className);
      return o;
   }

   private static long nextval(Connection conn)
   throws SQLException
   {
      Statement stmt = conn.createStatement();
      ResultSet rs = stmt.executeQuery(genID);
      rs.next();
      long id = rs.getLong(1);
      rs.close();
      stmt.close();
      return id;
   }

   public static void main(String[] argv)
   throws Exception
   {
      String cs = "jdbc:oracle:oci8:@ora8idev";
      String user = "scott";
      String pass = "tiger";

      DriverManager.registerDriver(new OracleDriver());
      Connection conn =
          DriverManager.getConnection(cs, user, pass);
      conn.setAutoCommit(false);

      LinkedList l = new LinkedList();
      l.add("This");
      l.add("is");
      l.add("a");
      l.add("test");
      l.add(new Long(123123123));
      l.add(new java.util.Date());

      long id = write(conn, l);
      conn.commit();

      System.out.println("ID= " + id);
      System.out.println("Object= " + read(conn, id));
      conn.close();
   }
}

Now, if we run the code:

$ /usr/java1.2/bin/java serial2
Done serializing java.util.LinkedList
ID= 1
Done de-serializing java.util.LinkedList
Object= [This, is, a, test, 123123123, Sun Jan 21 09:45:21 EST
2001]

So, according to that, the object has been serialized and stored
in the database.  In fact, we can log in using sqlplus and see
it if we like:

scott@DEV816> create or replace function clean( p_raw in blob )
              return varchar2
  2  as
  3      l_tmp long default utl_raw.cast_to_varchar2(
                              dbms_lob.substr(p_raw,2000,1));
  4      l_char   char(1);
  5      l_return long;
  6  begin
  7      for i in 1 .. length(l_tmp)
  8      loop
  9          l_char := substr( l_tmp, i, 1 );
 10          if ( ascii(l_char) between 32 and 127 )
 11          then
 12              l_return := l_return || l_char;
 13          else
 14              l_return := l_return || '.';
 15          end if;
 16      end loop;
 17      return l_return;
 18  end;
 19  /

Function created.

scott@DEV816>
scott@DEV816> select id, classname,
  2         dbms_lob.getlength(bytes) len,
  3         clean(bytes) clean
  4    from java_objects
  5  /

        ID CLASSNAME             LEN CLEAN
---------- -------------------- ---- --------------------
         1 java.util.LinkedList  191 ....sr..java.util.Li
                                     nkedList.)S]J`."...x
                                     pw.....t..Thist..ist
                                     ..at..testsr..java.l
                                     ang.Long;.....#....J
                                     ..valuexr..java.lang
                                     .Number...........xp
                                     .....V..sr..java.uti
                                     l.Datehj..KYt....xpw
                                     .....1...xx

So, we see now how to serialize using a BLOB as the input stream
and deserialize using the BLOB again.  We can also review the
contents (sort of) in SQLPlus.

UTL_RAW might not be installed on your system -- it comes with
Oracle7.1.6 and up.  To install if you do not have it:

o cd $ORACLE_HOME/rdbms/admin
o connect as SYS or INTERNAL only using SVRMGRL
o @utlraw
o @prvtrawb.plb

Now for the longer, more robust example with error handling and
comments.  You have all you need above -- the following is just
a little more sophisticated.  After you compile this:

$ /usr/java1.2/bin/javac serial.java

we can run:

$ /usr/java1.2/bin/java serial 1
Done de-serializing java.util.LinkedList
Object= [This, is, a, test, 123123123, Sun Jan 21 09:45:21 EST
2001]

(after running the first example serial2.java that is).  All
this did was read out our existing java instance and "rebuild
it" for us and then print it out.  You can run serial without
any arguments to create new instances of the linked list and
read them back out by ID later.  Here is the code:

import java.io.*;
import java.lang.*;
import java.sql.*;
import java.util.*;
import oracle.jdbc.driver.*;
import oracle.sql.*;

public class serial {
   static String genID =
        "select java_obj_seq.nextval from dual";

   static String writeSQL = "begin insert into java_objects "+
           " (id,classname,bytes) values (?,?,empty_blob()) "+
           " return bytes into ?; end;";

   static String readSQL =
       "select bytes from java_objects where id = ?";

   // We attempt to serialize the object to the database table.
   // We use a
   // sequence number to identify the object and return that
   // value to the
   // caller. We also save the class name in case someone wants
   // to search
   // for objects based on that name. If we get any exception,
   // we rethrow
   // it to the caller but we always try to clean up after
   // ourself.

   public static long write(Connection conn, Object o)
   throws Exception
   {
      ObjectOutputStream oop = null;
      CallableStatement stmt = null;
      OutputStream os = null;
      long id = nextval(conn);
      String className = o.getClass().getName();

      try {
         stmt = conn.prepareCall(writeSQL);
         stmt.setLong(1, id);
         stmt.setString(2, className);
         stmt.registerOutParameter(3, java.sql.Types.BLOB);
         stmt.executeUpdate();
         BLOB blob = (BLOB) stmt.getBlob(3);
         os = blob.getBinaryOutputStream();
         oop = new ObjectOutputStream(os);
         oop.writeObject(o);
         oop.flush();
      } catch (Exception e) {
         throw e;
      } finally {
         if (oop  != null) { oop.close(); }
         if (os   != null) { os.close(); }
         if (stmt != null) { stmt.close(); }
         System.out.println("Done serializing " + className);
      }

      return id;
   }

   // We attempt to de-serialize the object from the database
   // table using
   // the given identifier. If we get any exception, we rethrow
   // it to the
   // caller but we always try to clean up after ourself.

   public static Object read(Connection conn, long id)
   throws Exception
   {
      ObjectInputStream oip = null;
      PreparedStatement stmt = null;
      InputStream is = null;
      Object o = null;
      String className = null;

      try {
         stmt = conn.prepareStatement(readSQL);
         stmt.setLong(1, id);
         ResultSet rs = stmt.executeQuery();

         // Even though we expect only one row back, the caller
         // could have
         // passed an invalid identifier so we wrap this in a
         // loop to
         // make sure we don't get null pointer exceptions. In
         // the case
         // where there are 0 rows, we would return a null
         // value. Where
         // there are > 1 rows, we would return only the first
         // one.

         while (rs.next()) {
            is = rs.getBlob(1).getBinaryStream();
            oip = new ObjectInputStream(is);
            o = oip.readObject();
            className = o.getClass().getName();
            break;
         }
      } catch (Exception e) {
         throw e;
      } finally {
         if (oip  != null) { oip.close(); }
         if (is   != null) { is.close(); }
         if (stmt != null) { stmt.close(); }
         System.out.println("Done de-serializing " + className);
      }

      return o;
   }

   // Get the next sequence value. Ideally, this would be better

   // if
   // we just created a prepared statement and repeatedly used
   // that.
   // Obviously, we must assume the same connection is always
   // used.

   private static long nextval(Connection conn)
   throws SQLException {
      long id = -1;
      Statement stmt = null;
      ResultSet rs = null;

      try {
         stmt = conn.createStatement();
         rs = stmt.executeQuery(genID);

         while (rs.next()) {
            id = rs.getLong(1);
         }
      } catch (SQLException e) {
         throw e;
      } finally {
         if (rs != null) { rs.close(); }
         if (stmt != null) { stmt.close(); }
      }

      return id;
   }

   // Here we just simply connect to the database and either
   // serialize or de-serialize an object. If we get an
   // exception then we will rollback whatever we have done.
   // Commit changes as necessary and close the connection.
   public static void main(String[] argv) throws Exception {
      long id;
      Connection conn = null;
      String cs = "jdbc:oracle:oci8:@ora816dev";
      String user = "scott";
      String pass = "tiger";

      try {
         DriverManager.registerDriver(new OracleDriver());
         conn = DriverManager.getConnection(cs, user, pass);
         conn.setAutoCommit(false);

         if (argv.length == 0) {
            id = write(conn, new java.util.Date());
            conn.commit();
            System.out.println("ID= " + id);

            LinkedList l = new LinkedList();
            l.add("This");
            l.add("is");
            l.add("a");
            l.add("test");
            id = write(conn, l);
            conn.commit();
            System.out.println("ID= " + id);
         } else {
            id = Long.parseLong(argv[0]);
            Object o = read(conn, id);
            System.out.println("Object= " + o);
         }
      } catch (Exception e) {
         e.printStackTrace();
         if (conn != null) { conn.rollback(); }
      } finally {
         if (conn != null) { conn.close(); }
      }
   }
}

Review &
      Followup

      Rating: 5
"Serializing
      Java Objects into the database (and getting them back out)", version
      version 8.1.6  April 30, 2001
Reviewer:  Brad
      Bittiker  from San Francisco, CA USA
Excellent.  This is exactly what I was looking for.

      Rating: 5
Very Useful but
      .....  May 31, 2002
Reviewer:  Damien
      Michalosky  from Cambridge, MA USA
It is a great alternative to what I am actually looking for. I wanted to write a
Java Stored Function that returns a Java Object. It looks like that is not
possible for my research but serializing an object created by a Java Stored
Procedure is possible and can be retrieved by an outside class. Any other ideas?
Please post or email me.

          Followup:

regardless of the method -- you will have to have a serializable class (we are
going from JVM to JVM after all -- not within a single JVM)

So, given that, I think this would be the most straight forward way.  You can
use a temporary blob instead of a table and have your java stored procedure
return a blob.  Your java client code will

a) connect
b) run the java stored procedure
c) get the blob back to the java client
d) deserialized it

      Rating: 4
How about
      serialized object to stored java procedure?  October 18,
      2002
Reviewer:  Chris Bruun  from Vienna VA

This example is great for updating an object table.  How can serialized objects
be passed to and deserialized by a java stored procedure?

          Followup:

Well, you could just use this technique -- the caller would serialize into a LOB
(Temporary lob for example) and pass the LOB locator to the java stored proc.

October 18, 2002 John -- Thanks for the question regarding "Use of
      functions within a procedure...", version 8.1.7

      You Asked
Hi Tom,

I guess I sort of have a 'best practices' question.  To give some background, I
have some procedures that are using analytic functions in 8.1.7.  I am using
dynamic SQL in orders to get around the PL/SQL parser issue with said functions.
 Now, I am also using considering the use of standard functions in the following
way:

I am selecting a list of columns from a table, say ORDERS (ID, ITEM_NAME).  In
addition to those two columns, I need some ancilliary information not found in
the ORDERS table.  Let's say a first name, last name, and id for a salesperson
for a given order.  I need to make this kind of connection in a number of
procedures.  Is it better to write the join logic into multiple procedures in
order to get the employee info?  Or would it be better to write a dynamic
function similar to the following:

FUNCTION GETSPINFO( p_ord_id IN NUMBER, p_request IN VARCHAR2 )
RETURN
VARCHAR2
     AS
     l_result     VARCHAR2(64);
--
     BEGIN
         EXECUTE IMMEDIATE
         'SELECT '||p_request||
         ' FROM orders o
           , order_salesperson os
           , salesperson s
         WHERE o.id = :bv1
         AND o.id = os.oid
         AND os.sp_id = sp.id' INTO l_result USING p_ord_id;'
--
         RETURN l_result;
     END;

Doing the same join in multiple procedures seems something like redundant code,
but doing a dynamic function and calling it several times for different columns
within the same procedure seems like it would add considerable overhead....  At
the very least it is going to soft parse over and over...  Is there a sort of
rule of thumb regarding when it is efficient/not efficient to use functions?  Or
is there some other solution all together?

Thanks!

- john

      and we said...
It is much better to write this into the query in the first place.

You should call PLSQL from SQL only when you cannot do it in SQL itself. Every
call out to PLSQL from SQL adds overhead -- cycles to your processing.
Especially if you are using DYNAMIC sql like that.

I would MUCH rather see you code:

  select id, item_name, (select foo
                           from order_salesperson os, salesperson s
                          where os.id = orders.id
                            and os.sp_id = s.id ) foo
    from orders

then to call a function like that!

My "ROT" on this one -- use PLSQL if and only if and when you cannot do it in
SQL.

October 18, 2002 Veerakumar -- Thanks for the question regarding
      "Connection to MS SQL through java stored Procedure using JDBC", version
      8.1.7

      You Asked
Hi Tom,

I need to connect to MS SQL database from my PL SQL procedure so that i can
exchange data between Oracle and MS SQL. Using oracle JDBC i am able to write
Java Stored Procedure (JSP) but i am not able to call the Java program which
connects to MS SQL thru JSP and i understand i should use the correct JDBC
driver which oracle supports. Is there any way i can get that one without going
for the Oracle Gateway options? Pls give me a sample program if possible.

Thanks a lot for your support to the oracle World!

      and we said...
There are two ways

a) configure the generic connectivity via ODBC, gives you sql access.  see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4406709207206

b) load a type-4 thin jdbc driver to MS sqlserver if one exists into Oracle and
use the existing java code you have!

October 19, 2002 Yves -- Thanks for the question regarding
      "INITRANS Cause of deadlock", version 9.2.0.1.0

      You Asked
Hi Tom.

I am getting the following errors in my alert log:

ORA-000060: Deadlock detected. More info in file
d:\oracle\admin\vndb\udump\vndb_ora_1620.trc.

The first few lines of the above trace file are as follows:
Dump file d:\oracle\admin\vndb\udump\vndb_ora_1312.trc
Thu Oct 17 20:03:58 2002
ORACLE V9.2.0.1.0 - Production vsnsta=0
vsnsql=12 vsnxtr=3
Windows 2000 Version 5.0 Service Pack 1, CPU type 586
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production
Windows 2000 Version 5.0 Service Pack 1, CPU type 586
Instance name: vndb

Redo thread mounted by this instance: 1

Oracle process number: 15

Windows thread id: 1312, image: ORACLE.EXE

*** 2002-10-17 20:03:58.000
*** SESSION ID:(10.1301) 2002-10-17 20:03:58.000
DEADLOCK DETECTED
Current SQL statement for this session:
INSERT INTO vn_valuation (VALUATION_ID,
                                                   GROUP_ID,
                                                   TASK_NO,
                                                   PRIORITY,
                                                   HOST,
                                                   REQUEST,
                                                   SERVER_NO,
                                                   EXECUTION_TIME,
                                                   VERSION,
                                                   INSTR_ID,
                                                   MODEL_ID,
                                                   CALC_ID,
                                                   MARKET_ID,
                                                   EXCEPTION) values (
                                                    :1 ,
                                                    :2 ,
                                                    :3 ,
                                                    :4 ,
                                                    :5 ,
                                                    :6 ,
                                                    :7 ,
                                                    :8 ,
                                                    :9 ,
                                                    :10 ,
                                                    :11 ,
                                                    :12 ,
                                                    :13 ,
                                                    :14 )
The following deadlock is not an ORACLE error. It is a
deadlock due to user error in the design of an application
or from issuing incorrect ad-hoc SQL. The following
information may aid in determining the deadlock:
Deadlock graph:
                       ---------Blocker(s)--------  ---------Waiter(s)---------
Resource Name          process session holds waits  process session holds waits
TX-00050029-0000783b        15      10     X             12      14           S
TX-000a001d-00007466        12      14     X             15      10           S
session 10: DID 0001-000F-00000002    session 14: DID 0001-000C-00000002
session 14: DID 0001-000C-00000002    session 10: DID 0001-000F-00000002
Rows waited on:
Session 14: no row
Session 10: no row
Information on the OTHER waiting sessions:
Session 14:
  pid=12 serial=1012 audsid=7504 user: 68/VNUSER
  O/S info: user: vndbap, term: , ospid: 11476, machine: dfin03
            program: java@dfin03 (TNS V1-V3)
  Current SQL Statement:

INSERT INTO vn_valuation (VALUATION_ID,
                                                   GROUP_ID,
                                                   TASK_NO,
                                                   PRIORITY,
                                                   HOST,
                                                   REQUEST,
                                                   SERVER_NO,
                                                   EXECUTION_TIME,
                                                   VERSION,
                                                   INSTR_ID,
                                                   MODEL_ID,
                                                   CALC_ID,
                                                   MARKET_ID,
                                                   EXCEPTION) values (
                                                    :1 ,
                                                    :2 ,
                                                    :3 ,
                                                    :4 ,
                                                    :5 ,
                                                    :6 ,
                                                    :7 ,
                                                    :8 ,
                                                    :9 ,
                                                    :10 ,
                                                    :11 ,
                                                    :12 ,
                                                    :13 ,
                                                    :14 )
End of information on OTHER waiting sessions.
===================================================

The deadlock seems to occur when I have concurrent threads inserting into the
same table.  There are no updates involved here at all.

The table in question has 1 primary key which is a sequence number, and one hash
index.  This table has an initrans of 1 and a maxtrans of 256.
According through Oracle support (through a TAR I have opened), the dealock is
probably caused by a the lack of room in the row header.

I have increased initrans to 100, and when I have two concurrent threads
running, at some point, one thread goes in sleep state (my own characterization)
and only continues when the other thread has completed.  I am no longer getting
the ora-60 deadlock errors in the alert log.

My questions are:  does this all make sense, and what should values of inittrans
and maxtrans be based on?  I have only simulated two threads, but if there are
multiple threads, should initrans be like 1000, and maxtrans be like 2000?

Please advise.

Thanks.

No the table does not have a hash cluster.  I meant to say it has a bitmap index
on a field that is non allways unique and sometimes may be null.  There are no
foreign keys on this table.

      and we said...
it is not initrans then -- it is the bitmap index.

bitmap indexes are not appropriate for tables that have lots of single row DML
operations (your inserts) and especially -- i mean ESPECIALLY -- concurrent
single row DML operations.

The bitmap index is the cause of your heartburn and is something you cannot
afford to have one a table that you feed in the manner you are.  You need to get
rid of that bitmap index!

Think about how they work:

KEY_VALUE1    row1 row5 row10 row11 row12 row100 row102 ......
KEY_VALUE2    row2 row3 row4 row14 row22 row99 row103 ......

each entry in the bitmap index points to many many many rows.

Suppose thread 1 inserts key_value1.  It'll lock that entry in the bitmap index.

Now thread 2 inserts key_value2.  It'll lock THAT entry in the bitmap index.

Now, thread 1 inserts key_value2 -- it'll block, waiting for thread 2 to commit.

Now, thread 2 inserts key_value1 -- it'll block -- waiting for thread 1 to
commit.

DEADLOCK.  Bam -- game over player 1 -- you lose.

Not only that but bitmap indexes deteriorate faster then you can spell Oracle in
single row DML situations.

Bitmaps are appropriate in read only/ read mostly environments (data
warehouses).  Bitmaps should generally be disabled and rebuilt before/after
loads into these systems.  They are a special purpose index that is not at all
appropriate for the type of environment you describe.

Simple test for you:

---------------- test1.sql ---------------------------------
set echo on

drop table t;
create table t ( x varchar2(20) );
insert into t select decode( mod(rownum,2), 0, 'HI', 'BYE' ) from all_objects;

create bitmap index t_idx on t(x);

insert into t values ( 'HI' );
prompt goto another session and @test2.sql
pause

prompt goto the other session and hit enter
insert into t values ( 'BYE' );
----------------------------------------------------------------------

----------------------- test2.sql ------------------------------------
set echo on

insert into t values ( 'BYE' );
prompt goto window1 and hit enter
pause
insert into t values ( 'HI' );
-----------------------------------------------------------------------

results of test1.sql on my system:

ops$tkyte@ORA920.US.ORACLE.COM> drop table t;

Table dropped.

ops$tkyte@ORA920.US.ORACLE.COM> create table t ( x varchar2(20) );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into t select decode( mod(rownum,2), 0,
'HI', 'BYE' ) from all_objects;

29311 rows created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create bitmap index t_idx on t(x);

Index created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into t values ( 'HI' );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM> prompt goto another session and @test2.sql
goto another session and @test2.sql
ops$tkyte@ORA920.US.ORACLE.COM> pause

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> prompt goto the other session and hit enter
goto the other session and hit enter
ops$tkyte@ORA920.US.ORACLE.COM> insert into t values ( 'BYE' );
insert into t values ( 'BYE' )
            *
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource

you could set initrans to as high as you wanted -- won't solve the issue --
the issue here is the bitmap index!!!

Review &
      Followup

      Rating: 5
Wouldn't have
      thought to look there  October 21, 2002
Reviewer:
      A reader
Cool, now I just have to explain to the developers that it WASN'T their code.

October 19, 2002 Ashiq -- Thanks for the question regarding "how
      to run OS commands in pl/sql block", version 8.1.7

      You Asked
Hi Tom,

1) Is it any possible run a SQL loader through pl/sql block.

2).Tom i've a 2 database  in different location .Say i'm using database1 and i
want to access database2 which is in different location.explaing me how to
create a global link for the database2.

thanx,
A.Ashiq

      and we said...
1)
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:952229840241
found by searching for

host command stored procedure

on this site

2) fortunately we've taken the unprecedented step of documenting all SQL
commands you can execute!

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a85397/statem6b.htm#2061507
is the documentation for the CREATE DATABASE LINK command which does this!!  For
all of the details, we have a document:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76960/toc.htm
 Oracle8i Distributed Database Systems

all documentation is available on
http://otn.oracle.com/
under "documentation".

Sorry if this sounds "sarcastic", it just seems that no one uses the search OR
the documentation.  Don't take it personally -- not venting at you personally --
but just putting this out there as a "gentle" reminder that these resources do
exist!

Review &
      Followup

      Rating: 5
Absolutely
      right  October 19, 2002
Reviewer:
      A reader  from Pitts, PA

October 19, 2002 Awan -- Thanks for the question regarding "How
      can we design the big database. e.g. Tera byte Database", version 8.1.7

      You Asked
Dear Kyte:

You are really doing a great job, and the way you explain the questions, I
really love that style, because it really make the things clear.

Well my question is:

1)Some times we asked in interview, that how will you design the very big
database, e.g. of tera bytes or may be more than this. And how you will
confiugre the SGA? How many tablespaces you will make? And how many datafile
will you make? And how many rollback segments will be created etc. So how can we
answer this question.

2) And if we have some powerfull machine with multiple processors and many
physical hard disks, then the database will be designed differently or will be
changed with respect to the multiple processors.

Thanks and Regards,
Awan

      and we said...
1) and I would look at them like they were crazy.

I would ask them -- "ok, so tell me how you would configure the SGA and I'll
give you 5 examples of tbyte+ instances where your method would be totally
messed up"

"tell me how many tablespaces you would have" and I'll give you 5 example where
your method would be totally messed up

same with the datafiles

and rollback segments -- ESPECIALLY this one (well, the SGA question is a close
second).

You can only talk in generalities here.  You size the SGA based on the needs of
of the application.  It this a terabyte transactional system or a terabyte data
warehouse (we do both).  The needs would be totally different.  How much ram is
on the machine, how many users (wanna leave a bit of space for the PGA you
know).  Anything else going on here on this machine we need to be aware of?  My
first response would be "well, since this database doesn't exist yet, we should
not even for a second consider using 817 but rather 9iR2 (or whatever is the
latest production release of Oracle as you are reading this) since it has about
a gazillion features for doing big databases that 817 didn't.

How many tablespaces?  well, won't that be a function of the number of objects
-- and you are using locally managed tablespaces so the right answer might be "I
want three -- small, medium and large".  For a system like think, you won't be
micromanaging storage -- you'll be letting the hardware dudes do that at the
storage array you'll be using.  So, you don't need to segregate objects by
tablespace and GIVEN that we are using 9i, we'll have block level recovery which
means we can recover from corruptions keeping the datafile and tablespaces
online and avaiable so we don't need thousands of tablespaces.  (the answer
won't be three, it'll be some number greater then three, but that is about all I
can say).

How many datafiles?  depends on the OS doesn't it.  We'll be using the biggest
we can -- generally in the 16 to 32gig range most likely.

Rollback segments -- we, we are going to use 9i, so RBS configuration is not
done by "number of rbs's and size of each" but rather "what is the retention
period we need to specify".  and that is based solely on the length of the
longest running query so..... insufficient data to compute!

2) I've never heard of taking the number of CPU's into consideration when
designing a physical schema personally.

We might use different features (lots more PARALLEL operations for loading,
indexing, bulk updates, etc) but I've never thought about doing something in my
physical design based on the number of cpu's -- no.  (the one thing that'll
change is the number of cpu's - we add more, we change hardware -- #cpu's is not
a constant.  in fact, it can change while the system is up and running these
days!)
Review
      & Followup

      Rating:
      5
  October 20, 2002
Reviewer:  Ghulam
      Rasool
Awan asked a good question and as usual YOU replied well. I wish the people who
ask these kind of questions, read this.

      Rating: 5
Many thnaks for
      such a nice answer.  October 20, 2002
Reviewer:
      Awan
Dear Kyte:

Well it is very good answer indeed. You are smart. I am happy that you explain
the things in a very proper manner.

October 19, 2002 Syed Jaffar -- Thanks for the question regarding
      "automatically revoking permission from objects", version 8.1.7

      You Asked
Hi Tom,

Is there any way to revoke permission on a given object automatically? I mean,
if I want to grant some permission on a object for some time and then it should
revoke those permission automatically after specified time. For example, I want
to givem permission like select on a table for one week time only? Is it
possible rather than revoking manually.  Or do we have to write a prcoedure to
submit a job that revoke the permission after specified time? Thanks in
advance..

      and we said...
You use dbms_job to submit the command:

dbms_job.submit( l_job,
                 'execute immediate ''revoke select on t from bob'';',
                 sysdate+7 );

You don't need to write a procedure, just execute the above command.
Review &
      Followup

      Rating:
      4
  October 19, 2002
Reviewer:
      Pauline  from NY,USA

October 19, 2002 raja -- Thanks for the question regarding
      "message from sales to store department", version 6i

      You Asked
Hi,

  I'm using oracle 8.1.6 and developer 6i. I'm using only one user "xyz" behind
all forms. I want that as the operator in sales department enters "sales
invoice" and reserve the stock against that invoice, the message or some kind of
indication should be displayed on the "issue form" automatically to the operator
of store working on this form.

Thanks
Raja M Salman
Analyst Programmer
Zensoft

      and we said...
One might use DBMS_PIPE or DBMS_ALERT for this sort of interprocess
communication.

The "issue form" would periodically poll a pipe or look to see if an alert was
signalled and display whatever information it wanted to.

See the supplied packages guide that comes with the database (documentation)
October 20, 2002 Ashiq -- Thanks for the question regarding "To
      check performance issue", version 8.1.7

      You Asked
Hi Tom,

  It's nice to ask u questions again.

1).User is  complaining the databases slow .As a dba what are the all the things
i've to check first and take appropriate action .Since i'm fresh in dba related
issue ,plz explain me in detail.

2).how to find a table(for example emp) how much of  bytes and number of extents
it used.

3)how to solve this error
scott@RMSREAL> ed
Wrote file afiedt.buf

  1  declare
  2  v_sql  varchar2(4000);
  3  begin
  4    v_sql := 'SELECT   rmgt_t_rates.ck_rate_config_no,
rmgt_t_rates.rate_config_description,
  5                    rmgt_t_rates.rate_start_date, rmgt_t_rates.rate_end_date,
  6                    rmgt_t_rates.fk_billing_type, rmgt_t_rates.rates_type,
  7                    rmgt_t_rates.rates_service_time,
rmgt_t_rates.rates_service_type,
  8                    rmgt_t_rates.rates_linen_time,
rmgt_t_rates.rates_linen_type,
  9                    SUM (rmgt_t_rates_split.rates_split_amount) AS totalamt
 10               FROM rmgt_t_rates, rmgt_t_rates_split
 11              WHERE rmgt_t_rates_split.ck_rate_code =
rmgt_t_rates.ck_rate_code
 12                AND rmgt_t_rates_split.ck_rate_config_no =
rmgt_t_rates.ck_rate_config_no
 13                AND rmgt_t_rates.ck_rate_code = strratecode
 14           GROUP BY rmgt_t_rates.ck_rate_config_no,
 15                    rmgt_t_rates.rate_config_description,
 16                    rmgt_t_rates.rate_start_date,
 17                    rmgt_t_rates.rate_end_date,
 18                    rmgt_t_rates.fk_billing_type,
 19                    rmgt_t_rates.rates_type,
 20                    rmgt_t_rates.rates_service_time,
 21                    rmgt_t_rates.rates_service_type,
 22                    rmgt_t_rates.rates_linen_time,
 23                    rmgt_t_rates.rates_linen_type' ;
 24  dbms_output.put_line(v_sql);
 25* end;
scott@RMSREAL> /
declare
*
ERROR at line 1:
ORA-20000: ORU-10028: line length overflow, limit of 255 bytes/chars per line
ORA-06512: at "SYS.DBMS_OUTPUT", line 34
ORA-06512: at "SYS.DBMS_OUTPUT", line 110
ORA-06512: at "SYS.DBMS_OUTPUT", line 81
ORA-06512: at line 24

------
thanx,

A.Ashiq

      and we said...
1) arg -- if only people knew how frustrating this quesiton is to me!!!!

"it is slow, what do I do".  Well, it all starts way back in the development and
design phase.  You would have set standards by which you can benchmark your
application (so the developers know they built it right).  You would have
designed the physical structures to answer your questions efficiently (not
developed some generic funky model that can do everything including slice bread
on the weekends).  You would have statistics collections in place so that YOU
would have known before a single end user came to you that there was a problem
brewing out there.  You would know when it began, and that would help you
pinpoint what changed.  Oh yeah, you would have a change log too so that when
you detected it began, it would be trivial actually to see what changed.

Anyway.  given that you are doing this backwards the only thing I know to do is

a) get all of the people involved into a room
b) develop a plan of attack
c) first step if the only thing you can plan now and that is "DETERMINE WHY" it
is "slow" and determine how "fast is fast" (so you know when you are done)

DO NOT, repeat DO NOT just start flipping switches, moving datafiles, rebuilding
indexes, re-orging tables, speaking magic incantations over the database, etc.
First and foremost you must identify WHAT is the problem and then come up with a
solution to fixing the problem.  Do not "guess", find it -- fix it and then
implement the stuff I talked about above so it doesn't happen ever again.

Sorry I cannot tell you exactly what to do -- you must get the people who know
how the application works working on this in order to see what possibly could be
the bottleneck.  Is your problem a SCREEN, an APPLICATION, the entire SYSTEM, a
certain key function, etc -- isolate the problem first.

2) based on this question and #1 -- I'll augment #1.  Get an experience group of
people together to help you.  You sound fairly new to Oracle and databases....

DBA_EXTENTS is the view you are looking for, query that and you'll find the
info.

3) put out less then 255 bytes per line???  see the error message -- "limit of
255 bytes/chars per line"....

create or replace procedure p ( p_str in varchar2 )
is
   l_str   long := p_str;
begin
   loop
      exit when l_str is null;
      dbms_output.put_line( substr( l_str, 1, 250 ) );
      l_str := substr( l_str, 251 );
   end loop;
end;
/

is a function I use myself to print long lines like that for debug purposes.

Review &
      Followup

      Rating: 5
regarding
      2  October 20, 2002
Reviewer:  A reader

Hi

Regarding question 2 we can also get the info from dba_segments right? I am
asking because I recall in OCP tests they ask how to obtain number of extents
and answer was dba_extents however it's my understanding that dba_segments has
an EXTENTS column showing number of extents of tables, partitioned tables
partitions, indexes etc etc

          Followup:

sure, as USUAL there are many ways to crack this nut.

      Rating:
      5
  October 22, 2002
Reviewer:  Ashiq
       from Karnataka,India
Thanx for ur valuable advice.

October 20, 2002 J. Laurindo -- Thanks for the question regarding
      "Commit someone´s session", version 8.1.7

      You Asked
We all know the ALTER xxx DISCONNECT option, but how to do the opposite, COMMIT
another session ? AFAIK Oracle don´t give this option to DBAs , AFAIK, but
techcally it can be done, I think : afterall, in the server there is a listener
process waiting por network packets : IF, from another machine, I spoof and send
to this listener process one packet containing the COMMIT instruction and the
right headers (as if it was send from the pending process´s machine) , it will
works ? Or no, there is something more impending Oracle from give us this
ability ?

Regards,

 Chiappa

      and we said...
This would be the worst of all possible capabilities.

Why are you smart enough to know that another session, sitting there in some
unknown (TO YOU) state is in need of committing?  This option if provided would
probably be enabled via an init.ora parameter like:

  allow_dba_to_logically_corrupt_database_at_will = true

No, this "feature" doesn't exist and hopefully never will.  The only thing you
can do it roll it back -- that is "safe", that will never logically corrupt the
database.
Review & Followup

      Rating: 3
Useful answer,
      confirmed others thoughts about  October 20, 2002

Reviewer:  J. Laurindo Chiappa  from Sao paulo, SP,
      Brazil
Thank you, it was userful to confirm my thoughts about this doubt.

October 20, 2002 Raj -- Thanks for the question regarding "When
      you can say a Database is VLDB in Oracle ?? any standards ?", version
9.0.1

      You Asked
I was asked this Question in a recent Interview.When you can say that a database
is VLDB( very large database base)?.Is there any standards which talks about
database sizes,where by you can day this database is "Small"/medium/large. and
what init.ora parameters you set different for these databases.

My Anwser is : There is No standards exists which speaks about this and there is
No exact defined size range or limit to distinguish database size and term
"VLDB" usually used for database of large size ,but again there is no size range
or limit..and usally terabyte size database are usally called VLDB....Even the
famous TPC doesn't distinguish and use names like VLDB for databases.Some people
use custom in-house standards distinguish depending on application usage/amount
of data/amount of growth/...

The second part of Question : I simply don't set the init.ora parameters ,,just
only considering the size of database into account.Many parameters depends the
applications usage/hardware resources /data or dimesional model/#users/.. and
started explaining with few examples.

Interviewer bluntly disagreed with my explanation. Please provide your opinion
and explaination ? how some one can convince interviewer on such questions.

-Raj

      and we said...
You got lucky then -- you barely missed getting hired into a place you wouldn't
want to be!

If someone asked me what VLDB was -- I would say basically the same thing.  I've
seen VLDB go from 500meg to 50terabytes.  Today anything in that *range* would
be "vldb".  There is now talk of XLDB ;)  (extra-large database).  Wonder what
that is.

This is similar to this recently question:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6047656036459

And you are right about the init.ora parameters -- all of them are set
differently for each database.  Riddle me this batman:

Is this VLDB transactional or DW?  -- log buffer will vary depending on that now
won't it.  undo_retention will differ radicially.

How many users?  -- that might affect processes, sessions and other similar
settings.

Going to use shared server or not -- that'll have something to do with our
parameter setting.  (hint large_pool_size..., mts_* ....)

Using java stored procedures? -- (getting the point by now right...)

OLTP -- shared pool will be setup slightly differently then a DW would be.

Note that none of the above has SQUAT to do with the size of the database does
it?

Review &
      Followup

      Rating: 4
Thanks
      tom  November 10, 2002
Reviewer:
      A reader
Thanks tom

October 20, 2002 Albert -- Thanks for the question regarding "how
      to rebuild a table in 8.0.6.1.0 EE", version 8.0.6

      You Asked
Hi Tom,
Please help me out here. We have an 8.0.6.1.0 EE system on solaris 2.6. (due to
a absolute awfull financial application, we cannot upgrade to decent Oracle
version like 8.1.7.x.y).
We have about 30 tablespaces, db size is about 16 GB. Most tablespaces are
around 600M. db blocksize=16384. I have plenty of diskspace free. I don't have a
testmachine :-((
A certain table reaches its maximum number of extents: its now at 980 extents
and the max_extents is 1024. (The initial and next extent was only defined at
160 K). I know that in 8i one can issue the "ALTER TABLE.. MOVE" command, which
is not possible in my case. Also, this particular table has many triggers
defined, an there are also many PK-FK relations with other tables. I do not know
how to rebuild this table with a better extent definition. Can I just export the
table and then imp it again, but what about triggers and PK-FK relations?
Can I use "insert into temp_table as select from ..."?
Thanks for your support!!

      and we said...
Alter it's next extent to be more reasonable and set max extents to a larger
size and don't lose sleep over it.

Unless you have identified this as being a verifable, provable performance issue
and have the hard numbers to back it up -- don't touch it.  Fix the impending
issue (set next bigger, up max extents) but don't touch it otherwise.

Having hundreds/thousands of extents is OK.  In 8.0 -- the types of operations
that would be most impacted by this are:

o drop table
o truncate table
o altering the table and moving it (if you could)

because of the dictionary managed tablespace.  So, to avoid the performance hit,
don't drop it, don't truncate it, don't try to move it!

Review &
      Followup

      Rating:
      5
Thanks!  October 20, 2002
Reviewer:
      Albert  from Netherlands
Really, thank you! I did not now I could just increase the max number of
extents.

October 20, 2002 Remigiusz -- Thanks for the question regarding
      "R-trees", version 8.1.7

      You Asked
Could u be so kind and give me some guidelines how to implement R-tree in Oracle
(external procedures should be written in C)?

Remigiusz

      and we said...
You do not have to write it

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/inter.817/a85337/sdo_intr.htm#877657
you buy it.  To write it would take such an effort that honestly - buying it is
the right choice.  If you decide to do it yourself (bad choice), read:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76937/toc.htm
about building domain indexes (and talk to you in a year or two when you are
finally finished with lots of code you have to maintain!!)

October 20, 2002 -- Thanks for the question regarding "commit",
      version 9.0.1

      You Asked
Hi Tom,

I am using 9.0.1. I just want to confirm function about "commit". I have sql
code below in a procedure.

delete from abc;
insert into abc select * from xyz where column1='xxx';
commit;

Is it possible other user to see nothing on table abc when above code is
running. You know the user may query on table abc after deletion and before
insertion.

If yes, how can I avoid this. I don't want any one to see the empty table.

Thanks in advance.

      and we said...
It is not possible.

No other transaction will ever see the effects of your delete until you commit
and by then -- you've inserted so they'll see that data.

That is what transactions are all about!
Review &
      Followup

      Rating:
      5
  October 20, 2002
Reviewer:
      A reader
Thanks Tom.

October 20, 2002 -- Thanks for the question regarding "what does a
      DBA need?", version 9i

      You Asked
First of all thanks a lot for your prompt and useful help.
I have tow questions.

1) What is needed for a person to apply for an Oracle DBA job? Is it enough to
be an Oracle Certified Professional and having expertise on Database
Administration or there is some other Computer Degrees are needed?

2) What a DBA you expect from a finely tuned DATABASE?

Thanks
Natalia

      and we said...
1) it depends wholly on who is interviewing you.

OCP is almost certainly not enough for anything beyond an ENTRY LEVEL (eg:
bottom of the ladder) positition.  OCP means you read the books -- good start,
but not good enough.  Sort of like coming out of college -- brimming with
potential but unproven.

For anything above entry level -- people would expect experience, a proven track
record, good references, ability to communicate (me, I value that somewhat more
then anything else).

2) long lunches, nights full of sleep, late mornings, early dinners....

Review &
      Followup

      Rating:
      5
  October 20, 2002
Reviewer:
      A reader
nice!

      Rating: 4
NICE
      REPLY  October 20, 2002
Reviewer:
      Parthesh.G.Kavishwar  from INDIA,Ahmedabad
I totally agree with tom and by his words as "people would expect experience, a
proven track record, good references, ability to communicate ".In india too we
believe in communication.

It would be nice if a person has on OCP certification and to top it all a VERY
GOOD Experience.

      Rating:
      2
  October 20, 2002
Reviewer:
      A reader
Tom,

If everybody needs experienced dba, then how will freshers get chance, will they
ever be experienced...

          Followup:

I didn't say everyone needs experience.  I broke it into two categories

o ENTRY LEVEL (eg: no experience required).  There an OCP is like a college
degree -- briming with potential

o every thing else above entry level.  There is OCP is like a college degree --
nice to have but hey -- your experiences, references, ability to communicate,
etc -- they count tons

So, you need to look for entry level positions - typically in a larger company
with an established DBA team.  The ad I answered when I got out of college was:

"PL/I programmers wanted.  No experience necessary. Must be able to get security
clearance."

Hey, that described me in a nutshell "no experience necessary".  I had no idea
with PL/I was (turned out to be about the coolest language I ever programmed
with -- too bad it isn't more mainstream, it was really cool).  But I definitely
"had no experience".  I had transcripts from college, I had interview abilities
(communication skills) and my good looks -- well, two out of three.   It took a
long time to get to know what I know now.

      Rating:
      5
  October 20, 2002
Reviewer:
      A reader
Cool..thanks Tom

      Rating: 3
You forgot
      one!  October 21, 2002
Reviewer:  Steve
       from UK
1.  A sense of humour (or humor if on the left hand side of the pond!!!).

October 20, 2002 -- Thanks for the question regarding "performance
      on index and view", version 9.0.1

      You Asked
Hi Tom,

I have couple question about index and view.

1. Can I create an index on view? If yes, is it same as index on table?

2. I need first 10 rows in the query. If I add hint on it, will it does the job?
more efficient? If not, is there any way to get more effecient.
   select * from
     (
       select /*+ FIRST_ROWS(10) */ col1,col2 from t where col3='aaa' order by
seqid desc
     )
   where rownum<=10;

3. I have a small table (200 - 300 rows). The rows are frequently changed( only
delete, insert, no update transaction) Within 20 seconds all the rows are
deleted and new rows are inserted. This table is queried every seconds by the
users. Is it worth to create any index on it?

4. I have two views. view_a is changed frequently. The query results is changed
every seconds. view_b is more stable than view_a. The query results may be
changed every 3-4 hours. If I want to join both views together(view_a is
master). Do you think it is efficient than I join view_a and the tables instead
of view_b? see following code.
  create view view_a (c1,c2,...) as
    select c1,c2... from t1,t2,t3 where ... with read only;
  create view view_b (d1,d2,...) as
    select d1,d2... from s1,s2,s3 where ... with read only;

approch 1:
select c1,c2,...,d1,d2... from view_a, view_b where c1=d1;
approch 2:
select c1,c2,...,d1,d2... from view_a,s1,s2,s3 where c1=d1 and d2=...

which approch is more efficient? A view query on another view is good thing or
depends?

Thanks in advance.

      and we said...
1) no, you create indexes on base tables.  Views can use those indexes.

2) That is the design intention of that hint, yes.

3) depends on how they query it, how big it is and what they do with it.  so,
insufficient data to compute answer.

4) I don't know what you mean by "view_a is changed frequently".  what are you
doing there -- I suspect you really mean "the data underlying the view is
changing" in which case -- it is pretty much "business as usual for Oracle".

I would probably tend to use approach #3

create view view_c
as
select ... from t1, t2, t3, s1, s2, s3 where .....;

Review &
      Followup

      Rating:
      3
  October 20, 2002
Reviewer:
      A reader
Thanks for answering.
Regarding #4. view_a and view_b are existing views. People are using them now.
For you approach, do you mean create a view base on the table instead of
existing view is more efficient?

Thanks.

          Followup:

It is my custom to create a view and use a view and rarely join views with other
stuff as there can be limits (due 100% to the syntax - not because of any limits
in a views in general) as to what can be pushed down.

So, if I know I have a req to constantly join view_a to view_b, I would instead
create view_c that does the work that joining view_a and b would have done.

October 20, 2002 Pawan -- Thanks for the question regarding "Which
      is faster - Database or a programming language like C++", version 8.1.7

      You Asked
I recently interviewed at a very large organization for a Data Warehouse
Developer position. One of the questions I was asked was what would be the best
way to load huge amounts of data (600 million records) from a flat file to a
table or from one or more tables into a target table. I said depending on the
complexity and other issues I will use SQL*loader or Direct path insert etc. But
the interviewer was of the opinion that writing code in C++ was much more
efficient than doing it in the database. I have had prior experience of loading
very complex data and always used the database to do it and never was
performance an issue. I just wanted to know your opinion in this regard.
Thanks

      and we said...
Wow -- this is like the tenth interview question this WEEKEND!  Whats up with
that (not all from you of course -- just got lots of them)

The interviewer was, IMHO, smoking something.  Totally.

I would love to see their code beat the direct path loader -- I seriously doubt
they were using the direct path OCI API's.

Even if theirs tweaked out the direct path loader, I would still say "not the
right way, I personally do not want to maintain a ton of custom C++ (especially
c++) code" -- but, it wouldn't.

My answer would have been:

you want to be in 9i using external tables so you can do a direct path load
using CREATE TABLE AS SELECT or INSERT /*+ APPEND */ in parallel (letting the
database determine the DOP even) without generating the gobs of undo and redo
you are.

Short of that, if you force me to use 817 even today -- I would use sqlldr and
if the data needed to be "tweaked" a bit before going in -- a simple

cat inputs | sed | awk > named_piped &
sqllldr data = named_pipe

would be preferable to tons of custom code.

Review &
      Followup

      Rating:
      4
Thanks  October 20, 2002
Reviewer:
      Pawan Putra  from NJ, USA
I talked about the External table feature and the interviwer said - " Not until
2004 are we moving to 9i". Anyway I came to know from the recruiter that I might
be offerred the position but I am seriously thinking if I should take it and
then have arguments everyday.
      You will be happy to know that another person who interviewed me at the
same place asked questions ( about 15) all chapter wise starting with
Architecture to Invoker and definer rights from your book.

      One Suggestion - Is it possible to compile all the interview type
questions and place it under a separate subject?
Thanks again.

          Followup:

2004, great, 10i release 2 will be out by then.

anyway - since I didn't have a "faq" for interviews (and I've been really truly
lazy about using that feature here), no -- beyond searching for "interview"
here, no not really.  sorry.

      Rating: 3
Regarding Your
      Reluctance to Create an Interview FAQ.  October 21, 2002

Reviewer:  Richard  from Oxfordshire, UK
Oh, go on, Tom! Imagine the good will engendered by setting up that FAQ... it
could lead to an international hands-across-the-water love-in!

October 21, 2002 yuwen -- Thanks for the question regarding
      "oracle8i database full EXP/IMP", version 8.1.7

      You Asked
tom:

   the following is what I want to do:

   FROM win2000(serverA) I exp full oracle8i database.
   if i want to imp(full way) it to serverB(win2000+8i),Does serverB
   must have the same datafile location as serverA(I must create the
   same datafile directory in serverB as serverA)?
   can I imp(full way) it to a unix machine?

 thanks

      and we said...
if you have a full database export, imp will create everything (users,
tablespaces, files, everything).  If you want the filenames to be different,
you'll have to take care of that ahead of time.

You can precreate all or part of the database (eg: if the source system was Unix
and the target NT, you'll need to precreate the tablespaces since the filenames
from unix won't work)

Review &
      Followup

      Rating: 3
Export/Import
      Oracle 8i and eBusiness Suite 11i (11.5.5)  November 27,
      2001
Reviewer:  John E. Peterson  from Greenwood
      Village, CO 80111 USA
Hi Tom,
I am trying to export/import an Oracle eBusiness Suite db,
source is 11.5.5 on a sun, target is on a Compaq Tru64.
DB is 8.1.7. right out of the 11i box.

Problem is IMP does not work, leaves stuff like Java and XML far behind,
thousands of these dba_objects are missing.
OWS is working on it. and from 11.5.1 to 11.5.5 imp is getting better, but still
isn't there someplace to find
a work around when IMP doesn't work with the new tech stack?

      Rating: 5
other
      way  October 21, 2002
Reviewer:  atul
       from India
Sir,

If we have to do exp/imp from NT to unix
ie source is NT and destinatio is Unix
Should we have to precreate tablespaces in unix??

Thanks.
atul

          Followup:

if the file naming conventions are different, or even just the paths you want to
use are different -- yes you will.

October 21, 2002 Muhammad Ahmad -- Thanks for the question
      regarding "Inserting Unknown values", version 8.1.7

      You Asked
I have a table "trans" with date column "tdate". In this column there are more
than 10000 records of different and same dates. The dates which are inserted
contains all the months except "JULY" and "DECEMBER".

My Problem is: I want a query FROM this table so that it give me the Output as

Month
------
JANUARY
FEBRUARY
MARCH
APRIL
MAY
JUNE
JULY
AUGUST
SEPTEMBER
OCTOBER
NOVEMBER
DECEMBER

12 ROWS SELECTED.

I mean the result contains not only all the 12 months but also by monthwise
order not by alphabetically. I have done all except for showing "JULY" and
"DECEMBER".

I use the query:

select distinct to_char(tdate,'MONTH') "Month" from trans
order by  to_date(to_char(tdate,'MONTH'),'MONTH');

      and we said...
if you just want a list of months ordered by when they "happen" calendar wise,
that would be:

select to_char( add_months( trunc(sysdate,'year'), rownum-1 ), 'Month' )
  from all_objects
 where rownum <= 12
 order by add_months( trunc(sysdate,'year'), rownum-1 );

October 21, 2002 raj -- Thanks for the question regarding "queue
      tablespace nologging", version 9.0.1

      You Asked
Hi Tom,
Is it possible (or does make sense ) to have all AQ created on a separate
NOLOGGING tablespace, with a view to improve queing/dequing performance? If I do
create queues in a nologging tbsp, what happens if by enqueue/dequeue stmt are
embedded within a transaction, among other sql stmts?

Thanks,
Raj

      and we said...
Nologging works ONLY for very very specific statements, see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:485221567528

So, it'll have NO effect whatsoever.

But suppose it "did" work -- that would make the AQ tables unrecoverable in the
event of a media failure -- so, is the data in these queues important or not?
If it is "not important" -- eg: it is OK to lose messages -- you should use the
non-persistent (memory) queues.  If it is important, you don't want NOLOGGING to
work as you think it did! (as it won't)
Review &
      Followup

      Rating: 3
which pool are
      non-persistent queues stored in?  December 13, 2002

Reviewer:  A reader
Hi Tom, its strange but non-persistent queues for me atleast, are performing a
lot worse than persistent queues and I am trying to make non-persistent queues
faster. At yesterday's (12/12/02) NYOUG meeting, someone told me that np queues
are stored in the large pool but it doesn't make a diff if I increase
large_pool. Any idea where these are stored in memory? I want to know if there
is something that is fundamentally wrong with np-queue architecture within
oracle or am i doing something stupid.

Thanks as always.

October 21, 2002 Piyush -- Thanks for the question regarding
      "Concatenation problem", version oracle 8.1.5

      You Asked
Hi Tom,

I have a table like this

Class Table
classId className parent Id
1    Aviation    0
2       Space        1
3    Flying        2

Product table
classId PrdouctId ProductName
3    1      AA

Counterparty table
CounterpartyId PrdouctId Counterparty Name
1        1      XX

Input to my procedure would be Counterparty ID.
I have to get the names and Ids of all the products offered by
this counterpary with the name of the class for the particular
product concatenated with names of the parent classes.
The output should be in a cursor variable.

e.g. Input is counterpartyId = 1
Output should be

PrdouctId ProductName className
1        AA    Aviation Space Flying

Can I solve the problem in a single select?

Thanks and regards
Piyush SInghal

      and we said...
If you know the maximum number of classes in a hierarchy, the
following will work well for you:

ops$tkyte@DEV816> select c.productId, p.productName,
  2         ( select ltrim(
  3            max(decode(rownum,10,' '||className,null)) ||
  4            max(decode(rownum, 9,' '||className,null)) ||
  5            max(decode(rownum, 8,' '||className,null)) ||
  6            max(decode(rownum, 7,' '||className,null)) ||
  7            max(decode(rownum, 6,' '||className,null)) ||
  8            max(decode(rownum, 5,' '||className,null)) ||
  9            max(decode(rownum, 4,' '||className,null)) ||
 10            max(decode(rownum, 3,' '||className,null)) ||
 11            max(decode(rownum, 2,' '||className,null)) ||
 12            max(decode(rownum, 1,' '||className,null)) )
 13             from class
 14            start with classId = p.classId
 15          connect by prior parent_id = classId ) classNames
 16    from counterParty C, product P
 17   where c.counterPartyId = 1
 18     and c.productId = p.productId
 19  /

 PRODUCTID PR CLASSNAMES
---------- -- ------------------------------
         1 AA Aviation Space Flying

In this case, I said "there will be no more then 10 classes to
glue together"...

if the total number is unknown, this will work:

ops$tkyte@DEV816> create or replace
  2  function get_classes( p_classId in varchar2 )
  3  return varchar2
  4  as
  5      l_return varchar2(4000);
  6  begin
  7      for x in ( select className
  8                   from class
  9                  start with classId = p_classid
 10                connect by prior parent_id = classId )
 11      loop
 12          l_return := x.className || ' ' || l_return;
 13      end loop;
 14
 15      return l_return;
 16  end;
 17  /

Function created.

ops$tkyte@DEV816> select c.productId, p.productName,
  2             get_classes( p.classId ) classNames
  3    from counterParty C, product P
  4   where c.counterPartyId = 1
  5     and c.productId = p.productId
  6  /

 PRODUCTID PR CLASSNAMES
---------- -- ------------------------------
         1 AA Aviation Space Flying

ops$tkyte@DEV816>

Note that the string returned by the function (in both cases)
will be limited to 4000 bytes and you will not be successful
exceeding that.  If you need to exceed that you will have to use
a TEMPORARY CLOB and a function that creates it and returns it.

Review
      & Followup

      Rating: 3
Using temporary
      clobs for this ?  October 21, 2002
Reviewer:
      Kim Berg Hansen  from Middelfart, Denmark
Hi, Tom

I have a similar problem to this, and I've planned to make a function such as
your get_classes() to return a temporary clob and do a select of that function.

My question is : How do I handle memory when doing this ?

If I do a select of get_classes() that returns multiple rows, then get_classes()
will create several temporary clobs, right? How can I free that memory after the
select statement ?

Thank you for allways informative answers.

Regards

Kim Berg Hansen

          Followup:

the CALLER that retrieves this clob (assuming a temporary clob) would be
responsible for freeing the temporary clob using the dbms_lob package.

October 21, 2002 Rob -- Thanks for the question regarding
      "enabling Function-Based Indexing", version 8.1.6

      You Asked
Hi Tom,

I want to avail myself of F-B-indexes, and am trying to issue
DDL simialr to the following:

CREATE INDEX ix_ssn_part4_bulk08
  ON x_tbindpvoiecvbulk08 (SUBSTR(ssn,6,4))
  TABLEPSACE testbulkindex
  PCTFREE 5
  STORAGE (INITIAL 10M NEXT 10M PCTINCREASE 0 )
  UNRECOVERABLE;

Problem is, I get an ORA-00439 "feature not enabled" message.
I've tried to work around this by adjusting the init.ora
parameters COMPATIBLE (up to 8.1.6) and also
QUERY_REWRITE_ENABLED to TRUE.  But I still receive the
ORA-00439.  I'm running 8.1.6 on AIX.  Maddeningly, my little
Personal Oracle8i DB running on an NT workstation has no problem
with this DDL.

Do you know what's wrong?
Thanks,
Rob

      and we said...
A matrix of what features/options are available with Enterprise,
Standard and Personal Oracle can be found at:

http://technet.oracle.com/doc/oracle8i_816/server.816/a76962/ch4.htm#73594
Function Based Indexes are part of Enterprise Edition.  Most of
Enterprise is also available in Personal for development
purposes.  Function based indexes are not available on Oracle
Standard Edition.
Review & Followup

      Rating: 3
9i Personal
      Edition on Win98  October 21, 2002
Reviewer:
      Richard Armstrong-Finnerty  from Oxfordshire, UK
Hi,

The response was great for 8.0, but what about 9i?

I have 9i Personal Edition at home, on Win98, and recently bought a copy of
Beginning Oracle Programming. I have attempted installation of the new Sample
Schemas (HR, OE etc.), as per Appendix D, but am getting ORA-00439 (Partioning
not allowed) when installing the Sales History schema.

Is partitioning possible with this edition? If so, how's it done?

Regards,

Richard

          Followup:

Partitioning is an option that is separately licensed.

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96531/ch5_edit.htm#73594
is the link for 9i like the 8i link above.

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96539/installa.htm#4371
points into the sample schemas documentation -- the SH schema is not available
for use with PE or SE as per that document.  The remainder should install just
fine.

You should have no issues installing all of the samples beyond that.

October 21, 2002 fabio -- Thanks for the question regarding
      "Snapshot too old", version 8.0.6

      You Asked
My question is about fetch across commit.
My understanding is that when I open the cursor the data fetched from the DB are
consistent.
If it so why the update can make my data inconsistent?

      and we said...
The update will NOT make the data inconsistent -- that is the whole point here.

In order for the data to be "consistent", we may need information from the
rollback segments (so we can see what the data USED to look like -- to provide a
consistent read).

If you go into a loop like this:

  for x in ( select * from emp )<<<=== may need the rollback generated by update
  loop
      update emp set ... where empno = x.empno;   <<<== generates ROLLBACK
      commit;                                     <<<== allows that ROLLBACK to
                                                        be overwritten
  end loop;

you will very likely get an ora-1555 since the rollback generated by your update
was released and your QUERY may very well need it.

If you have my book "Expert One on One Oracle", I cover this topic in great
detail.

Or search for

ora-01555

on this site.
October 21, 2002
      Juan Carlos -- Thanks for the question
      regarding "Lock and performance", version 9.2

      You Asked
Hi Tom,

If you are going to run a long process in a table ( inserts, updates ) and no
other person is going to access that table, is advisable to lock it.

You can get some extra perfomance if lock a table in which you are going to run
process that can run more than two hours?

And Why?

Thanks.

P.D. Which answer won the dba challenge I really would like to read that answer.

      and we said...
Oracle never escalates locks.

if no one else is accessing it, just do your DML, there will be NO performance
gain by locking the table.  This isn't DB2!

If others might access it and you want to prevent them, then lock the table.

(we are grading the answers now for smartdba)
Review &
      Followup

      Rating:
      5
  October 16, 2002
Reviewer:
      A reader
Thank you

      Rating: 5
What's the
      answer to the Smart DBA contest?  October 21, 2002

Reviewer:  a reader  from USA
Tom,

This might be a off site question, but I would love to know the answer to the
SmartDBA contest. The dbazine website still shows the old "Extended till October
11th" sign.

And, who was the licky one to win the coveted award?

          Followup:

The licky one -- yuck, gross.

Anyway -- we finished this weekend - they are getting in contact with them now
(I know who it is ;).  Should be posted shortly.

Interesting to note:  we didn't know WHO wrote the papers when ranking them,
they were given to us "blind" if you will.


Subject | Article | Last Updated (East coast time)
Re: Creating a DB with dbassist | article only | Oct 22, 2002 07:12 am
Resumable exports | article only | Oct 22, 2002 07:35 am
Best way to copy From Global TEMP Table to an empty Standard Table | article only | Oct 22, 2002 07:36 am
different results from SP versus SQL under system account | article only | Oct 22, 2002 07:57 am
Number Function how to select | article only | Oct 22, 2002 08:45 am
how many days in a year | article only | Oct 22, 2002 08:51 am
Writing triggers using Dynamic SQL(It does not work) | article only | Oct 22, 2002 09:48 am
SQL*Plus Output Buffer Limit | article only | Oct 22, 2002 11:19 am
Advantage of using Java inside Database | article only | Oct 22, 2002 01:08 pm
why logging in noarchivelog mode ? | article only | Oct 22, 2002 01:53 pm
Ouput in single line | article only | Oct 22, 2002 03:02 pm
SQLPLUS options | article only | Oct 22, 2002 04:10 pm
export >2gig | article only | Oct 22, 2002 04:22 pm
How to retrieve table name using DBMS_SQL | article only | Oct 22, 2002 06:58 pm
UPDATE THROUGH A VIEW WITH JOIN | article only | Oct 22, 2002 07:16 pm
OK to Rebuild Sys owned Indexes Online? | article only | Oct 22, 2002 09:05 pm
Download WEBDB3.0 | article only | Oct 23, 2002 07:02 am
How to find oracle application sigon users and their session id | article only | Oct 23, 2002 07:25 am
dbms_stats | article only | Oct 23, 2002 08:06 am
encrypt, monitoring | article only | Oct 23, 2002 08:36 am
Created new user | article only | Oct 23, 2002 09:30 am
Call an even from trigger only when Commit Issued | article only | Oct 23, 2002 09:41 am
Find relevant companies name | article only | Oct 23, 2002 09:50 am
Query fir retreiving consecutive transactions | article only | Oct 23, 2002 11:17 am
ltrim | article only | Oct 23, 2002 05:23 pm
Upcoming AOTC Presentation in DC | article only | Oct 24, 2002 06:56 am
Oracle Names Implementation | article only | Oct 24, 2002 02:16 pm
Optimizing a view | article only | Oct 24, 2002 02:27 pm
Trigger | article only | Oct 24, 2002 03:16 pm
Database hangs, locking problem? | article only | Oct 24, 2002 03:27 pm
Direct Path for the INSERTS using MERGE | article only | Oct 24, 2002 05:12 pm
Miscellaneous Questions on 8i/9i | article only | Oct 24, 2002 07:00 pm
Fetch Across Commit | article only | Oct 25, 2002 06:01 am
Configuring Personal ORacle With Developer 2000 | article only | Oct 25, 2002 06:11 am
Rollback segment Issue - DBA_ROLLBACK_SEGS -different Initial, Min extent values | article only | Oct 26, 2002 11:34 am
DBLinks between two DBes | article only | Oct 26, 2002 11:45 am
how can I organise a tree struct ? | article only | Oct 26, 2002 12:13 pm
Single SQL statement in a procedure or multiple lines of code? | article only | Oct 26, 2002 12:57 pm
client server problem | article only | Oct 26, 2002 01:06 pm
Hot Backup concept and Changing from Rule to Cost based DB | article only | Oct 26, 2002 01:13 pm
tuning ejb, jdo or other | article only | Oct 26, 2002 02:59 pm
Order of mandatory and optional columns in a row | article only | Oct 27, 2002 10:16 am
Removing Duplicates by keeping unique records. | article only | Oct 27, 2002 10:23 am
Loading data from external file | article only | Oct 27, 2002 11:24 am
B-Tree index and Reverse Key Index | article only | Oct 27, 2002 02:03 pm
Can PL/SQL be pre-compiled by users? P-code? | article only | Oct 27, 2002 04:23 pm
Application Servers and Transactions | article only | Oct 27, 2002 04:25 pm
PL/SQL Error Message | article only | Oct 27, 2002 07:00 pm
Query to Tie Requests to the CC Manager that runs it. | article only | Oct 27, 2002 07:12 pm
Trigger to insert id from sequence | article only | Oct 27, 2002 08:32 pm
Getting the user updating the data through EJB's - using generic a login | article only | Oct 28, 2002 07:17 am
How to avoid compiling 8.1.7.2 ProC code when upgrade to 9i? | article only | Oct 28, 2002 07:25 am
Spell the number | article only | Oct 28, 2002 07:28 am
How many buckets to specify for histograms? | article only | Oct 28, 2002 07:33 am
rollup or cube would calculate the total? | article only | Oct 28, 2002 07:08 pm
Loading Data through C | article only | Oct 28, 2002 07:10 pm
ARCH Destination Failure | article only | Oct 28, 2002 07:26 pm
Extents | article only | Oct 28, 2002 07:53 pm
row(s) 1 - 58 of 58
October 22, 2002 Eduardo -- Thanks for the question regarding "Re: Creating a DB with dbassist ", version 8.1.7
You Asked
Hi Tom, How you doing ?

I'am a new database administrator, and I'm trying to create a new database with
dbassist in Unix IBM-AIX 4.3.3 with Oracle Rel. 3 (8.1.7), but I'm confused
about some parameters that I have to provide to dbassist, in fact, I do not know
what is the right values to give. I want to create a large DB.

I make next selections through dbassist :

1. I select CUSTOM type database to create.
2. OLTP
3. Connected users (300)
4. Dedicated Server Mode
5. Here are select some products by default
   - Oracle Times Series
   - Oracle Spatial
   - Oracle Jserver
   - Oracle InterMedia
   - Oracle Visual Information Retrieval
   - Advanced Replication

   Doubts : 5.1 What are needed this products for ?
            5.2 Do I have to select some of them or all ?
            5.3 What happen if I do not select none of them ?

6. Global Database name ...

   Doubt : 6.1 Parameter Compatible. Do I have to select 8.0.5
               or 8.1.0 ?
           6.2 Database Character Set
               I want create a Database for spanish language.
               What type of Character Set and National Character Set
               do I have to set for Spanish. W8EDEC or E7DEC ?

           6.3 Regarding to SID name. Somebody told me that SID must
               be of four characters long and that it is a Oracle
               recommendation. Is it true?. Or I can assign the SID
               with more than 4 characters ?

7. Control Files
   No Doubts.

8. SYSTEM tbs.

   Doubts :
            8.1 SYSTEM tbs size MB. 260 for large DB ?
            8.2 Extents.
                8.2.1 It is recommended to set autoextend ON ?
                8.2.2 Next (KB) 10240 ?
                8.2.3 Minimum Ext. 64 KB ?
            8.3 Storage
                Is this storage for objects to be created on this TBS.
                8.3.1 It is recommended to set PCTINCREASE to 0%.?
                8.3.2 Initial=Next=64KB for large DB?

9. Redo Log Files

   Doubt : 9.1 It is recommended to set 1MB for each redo log file?

10. Is it enough 10000 for checkpoint Interval and checkpoint time out to 1800
sec.?
What are needed this logging parameter for? Where are used ?

11. SGA information parameter.

    Doubt : What is it the appropiate size for :
            - Shared Pool Size
            - Block Buffers
            - Log Buffer size
            - Process
            - Block size

Can you give me some explanation on this issues?

Thanks in advance.
and we said...
Wow, this is a big one, here goes:

1) ok
2) ok
3) ok
4) ok
5)

5.1)
Oracle Time Series is an extension that allows for OLAP like analysis of time
series data -- computing moving averages, period over period results and such.
You would know if you needed it I believe based on your application and whether
you licensed it (it is a separately licensed option)

Spatial -- like time series, is an extension that allows for analysis of
non-relational data, point data specifically.  You can load a database table
with point data (lat/long for example) and then perform searches of that data to
find all of the points in some bounded polygon around a given point and so on.
Again, you would know if you needed it based on your application implementation
and whether you licensed it (it is a separately licensed option)

Jserver -- that is java in the database.  It comes with EE (but not SE).  It
allows for server side java execution (java stored procedures, EJBs in the
database and so on).  It is required for certain PLSQL functions as well such as
UTL_SMTP (send mail) and UTL_TPC ( socket IO from plsql ).   You would install
this if you have EE and you wish to make use of that functionality.

Intermedia -- give you text indexing via interMedia text.  As well there are
image capabilities (load a gif into the database, scale it, crop it, convert it
to jpg, extract the meta data from it and so on).  There are similar functions
for video and audio clips as well.  Lastly, it supports a simplified spatial
functionality call the "locator".  this works on point data as well but only
works with circles, not polygons.

VIR -- like spatial and time series but works with images.  allows you to search
images and find an image that "looks like" another image. You would know if you
needed it I believe based on your application and whether you licensed it (it is
a separately licensed option)

Advanced replication -- bi-directional, update anywhere replication.  Comes with
EE but no SE.  You would install this if you believe you need replication.

5.2)
you need not select any of them.  You can select all of them.  You pick what you
want.  You can add them at a later date.

5.3)
they will not be installed.

6)
6.1) I would go with 8.1 on a new install of 8i.  You might use 8.0 if you were
upgrading a database
6.2)
WE8ISO8859P1 supports the following Western European languages:

Catalan Finnish Italian Swedish
Danish French Norwegian
Dutch German Portuguese
English Icelandic Spanish

that would probably be the best overall choice.

6.3) you can use a sid with more then 4 (my sid on windows is tkyte816 for
example) however, for maximum portability, 4 characters is suggested.

7)

8)
8.1) 260m should be good for most things with java installed.  I have jserver
and intermedia install, my system tablespace is about 200meg.

8.2)
8.2.1) some people love it (me) some people hate it..  I like to have system set
to autoextend, yes -- if it cannot extend and it needs to, lots of things are
affected.  It is up to you as to whether autoextend is something you want.  If
you use it MAKE SURE to set a maxsize!

I like to let it grow by 5meg increments, but again, it is wholly up to you.

8.2.3) depending on what you use and what you do, there could be upwards of
300-400 one extent tables in system (i have 335).  64k would be an OK choice for
this as it gives them a little room but doesn't waste too much for empty tables.

9) for a 300 user OLTP system that is "big" you are going to need to size these
after experience.  You want enough logs to keep you going through your busy
cycles.  On a moderately sized, mixed use database I use 4 or 5 25meg redo log
files.  You'll be dropping and recreating these as part of your tuning process.
Don't skimp on them -- go much higher, 10-20 meg at least and have a couple (4
or 5).  don't forget to multiplex on different physical devices!

10)
see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:344617462392
11)
shared pool, probably start with 50meg and monitor its usage over time using
stats pack.

block buffers -- function of need.  start with 100meg cache (function of block
size which I don't know) and see how that works for you (monitor cache hit ratio
using stats pack.

log buffer size -- say 512k, monitor over time using stats pack

process -- that should have been set back at 3 and 4 for you.  About 400 I would
guess.

block size -- depends. see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:329017235004
for some of the considerations.
Review & Followup
Rating: 5
April 02, 2001
Reviewer: Eduardo from Mexico

Rating: 3
Java Option October 21, 2002
Reviewer: Abdal-Rahman Agha from Yemen
Hi Tom,

  Please tell how to add java option to my already created DB (817). Should I
need to drop it and create again? Or what are the scripts I have to run.

Thanks.

Followup:
@$ORACLE_HOME/javavm/install/initjvm.sql

Rating: 5
October 22, 2002
Reviewer: A reader
Hi tom
If i select the following options which i don't use
   - Oracle Times Series
   - Oracle Spatial
   - Oracle Jserver
   - Oracle InterMedia
   - Oracle Visual Information Retrieval
   - Advanced Replication
   Is there any impact on performace of database ?
If it is Can you exaplain how it is effected ?

Samit

Followup:
beyond taking additional system tablespace - there isn't any.

Rating: 4
Adding Java Option October 22, 2002
Reviewer: Abdal-Rahman Agha from Yemen
Thank you very much Tom.

October 22, 2002 Connor -- Thanks for the question regarding "Resumable exports", version 9.2
You Asked
In 9.x, 'exp' has a "resumable" parameter.  Makes enough sense with import, but
with export? I'm curious as to how one would generate a space error with export
that would qualify for "resumable assistance".

Cheers
Connor
and we said...
what, was there a seminar again ;)

I got this question just two hours ago from someone else!

but anyway, i suppose it is mostly for completeness but EXP does do lots of
recursive SQL (lots of updates in there)

Also, a quick peek at V$SESSTAT on a smallish database (single user) showed:

NAME                                                    VALUE
-------------------------------------------------- ----------
...
recursive calls                                        341769
...
physical reads direct                                     182
physical writes direct                                    182
...
redo entries                                             1656
redo size                                              106800
...
sorts (memory)                                          50256
sorts (disk)                                                1
sorts (rows)                                           115419

81 rows selected.

So, there is some sorting going on (TEMP space) -- thats the physical IO's.
Lots of recursive SQL (mostly sequences but could hit the IDL tables hard
depending on what needs to be compiled so it could need some amount of UNDO
space)
Review & Followup
Rating: 3
Resumable exports October 22, 2002
Reviewer: Harri from Finland
Don't you have never bumped into disk space problems during export!? Resumable
feature covers that too.

Followup:
Umm -- what do you mean by that?

If you mean "I ran out of disk space writing the dmp file", that is *NOT*
covered.  Only things "in the database" are covered.

Rating: 3
Resumable exports October 22, 2002
Reviewer: Harri from Finland
Sorry, my mistake. Maybe disk space problems are covered in future versions :)

October 22, 2002 Tom -- Thanks for the question regarding "Best way to copy From Global TEMP Table to an empty Standard Table", version 9.2.0
You Asked
Q1. I have a Global Temp (GT) (ON commit Preserve Rows).
    I do lots of processsing (mostly inserts, some updates)
    After processing and committing, I trucate a Permanent Table (P)   and want
to copy  GT ->P, say a million rows.
 What is the quickest way of achieving that
   - Drop P, CTAS P as select from GT
   - Some other ?

Q2. Curiosity - there does not seem a compelling reason why TEMP tables should
not persist across sessions. In which case I would not need to do the above
operation (i.e GT -P). That is, I want to exploit the minimal redo generation
aspect of GT but want more persistance. Is that a reasonable demand from Oracle?

thanks
Tom
and we said...
q1) insert /*+ APPEND */ into P select * from GT;

see
http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96524/c21dlins.htm#11358
for detalis on that -- it is known as a direct path insert

q2) No, we give you the tools to do minimal redo generation on a permanent table
via non-logged bulk operations.

You do understand that even a GTT will generate GOBS of redo when you use UPDATE
and DELETE on it (generates MINIMAL with INSERT).

So, against a permanent table you can do many of the things you are asking with
bulk operations (CTAS -- doesn't have to be logged, INSERT /*+ APPEND */ -- does
not have to be logged, truncate -- no log)

But just remember that with GTTs and permanent tables -- UPDATES and DELETES --
whole lotta logging going on ...

ops$tkyte@ORA920.US.ORACLE.COM> create global temporary table t1
  2  on commit preserve rows
  3  as
  4  select * from all_objects
  5  where 1=0;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create table t2
  2  as
  3  select * from all_objects
  4  where 1=0;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> set autotrace on statistics
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into t1 select * from all_objects;

29303 rows created.

Statistics
----------------------------------------------------------
         97  recursive calls
      31377  db block gets
     138393  consistent gets
          0  physical reads
    3767208  redo size
        789  bytes sent via SQL*Net to client
        809  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
      29303  rows processed

ops$tkyte@ORA920.US.ORACLE.COM> insert /*+ APPEND */ into t2 select * from all_o
                      bjects;

29303 rows created.

Statistics
----------------------------------------------------------
        387  recursive calls
        483  db block gets
     138450  consistent gets
          0  physical reads
    3390208  redo size
        773  bytes sent via SQL*Net to client
        823  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
      29303  rows processed

ops$tkyte@ORA920.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> update t1 set object_id = object_id+1;

29303 rows updated.

Statistics
----------------------------------------------------------
          0  recursive calls
      30079  db block gets
        799  consistent gets
          0  physical reads
    4472720  redo size
        789  bytes sent via SQL*Net to client
        806  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
      29303  rows processed

ops$tkyte@ORA920.US.ORACLE.COM> update t2 set object_id = object_id+1;

29303 rows updated.

Statistics
----------------------------------------------------------
          0  recursive calls
      29986  db block gets
        407  consistent gets
        400  physical reads
    7171100  redo size
        789  bytes sent via SQL*Net to client
        806  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
      29303  rows processed

ops$tkyte@ORA920.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> delete from t1;

29303 rows deleted.

Statistics
----------------------------------------------------------
        128  recursive calls
      31547  db block gets
        863  consistent gets
          0  physical reads
    8449052  redo size
        790  bytes sent via SQL*Net to client
        783  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
      29303  rows processed

ops$tkyte@ORA920.US.ORACLE.COM> delete from t2;

29303 rows deleted.

Statistics
----------------------------------------------------------
         56  recursive calls
      32713  db block gets
        810  consistent gets
        221  physical reads
   10464456  redo size
        790  bytes sent via SQL*Net to client
        783  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
      29303  rows processed

ops$tkyte@ORA920.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> set autotrace off

so no, I would say "not a reasonable 'demand'" as we already supply it for the
most part with non-logged operations
Review & Followup
Rating: 4
Direct Path Load into GTT ? October 19, 2002
Reviewer: Robert C from PA
>>insert /*+ APPEND */ into P select * from GT;

Tom , does it do any good to do a "Insert /*+ APPEND */" into a GTT ?

thanks

Followup:
yes it can.

What happens (if you have my book "Expert one on one Oracle" i go into things
about temp tables...) is that

a) no REDO is generated for the GTT
b) however UNDO is generated for the GTT
c) and REDO for the UNDO *must* be generated

since an insert /*+ append */ can skip UNDO generation for the table (but not
index) segment -- it'll generate that much less REDO.

ops$tkyte@ORA920.US.ORACLE.COM> create global temporary table gtt on commit
preserve rows
  2  as
  3  select * from all_objects where 1=0;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> set autotrace traceonly statistics;
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into gtt select * from all_objects;

29317 rows created.

Statistics
----------------------------------------------------------
         25  recursive calls
      31307  db block gets
     138355  consistent gets
          0  physical reads
    3765980  redo size
        782  bytes sent via SQL*Net to client
        810  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
      29317  rows processed

ops$tkyte@ORA920.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM> insert /*+ APPEND */ into gtt select * from
all_objects;

29317 rows created.

Statistics
----------------------------------------------------------
          7  recursive calls
          8  db block gets
     138276  consistent gets
          0  physical reads
        224  redo size
        768  bytes sent via SQL*Net to client
        824  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
      29317  rows processed

Rating: 5
Space above HWM usage... October 21, 2002
Reviewer: pasko from HH,Germany
HI Tom,
As usual, thanks for a Great Discussion above!

I have this doubt:
I know that :

with : "insert /*+ APPEND */ into some_table
        select * from some_other_table "   ,

we use space only above the HWM  and less REDO generated.

But does the same hold true for insert without the APPEND HINT:

       "insert    into some_table
        select * from some_other_table "   ?

What are other major differences between the two ?

Thanks in advance.

Followup:
it'll generate LESS UNDO (which leads to less REDO) but in archive log mode,
it'll generate gobs of REDO as well unless the table is nologging.

without the append hint, it'll work as normal -- all UNDO and REDO is fully
generated and there is no way to reduce the amount.

If you click on that first link above -- you'll learn lots about what this
direct path insert does.

Rating: 4
October 21, 2002
Reviewer: Sagi from India
Hi !

Tom, Your solutions are the BEST. But for this question the user was :

1) Creating/Has a Global Temporary Table (GTT)
2) Does some operations on GTT.
3) Truncates the table P (Permanent Table)
4) CTAS or any other Solution which he asked...

You told that he can use

INSERT /*+ APPEND */ INTO P
AS SELECT * FROM GTT

But I was thinking he can as well do this, which is more faster:

1) Anyway doing TRUNCATE TABLE P
2) Now DROP TABLE P
3) RENAME GTT_TBL TO P
4) Create the TEMPORARY TABLE agin.

This is much more faster than the Direct Load. What do you say?

Regards,
Sagi

Followup:
renaming a gtt would be sort of useless.

a) no one else can ever see the data in it
b) the data would disappear right after you logged out.

so, while it might be "fast", it would not be "useful"

Rating: 4
October 21, 2002
Reviewer: Sagi from India
Hi Tom,

To see whether a table is GLOBAL TEMPORARY TABLE (GTT) or not we can execute the
below query:

SELECT * from USER_OBJECTS
where temporary='Y'

But i wanted to know if the GTT was a Session Based or Transaction Based i.e. ON
COMMIT PRESERVER ROWS or ON COMMIT DELETE ROWS.

Which Datadictionary (DD) table or columns in DD tells us this info?

Thanx in advance.

Regards,
Sagi

Followup:
select duration from user_tables;

Rating: 2
Why the Redo? October 21, 2002
Reviewer: A reader
Ok, so I give up.  How come GTTs need redo?  In the event of a server failure
both the transaction and session will end, so what value is redo?

Followup:
GTTs don't

UNDO does

GTTs generate undo (they have to -- you can rollback, rollback to savepoint and
have statements that fail halfway thru -- we roll those back as well).  In order
to rollback, we need UNDO.

For an INSERT, minimal undo.
For an UPDATE/DELETE -- lots more.

if all you do is INSERT and SELECT from GTT's -- little redo.  If you UPDATE
DELETE them -- correspondinly more.

Rating: 3
October 22, 2002
Reviewer: Sagi from India
Hi Tom,

I have one silly doubt. You said

"an insert /*+ append */ can skip UNDO generation"

q1) It can skip or it skips. If you say "can skik" can you give us example.
Because i was under assumption that always it skips.

q2) Why does REDO log get generated using "INSERT /*+ APPEND */. Because anyway
we it does not generate UNDO, and recovery is not possible, So why?

Thank you in Advance.

Regards,
Sagi

Followup:
q1) insert /*+ append */ into table_with_indexes select ....

that'll skip UNDO on the table
it cannot skip UNDO on the indexes

q2) see #1

October 22, 2002 Mike -- Thanks for the question regarding "different results from SP versus SQL under system account", version 8.1.7.1.1
You Asked
Tom,
 I am using Oracle8i Enterprise Edition Release 8.1.7.1.1
 The following stored procedure returns unexpected output
 then created/executed under SYSTEM account,
 but it works fine if created/executed  under SYS.

 Then I run SQL defined in CURSOR under SYSTEM from SQLPLUS
 it return not the same that the following SP returns :

CREATE or REPLACE PROCEDURE table_owners (
            v_stat_type IN VARCHAR2 := 'DUMMY') AS
    CURSOR c IS
            SELECT DISTINCT owner FROM   all_tables
            WHERE  owner not in ('SYS','SYSTEM');
    BEGIN
            FOR any_row IN c LOOP
              DBMS_OUTPUT.PUT_LINE ('OWNER: ' || any_row.owner );
            END LOOP;
    END;
/
-- UNDER SYSTEM account :
SQL>set serveroutput on
SQL>exec table_owners;
OWNER: MDSYS
OWNER: MTSSYS

-- if I run simlpy SQL or the same  sp under SYS account :
MDSYS
MTSSYS
OUTLN
SCOTT

 I guess  all_tables view is not the same then you call it from SP.

 Thanks,
   Mike.
and we said...
a) do not use SYSTEM
b) really do not use SYS
c) roles are not enabled in a definers rights routine.  SYSTEM can see various
objects due to roles.

See
http://asktom.oracle.com/~tkyte/Misc/RolesAndProcedures.html

and if you run the sql with

SQL> set role none;

you'll find you get the same results -- the ALL_* tables only show you what you
are supposed to be able to see given the current privilege domain.

To re-inforce something here:

a) do not use SYSTEM
b) really do not use SYS

even to test or play with -- just leave them be.  They are ours.  You never need
to log in with them -- ever.
October 22, 2002 -- Thanks for the question regarding "Number Function how to select", version 8.1.7
You Asked
Tom,
I am sure there will be a simple trick to get the result , but I am struggling
to come out with a good one.

I have a table empsal ( emp_no number , sal number )
Sample data
 emp_no                    sal
 11                        1100
 22                        2200.5
 33                        3300.34
 44                        4586.5656

I want to select
 emp_no                       sal
 11                           1100.00
 22                           2200.50
 33                           3300.34
 44                           4586.565

Appreciated your time that you are spending with us teaching cool stuff
everyday.
and we said...
ops$tkyte@ORA817DEV.US.ORACLE.COM> select empno,
  2  decode( mod(sal*1000,10), 0, to_char(sal,'9999999.00'),
              to_char(trunc(sal*1000)/1000,'9999999.999'))
  3    from t;

     EMPNO DECODE(MOD(S
---------- ------------
        11     1100.00
        22     2200.50
        33     3300.34
        44     4586.565

that was the first thing that popped into my head -- i'm sure there are others
but given the wacky format (2 trailing digits, maybe three -- but no rounding
allowed) -- that works
Review & Followup
Rating: 3
Another one October 22, 2002
Reviewer: Michel Cadot from France
Hi Tom,

Here's another query:

select emp_no, to_char(trunc(sal,3),'fm999999990.009') from empsal;

    EMP_NO TO_CHAR(TRUNC(
---------- --------------
        11 1100.00
        22 2200.50
        33 3300.34
        44 4586.565

Regards

Followup:
Thats the ticket -- thats what I was looking for ;)

thanks

Rating: 3
October 22, 2002
Reviewer: Sagi from India.
Hi Tom,

Sorry for posting this question here. I did not want to waste the resource and
block someone else to post a question. Therefore puting here.

I read always that if we use a table alias then all the columns must be prefixed
with it in SELECT. Right? But to my surprise the below works:

select e.empno, e.ename, deptno from emp e ;

Gives me the output. I have not prefixed the table alias 'e' before the 3rd
column. So how come it works without giving me error.

I have tried the below query:

select e.empno, e.ename, dname from emp e, dept d
where e.deptno=dept.deptno

I encounter error here because in the WHERE clause i am not using the alias for
dept table.

So it shows that the alias must be used in the WHERE clause and for the columns
in the SELECT its an Optional. Is this right?

Is this version specific or works the same way on all versions. By the way, I am
on 8.1.7.

Regards,
Sagi.

Followup:
How kind??  (given that questions were not being accepted when you posted this
anyway....)

where did you read that.  it is not correct.

The situation is:  once you rename a table using a correlation name, you may
ONLY refer to the correlation name -- the other "real name" is permanently
hidden in that query.  once you rename the table -- you have to use the
alternate name always.  it is not that you ALWAYS have to use the alternate name
-- rather that if you use a name -- it must be the ALTERNATE name.

Your test does *not* show that it must be used in the where clause.  It only
shows that when you use a correlation name to qualify a column reference, it
must be the right name.  that is all.

Rating: 3
October 22, 2002
Reviewer: Sagi from India
Hi Tom,

Sorry, I could not get you. Could you explain it a bit more clear. Please.

Regards,
Sagi.

Followup:
which part is hard to understand?

if you query:

select * from T1 a

then anytime you want to qualify a column (have to qualify a column) in the
query - you must use "A" -- T1 does not exist as far as we are concerned.  You
hid it by using the correlation name.

In your example with emp and dept -- both have a DEPTNO column, hence, you MUST
qualify it in the query so we know which deptno column you mean.  Additionally,
since you used a correlation name to hide EMP and DEPT -- you had to use that.

Rating: 5
Just like Sagi October 22, 2002
Reviewer: J from ma
Hi Tom,

Sorry for posting this question here. I did not want to waste the resource and
block someone else to post a question. Therefore puting here.

It is MOST useful of you to allow Sagi and me to cut in line.

 a couple o Q's

1. What was the answer to the challenge?

2. Who won?

3. How do I find technet.com??

4. Where is the Oracle Docs, anyway??

5. How could someone like Sagi, Who's name I see fairly regularly, not know the
not ask the q's here????

October 22, 2002 jimmy -- Thanks for the question regarding "how many days in a year", version
You Asked
How could i count how many days in a
year by SQL query.
ie. this year 2000 is a leap year
it has 366 days?
and we said...
truncate the date to the YEAR boundary, add 12 months and
subtract from that the date trunced to the year again:

ops$tkyte@8.0> select add_months(trunc(sysdate,'year'), 12) -
trunc(sysdate,'year') from dual
  2  /

ADD_MONTHS(TRUNC(SYSDATE,'YEAR'),12)-TRUNC(SYSDATE,'YEAR')
----------------------------------------------------------
                                                       366

ops$tkyte@8.0> select
add_months(trunc(to_date('01-jan-1999'),'year'), 12) -
trunc(to_date('01-jan-1999'),'year') from dual
  2  /

ADD_MONTHS(TRUNC(TO_DATE('01-JAN-1999'),'YEAR'),12)-TRUNC(TO_DATE
('01-JAN-1999'),'YEAR')
-----------------------------------------------------------------
-----------------------

                    365
Review & Followup
Rating: 5
Determination of Leap Year. October 02, 2002
Reviewer: Steve Booth from Waukesha, WI USA
I needed to determine whether a year was a leap year and was able to
successfully use his code snippet.

Followup:
even easier might be

to_char( last_day( to_date('02' || :year,'mmyyyy') ), 'dd' )

if that is 28, no leap year, 29 leap year.

Rating: 5
lesser code.. October 02, 2002
Reviewer: SB from CA
mod(:year, 4)
If the result is 0 then it's a leap year.

Rating: 3
Leap year is not so easy October 02, 2002
Reviewer: Rick from East Haven, CT
>
  mod(:year, 4)
  -- If the result is 0 then it's a leap year.
>
This is wrong!

Years divisible by 4 are leap years
   BUT
Years divisible by 100 are NOT leap years
   BUT
Years divisible by 400 ARE leap years

1900 was not a leap year, 2000 was a leap year

Followup:
Thanks, you saved me the effort of writing the query to show them wrong ;)

Rating: 5
But look out for the 'Gotcha!' October 02, 2002
Reviewer: Phil Singer from Brighton, MI USA
In terms of using Oracle functions to answer the question of
how many calendar days in a calendar year, this cannot be
questioned.  However, this brings back memories of my prior
life of doing financial calculations.  In the USA at least,
these questions are much more difficult.  For example,
depending on State law, and when the payments of a load are
made, a year can turn out to have anywhere from 358 to 367 days.  So, if the
question is not really about leap years,
but really about days to collect interest, I urge you to
contact your compliance officer.

Rating: 4
Year 2000 gives 366 days October 03, 2002
Reviewer: Dawar from CA , USA
Hello Tom,

It is interested when I used your defined query to the
year 2000. Output always is 366 days.
Rest of the years are fine. Even year 1900 gives 365 days.
why is that?
select add_months(trunc(to_date('01-jan-2000'),'year'), 12) -
trunc(to_date('01-jan-2000'),'year') from dual;

366

Followup:
  1* select last_day( to_date( '02-2000', 'mm-yyyy' )) from dual
scott@ORA920.LOCALHOST> /

LAST_DAY(
---------
29-FEB-00

2000 was a LEAP YEAR, 1900 was not, that is why.

Rating: 5
Sorry and thankx for the clarification !!! October 03, 2002
Reviewer: SB from CA

Rating: 5
RE: Year 2000 gives 366 days (dawar) October 03, 2002
Reviewer: Mark A. Williams from Indianapolis, IN USA
Dawar:

Because 2000 was a leap year, 366 is the correct NOD...

HTH,

Mark

Rating: 5
Leap Year Error. October 22, 2002
Reviewer: SSN. from Madurai, India.
Hi Tom,

last_day(to_date('01-FEB-1500','dd-mon-yyyy')) returns 29-FEB-1500. Is it
correct ? If not, then why it is like that?

I have tested it in 8.1.7 under HP-UX 11.0.

Thanks.

Followup:

Rating: 3
1500 was not a leap year October 22, 2002
Reviewer: A P Clarke from London, England
Tom

I was able to reproduce SBB's problem with 1500:

SQL> select last_day(to_date('01-FEB-1500','dd-mon-yyyy')) from dual;

LAST_DAY(
---------
29-FEB-00

This is wrong because 1500 was not a leap year - divisble by 100 but not 400.
This is also a problem with 1400, etc.  It isn't a problem with 1700, 1800 or
1900.  Nor does it apply to 2500, 2600 or 2900.

Yes, I have too much time on my hands - it's lunchtime and it's raining - but
this does imply that Oracle's date generation lacks a consistent algorithm.
Another good reason for hoping that a wormhole in the spacetime continuum
doesn't open up and send us back to the Middle Ages :P

Cheers, APC

Followup:
$ cal 02 1500
   February 1500
 S  M Tu  W Th  F  S
                   1
 2  3  4  5  6  7  8
 9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29

and see below...

Rating: 3
Leap year October 22, 2002
Reviewer: Michel Cadot from France
Hi,

1200 is a leap year
1300 is a leap year
1400 is a leap year
1500 is a leap year
1600 is a leap year
1700 is not a leap year
1800 is not a leap year
1900 is not a leap year
2000 is a leap year
...

Why ?

Because in Gregorian calendar xx00 is a leap year if and only if xx is divisible
by 4.
But what about 1300, 1400 and 1500?
Gregorian calendar started during the night of 4 october 1582 to 15 october
1582. Before there was the Julian calendar in which all of the years divisible
by 4 are leaps.

select to_char(to_date('04/10/1582','DD/MM/YYYY')+1,'DD/MM/YYYY') from dual;
TO_CHAR(TO
----------
15/10/1582

Regards

Rating: 3
1500 October 22, 2002
Reviewer: Helen from UK
Up until 1582 any year divisibly by 4 qualified as a leap year.  After this Pope
Gregory XIII added that centennial years should also be divisible by 400 to
qualify.

Rating: 5
The metric system October 22, 2002
Reviewer: Connor McDonald from UK
I look forward to the day when we go metric with this...

100 mins in an hour
10 hours in a day
10 days in a month
10 months in a year

Simple!

:-)
Connor

October 22, 2002 Lulekwa -- Thanks for the question regarding "Writing triggers using Dynamic SQL(It does not work)", version 8.17
You Asked
Hi Tom,
I am writing a table level trigger, so that who ever changed a column on a
form,report any where should be seen.I have written the below code but it does
not work.I am writing it to a file and later run the file with correct code.Iam
not getting any error, just my file is empty.
Do you know what might be the problem?I have written same code to update the
date_modified and it worked.This one does not work.

CREATE OR REPLACE PROCEDURE USER_ID is

   cursor c1 is
         SELECT *
           FROM TS20_DATE_TABLES
          WHERE CS20_OWNER =
                (SELECT USERNAME
                   FROM SYS.DBA_USERS
                  WHERE USER_ID = UID
                )
             AND CS20_COLUMN_NAME LIKE '%USER_ID'
            AND CS20_DATE_PROCESSED IS NULL
          ORDER BY CS20_SEQUENCE;

   r1                c1%rowtype;
   v_sql_statement   varchar2(500);
   out_file          utl_file.file_type;
BEGIN
   out_file    := utl_file.fopen('/ora_load/wizard/out',
'wdpk_user_check.user_id.txt', 'w');

   open c1;
   loop
      fetch c1 into r1;
      exit when c1%notfound;

      v_sql_statement := NULL;

   -- Build the dynamic SQL that will create the trigger
      v_sql_statement := 'CREATE OR REPLACE TRIGGER ' || r1.cs20_owner || '.' ||
'WT_UserId' ||
                         ' BEFORE INSERT OR UPDATE ON ' || r1.cs20_owner || '.'
|| r1.cs20_table_name ||
                         ' REFERENCING NEW AS NEW OLD AS OLD FOR EACH ROW '||
                         ' BEGIN  '
                                ||r1.cs20_column_name|| ':=   :NEW.' ||
r1.cs20_column_name || '; END; /'||'';

      utl_file.put_line(out_file, v_sql_statement);

      UPDATE TS20_DATE_TABLES
         SET CS20_DATE_PROCESSED = sysdate
       WHERE CS20_SEQUENCE = r1.cs20_sequence
       AND CS20_COLUMN_NAME LIKE '%USER_ID';

      COMMIT;
   end loop;
   close c1;

    utl_file.fclose(out_file);
EXCEPTION
   when others THEN
      null;
END USER_ID;
/

I'll appreciate your help.
Thanx in advance.
and we said...
You have a

WHEN OTHERS THEN null;

Basically you have said

"Any and all errors, come and get me -- I don't care.  I'll just ignore you and
pretend you do not exist at all."

A when others that is not followed by RAISE is a bug in my opinion.

Your code is throwing an exception somewhere, you are ignoring it 100%.  That is
why your code doesn't work as expected.

The ONLY time you catch an exception and do not re-raise it is when you are
EXPECTING the exception to occur.  For example:

   begin
       select * into l_rec from t where x = 5;
   exception
       when no_data_found then
            l_rec.c1 := null;
            l_rec.c2 := null; -- ok to "ignore" this "error" cause it isn't an
                              -- error really but an expected condition
   end;

that is a valid time to ignore an error. But I am 100% certain that when I see
code:

   exception
      when others then
            null;
   end;

I am seeing a BUG in the developed code!!!
Review & Followup
Rating: 5
Indexes October 21, 2002
Reviewer: Dennis from Missouri, USA
Tom,

With 9i making direct grants needed, what do you need to have dynamic SQL
creating indexes?  I'm going to a lookup table (hey I think you're the one that
hinted me in this direction) and making sure they have the authority, but then I
get the old 1031 error.  They have CREATE TABLE directly granted to them, and
they actually own the schema that I'm trying to build the index in (yes, they
should just issue the statement since they have the schema, but I'm hoping to
ween them off having schema owner once we get the ETL tool in since that's our
standard).  I have a TAR and we've added CREATE ANY INDEX and ALTER ANY INDEX,
to no avail.  Yet they  say it works for them.  I am trying to get them to dump
the privs their user has, but they haven't yet.  What are your thoughts Sir Tom?

proc
-
CREATE OR REPLACE PROCEDURE WINKID1.perform_ddl(p_object_name IN varchar2,
p_operation IN varchar2)
as
   TYPE l_record_type is RECORD
   (r_object_name varchar2(32),
    r_user_id varchar2(32),
    r_operation varchar2(32),
    r_sql_statement varchar2(1000));
   CURSOR l_cursor is
     SELECT object_name, user_id, operation, sql_statement
     FROM winkid1.ddl_control;
   l_record l_record_type;
   l_owner varchar2(50);
begin
-- open cursor and do first fetch
    OPEN l_cursor;
-- get user
    winkid1.who_am_i(l_owner);
-- compare
    LOOP
          FETCH l_cursor INTO l_record;
             EXIT WHEN l_cursor%NOTFOUND;
     if (upper(p_object_name) != l_record.r_object_name) then
-- skip record, incorrect user
        dbms_output.put_line('Skipping due to object');
     elsif (l_owner != l_record.r_user_id) then
-- skip record, incorrect object
        dbms_output.put_line('Skipping due to user');
      elsif (upper(p_operation) != l_record.r_operation) then
-- skip record, incorrect operation
        dbms_output.put_line('Skipping due to operation');
     else
-- user is authorized for this operation on this object
        execute immediate (l_record.r_sql_statement);
        dbms_output.put_line('Statement executed');
     end if;
    end loop;
close l_cursor;
End;
/

- table and data
09:28:52 WINKID1@HII2:sca02> desc ddl_control
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 USER_ID                                            VARCHAR2(30)
 OPERATION                                          VARCHAR2(30)
 OBJECT_NAME                                        VARCHAR2(50)
 SQL_STATEMENT                                      VARCHAR2(1000)

data as follows
HRDMADM   TRUNCATE   TEST_A        TRUNCATE TABLE HRDMADM.TEST_A
HRDMADM   CREATE     TEST_IDX1     CREATE INDEX HRDMADM.TEST_IDX1 ON
HRDMADM.TEST_A(A) TABLESPACE HRDMADM_64K

- supplementary proc
CREATE OR REPLACE PROCEDURE WINKID1.who_am_i(p_owner OUT varchar2)
as
    l_owner        varchar2(30);
  begin
   select user into l_owner from dual;
   p_owner := l_owner;
end;
/

Thanks,
Dennis

Followup:
The OWNER of that procedure above would need

create ANY index
create ANY table

but that would be bad.  I would rather you use AUTHID CURRENT_USER (invokers
rights) and then the procedure would execute with the privs of the INVOKER of
the routine.  that way the invoker needs CREATE INDEX and CREATE TABLE (no ANY)
and can get this either directly or via a role.

If you have my book "Expert one on one Oracle" i wrote an entire chapter on this
topic in invoker/definer rights.

Rating: 5
you are the man October 22, 2002
Reviewer: Dennis from Missouri, USA
Thanks Tom.  Guess I need to hurry up and finish Lewis's book so I can read
yours :)  I knew you'd solve the problem for me.  Oracle Support and I were
looking at the wrong id.

Off topic - the font on the front page is horrible as far as reading goes (other
than the information postings you have there...the questions and the "ask a
question/backlog" box are horrible).  Other pages display normally.  I noticed
this yesterday (which unfortunately was the first time in a week or two since
I've been able to hit your site).

Anyway, thank you for your assistance yet again.

Dennis

Followup:
What browser are you using (they just put a CSS on the site to make it look
nicer -- it looks fine in Netscape 7.0)

Rating: 5
ah October 22, 2002
Reviewer: Dennis from Missouri, USA
Yeah that's probably it, I'm on Netscape 4.74.  Guess I should update to the
current times.  It's my work computer so I don't keep it as up to the minute as
my home (even though that's only 6.2).

Sorry for the inaccurate information.  Guess that's a push to upgrade :)

Dennis

Followup:
Just for grins, wanted to see the browser types accessing

(just the last 3 weeks -- not all of the entries, there were 2004 DISTINCT
browser types, i cleaned them up a bit and these are just the "most popular")

MSIE 5.5; AOL 7.0                                         271
Mozilla/5.0 (Windows;Netscape6/6.2.3                      271
MSIE 5.0; Windows 2000) Opera 6.01  [en]                  288
Mozilla/5.0 (Windows;Netscape6/6.2.1                      294
Mozilla/4.75 [en] (Win98; U)                              295
MSIE 4.01; Windows 98                                     308
Mozilla/4.77 [en] (Windows NT 5.0; U)                     310
MSIE 4.01; Windows 95                                     312
Mozilla/4.76 [en] (WinNT; U)                              327
Mozilla/4.74 [en] (WinNT; U)                              334
Mozilla/4.51 [en] (WinNT; U)                              343
MSIE 6.0b; Windows 98                                     355
Mozilla/4.7 [en] (WinNT; I)                               358
Mozilla/4.7 [en] (WinNT; U)                               361
MSIE 5.0; Windows 2000) Opera 6.05  [en]                  400
Mozilla/4.76 [en] (Windows NT 5.0; U)                     407
Wget/1.5.3                                                443
Opera/6.04 (Windows 2000; U)  [en]                        447
Mozilla/4.77 [en] (Win98; U)                              456
MSIE 6.0b; Windows NT 4.0                                 457
Mozilla/4.75 [en] (Windows NT 5.0; U)                     541
MSIE 5.0; Windows 95                                      788
iSiloX/3.05 Windows/32                                    833
Mozilla/4.75 [en] (X11; U; HP-UX B.11.00 9000/785)        971
MSIE 5.01; Windows 95                                    1485
Mozilla/4.79 [en] (Windows NT 5.0; U)                    1560
Mozilla/5.0 (X11;Netscape/7.0                            1606
MSIE 6.0b; Windows NT 5.0                                1930
MSIE 4.01; Windows NT                                    2138
MSIE 5.01; Windows 98                                    2593
MSIE 5.5; Windows 95                                     3164
Mozilla/5.0 (X11;Gecko/                                  3219
Mozilla/5.0 (Windows;Netscape/7.0                        3536
Mozilla/5.0 (Windows;Gecko/                              5545
MSIE 5.0; Windows NT                                     8007
MSIE 5.5; Windows 98                                     8434
MSIE 6.0; Windows 98                                     9176
MSIE 5.01; Windows NT                                   14867
MSIE 6.0; Windows NT 4.0                                16547
MSIE 6.0; Windows NT 5.1                                28480
MSIE 5.0; Windows 98                                    36253
MSIE 5.5; Windows NT 4.0                                38475
MSIE 5.01; Windows NT 5.0                               43785
MSIE 5.5; Windows NT 5.0                                53770
MSIE 6.0; Windows NT 5.0                               141446

sort of disappointing to see so much "windows" and MSIE... oh well - at least
Mozilla is getting up there -- that is a recent change

Rating: 5
yar for Mozilla October 22, 2002
Reviewer: Dennis from Missouri, USA
Mozilla rules.  That was a good move by Netscape.  My only problem with it is
that I have found running Mozilla, exiting out, and then trying to run any other
browser (including Mozilla) causes the browser to crash, but the pop up killer
is extreme :)

Here's a fearful thought about MS.  Windows XP requires a full
install/re-install if you swap out the motherboard or CPU.  Kinda disgusting.  I
upgraded my computer, swapping the chip and the board, and just plugged in my
Win98 hard drive and was up to speed.  No reinstalling every app or anything.
The "next" button might be hard to spot. It's like four pages.
http://www.extremetech.com/article2/0,3973,10333,00.asp

Back to topic:

When this stuff goes live, I plan on having a non-schema (more like an app)
user, and then the schema which the developers will lose access to.  Would I
switch to definer's rights then for this procedure?  Meaning I should give
myself create any table/create any index, and then let them run wild?  That way
they cannot make a table?  Or do I just give them Create Table and put 0 quota
on everything?

What do you think is the 'best' choice?

Thanks,
Dennis

Followup:
Try linux -- I've yet to have mozilla crash and burn -- the pop up killer is
awesome, coupled with "right click on a image and select 'block images from this
site'" -- I don't see ads anymore!

I can highly recommend crossover office
http://www.codeweavers.com/home/
for linux users that want to run MS
office (i sort of needed word and powerpoint).  It let me install  work, ppt,
xls, outlook and even MS IE5.0/5.5 on linux.  I use word and ppt all of the time
and IE on those sites that just plain refuse to work with open systems.

I would give them create table and use quotas.  Else you have to re-invent
security in your procedure.

Rating: 5
bonus October 22, 2002
Reviewer: Dennis from Missouri, USA
Thanks Tom.  Ever since I read that article I have had the bug to go to Linux.
Well, I had it beforehand, but been able to suppress it.  Thanks for the
website, that will prove most useful.

Quotas it is then.  I like taking the easy road out when it's a good choice.

I'll try not to disturb you anymore today :)  But it was a good conversation :)

Have a good one.
Dennis

October 22, 2002 Jerry -- Thanks for the question regarding "SQL*Plus Output Buffer Limit", version 8.1.7
You Asked
I have taken a subset of your showsql.sql, made a couple of changes, including
your "p" procedure, hoping to get it to capture the whole SQL statement.

But it seems like I am hitting a limit, even after substringing 250 bytes chunks
off and printing them.  It will only display the SQL statement out to about 2000
bytes, or so.

Can you tell me if I can extend this in any way to get more output?

SET feedback OFF
set serveroutput on size 1000000
set autotrace OFF
set pagesize 40
set linesize 140
set echo OFF

column status format a10
column username format a15
column logon_time format a12

column username format a20
column sql_text format a55 word_wrapped

DECLARE
    x  NUMBER;
    LONGTEXT VARCHAR2(32767);
BEGIN
    FOR x IN
    ( SELECT username||'('||sid||','||serial#||') ospid=' ||
             process || ' ' || 'osuser=' || osuser ||
             ' program=' || decode(nvl(program,' '),' ','?',program) Username,
             to_char(LOGON_TIME,' Dy-HH24:MI')                       Logon_Time,
             to_char(sysdate,' Dy-HH24:MI')
Current_Time,
             sql_address,
             'Last_Call_ET=' || LAST_CALL_ET
Last_Call_ET,
             'Min=' || to_char(LAST_CALL_ET / 60,'FM9999.9')         Last_Min
        FROM v$session
       WHERE status = 'ACTIVE'
         AND rawtohex(sql_address) <> '00'
         AND username IS NOT NULL
       ORDER BY last_call_et )
    LOOP
        FOR y IN ( SELECT Sql_Text
                     FROM v$sqlarea
                    WHERE address = x.sql_address)
        LOOP
            IF ( y.sql_text not like '%LAST_CALL_ET%')
            THEN
               DBMS_OUTPUT.PUT_LINE( '--------------------' );
               DBMS_OUTPUT.PUT_LINE( x.username     || ' ' ||
                                     x.logon_time   || ' ' ||
                                     x.current_time || ' ' ||
                                     x.last_call_et || ' ' ||
                                     x.last_min);
               LONGTEXT := y.sql_text;
               BEGIN
                 LOOP
                   EXIT WHEN LONGTEXT IS NULL;
                   DBMS_OUTPUT.PUT_LINE( SUBSTR( LONGTEXT, 1, 250 ) );
                   LONGTEXT := SUBSTR( LONGTEXT, 251 );
                 END LOOP;
               DBMS_OUTPUT.PUT_LINE( SUBSTR( LONGTEXT, 1, 250 ) );
               END;
            END IF;
        END LOOP;
    END LOOP;
END;
/
and we said...
you have to use a different v$ view, v$sqlarea only has so much.  Here is my
most recent incarnation of this:

column username format a15 word_wrapped
column module format a15 word_wrapped
column action format a15 word_wrapped
column client_info format a15 word_wrapped
column status format a10
column sid_serial format a15
set feedback off
set serveroutput on

set feedback on
select username, ''''||sid||','||serial#||'''' sid_serial, status , module,
action, client_info,
last_call_et
from v$session
where username is not null
/
set feedback off

column username format a20
column sql_text format a55 word_wrapped

set serveroutput on size 1000000

declare
    x number;
    pid number;
procedure p ( p_str in varchar2 )
is
   l_str   long := p_str;
begin
   loop
      exit when l_str is null;
      dbms_output.put_line( substr( l_str, 1, 250 ) );
      l_str := substr( l_str, 251 );
   end loop;
end;
begin
    for x in
    ( select username||'('||sid||','||serial#||
                ') ospid = ' ||  process ||
                ' command = ' || command ||
                ' program = ' || program username,
             to_char(LOGON_TIME,' Day HH24:MI') logon_time,
             to_char(sysdate,' Day HH24:MI') current_time,
             sql_address, LAST_CALL_ET, sid, paddr
        from v$session
       where status = 'ACTIVE'
         and rawtohex(sql_address) <> '00'
         and username is not null order by last_call_et )
    loop
        select spid into pid from v$process where addr = x.paddr;

        dbms_output.put_line( '--------------------' );
        dbms_output.put_line( x.username || ' dedicated server=' || pid );
        dbms_output.put_line( x.logon_time || ' ' ||
                          x.current_time||
                          ' last et = ' ||
                          x.LAST_CALL_ET);
        for y in ( select sql_text
                     from v$sqltext_with_newlines
                    where address = x.sql_address
                      order by piece )
        loop
            p( y.sql_text );
        end loop;

    end loop;
end;
/
set feedback on
October 22, 2002 Kumpatla -- Thanks for the question regarding "Advantage of using Java inside Database", version 8.1.5
You Asked
i have recently started working on Oracle 8.1.5
i was looking at new feature in 8i.
Oracle says one can publish Java objects into database.
i read through the Documentation, how to do it.
i found that to publish and utilise a Java object inside a
database, we need to write a java code as other external
javacode and write another pl/sql procedure to access it.

at the top level implementation of java object, i did not find
any major advantage of using Java objects.
the same implementation can be handled by external Java objects
also. which can also give me additional advantage of using
external JVM, instead of using embeded JVM inside the database
kernel which is used by Java objects inside the database.

can use pls tell me what is the major advantage of using Java
objects inside database. and can u give me any senarios where
this feature can be utilised.

thankx in advance
rgds
rav
and we said...
Well, one advantage is -- if you have java programmers and want
to write some triggers -- being able to do so in java might be
to your advantage.

If you have java programmers and need to supply some non-java
programmers with stored procedures to access your data, you
might find the fact that having java in there is good.

If you have a java program that does lots of data manipulation
-- you might find that by putting it right next to the data, it
works faster (no network round trips).

The advantage of having the jvm in the database is mostly about
choice.  You have the CHOICE of puttng the java on the client,
in an application server, or in the database.

Lets say you wanted to send an email with an attachment from a
trigger in the datatabase.  Hard to do in PLSQL.  Easy to do in
Java (in fact, sun already wrote the code, just drop it in).

Lets say your trigger needs to do some complex computation to
supply a default value for a column -- you have the code in java
-- just drop it in.

You might find that less moving parts is sometimes a good thing
as well.  If everything can fit into the database and you don't
have to worry about coordinating an app server with the database
-- it is sometimes easier.

It is all about choice and flexibility.  Some people just want
to use java as their only language -- now you can.
Review & Followup
Rating: 5
java support October 22, 2002
Reviewer: mo
Tom:

One DBA is saying that. Can this be true??

In Oracle 9.0.1, Oracle won't support:

CORBA;
Enterprise Java Beans (EJB);
Oracle Server Java Pages;
Oracle Servlet Engine.

Followup:
they left out the important phrase

running inside of the database itself.

We support that stuff in 9iAS now, not inside the database.  Eg: no more beans
in the database.  And they meant to say "9iR2" - it is still in there for 9iR1.

the jvm is there, will be there, won't be going anywhere.

October 22, 2002 -- Thanks for the question regarding "why logging in noarchivelog mode ?", version 8.1.7
You Asked
Hi Tom,

Thank you for your answers on this site and we learn a lot day by day.
I am two questions today...

1.How can i create a table with 600M initial size with in a
tablespace with 3 datafiles each of size 250M.

2.Why we need logging the db activities into log buffer in the no archive log
mode of the database.Dont you think that we are doing some unnecessay work as we
dont want the data to be recovered.At the same time why to move the log buffer
to the log files when we are in no archive log mode.

regards,
Vijay
and we said...
1) trick question -- you cannot.  extents are contained in a file.

In 8i, you should NEVER be asking this question anyway.  You would be using
locally managed tablespaces only and never using initial, next, pctincrease,
minextents!!!

2) noarchivelog mode means "no media recovery".

noarchivelog mode does mean "instance recovery".

the logging is used to protect from any system crash (bug, power outage,
shutdown abort for example)(
Review & Followup
Rating: 5
Thank you October 23, 2002
Reviewer: A reader
Thank you for your answers.

October 22, 2002 sfar -- Thanks for the question regarding "Ouput in single line", version 8.1.7
You Asked
Hi Tom,
I have a table   t1, and it has 2 columns.
    Table_name        column_name
              A1          pk1
      A1              pk2
      A1              pk3
      A2              pk21
          A3                      pk31
      A3                 pk32

I want output like this:
  A1, pk1,pk2,pk3
  A2, pk21
  A3,pk31,pk32

How can I do this in single SQL statement in 8.1.7 or oracle 9i.  Can I also
achieve this using analytical functions.
An early reply will be highly appreciated.
and we said...
In 8i and before, you need to know the "max number" of observations.   Then you
can

select table_name,
       rtrim(
         max( decode( rn, 1, column_name, null ) ) || ',' ||
         max( decode( rn, 2, column_name, null ) ) || ',' ||
         max( decode( rn, 3, column_name, null ) ) || ',' ||
         max( decode( rn, 4, column_name, null ) ) || ',' ||
         .....
         max( decode( rn, N, column_name, null ) ), ',' ) cnames
  from ( select table_name, column_name,
         row_number() over ( partion by table_name order by column_name )
         from t )
group by table_name;

if you search this site for stragg -- you'll find a 9i way that removes the need
to know the number of max observations / key.
October 22, 2002 Santosh -- Thanks for the question regarding "SQLPLUS options", version 8.1.5
You Asked
Hi Tom
Ive written a piece of code in shell to extract data from
various tables;;wherein eac table is conatained in several
schemas.. The problem im facing is that the spool file contains
some sql lines which are unwanted...
hereis the text of the shell program

while read TABLE
do
while read USER
do
sqlplus -s $ouser/$opassword <<EOF
set array 100
set pages 0
set wrap off
set flush off
set feed off
set feedback off
set echo off
set verify off
set term off
set head off
set trim on
set colsep "|"
set linesize 800

spool $USER.dat
select '$USER', $TABLE.* from $USER.$TABLE;
!cat $USER.dat >> $TABLE.dat
!rm $USER.dat
spool off
exit
EOF
done < user.lst  ((CONTAINS SCHEMA names))
done < tables.lst   (( CONTAINS TABLE names ))
_____________________________________________________

with this above routine a file $TABLE.dat is created..
The problem in this file is that besides conating the data it
also contains certain SQL lines ..ill show a piece of data file
to show the ouput..

SQL> select 'ABTASMA001', ALLNETWORKASSETS.* from
ABTASMA001.ALLNETWORKASSETS;
colval11 | colval12 | colval13 |
colval21 | colval22 |colval23  |
SQL> select 'ABCD', PERF_FACT_T.* from ABCD.PERF_FACT_T;
colval31 | colval32 |col val 33|
.............
............
SQL> select "EFGH' , XYZ.* from EFGH.XYZ;
...............
.................

and so on...
Kindly tell how to remove these """SQL> select 'EFGH',XYZ.* from
EGFH.XYZ"" lines from my spool files.
Also tell a way so that i can add the unix system date to my
filename while spooling...somethin like $TAVBLE0910.dat
thanks
and we said...
Don't use spool -- use redirection OR
Use spool but put the script into a temp file and just run

sqlplus u/p @tempscript.sql

To use redirection and name the file after "today", you can:

#!/bin/csh

setenv UNAME SCOTT
setenv TABLE EMP

sqlplus -s scott/tiger <<EOF >> $TABLE"_`date +%j_%Y`.dat"
set array 100
set pages 0
set wrap off
set flush off
set feed off
set feedback off
set echo off
set verify off
set term off
set head off
set trim on
set colsep "|"
set linesize 800

select '$UNAME', $TABLE.* from $UNAME.$TABLE;
exit
EOF
Review & Followup
Rating: 4
How to supress a warning message genrate in spool file February 12, 2002
Reviewer: kumar from San Diego
Tom,

How can I supress a particular warning message that generate in my spool file.
When I create procedure(s), some times the proceudures has a dependecy on other
obejects. So at the time of Creation they throw an warning in the spool file
like below.

Warning: Procedure created with compilation errors.

I don't want this warning in my spool file, but I want the rest like any ORA-XXX
errors. Can you suggest me a way how I can produce the spool file with this
warning supress.

Rating: 5
what is trim on, flush off October 22, 2002
Reviewer: ravi
tom,

can you tell us what for these

set trim on
set flush off

Followup:
fortunately we document all of these things -- makes it easier to remember them!
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a82950/ch8.htm#1001850

October 22, 2002 tyrone -- Thanks for the question regarding "export >2gig", version
You Asked
I am running on Oracle 7.3.4 on HP11 Unix configured
with EMC storage drives. I have a 30G drive. I need to do a full

export of about 16G. Do you think the 2g unix file system limit
applies. I should be able to export the whole DB to 16G DMP
file.
and we said...
The 2 gig limit will apply IF you let export write directly to
the file.  I use export to a PIPE and have compress and split
read the pipe.  The result is a couple of 500meg compressed
files that consistute the export.  At 500meg, any utility can
deal with this files and I can move them around easier.

Here is the CSH script I use to show you how it is done.  It
does a full export and then tests the integrity of the export by
doing a full import show = y.  that gives me a file with all of
my source code and ddl to boot.

#!/bin/csh -vx

setenv UID /
setenv FN  exp.`date +%j_%Y`.dmp
setenv PIPE /tmp/exp_tmp_ora8i.dmp

setenv MAXSIZE 500m
setenv EXPORT_WHAT "full=y COMPRESS=n"

echo $FN

cd /nfs/atc-netapp1/expbkup_ora8i
ls -l

rm expbkup.log export.test exp.*.dmp* $PIPE
mknod $PIPE p

date > expbkup.log
( gzip < $PIPE ) | split -b $MAXSIZE - $FN. &
#split -b $MAXSIZE $PIPE $FN. &

exp userid=$UID buffer=20000000 file=$PIPE $EXPORT_WHAT >>&
expbkup.log
date >> expbkup.log

date > export.test
cat `echo $FN.* | sort` | gunzip > $PIPE &
#cat `echo $FN.* | sort` > $PIPE &
imp userid=sys/o8isgr8 file=$PIPE show=y full=y >>& export.test
date >> export.test

tail expbkup.log
tail export.test

ls -l
rm -f $PIPE

------------ eof -------------------------
Review & Followup
Rating: 4
March 22, 2001
Reviewer: A reader

Rating: 3
July 24, 2002
Reviewer: A reader from Argentina
Tom:

     I run your code on linux RedHat 6.2 and linux Mandrake 8.2 connecting to an
8.1.6 on NT but it doesn't work. I guess the problem is regarding with the pipe
because the program start well but suddenly when the output file reach 177M the
process is killed. Any idea?.Have you ever run it on linux?

Thanks in Advance
PAblo from Argentina

Rating: 5
exp/imp October 21, 2002
Reviewer: atul from india
Sir,

I have to do 8i oracle exp on NT to 9i sco-unixware..
But if exp file taken on NT is > 2gb

Then may i face the problem??
also i'm going to FTP that dmp file to Unix machine..
so which mode i have to do that?? binary/data???

Thanks.
atul

Followup:
use

exp files=(file1.dmp,file2.dmp,file3.dmp,....) filesize=N

and export will create many dmp files, each N bytes in size -- see the server
utilities guide for all of the details.

Rating: 5
Reader October 22, 2002
Reviewer: A reader
Tom,
"
It does a full export
and then tests the integrity of the export by doing a full import show = y.
"

Does this technique detects corrupted export file. Some
time ago, I did a export direct=y of an table and it
completed without any errors. When trying to import the
the data, it produced errors and import failed

Thanks

Followup:
It tests much of the integrity -- it skips the actual data so data level
corruption might very well go undetected.

October 22, 2002 Robert -- Thanks for the question regarding "How to retrieve table name using DBMS_SQL", version 8.1.7.2
You Asked
Can I retrieve a table name with DBMS_SQL package.
I have a package that accepts a sql string.
after dbms_sql.parse I would like to get the table name(s) used in the
sql...similiar to getting the column name with dbms_sql.describe_columns.

Thanks
Rob
and we said...
There is no such facility in any of the languages.

Now, you can *sort of* get this

ps$tkyte@ORA920.US.ORACLE.COM> drop table tmp_addrs;

Table dropped.

ops$tkyte@ORA920.US.ORACLE.COM> create global temporary table tmp_addrs on
commit delete rows
  2  as
  3  select address, sql_text from v$open_cursor where 1=0;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create or replace procedure parse_it( p_str in
varchar2 )
  2  as
  3      l_cursor number;
  4  begin
  5      delete from tmp_addrs;
  6      insert into tmp_addrs (address, sql_text)
  7      select address, sql_text
  8        from v$open_cursor
  9       where sid = (select sid from v$mystat where rownum=1 );
 10
 11      l_cursor := dbms_sql.open_cursor;
 12      dbms_sql.parse( l_cursor, p_str, dbms_sql.native );
 13
 14      dbms_output.put_line( '--------------' );
 15      for x in ( select distinct *
 16                   from v$object_dependency
 17                  where from_address = ( select address
 18                                           from (select address, sql_text
 19                                                   from v$open_cursor
 20                                                  where sid = (select sid
 21                                                                 from
v$mystat
 22                                                                where rownum
= 1)
 23                                                    and sql_text like
substr(p_str,1,60)||'%'
 24                                                 MINUS
 25                                                 select address, sql_text
 26                                                   from tmp_addrs)
 27                                       )
 28            )
 29      loop
 30          dbms_output.put_line( x.to_owner || '.' || x.to_name );
 31      end loop;
 32      dbms_output.put_line( '--------------' );
 33
 34      dbms_sql.close_cursor(l_cursor);
 35  end;
 36  /

Procedure created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> exec parse_it( 'select * from scott.emp' );
--------------
SCOTT.EMP
--------------

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> exec parse_it( 'select * from dual' );
--------------
OPS$TKYTE.DUAL
SYS.DUAL
PUBLIC.DUAL
--------------

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> exec parse_it( 'select * from scott.emp,
scott.dept, dual' );
--------------
OPS$TKYTE.DUAL
SYS.DUAL
SCOTT.EMP
SCOTT.DEPT
PUBLIC.DUAL
--------------

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> exec parse_it( 'select * from all_objects' );
--------------
SYS.X$KZSRO
SYS.X$KZSPR
SYS.OBJ$
SYS.V$ENABLEDPRIVS
SYS.OBJAUTH$
PUBLIC.ALL_OBJECTS
SYS.IND$
SYS.ALL_OBJECTS
OPS$TKYTE.ALL_OBJECTS
SYS.GV$ENABLEDPRIVS
SYS.USER$
--------------

PL/SQL procedure successfully completed.

but as you see - you get more then you bargained for!  you get the viewname, and
all of the tables recursively on down as well....
Review & Followup
Rating: 4
October 22, 2002
Reviewer: robert brennan from New Orleans
Thanks for the quick turn around...and masterfull code.
I was really hoping it would be something simple. Seems to me that after the
parse we would know the object_id's involved...were they not just validated ?

Followup:
Sure -- but not everything is "always presented".  there are thousands of bits
of information out there -- not everything is "transparently there", exposed if
you will.

October 22, 2002 JESUS -- Thanks for the question regarding "UPDATE THROUGH A VIEW WITH JOIN", version 9.0.1
You Asked
Hello Tom, your website is wonderful and the methodology used too.  I visit your
site almost every day and I have learned a lot.
Mi question is :

We have an ORACLE DB application whose DB user is USRMAILING
We have 3 tables : IOPPROADM.AFI, IOPPROADM.CIC, SERVICIOS.AFI_SERV (from
another schemas of another applications)
We have a view : USRMAILING.VW_DESTINATARIO_AFILIADO with the following SQL
sentence :

create or replace view vw_destinatarioafiliado as
select  b.cod_afi  vchcodigodestinatario,
        a.cuspp,
        a.cod_tip_doc_ident,
        a.num_doc_ident,
        a.ape_pat,
        a.ape_mat,
        a.pri_nom,
        a.seg_nom,
        a.fec_ncmto,
        a.cod_sexo,
        a.num_carnet_essalud,
        b.dir,
        b.loc,
        b.cod_ubigeo,
        b.dir_corres,
        b.loc_dir_corres,
        b.cod_ubigeo_dir_corres,
        b.num_telf,
        c.cod_ddn_telf_ofi||c.num_telf_ofi  num_telf_ofi,
        c.anx_telf_ofi,
        b.email,
        c.cod_ddn_fax_ofi||c.num_fax_ofi  num_fax_ofi,
        c.anx_fax_ofi,
        b.fec_afi,
        'N'  chrindicadorjubilacion,
        b.clave_secreta,
        b.cod_clasificacion  vchsegmento,
        'V'  chrindicadoremailhabil,
        c.ind_env_eecc_mail,
        b.cod_frecuencia_env_email,
        c.ind_email_saludos,
        c.ind_email_info_prev,
        decode(b.cod_est_civil, 'C',decode(a.cod_sexo, 'M','Señor',
'F','Señora'), 'D',decode(a.cod_sexo, 'M','Señor', 'F','Señora'),

decode(a.cod_sexo, 'M','Señor', 'F','Señorita'))  chrsaludo,
        decode(a.cod_sexo, 'M','o', 'F','a', 'o')  chrvocalporsexo,
        b.cod_est_civil,
        b.fec_pri_pago,
        b.per_pri_dev_real,
        decode(b.cod_admori, '','A', 'T')  chrindicadororigen,
        a.fec_ing_spp,
        c.num_buzon_email_gratuito,
        c.email_gratuito,
        '000'  chrcodigodevolucion,
        c.ind_rcv_html,
        c.ind_activo_email_gratuito,
        c.ind_web,
        b.cod_tip_trabajador,
        b.cod_admori,
        a.ind_vig_afi,
        b.cod_sub_est_afi,
        null  chrdepuracion,
        null  vchcodigogrupo,
        b.email_alternativo,
        c.fec_ult_act_dir,
        c.fec_ult_act_dir_corresp,
        c.fec_ult_mod_email,
        c.fec_ult_mod_email_alt,
        c.fec_ult_act_envio,
        b.num_celular
from    afi      a,
        cic      b,
        afi_serv c
where    a.cuspp           = b.cuspp
and     a.cod_afi_vigente = b.cod_afi
and     b.cod_afi         = c.cod_afi
and     a.ind_vig_afi = '1';

We want to update (as USRMAILING) a single column of the base table AFI_SERV
using this view.
User usrmailing has select privileges on AFI, CIC tables and update privileges
on IND_RCV_HTML column of AFI_SERV table (ONLY on this

column).
These privileges were granted directly to usrmailing and not through a role.

Connected as usrmailing :

SQL> select * from User_Col_Privs;

GRANTEE          OWNER       TABLE_NAME  COLUMN_NAME   GRANTOR     PRIVILEGE
GRA
---------------- ----------- ----------- ------------- ----------- ----------
---
USRMAILING       SERVICIOS   AFI_SERV    IND_RCV_HTML  SERVICIOS   UPDATE     NO

1 row selected.

SQL> select * from user_tab_privs where grantee = 'USRMAILING' and table_name in
('AFI','CIC','AFI_SERV');

GRANTEE            OWNER       TABLE_NAME    GRANTOR     PRIVILEGE   GRA HIE
------------------ ----------- ------------- ----------- ----------- --- ---
USRMAILING         IOPPROADM   AFI           IOPPROADM   SELECT      NO  NO
USRMAILING         IOPPROADM   CIC           IOPPROADM   SELECT      NO  NO
USRMAILING         SERVICIOS   AFI_SERV      SERVICIOS   SELECT      NO  NO

3 rows selected.

SQL> select * from user_updatable_columns where table_name =
'VW_DESTINATARIOAFILIADO' and column_name = 'IND_RCV_HTML';

OWNER                          TABLE_NAME                     COLUMN_NAME
            UPD INS DEL
------------------------------ ------------------------------
------------------------------ --- --- ---
USRMAILING                     VW_DESTINATARIOAFILIADO        IND_RCV_HTML
            YES YES YES

1 row selected.

The base table AFI_SERV is a key-preserved table.

When I issue :

SQL> update VW_DESTINATARIOAFILIADO set ind_rcv_html = 'S' where
vchcodigodestinatario = '9000674419';
update VW_DESTINATARIOAFILIADO set ind_rcv_html = 'S' where
vchcodigodestinatario = '9000674419'
       *
ERROR at line 1:
ORA-01031: insufficient privileges

But when I issue UPDATE on the base table DIRECTLY :

SQL> update afi_serv set ind_rcv_html = 'S' where cod_afi = '9000674419';

1 row updated.

It works!!!
Do I need to do something else to get the update?
Do the other schemas need aditonal grants?
I proved granting update on the whole base tables and the UPDATE through the
view worked fine, but that is not the right way to do it,

I think.
Any suggestion?

My DB is ORACLE 9i Release 1 on Win2000 SP3.

Thanks in advance.

JESUS BEJARANO
e-Solutions Consulting Services
jesusbh74@hotmail.com
and we said...
You have the grants on the base tables -- NOT on the views!!!  You need to have
granted on the view (a view is a useful tool for selectively granting things).

Consider this simple example (wish you had given me a simple case from which to
start with -- I had to sort of reverse engineer everything from the question!)

ops$tkyte@ORA920.US.ORACLE.COM> drop user a cascade;
User dropped.

ops$tkyte@ORA920.US.ORACLE.COM> drop user b cascade;
User dropped.

ops$tkyte@ORA920.US.ORACLE.COM> grant create session to a identified by a;
Grant succeeded.

ops$tkyte@ORA920.US.ORACLE.COM> grant create session, create table, create view
to b identified by b;
Grant succeeded.

ops$tkyte@ORA920.US.ORACLE.COM> alter user b default tablespace users quota 500k
on users;
User altered.

User B is your table owner, User A is the application schema you are trying to
let update some of B's tables via a view with..

ops$tkyte@ORA920.US.ORACLE.COM> @connect b/b

b@ORA920.US.ORACLE.COM> create table t1 ( x int primary key );
Table created.

b@ORA920.US.ORACLE.COM> create table t2 ( x int primary key );
Table created.

b@ORA920.US.ORACLE.COM> grant update (x) on t1 to a;
Grant succeeded.

b@ORA920.US.ORACLE.COM> create view v
  2  as
  3  select t1.x t1_x, t2.x t2_x
  4    from t1, t2
  5   where t1.x = t2.x;
View created.

b@ORA920.US.ORACLE.COM> grant select on v to a;
Grant succeeded.

so, much like your example, update is on the base tables *but not the view*
and that is what you are missing.  So, just like you when we:

b@ORA920.US.ORACLE.COM> @connect a/a

a@ORA920.US.ORACLE.COM> update b.t1 set x = x;
0 rows updated.

a@ORA920.US.ORACLE.COM> update b.v  set t1_x = t1_x;
update b.v  set t1_x = t1_x
         *
ERROR at line 1:
ORA-01031: insufficient privileges

we are allowed to update the base table but not the view -- that is because
the view is what needs the GRANT!

a@ORA920.US.ORACLE.COM> @connect b/b

b@ORA920.US.ORACLE.COM> grant update (t1_x) on v to a;
Grant succeeded.

b@ORA920.US.ORACLE.COM> @connect a/a

a@ORA920.US.ORACLE.COM> update b.t1 set x = x;
0 rows updated.

a@ORA920.US.ORACLE.COM> update b.v  set t1_x = t1_x;
0 rows updated.

a@ORA920.US.ORACLE.COM>

and now it all "works" as you want.  You probably don't even want the grants
on the base tables in this case!
Review & Followup
Rating: 4
VIEW BELONGS TO B, NOT A October 21, 2002
Reviewer: JESUS BEJARANO from LIMA, PERU
Hi Tom! Thanks for answer my question, but in my case (and following your
example) :
User B was the table owner, User A was the application schema and OWNER of the
view and A was trying to update some of B's tables via this view.
We decide to create the view in schema B, not in A!!!
That's the way the views work, not our way!!!
Thanks for clear us the things, we really appreciate your help, your forum is
such a great thing for us, people who belongs to the world of Oracle.

P.D. I'm suscribed to Oracle Magazine and I read your column on it.  In fact, I
have the intention to buy your book Expert One-On-One soon.

Followup:
Ahh, ok -- it was hard to tell with such a big query and 3 schemas and all...

It is a problem in 9iR1 and before, appears to be fixed in the current release
9iR2.  To observe it, you would:

drop user a cascade;
drop user b cascade;
grant create session, create view to a identified by a;
grant create session, create table to b identified by b;
alter user b default tablespace users quota 500m on users;

@connect b/b

create table t1 ( x int primary key );
create table t2 ( x int primary key );

grant update (x) on t1 to a;
grant select on t1 to a;
grant select on t2 to a;

@connect a/a

create view v
as
select t1.x t1_x, t2.x t2_x
  from b.t1, b.t2
 where t1.x = t2.x;

update b.t1 set x = x;
update v set t1_x = t1_x;

In 9iR1 and before, the update will get:

a@ORA817DEV.US.ORACLE.COM> update b.t1 set x = x;

0 rows updated.

a@ORA817DEV.US.ORACLE.COM>
a@ORA817DEV.US.ORACLE.COM> update v set t1_x = t1_x;
update v set t1_x = t1_x
       *
ERROR at line 1:
ORA-01031: insufficient privileges

In 9iR2, you'll get:

a@ORA920.US.ORACLE.COM> update b.t1 set x = x;

0 rows updated.

a@ORA920.US.ORACLE.COM>
a@ORA920.US.ORACLE.COM> update v set t1_x = t1_x;

0 rows updated.

and it works as intended.

Rating: 5
9iR2 IS THE WORD!!! October 22, 2002
Reviewer: JESUS BEJARANO from LIMA PERU
Hi again Tom!  We had no idea about this problem in 9iR1, we commented that
issue with our managers and they decided to upgrade soon to 9iR2.  The view will
be created in the application schema (schema A), not in the schema of the owner
of the base tables.
Thanks again for your appreciated help.

P.D. By the way, do you have plans to release a 9i version of your book Expert
One-On-One?  And if you have these plans, when will be the aprox. date of this
new release?  (in that case I would buy only the 9i version).

JESUS BEJARANO
e-Solutions Consulting Services
jesusbh74@hotmail.com

Followup:
I am working on right now a book on performance to be released spring of 2003.
It'll not be expert one on one oracle v2 (maybe for the next release of Oracle).

October 22, 2002 Brian -- Thanks for the question regarding "OK to Rebuild Sys owned Indexes Online?", version 8.0.5
You Asked
I have many sys owned indexes with many extents.  Would rebuilding these online
(alter index sys.XYZ rebuild...) give us any performance improvement, and/or is
it advisable to rebuild these while the database is online?
and we said...
No, No, and No.

rule #1) many extents is OK, really -- it is.  ESPECIALLY for an index.  We read
them a block at a time!  We read the root block and then figure out whether to
go right or left.  We read another block -- the one the root block told us to.
And so on -- until we get to a leaf node.  then we read them a block at a time.

1 extent, 1000 extents, 10000 extents -- doesn't matter here.

rule #2) many extents affects DDL like rebuild, drop and truncate horribly when
using dictionary managed tablespaces.  So, if you were to rebuild this, it could
take a really long time just to clean up space (and at no gain to you!)

rule #3) don't do anything to SYS owned objects unless and until support says
"please do this to that sys owned object for us"

The myth of "few extents good, one extent best of all" just won't die...
October 23, 2002 Vishal -- Thanks for the question regarding "Download WEBDB3.0", version version 3.0
You Asked
How do I download ORACLE WEBDB3.0 the latest version of WEBDB
also called ORACLE PORTAL 3.0. I tried using the link on
http://technet.oracle.com/products/iportal/
The title of the page reads
Oracle Portal 3.0 (the replacement for WebDB) is now available!
Click here to learn all about it and download the software!

But when I click on the above link it takes me t a page where it
allows me to download  "Oracle iAS download for Windows NT".
I have existing WEBDB2.2 installed on my machine. I want to
upgrade to WEBDB3.0(aka PORTAL3.0). Please find me the most
relevant link where I can  download the software.
I have visited the following links but could not find anything
useful:
http://technet.oracle.com/products/iportal/
http://technet.oracle.com/software/products/ias/software_index.htm
http://technet.oracle.com/software/products/iportal/software_index.htm
Please help,
Vishal

      and we said...
But as it says on the webpage;

 Oracle Portal 3.0 Production --available now in the Oracle9i
Application Server includes the portal framework, self-service
publishing, and "built-in" services for portlet development

WebDb is no longer a standalone webserver.  It is part of iAS.

You were at the right place.  Portal 3.0 is in iAS.
Review &
      Followup

      Rating: 3
Portal and
      Forms6i server  October 16, 2002
Reviewer:
      Abdal-Rahman Agha  from Yemen
Hi Tom,

   I have installed the minimal software from the oracle9iAS (1.0.2.2) which is
installed Portal + apache HTTP server and other management tools. I have already
Forms6i server installed in my machine. I could run Portal using its URL:
"
http://test:7778/pls/portal30
Now, I need to configure my already existing Forms6i Server with this
portal so as to access my FMX forms files through the browser. I failed finding
any place to describe this as step-by-step. Please, can help?
Thanks.

          Followup:

try metalink (support web site)...

I don't configure each and every piece of software myself

      Rating: 4
Installation
      Problem  October 23, 2002
Reviewer:  Praveen
       from India
Hi Tom,

I am facing some problem in installation of Oracle9iAS. The problem is I started
installation on newly installed plain NT Server. I am installing 9iAS and isuite
for Discoverer. When I first started the installation, It asked for two homes
one for 9iAS and one for isuite. But after some time the installation got
aborted due to some error. When I started the installation again, this time it
asked only one home for 9iAS and continued to install for isuite in old path for
home which I have given in first installation.
I have also cleaned the registry still the problem continues.
Why this is happening. Can you explain or give some link to the document.

          Followup:

Please open a TAR with support for installation / configuration assistance.  I
do not install every product myself.

metalink.oracle.com, the Oracle support website, would be a good place to start
for install notes and such.

October 23, 2002 Sabu -- Thanks for the question regarding "How to
      find oracle application sigon users and their session id", version 8.0.5

      You Asked
I am using oracle applications 11.0.3 and 8.0.5 database.

I want to find the oracle applications username and their session ids from the
database.

Thanks

Sabu

      and we said...
This query in SQL*Plus will show the Application User's name and session ID
along with their associated oracle and forms process IDs on the application
server:

column oracle format a12 heading 'Oracle|Process ID'
column forms format a12 heading 'Forms|Process ID'
column sess_id format 9999999 heading 'Session ID'

select u.user_name, l.pid sess_id, l.process_spid oracle, l.spid forms
from fnd_user u, fnd_logins l, v$process p
where u.user_id = l.user_id
and   p.pid = l.pid
and   p.spid = l.process_spid
/

followup to comment one below

read the question.  It is in regards to Oracle APPLICATIONS (like HR, fin apps,
ERP, etc).  In this environment, everyone logs in with the same account.  They
specifically asked to find the APPLICATION username.  Querying v$session would
return a constant (wrong) answer in this case.

Review &
      Followup

      Rating:
      4
  May 11, 2001
Reviewer:  Sabu
       from San Franscisco USA
Thanks

      Rating: 1
Alternate
      Way  May 12, 2001
Reviewer:  Riaz  from
      Pakistan
You can also do that much easily using following query:

select * from v$session

-- This table is owned by sys

      Rating:
      4
  April 12, 2002
Reviewer:  Anil
      Sekar  from US
Tom,
I have never used Oracle ERP applications, eventhough I worked on other ERP
products like SAP, Peoplesoft and Baan using Oracle databases. All these
products use database as a huge storage and retrieve mechanism only (handle the
application and security logic outside the database). I am very cuirous to
understand how Oracle applications differ from these products architecturally.
From this question and answer I understand that in Oracle applications too we
have application users and database users. Isnt it something you advocate
against? Can you also pls give a brief on the architecture of Oracle Apps. Is
Oracle Applications 11i and Oracle eBusiness suite one and the same? Thanks.

          Followup:

Oracle apps differ from that in that

o they use primary keys and other declaritive features of the database
o they use stored procedures
o they use triggers
o basically, they are not afraid to use the database.

Some of the architectural decisions (such as the "common" userids) stem way back
from legacy designs.  Oracle apps has grown up with the database from version 6,
7, 8 and 9.  In version 6 -- no roles, no fine grained access control, etc etc
etc.  They rolled some of their own.

They are in the process of incorporating this into the product now using some of
the new, more advanced features.  This will take time -- it is a major change.
Some features such as fine grained access control and such were developed with
their specific requirements in mind.

Apps architecture briefly?  I don't think so....  In a few words:

- three tier, java client for some things, pure html for much
- lots of stuff in the database (data oriented things)
- lots of stuff in the middle tier (logic oriented stuff)
- uses the oracle stack (reports, discoverer, oem, etc)

      Rating: 4
- three tier,
      java client for some things, pure html for much?  April 12,
      2002
Reviewer:  j  from ma
So, does this mean that Oracle has abandoned its own products (specifically
forms 6.0)? Is there a future for those of us that opted to use Oracle forms as
opposed to VB, peoplesoft etc some 5 years back?

          Followup:

forms 6.0 is three tier.

The client interface is delivered via java.
The middle tier is the forms server.
The database is the database.

Forms 6i is the same.  The next release of forms will be 3 tier only (no client
server as forms 6/6i have).  It is (and will be) forms.

So the answer is NO, forms is not abandoned, it is what I described above -- a 3
tier infrastructure with a java client.

      Rating:
      4
  April 12, 2002
Reviewer:
      A reader
Could you kindly refer to a website (other than oracle.com/appsnet - I need
more) with more information on the technical side of the Oracle Applications
product. Thanks.

          Followup:

Sorry, this is the source of the information -- maybe you didn't find this page:

http://www.oracle.com/appsnet/technology/architecture/content.html
but there it all is.

      Rating: 2
Finding
      application user in a 3-tier application  October 23, 2002

Reviewer:  Abu Ahmadh
Hi Tom,

In a 3-tier application with web-client and an application server every user has
their own userid at the application level and there is a generic userid at the
middle-tier which makes connection to the database.  Is it possible to find the
application userid from the database?

The requirement is to audit on a column of a table to capture old_value,
new_value and application_userid (since database userid is generic from
middle-tier).

Appreciate your help.

          Followup:

http://otn.oracle.com/docs/products/oracle9i/doc_library/release2/network.920/a96582/galsyste.htm#1022066
describes that perfectly.

October 23, 2002 Venkatasubramanian -- Thanks for the question
      regarding "dbms_stats", version 8.1.7.4

      You Asked
Dear Tom,

In the production scenario, analyzing very large tables frequently take a lot of
time. Can we always use DBMS_STATS to populate statistics for such tables and
look into the execution plan towards our tuning exercise?

Is the above method the  right substitute for analyzing large tables? Kindly
clarify.

Thanks and Regards,
Venkat

      and we said...
dbms_stats lets you analyze in parallel, lets you estimate, lets you gather
stats just on tables that need it (see ALTER TABLE tablename MONITORING).

That would be more proper.  Use parallel, make sure to gather just the stats you
need (eg: you don't need histograms on EVERY column, make sure you don't gather
then on EVERY column).  Use an appropriate estimate amount -- the bigger the
table, the smaller the estimate can be.

October 23, 2002 Krishnamoorthi -- Thanks for the question
      regarding "encrypt, monitoring", version 8.1.7.4

      You Asked
Dear Tom,

1. We give below a sample  table wherein we like to encrypt the data for sal
column.How to implement this? Can you explain the procedure? How to decrypt the
value when we want it?

sitadmin@OASIS817>desc cyg_emp
 Name                                                  Null?    Type
 ----------------------------------------------------- -------- ----------------
--------------------
 NAME                                                           VARCHAR2(20)
 SAL                                                            NUMBER

2. When I monitor the tables using 'alter table <tname> monitoring', the DML
operations get propagated to the view user_tab_partitions only after some time.
Is this normal? How long these details will remain in the view?

Thanks and Regards,
Venkat

      and we said...
1) search this site for

dbms_obfuscation_toolkit

or if you have my book "Expert one on one Oracle" -- read the section on that in
the appendix.  Lots of examples on crypting and de-crypting.

YOU have to manage (generate, maintain, protect) the keys and that is the hard
part.

2) Yes, it is normal.  if you use dbms_stats, they'll get flushed so dbms_stats
with the gather stale option will be able to see them.  they stay until you
gather stats again on the object at which point they become invalid (not
meaningful anymore)
Review & Followup

      Rating:
      3
  October 23, 2002
Reviewer:  Sagi
       from India
Hi Tom,

Can you explain the 2nd Question with a example. Please

Regards,
Sagi

          Followup:

come on -- not everything needs an example.  you have a database -- play with
it!  try it out yourself.

October 23, 2002 Hitesh -- Thanks for the question regarding
      "Created new user", version 8.1.5

      You Asked
Hi...Tom..
I created new user named A and wants give all the priveledges of user B schema.
So, I gave him all appropriate rights and created synonym for user A to access
tables of user B. Is there any other way to do this?

Pls. give ur valuable comments.......

      and we said...
create a role, grant access to B's objects to that role.

In the future, just grant the role to A or whatever user needs it.

You don't need synonyms -- I would much prefer A just used "select * from
b.table" but if that is deemed too onerous, have A issue "alter session set
current_schema=B" and then they can "select * from table" and it'll be B.table.
Review &
      Followup

      Rating:
      4
Hitesh  October 23, 2002
Reviewer:
      Hitesh  from India
Thankx a lot.......

      Rating: 3
Grant
      all  October 23, 2002
Reviewer:  Rob
       from Pittsburgh, PA
Tom:

Is there any way to do something like:

Grant execute any procedure owned by user b to a role.

I would like to be able to grant this role to user a and the effect be that user
a can execute any package owned by b without having to specify the individual
packages. This way any time a package is added to schema b the role would not
have to be updated. I would rather not grant the powerful "execute any
procedure" to a.

          Followup:

No, there is not.

You can use a DDL trigger to help automate this if you like.

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:646423863863

      Rating:
      3
  October 24, 2002
Reviewer:
      A reader

October 23, 2002 Ramachandran -- Thanks for the question regarding
      "Call an even from trigger only when Commit Issued", version 8.1.7

      You Asked
Hi Tom

I have a Requirement.  I have to send mail to the internal users whenever a
proposal created.  I thought of sending mails from Insert trigger.  But dont
want to send the mail in case of entry rolled back.  Is there any database
trigger available on Commmit.  can you give me some idea of how to execute the
same?

The entire above process is run in the back end.

Thanking you in advance

      and we said...
You would use DBMS_JOB to schedule a call to send an email right after the
commit took place.

If you have my book "Expert one on one Oracle" -- not only is the code there,
but the detailed explanation of everything related to this.

So, my suggestion is you set up a table:

create table emails (id number primary key, subj varchar2(30), emailto
varchar2(30), body varchar2(4000) /* or clob if needed */ );

and a procedure

create procedure submit_email( p_subj in varchar2, p_emailto in varchar2, p_body
in varchar2 )
as
   l_job number;
begin
   dbms_job.submit( l_job, 'send_email( JOB );' );
   insert into emails values ( l_job, p_subj, p_emailto, p_body );
end;
/

and another procedure:

create procedure send_email( p_job in number )
as
   l_rec emails%rowtype;
begin
   select * into l_rec from emails where id = p_job;
   utl_smtp calls to send it
   delete from emails where id = p_job;
   commit;
end;
/

and in your trigger, just call submit_email.  After you commit -- dbms_job will
send the email.

Make sure to set the job_queue_* init.ora parameters if they are not already!

Review &
      Followup

      Rating: 4
If roll back
      fired   October 23, 2002
Reviewer:  Lakshmi
      narasimhan  from Dubai
Tom

Thank u for immediate answer.  I have a doubt.  in case afer insert, due to some
operation error or oracle error if roll back applied, whether the job sends the
email since trigger fired already or it will not.  I am slightly confused in
this part only.

thanks in advance for ur feedback

          Followup:

The job does not appear in the job queue to be processed until you commit.

If you roll back -- the job "goes away", will never be committed into the job
queue.

It is exactly what you are looking for (and a way to make things like this
"transactional")

      Rating:
      5
Great  October 23, 2002
Reviewer:
      lakshmi narasimhan R  from dubai
Its simply superb.

thanks tom for the suggestion.

October 23, 2002 Hitesh -- Thanks for the question regarding "Find
      relevant companies name", version 8.1.5

      You Asked
Hi...Tom
I have two tables, Customer and Company_master. There is a column named
Company_name which are in both the tables, but no integrity constraint between
them.The sample data are given below

Customer            Company_Master
================================================================
Oracle ltd.            Oracle Corporation Ltd.
Oracle corporation
Oracle corporation ltd.
Oracle corporation India ltd.

Now, I wants to find related companies name like above

      and we said...
The product that does this sort of stuff is:

http://www.oracle.com/ip/analyze/warehouse/owb/index.html?integrate.html
short of that, you have to write your own algorithms for determining when these
two strings are actually "equivalent"
Review &
      Followup

      Rating:
      2
Thankx  October 24, 2002
Reviewer:
      Hitesh  from India

October 23, 2002 Budhi -- Thanks for the question regarding "Query
      fir retreiving consecutive transactions", version 8.1.7

      You Asked
Hi Tok,

I havae a table with columns

trans_id
em_id_c
trans_strt_d
trans_end_d
trans_type

Now my task here is to get all the transactions that are consecutive
that is get all the transactions in a cursor in order such that the preceding
transaction has the same end date as the start date of current transaction:

For emp_id_c : 9MRE2

id   trans strt d     trans end d  sort_seq(which can be
                                           the rownum i suppose)
11    01/01/2002       01/07/2002  1
27    01/08/2002       01/11/2002  2
32    01/12/2002       01/13/2002  3

Please respond asap

and Thanks in advance Tom

      and we said...
analytics ROCK:

ops$tkyte@ORA920.US.ORACLE.COM> select id, start_dt, end_dt,
  2         lag(end_dt) over ( order by start_dt,end_dt ) prior_end
  3    from t
  4   order by start_dt, end_dt
  5  /

        ID START_DT  END_DT    PRIOR_END
---------- --------- --------- ---------
         4 03-OCT-02 08-OCT-02
         3 08-OCT-02 11-OCT-02 08-OCT-02
         2 18-OCT-02 22-OCT-02 11-OCT-02
         1 23-OCT-02 28-OCT-02 22-OCT-02

ops$tkyte@ORA920.US.ORACLE.COM> select *
  2    from (
  3  select id, start_dt, end_dt,
  4         lag(end_dt) over ( order by start_dt,end_dt ) prior_end
  5    from t
  6         )
  7   where start_dt = prior_end
  8   order by start_dt, end_dt
  9  /

        ID START_DT  END_DT    PRIOR_END
---------- --------- --------- ---------
         3 08-OCT-02 11-OCT-02 08-OCT-02

Review &
      Followup

      Rating:
      3
  October 23, 2002
Reviewer:
      A reader
Hi tom, that looks close but i want that record for is 4 also to be retreived

My result set should be:

Id Start_d   End_d
----------------------
4 03-OCT-02 08-OCT-02
3 08-OCT-02 11-OCT-02

Thanks in advance

          Followup:

add "or prior_end is NULL"

      Rating:
      3
  October 23, 2002
Reviewer:
      A reader
Hi tom, that looks close but i want the record for id 4 also to be retreived

My result set should be:

Id Start_d   End_d
----------------------
4 03-OCT-02 08-OCT-02
3 08-OCT-02 11-OCT-02

Thanks in advance

October 23, 2002 ilker -- Thanks for the question regarding
      "ltrim", version 8.0

      You Asked
Hi,
I have some problem with ltrim:

when I use ltrim in a procedure like this (actually this is
dynamic and variables comes from database)

ltrim('$xrmid=ds','$xrmid=')

result of this, I get only 's'. I can't understand why also d is
trimmed. But if variable comes like 'kds' not 'ds', I get 'kds'
after operation which is above...

thanks

      and we said...
You've assumed ltrim() works on a STRING when in fact it works
on a SET of characters.  From the doc:

Purpose
LTRIM removes characters from the left of char, with all the
leftmost characters that appear in set removed; set defaults to
a single blank. If char is a character literal, you must enclose
it in single quotes. Oracle begins scanning char from its first
character and removes all characters that appear in set until
reaching a character not in set and then returns the result.
....

So:

ops$tkyte@DEV816> select ltrim( 'aaaabbbbbbccccccx', 'abc' )
                    from dual;

L
-
x

ops$tkyte@DEV816> select ltrim( 'abcabcabcx', 'abc' ) from dual;

L
-
x

ops$tkyte@DEV816> select ltrim( 'abcabcabcx', 'bca' ) from dual;

L
-
x

ops$tkyte@DEV816> select ltrim( 'aaabbbcccx', 'abc' ) from dual;

L
-
x

That removes ALL leading A's, B's, and C's -- not the string
'abc' or 'bca'.
Review & Followup

      Rating: 5
How to trim
      'Y'  October 23, 2002
Reviewer:
      A reader
tom ,

  can you pleae tell me how to trim single quotes from this
I just need Y not 'Y'

TIA

          Followup:

select trim( both '''' from column ) from t;

      Rating: 3
Use
      REPLACE  October 24, 2002
Reviewer:
      Christophe  from Troyes, FRANCE
select replace('$xrmid=ds','$xrmid=','')
from dual

RE
--
ds

October 24, 2002 Robert -- Thanks for the question regarding
      "Upcoming AOTC Presentation in DC", version 8.1.7.2

      You Asked
Tom, I am debating whether to go to Atlantic Oracle Training Conference (AOTC)
in DC. The description about your presentation in the brochure looks a lot like
what you talked about last time.

Can you give a quick rundown of what you're going to talk about
this time round ?

Thanks

      and we said...
This time I'm going to be a little more concrete, looking at some tricks I use
in SQL and such.

I'm going to avoid the material I did last time (which was a little "preachy"
perhaps) and show off things I've used to make things go faster.  Using rownum
in inline views and how that can affect your queries runtime performance.  How
to purge data efficiently.  things like that.

I cannot counsel as to whether that makes it "worthwhile" to come.  If you like,
today (oct 23 2002) I'm giving a web broadcast -- see the asktom home page for
searchdatabase details.  I'll be using some of that (not much) at AOTC as well.
Review &
      Followup

      Rating:
      5
  October 23, 2002
Reviewer:
      A reader
will you let us, who don't live in the usa, to download the presentation as you
did last time
thanks a lot

          Followup:

Sure

      Rating:
      5
  October 24, 2002
Reviewer:
      A reader
Tom,

The web broadcast was pretty much good..Can we get to see a replay of your
conference.

Thanks..

          Followup:

thanks!  appreciate that.  I don't really sound like a chipmunk (the fidelity
has a way to go ;)

they should have it available today or tomorrow.  I'll post a link to it when it
is there.

      Rating: 5
Web Cast
      Material available for Download?  October 24, 2002

Reviewer:  pasko  from Hamburg,Germany
Hi Tom,

I had registered for your Webcast Presentation yesterday but somehow i missed
it.
Do you have some of the material somewhere for download  ?

Thanks .

          Followup:

see above

October 24, 2002 Orlando -- Thanks for the question regarding
      "Oracle Names Implementation", version 8i 8.1.5 / 8.1.7

      You Asked
Hi Tom,

I am trying to implement Oracle Names to do our databases administration.
Everywhere I read makes reference to a script I must run before anything else "
NAMESINI.sql" to create all the objects I need on the DB. The problem is I don't
find this script anywhere in my server. Do I need to install this script from
some a CD other than Oracle 8i enterprise edition?. Do I really need this script
or is there any other way to implement Oracle Names?. I tried without using the
Database, but the service does not want to start because there is a file
missing.

I would really appreciate any help with your answers and/or pointers about the
best way to implement Onames.

Thanks,

Orlando Reyes

      and we said...
Problem Description
-------------------

You have just completed the full, default install of Oracle 8.1.5 or
Oracle 8.1.6.  When trying to configure Onames using a regional table
(ROSDB), you cannot locate the file NAMESINI.SQL described in the Net8
Administrator's Guide.

Solution Description
--------------------

Rerun the install, and do the custom install.
DO NOT change any of the settings or unselect any components.

*Note:  In addition, the NAMESINI.SQL file can be found from exec.0.1.jar in:

   <drive>:\stage\Components\oracle.networking.names\8.1.6.0.0\1\DataFiles

You can extract it from there as well.

Explanation
-----------

NAMEINI.SQL and the other names server modules are not part of
the default install.  By rerunning the installation and doing custom,
and not changing any settings, the installer will write the
NAMESINI.SQL file into the correct directory which is:
$ORACLE_HOME/NETWORK/NAMES directory.  The Oracle Names services were
installed during the default install.

Review &
      Followup

      Rating: 5
Oracle
      Names  April 24, 2001
Reviewer:  Orlando
      Reyes  from MN, USA
These are the little things that help you to get the job done, but without them
your life can be misserable for a while.

Thanks for the help.

      Rating:
      5
  October 24, 2002
Reviewer:
      A reader
Hi tom,

I want to configure my database like this ..Say  database DB1 server is located
at location L1..I want to make the connection to the DB1 from another location
which is located at L2 .,there is no database sitting at L2 .. but group of
clients sitting over and has to run the VB application by connecting to DB1 .

I thought  setting the oracle names server will help .. I started creating the
oracle
Names service while starting I am getting the following error .

--------------------
NAMESCTL> start
Starting "names.exe"...Failed to start service, error 2.
server not started, errors follow
TNS-12536: TNS:operation would block
 TNS-12560: TNS:protocol adapter error
  TNS-00506: Operation would block
   32-bit Windows Error: 997: Unknown error
----------------------
Can you please tell me how to come over from this  problem ., Also give me the
solution to my problem.

Thanks
Ravi

          Followup:

Problem Description:
====================

When using a GUI Net tool (or command line) to start a newly configured
names server, An error message appears with the following contents:

TNS-12560: TNS:protocol adapter error
TNS-00530: Protocl adapter error
32-bit Windows Error: 61: Unknown error

(Later releases of Oracle include a "TNS-12536: TNS:operation would block" error
and may report the 32-bit Windows Error as "997: Unknown error".

On Unix, this error is reported as:
 "$ORACLE_HOME/bin/names" does not exist
 SNL-00044: snlfacc: file or path name does not exist

Solution Description:
=====================

Install Oracle Names from the 8i or 9i install CD-ROM

Solution Description:
=====================

Re-run the Oracle Installer.

1) Select "Oracle Enterprise Edition" as the product to install.

2) Select "Custom" as the installation type.

3) Make sure "Oracle Names" is selected.

4) Continue with install.

Explanation:
============

The names executable is not installed using the default installation of
Oracle 8i or Oracle 9i.

October 24, 2002 Leah -- Thanks for the question regarding
      "Optimizing a view", version 8.1.5

      You Asked
Here is a view that is used for login verification. I have made
the table names simpler for brevity since it is a multi-table
join.
create view login_v
(
        sid,
        user_login_id,
        password,
        password_hint,
        login_type_sid,
        user_sid,
        email_address_txt,
        first_nm,
        mid_init_nm,
        last_nm,
        key
) as
select
        a.sid,
        b.email_address_txt,
        b.password,
        b.password_hint,
        c.login_type_sid,
        c.sid,
        b.email_address_txt,
        d.first_nm,
        d.mid_init_nm,
        d.last_nm,
        c.key
from    a, c, t1, t2, b, d, t3, t4
where   c.login_type_sid   = 0
and     b.email_idx_nu     = 0
and     t4.description     = 'Work'
and     t3.description     = 'Default'
and     d.addr_grp_sid     = c.addr_grp_sid
and     b.addr_grp_sid     = d.addr_grp_sid
and     b.email_typ_sid    = t4.email_typ_sid
and     b.addr_grp_status_sid  = t3.addr_grp_status_sid
and     t1.for_user_sid    = c.sid
and     t2.customer_sid    = t1.sid
and     a.sid              = t2.shop_sid
union
select
        a.sid,
        e.user_login_id,
        b.password,
        b.password_hint,
        c.login_type_sid,
        c.sid,
        b.email_address_txt,
        d.first_nm,
        d.mid_init_nm,
        d.last_nm,
        c.key
from    a, c, t1, t2, e, b, d, t3, t4
where   c.login_type_sid     = 1
and     b.email_idx_nu       = 0
and     t4.description       = 'Work'
and     t3.description       = 'Default'
and     d.addr_grp_sid       = c.addr_grp_sid
and     b.addr_grp_sid       = d.addr_grp_sid
and     b.email_typ_sid      = t4.email_typ_sid
and     b.addr_grp_status_sid  = t3.addr_grp_status_sid
and     e.user_sid           = c.sid
and     t1.for_user_sid      = c.sid
and     t2.customer_sid      = t1.sid
and     a.sid                = t2.shop_sid;

Since this view is a multi-table join with a union, it takes a
long time get the results back. We have tried to merge the two
select statements with an outer join but the result set differs
between the two. Is there a way to rewrite this to get better
performance?

      and we said...
Well, if I'm reading this right -- if the login_type_sid is 0,
you want to get the user_login_id from B.  If the login_type_sid
is 1, you want to get the user_login_id from E.

Otherwise -- the queries are the same.

If that simple analysis is correct, the single query:

select  a.sid,
        decode( c.login_type_sid,
                0, b.email_address_txt,
                1, (select e.user_login_id
                      from e
                     where e.user_sid = c.sid ) )
        b.password,
        b.password_hint,
        c.login_type_sid,
        c.sid,
        b.email_address_txt,
        d.first_nm,
        d.mid_init_nm,
        d.last_nm,
        c.key
from    a, c, t1, t2, b, d, t3, t4
where   c.login_type_sid   between 0 and 1
and     b.email_idx_nu     = 0
and     t4.description     = 'Work'
and     t3.description     = 'Default'
and     d.addr_grp_sid     = c.addr_grp_sid
and     b.addr_grp_sid     = d.addr_grp_sid
and     b.email_typ_sid    = t4.email_typ_sid
and     b.addr_grp_status_sid  = t3.addr_grp_status_sid
and     t1.for_user_sid    = c.sid
and     t2.customer_sid    = t1.sid
and     a.sid              = t2.shop_sid

may be more effective for you.  Of course, if login_type_sid is
only 0 and 1 -- you can remove that predicate as well.

The trick of selecting a SELECT can come in quite handy at times
(but it is not a silver bullet either -- don't "over use" it).

Review &
      Followup

      Rating: 3
Adding hints to
      a select from a view  October 23, 2002
Reviewer:
      A reader
In a third party app that we have, I have seen this app generate the following
sql:

select /*+ no_expand +*/ * from a_view

where a_view is a view that not only joins several tables but also other views.

Note the syntax of the hint. That is not a typo on my part.

The question: Does the hint have any effect on the sql inside the views ?

Thanks and your responses are greatly appreciated.

          Followup:

Yes, the hint is valid and is "used".  You can see this via:

ops$tkyte@ORA920.LOCALHOST> create view emp_view as select * from scott.emp;

View created.

ops$tkyte@ORA920.LOCALHOST> select /*+ no_expand +*/ * from emp_view;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=327 Bytes=28449)
   1    0   TABLE ACCESS (FULL) OF 'EMP' (Cost=2 Card=327 Bytes=28449)

ops$tkyte@ORA920.LOCALHOST> select * from emp_view;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'EMP'

The first query -- CBO (you can tell by cost= card= bytes=).  The second query
-- RBO.  Hint influenced the query.

      Rating: 5
Optimizing the
      view  October 24, 2002
Reviewer:  Lalit
       from The Netherlands
Tom, your comments will be very useful.

Even I have a select which is selecting from only one table which is having a
huge(1000000) number of records and it is doing GROUP BY for 8/9 fields. This
select is taking a lot time to execute. Can you guide me which HINT I can use to
optimize the group by select.

Thanks in advance

          Followup:

lets see --

scanning 1,000,000 records
grouping by 8 or 9 fields -- aggregating some others I suppose as well....

The hint I would use is:

select /*+ FAST=TRUE */ .....

(that was a joke, ha ha).  anyway -- maybe parallel query is for you -- see the
concepts guide, it describes how it works and how to use it.

October 24, 2002 Ak -- Thanks for the question regarding
      "Trigger", version 8.1.5

      You Asked
Hi tom,
    1) Can You comment something on a point that, Having many triggers in a
table might degrade the performance ??

  2) Is there any limitation on number of triggers we can have on One
Perticular table ...Before 8i we had Only 12.

3)In My v$session_wait i see many waiting....that includes
   "NET8" and "rdbms ipc message" ,Can you coment something on this..
It's Normal or we need to take care for this...

Thanks,

Regards
   Ak

      and we said...
1) sure, having too much of a good thing is almost certainly BAD.

Having more indexes then you need will slow down your inserts/updates/deletes
unnessarily.

Having redundant or not necessary constraints would do the same.

Triggers are just like that.  Having more then you need would be "bad".  More
code to execute, more to do.  It only makes sense.

2) there is no limit on the number of triggers since 7.1.  7.1 added multiple
same type triggers.

3) Those are normal "idle wait" messages.  See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1154234872764
for a recent discussion.
Review &
      Followup

      Rating:
      5
  October 18, 2001
Reviewer:
      A reader
"Those are normal "idle wait" messages."

1.Tom, can you tell us about wait's that are not normal, i.e. waits about which
we have to be concerned, and do something about.

2.Apart from the tkprof trace output file, what are the other means(I see
session_wait above) of knowing the waits being encountered.

3.can you give the link to the document, which lists all the wait events, and
there meaning.

          Followup:

1) thanks to connor below for the list.

2) tkprof actually doesn't show you the waits.  The RAW trace file can (and if
you are interested in that technique, i'll refer you to my book where I go
through that in detail in chapter 10)....

3) the server reference manual:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76961/apa.htm#264187
....

      Rating: 5
idle
      events  October 18, 2001
Reviewer:  Connor
       from Scotland
A good list (as provided by Steve Adams) is:
        'Null event',
        'KXFQ: Dequeue Range Keys - Slave',
        'KXFQ: Dequeuing samples',
        'KXFQ: kxfqdeq - dequeue from specific qref',
        'KXFQ: kxfqdeq - normal deqeue',
        'KXFX: Execution Message Dequeue - Slave',
        'KXFX: Parse Reply Dequeue - Query Coord',
        'KXFX: Reply Message Dequeue - Query Coord',
        'PAR RECOV : Dequeue msg - Slave',
        'PAR RECOV : Wait for reply - Query Coord',
        'Parallel Query Idle Wait - Slaves',
        'PL/SQL lock timer',
        'PX Deq: Execute Reply',
        'PX Deq: Execution Msg',
        'PX Deq: Index Merge Execute',
        'PX Deq: Index Merge Reply',
        'PX Deq: Par Recov Change Vector',
        'PX Deq: Par Recov Execute',
        'PX Deq: Par Recov Reply',
        'PX Deq: Parse Reply',
        'PX Deq: Table Q Get Keys',
        'PX Deq: Table Q Normal',
        'PX Deq: Table Q Sample',
        'PX Deq: Table Q qref',
        'PX Deq: Txn Recovery Reply',
        'PX Deq: Txn Recovery Start',
        'PX Deque wait',
        'PX Idle Wait',
        'Replication Dequeue',
        'Replication Dequeue ',
        'SQL*Net message from client',
        'SQL*Net message from dblink',
        'debugger command',
        'dispatcher timer',
        'parallel query dequeue wait',
        'pipe get',
        'queue messages',
        'rdbms ipc message',
        'secondary event',
        'single-task message',
        'slave wait',
        'virtual circuit status'
        'resmgr:%'
Alternatively, a good rule of thumb is see if the timeouts and time waited align
to something "precise", for example, smon timers at 5min intervals etc.

      Rating:
      4
Mr.Connor  October 18, 2001
Reviewer:
      A reader
Can you tell us what are the most common waits which we encounter , in the huge
list above, and what they are related to , and what we have to overcome them.

thank you

          Followup:

The waits in the list above are the "idle" waits. Those which you can typically
choose to ignore (to overcome them, ignore them).

There is no such thing as the "most common wait" in my experience.  I see all of
the IO waits at the head frequently -- the way to overcome them, do less IO, buy
faster disks....

      Rating: 5
smon/pmon timer
      events  October 24, 2002
Reviewer:
      A reader
Tom, Can smon timer and pmon timer events also be added in the above list of
'ignore these' events?
Thanks.

          Followup:

yup

October 24, 2002 John -- Thanks for the question regarding
      "Database hangs, locking problem?", version 8.1.7

      You Asked
We are doing stress test with our application which is developed with Java.  The
test hangs at Oracle database.  When I looked at database, it seems one process
is blocking another.  For example, SID 55 initially blocked SID 50.  When I kill
SID 55, SID 50 blocked SID 35.  When I killed SID 50, SID 35 blocked SID 40.  It
goes on and on.

Before the stress test, and when we ran application by itself, it worked fine.
That means SQL statements are committed properly.  But somehow when muti-process
access the database resource, it broke.  I know this information is not
sufficient for you to give me a definitive answer.  But what are the
possibilities/most likely scenarios?  What issues should I consider and where
should I start?

      and we said...
How, how how can you say:

Before the stress test, and when we ran application by itself, it worked fine.
That means SQL statements are committed properly.

The point of a stress test is to prove that your application is working properly
-- and it is not here.

You have a problem with your transaction logic -- you need to find out what is
up with sid 50.  It OBVIOUSLY has locks that the others need.

Look at what SID 50 is currently executing (search this site for showsql to find
a script to help you do that).  Look in v$lock to see what sid 50 has and who is
waiting on it.
October 24, 2002
      Vasu -- Thanks for the question regarding
      "Direct Path for the INSERTS using MERGE", version 9.1.2

      You Asked
Hello Tom,

You site has been very much helpful.

We are using Oracle 9i feature MERGE as part of our incremental load approach.
My question is,
Would it be possible to make MERGE statement to use "direct path" for INSERTS ?.

Will the hints like /*+ APPEND */ works ?.

Thanks in advance.

Vasu

      and we said...
Nope -- think about it.

insert can append cause it adds new -- above the HWM.  to rollback -- just
ignore the newly loaded data (we don't have to UNDO the writes above the HWM)

Merge -- writes into the MIDDLE of the table.  Hence -- it cannot skip UNDO,
must generate UNDO in the event the statement is to be UNDONE.
Review &
      Followup

      Rating:
      4
  October 24, 2002
Reviewer:  Vasu
       from Beaverton, OR
So there is no way to make INSERTs cuased by MERGE to write above HWM ?

Thanks,
Vasu

          Followup:

nope

October 24, 2002 Sanjay -- Thanks for the question regarding
      "Miscellaneous Questions on 8i/9i", version 8i/9i

      You Asked
Tom,
Did not get enough details anywhere on following, please give some details and
links if possible:
a. SPARSELY POPULATED SEGMENT
b. ADD SUPPLEMENTAL LOG DATA Clause (Where would one use this?)
c. Undocumented Initialization Parameters (What are these and why they exist if
they need to be "undocumented"?)
d. I read Jonathan's book for index utilization and want to get your opinion on
which one to choose, DBMS_STATS.EXPORT/IMPORT_TBALE_STATS to export/import stats
and retrieve stats into data dictionary whenever required to "force" CBO to pick
up an execution plan OR as Jonathan suggested to "experiment" with
OPTIMIZER_INDEX_CACHING & OPTIMIZER_INDEX_COST_ADJ for the same effect?

Regards,

Sanjay

      and we said...
a) no idea what you are looking for.

b)

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96571/toc.htm
is the major reason for using that (analysis being another reason, log mining)

c) they are for support to tell you to turn on if and when needed only.

d) Anything JPL says should be taken as gospel truth.

I've never used export/import stats like that, defeats the purpose of
statistics.  I use the cost adj primarily. (i like the number "35" for some
reason)

Review &
      Followup

      Rating: 5
Export/Import
      stats  October 24, 2002
Reviewer:  Jonathan
      Lewis  from London, UK
Thank you for your comment (d) - although I should point out that I have been
known to make mistakes.

The bit about export/import sounds like the advice for getting production stats
down onto a development database.  Much easier to do now that you can attach a
query to the export to get a subset of a table, and then import it with
'statistics = safe' to get the exported stats applied to the imported data.

Jonathan Lewis.

http://www.jlcomp.demon.co.uk/

          Followup:

Uh-oh, didn't know you guys actually read this stuff....

And OEM has a new feature too that's pretty cool in 9iR2 -- an "intelligent data
subsetter" that pulls over a bit of a complex, interelated schema along with the
full up "stats" for testing.

      Rating: 5
What is
      intelligent data subsetter  October 24, 2002

Reviewer:  Arup Nanda  from Norwalk, CT, USA

Tom,

In the response to the last review, you mentioned something about "intelligent
data subsetter" in 9iR2. Could you explain a little bit on that, please.

TIA.

Arup Nanda

October 25, 2002 VIPIN -- Thanks for the question regarding "Fetch
      Across Commit", version 8.1.7

      You Asked
I am trying to run a procedure which deletes from 4 tables which are having
approx. half a million dollar each.
I take the cursor and then delete or update or insert in the loop body and
commit after each transaction. I am getting ORA-01555 snapshot too old.
i have seen few questions already answered by on this issue but unable to
understand the reason clearly. I will be greatful if you can elaborate.
What i know is that if i can commit after every insert/update/delete create then
there would not be this problem because my rollback is getting free as soon as
the commit is fired.
am i correct ?

      and we said...
It is BECAUSE the rollback is getting freed that the ora-1555 occurs.

The query that drives your loop is the thing that is getting the error.  It
*needs* that UNDO information in order to get the read consistent view of the
database table(s).  When your read query hits a block that was modified since
your query began -- it knows it is not allowed to see those modifications, even
if the session it is running in was the one that caused those modifications.  In
order to do that -- we use UNDO stored in the rollback segments.

Your modications are causing updates (lots of them) to the tables you are
reading.  You are releasing for reuse the rollback space when you commit.  You
eventually overwrite some UNDO you generated but that your QUERY will none the
less NEED later on.  Bamm -- ora-01555.

If you search this site for

ora-01555

or just

1555

you'll see tons of examples.  If you have my book "Expert on one on Oracle" I
cover this topic in great depth with -- tons of examples again.

The problem is the commit in the cursor for loop.  It does not belong there.  Or
if it "does", you need to restart your query every time you commit.

The right answer is to size your rollback segments for the work you do on your
system, the wrong answer is to code lots of procedural code to try and avoid
having to use lots of rollback.
Review &
      Followup

      Rating:
      5
ORA-01555  October 23, 2002
Reviewer:
      vipin  from Singapore
It was an excellent explanantion for the confusion i had. But i have got another
query, and this is regarding rollback segment. I was wondering the ROLLBACK
can't be size more than for maxextents i.e. 249 .which i have set for all the
rollback segs i have.
How else can u size your rollback segments ?.

Thanks a lot once again for you time.

          Followup:

I'm not sure what you are asking here.

      Rating:
      5
  October 24, 2002
Reviewer:  Paul
       from Houston
<QUOTE>When your read query hits a block that was modified since your query
began -- it knows it is not allowed to see those modifications, even if the
session it is running in was the one that caused those modifications.</QUOTE>

Can I assume here that the query I have executed is not complete ie. it is still
running.
If the query I have executed is complete, I am allowed to see the modifications.
Am I right?

          Followup:

  for x in ( select * from emp )   <<<== result set FIXED as of the time this
                                   <<<== query BEGAN, will NOT see any of the
                                   <<<== inserts
  loop
       insert into emp ( .... ) values ( ..... );

       select count(*) into l_cnt from emp;  <<<== this query will see the
                                             <<<== inserts made one line above
                                             <<<== but will NOT see any
                                                   uncommitted mods by other
                                                   sessions
       .....

      Rating:
      5
Excellent  October 26, 2002
Reviewer:
      Paul  from Houston
You are too good.  I can't ask for a better explanation.

October 25, 2002 David -- Thanks for the question regarding
      "Configuring Personal ORacle With Developer 2000", version Personal Oracle
      8.1.5.0.0

      You Asked
Hi Tom! Hope you can help me out with this problem.
I have Personal Oracle 8.1.5.0.0 installed on my PC. I also have

Oracle Developer Release 6.0. I wish to connect from Developer
to this version of Personal Oracle. Please advise what to do and

how to go about it.

Thanks,

David D'Lima

      and we said...
You need to be running Net8 and use a tns connect string in this
case.  This is because D2K 6.0 is based on 8.0 software (client
software) whereas 8.1 is not.  We need to use sqlnet to connect
this 8.0 client to the 8.1 server.

So, you need to make sure your sqlnet listener is running under
8.1.5 -- you'll configure the listener.ora to get it up and
running.  That file will be found in $ORACLE_HOME\network\admin
and will look something like:

# LISTENER.ORA Network Configuration File:
# Generated by Oracle configuration tools.

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS_LIST =
        (ADDRESS=(PROTOCOL=TCP)(HOST=tkyte-del)(PORT=1521))
      )
      (ADDRESS_LIST =
        (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1))
      )
    )
  )

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (SID_NAME = PLSExtProc)
      (ORACLE_HOME = C:\oracle)
      (PROGRAM = extproc)
    )
    (SID_DESC =
      (GLOBAL_DBNAME = tkyte816)
      (ORACLE_HOME = C:\oracle)
      (SID_NAME = tkyte816)
    )
  )

Yours will be similar but different.  You can verify if your
listener is running via:

C:\oracle\network\ADMIN>lsnrctl status

that'll show you if it is.  If not, you can use lsnrctl start to
start it (and you can set the service to "automatic" to get it
to start on your next reboot).

Then, you need to make sure you have configured your
TNSNAMES.ORA in your D2K oracle home (NOT in the 8.1 oracle
home).  D2K will be looking at its $ORACLE_HOME\net80\admin
(note the directory name is different).  That tnsnames.ora
should have an entry like:

mylocaldatabase =
  (DESCRIPTION =
      (ADDRESS=(PROTOCOL=TCP)(HOST=tkyte-dell)(PORT=1521))
          (CONNECT_DATA=(SID=tkyte816))
  )

Now, you can log in with the D2K user/pass@mylocaldatabase
syntax....

Review &
      Followup

      Rating: 5
Configuring
      Personal ORacle With Developer 2000  December 16, 2001

Reviewer:  janet ang  from Philippines
I have been installed PO817 on my notebook but i could only access the DB using
the user/pass but if i added the connect string i would always get the No
Listener error.  I didnt know what to do and kept on tinkering with the
listener.ora and tnsnames.orac but to no avail.  I have also consulted other
DBA's but the only help they could give me was create another database which i
didnt want to do.  Previously with PO7 it was no problem cause the @local would
always work.

Thank you so much, you've been a big help.

      Rating:
      3
ORA-205  December 19, 2001
Reviewer:
      Richard  from Nigeria
Hi Tom,
How do resolve this problem that ''ORA-205: Can not mount database''.
The error causes the database starter to fail even though i followed the process
you described.
am using V8.1.5.0.0

      Rating: 5
How to
      find  October 24, 2002
Reviewer:  atul
       from India
Sir,

If you go to fresh set up...How to know that two different oracle homes are
set??? or this set up is using single oracle home

Thanks.
atul

          Followup:

use the oracle home chooser (tiny application, should be on the start menu) to
pick your oracle home.  there will be a list there of all of the ones you have.

      Rating: 2
okay
      good  November 13, 2003
Reviewer:  Khalid
      Majeed  from Punjab, Pakistan
this is use full. but i know it already.

October 26, 2002 Harman -- Thanks for the question regarding
      "Rollback segment Issue - DBA_ROLLBACK_SEGS -different Initial, Min extent
      values", version 9.0.1

      You Asked
Hello Tom,

I am creating a rollback segment using dictionary manage tablespace on Oracle
9.0.1 on VMS as well as 8.1.7 on VMS.

I ran the following scripts on both Oracle9.0.1 & 8.1.7

SQL> CREATE TABLESPACE ROLLBACK_BIG1
2 DATAFILE 'ORACLE9_DB:[DBS]RB_BIG1_DF.DBS' SIZE 10M reuse
3 DEFAULT STORAGE (
4 INITIAL 1M
5 NEXT 1M
6 MINEXTENTS 2
7 PCTINCREASE 0) ;

Tablespace created.

SQL> create rollback segment rb_big1
2 storage ( initial 2048k next 2048k minextents 2)
3 tablespace rollback_big1;

Rollback segment created.

Then i query the dba_rollback_segs view and got the following results

SQL> select segment_name, tablespace_name, initial_extent, next_extent,
min_extents, max_extents from dba_rollback_segs where segment_name like 'RB%' ;

SEGMENT_NAME TABLESPACE_NAME INITIAL_EXTENT NEXT_EXTENT MIN_EXTENTS MAX_EXTENTS
------------------------------ ------------------------------ --------------
----------- ----------- -----------

RB_BIG1 ROLLBACK_BIG1 4194304 1048576 1 2147483645

However running the same query on Oracle8.1.7 produce the following result.

on 8.1.7
========
SQL> select segment_name, tablespace_name, initial_extent, next_extent,
min_extents, max_extents from dba_rollback_segs where segment_name like 'RB%' ;

SEGMENT_NAME TABLESPACE_NAME INITIAL_EXTENT NEXT_EXTENT MIN_EXTENTS MAX_EXTENTS
------------------------------ ------------------------------ --------------
----------- ----------- -----------
RB_BIG1 ROLLBACK_BIG1 2097152 2097152 2 1017

My question-
 In 9.0.1 why the INITIAL_EXTENT value is showing 4194304,
NEXT_EXTENT is showing 1048576 (as if it is aking the value from the base TBS)
and the MIN_EXTENTS is 1.? Is this a Bug ?

8.1.7 is Showing this right as 2048K(2097152)

Your Help is appreciated.
Thanks
HB

      and we said...
...

I am creating a rollback segment using dictionary manage tablespace on Oracle
9.0.1 on VMS as well as 8.1.7 on VMS.

.....

no you are not.  You are using an LMT in 9i, if you dump dba_tablespaces, you
would see something like this:

TABLESPACE_NAME               : ROLLBACK_BIG1
BLOCK_SIZE                    : 8192
INITIAL_EXTENT                : 1048576
NEXT_EXTENT                   : 1048576
MIN_EXTENTS                   : 1
MAX_EXTENTS                   : 2147483645
PCT_INCREASE                  : 0
MIN_EXTLEN                    : 1048576
STATUS                        : ONLINE
CONTENTS                      : PERMANENT
LOGGING                       : LOGGING
EXTENT_MANAGEMENT             : LOCAL
ALLOCATION_TYPE               : UNIFORM
PLUGGED_IN                    : NO
SEGMENT_SPACE_MANAGEMENT      : MANUAL
-----------------

the correct and default tablespace type in 9i is LMT!

So, when you issued:

SQL> create rollback segment rb_big1
2 storage ( initial 2048k next 2048k minextents 2)
3 tablespace rollback_big1;

it said "ok, 2 2m extents = 4m, thats my initial allocation.  Now, since my next
extent length is 1m, I'm going to very politely ignore your 2m request and go
with 1m"

So, you have a rollback segment with 4 1m extents and the next extent will be 1m
as well.

USE LMTS!  In 9iR2 we make it even harder to use a DMT.

Review &
      Followup

      Rating:
      3
  October 23, 2002
Reviewer:  Sagi
       from India
Hi Tom,

When you said

"ok, 2 2m extents = 4m, thats my initial allocation.  Now, since my next extent
length is 1m, I'm going to very politely ignore your 2m request and go with 1m

So, you have a rollback segment with 4 1m extents and the next extent will be 1m
as well."

Here you mean to say that if we are using LMT (In this case EXTENT MANAGEMENT
LOCAL UNIFORM 1M)

Now the NEXT EXTENT of ROLLBACK SEGMENT though specified as 2m gets ignored and
is over written with the NEXT EXTENT (i.e. UNIFORM 1M) of LMT.

Thanx in Advance.

Regards,
Sagi

          Followup:

I was talking in the context of a LMT yes -- because the tablespace they used
was in fact a locally managed one.

the next extent of a segment is totally ignored by definition in a locally
managed tablespace.  It is used ONLY during the initial segment allocation when
minextents > 1.  If you say:

initial 1m next 2m minextents 3

Oracle will allocate at least 5m of extents in a locally managed tablespace and
then it will utterly and totally ignore these storage settings forever (as it
manages extent sizes in LMTS -- that is the entire design goal of the LMT)

      Rating:
      3
  October 23, 2002
Reviewer:  Sagi
       from India
Hi Tom,

I am still not very clear how the calculations are done in LMTs.

You said

"the next extent of a segment is totally ignored by definition in a locally
managed tablespace.  It is used ONLY during the initial segment allocation when
minextents > 1.  "

Very clear till here....Further you said

"If you say:

initial 1m next 2m minextents 3

Oracle will allocate at least 5m of extents in a locally managed tablespace"

This is not clear. When are the actual circumstances where it gives me atleast
5m, why?

I have executed the below and the output is pasted too..

CREATE TABLESPACE TESTRBS
DATAFILE 'C:\TESTRBS.DBF' SIZE 10M
AUTOEXTEND ON NEXT 5M MAXSIZE 25M
EXTENT MANAGEMENT LOCAL UNIFORM 1M ;

create rollback segment testrbs1
tablespace testrbs
storage (initial 1m next 3m minextents 3) ;

create rollback segment testrbs2
tablespace testrbs
storage (initial 2m next 3m minextents 3) ;

create rollback segment testrbs3
tablespace testrbs
storage (initial 3m next 3m minextents 3) ;

SQL> select segment_name NAME, initial_extent INI, next_extent NXT, min_extents
MIN, max_extents  MAX
  2  from dba_rollback_segs
  3  WHERE SEGMENT_NAME LIKE 'TESTRBS%'
  4  order by 1 ;

NAME          INI           NXT           MIN        MAX
------------------------------ ---------- ---------- -------TESTRBS1     7340032
     1048576          1      32765
TESTRBS2     8388608      1048576          1      32765
TESTRBS3     9437184      1048576          1      32765

As per what you say it should have created me a INITIAL of 3M for TESTRBS1, 6M
for TESTRBS2 and 9M for TESTRBS3. Because the minextents > 1.

But only TESTRBS3 matches, why i get 7M and 8M for TESTRBS1 and TESTRBS2?

Would be helpful, if you could take time and explain the above and "Oracle will
allocate at least 5m of extents in a locally managed tablespace".

Thanx in Advance.

          Followup:

...
initial 1m next 2m minextents 3

Oracle will allocate at least 5m of extents in a locally managed tablespace"

This is not clear. When are the actual circumstances where it gives me atleast
5m, why?
......

it is math.  If you created that table in a DMT, what would happen?  You would
initially allocate 5m of storage (1m + 2m + 2m -- because minextents = 3).
Well, when you create in a LMT the same thing happens -- 5m of storage for the
same reasons.  We take your initial, next, minextents and compute the size that
would be allocated and then allocate enough extents in the LMT to at least
satisfy that.

I don't know how you are doing your math but:

storage (initial 1m next 3m minextents 3) ; gives me
1m + 3m + 3m = 7m for testrbs1

storage (initial 2m next 3m minextents 3) ;
2m+3m+3m = 8m for testrbs2

storage (initial 3m next 3m minextents 3) ;
3m+3m+3m = 9m for testrbs3

My "at least 5m" comment is applicable ONLY to the storage parameters they used
-- not yours.

Compute how much space Oracle would have allocated if that storage clause was
applied to a DMT.  Oracle will allocate that amount of storage (at least) in a
LMT

      Rating:
      3
  October 23, 2002
Reviewer:  Sagi
       from India
Hi Tom,

Thanx for your time and explanation. You said

"We take your initial, next, minextents and compute the size"

So when the STORAGE clause is:

1. storage (initial 1m next 3m minextents 3) ;
2. storage (initial 2m next 3m minextents 3) ;
3. storage (initial 3m next 3m minextents 3) ;

Should't it show 3M, 6M or 9M in my INITIAL EXTENT (Initial Extent Value * Min
#of Extents). The Next Extent Would come into picture only when we need more
space and the next allocation depends on the Value of NEXT_EXTENT. Is this not
right? So how come it calculates all (INITIAL + NEXT + MINEXTENTS) and gives me
that as INITIAL?

Your calculations Says INITIAL + NEXT + MINEXTENTS. But when we say INITIAL is
it not (INITIAL * MINEXTENTS).

I was in this assumption all these days. Hope to see your detail explanation.

Thanx in advance.

Regards,
Sagi

          Followup:

nope, it does not, should not and will not.

It is the way LMTs were designed to work.  NEXT_EXTENT has no purpose, no
MEANING in a LMT.

Initial is not initial*minextents, it is as I described it. it is initial + 1st
next + 2cnd next + .... + Nth next

      Rating:
      4
  October 23, 2002
Reviewer:  Sagi
       from India
Hi Tom,

Its clear now. So now when i had the RBS created with the below storage:

storage (initial 2m next 3m minextents 3) ;

You told it would be 8M (2m+3m+3m). So this is the INITAL. Its clear now.

Q1) This is irrespective whether we are in (DMT or LMT). Or this applies to only
LMT.

Q2) So now when we need more space i.e. beyond 8M, Oracle allocates me 1m
(Bcause LMT-UNIFORM LOCAL 1M) as it over rides the RBS storage Clause. Right?

At this stage when I query the DBA_ROLLBACK_SEGS the INITIAL_EXTENT would show
me 8M or 9M (Starting 2+3+3 an new 1M size now allocated one)

With answers to the above 2 questions, I think I should be having a clear
picture now.

Thanx in Advance.

Regards,
Sagi

          Followup:

q1) lmt only, lmt only.

q2) correct

      Rating:
      3
  October 24, 2002
Reviewer:  Sagi
       from India
Hi Tom,

You said in case of LMT the INITIAL is calculated as INITIAL+NEXT+MINEXTENTS.
But this does not work. The commands and output are below:

LMT
===
create tablespace test
datafile 'c:\test.dbf' size 10m REUSE
autoextend on next 5m maxsize 50m
extent management local uniform size 1m
/

create rollback segment rtesta
tablespace test
storage (initial 1m next 4m minextents 3)
/

create rollback segment rtestb
tablespace test
storage (initial 2m next 4m minextents 3)
/

create rollback segment rtestc
tablespace test
storage (initial 3m next 4m minextents 3)
/

DMT
===
create tablespace test1
datafile 'c:\test1.dbf' size 10m REUSE
autoextend on next 5m maxsize 50m
DEFAULT STORAGE ( INITIAL 2M NEXT 3M PCTINCREASE 0)
/

create rollback segment rtest1a
tablespace test1
storage (initial 1m next 4m minextents 3)
/

create rollback segment rtest1b
tablespace test1
storage (initial 2m next 4m minextents 3)
/

create rollback segment rtest1c
tablespace test1
storage (initial 3m next 4m minextents 3)
/

SQL>  select segment_name, initial_extent, next_extent, min_extents
  2  from dba_rollback_segs
  3  where tablespace_name like 'TEST%'
  4  ORDER BY SEGMENT_NAME ;

SEG_NAME  INITIAL_EXTENT     NEXT_EXTENT     MIN_EXTENTS
-----------------------------------------------------------
RTEST1A      1048576            4194304           3
RTEST1B      2097152            4194304           3
RTEST1C      3145728            4194304           3
RTESTA       9437184            1048576           1
RTESTB      10485760            1048576           1
RTESTC      11534336            1048576           1

The INITIAL for DMT (RTEST1A, RTEST1B, RTEST1C) shows as correct.

Q1) But INITIAL for LMT (RTESTA, RTESTB, RTESTC) shows incorrect. Because you
said that in case of LMT the initial is calculated as (INITIAL+NEXT+MINEXTENTS)
therefore it should be:

RTESTA =  8M i.e. (1 + 4 + 3) but shows 9M
RTESTB =  9M i.e. (2 + 4 + 3) but shows 10M
RTESTC = 10M i.e. (3 + 4 + 3) but shows 11M

Q2) Why does the MINEXTENTS change to 1, when i have given it as 3?

Where am i going wrong. Sorry for troubling you once again. But confussed.

Anticipating and hope you would cleare me on this. Please Please Please.

Regards,
Sagi.

          Followup:

Sagi --  "beating" and "dead horse" is coming to mind here....

Where pray tell did I say "You said in case of LMT the INITIAL is calculated as
INITIAL+NEXT+MINEXTENTS. "???

I said in the case of an LMT, the initially allocated amount of storage will use
initial, next, minextents in order to calculate what WOULD have been allocated
using DMTs and then it will allocate that much space in the LMT.

I said

"Initial is not initial*minextents, it is as I described it. it is initial + 1st

next + 2cnd next + .... + Nth next "

create rollback segment rtesta
tablespace test
storage (initial 1m next 4m minextents 3) =

1M + 4m + 4m = 9m

Look at my math again, read "slower" maybe?

      Rating:
      5
LMT  October 24, 2002
Reviewer:  Harman
      Brar  from Ontario
Hi Tom,
         I really appreciate your response on my Last question - Rollback
segment Issue on LMT/DMT. I have one more question regarding LMT and objects
that are created on LMT.

I would greatly appreciate, if you could spare some of your time going through
this.

CREATE TABLESPACE local_uniform DATAFILE
    'DSG$OR2:[RWDTA.ORA9_DB.DBS]LOCAL_UNIFORM_TEST_DF.DBS' SIZE 1M reuse
     EXTENT MANAGEMENT LOCAL UNIFORM SIZE 128K

CREATE TABLESPACE local_auto DATAFILE
    'DSG$OR2:[RWDTA.ORA9_DB.DBS]LOCAL_AUTO_TEST_DF.DBS' SIZE 1M reuse
     EXTENT MANAGEMENT LOCAL AUTOALLOCATE

Select TABLESPACE_NAME, INITIAL_EXTENT, NEXT_EXTENT, MIN_EXTENTS, MAX_EXTENTS,
  MIN_EXTLEN, EXTENT_MANAGEMENT, ALLOCATION_TYPE, PLUGGED_IN
  from dba_tablespaces
where tablespace_name like 'LOCAL%';

TABLESPACE_NAME  INITIAL_EXTENT NEXT_EXTENT MIN_EXTENTS MAX_EXTENTS MIN_EXTLEN
EXTENT_MANAGEMENT ALLOCATION_TYPE PLUGGED_IN
---------------- -------------- ----------- ----------- ----------- ----------
----------------- --------------- ----------
LOCAL_AUTO         65536                       1         2147483645      65536
LOCAL             SYSTEM          NO
LOCAL_UNIFORM      131072       131072         1         2147483645     131072
LOCAL             UNIFORM         NO
2 rows selected

Now I am creating the following Tables on above LMT's

CREATE TABLE local_table1 (
    DEPTNO              NUMBER(3) NOT NULL,
    DNAME               VARCHAR2(14),
    LOC                 VARCHAR2(13))
    storage  (initial 10k next 10k)
    tablespace local_uniform ;

CREATE TABLE local_table2 (
   DEPTNO              NUMBER(3) NOT NULL,
   DNAME               VARCHAR2(14),
   LOC                 VARCHAR2(13))
  storage (initial 10k next 10k)
  tablespace local_auto;

CREATE TABLE local_table3 (
    DEPTNO              NUMBER(3) NOT NULL,
    DNAME               VARCHAR2(14),
    LOC                 VARCHAR2(13))
    storage  (initial 20k next 20k)
    tablespace local_uniform

CREATE TABLE local_table4 (
   DEPTNO              NUMBER(3) NOT NULL,
   DNAME               VARCHAR2(14),
   LOC                 VARCHAR2(13))
  storage (initial 20k next 20k)
  tablespace local_auto;

  Select table_name, tablespace_name, INITIAL_EXTENT, NEXT_EXTENT
  from user_tables where table_name like 'LOCAL%';

 TABLE_NAME                     TABLESPACE_NAME                INITIAL_EXTENT
NEXT_EXTENT
 ------------------------------ ------------------------------ --------------
-----------
 LOCAL_TABLE1                   LOCAL_UNIFORM                           16384
  131072
 LOCAL_TABLE2                   LOCAL_AUTO                              16384

 LOCAL_TABLE3                   LOCAL_UNIFORM                           24576
  131072
 LOCAL_TABLE4                   LOCAL_AUTO                              24576

4 rows selected

Question 1: Why do  i get Initial Extent 16K when i defined 10K and 24K when i
defined 20K while creating the above tables?

Now look at the actual size of each allocated extent:

select SEGMENT_NAME, SEGMENT_TYPE, TABLESPACE_NAME, EXTENT_ID, BYTES, BLOCKS
    from user_extents
   where segment_name like 'LOCAL%';

SEGMENT_NAME         SEGMENT_TYPE   TABLESPACE_NAME    EXTENT_ID   BYTES
BLOCKS
-------------------- -------------- ------------------ ---------- ----------
----------
LOCAL_TABLE1         TABLE          LOCAL_UNIFORM                0     131072
     16
LOCAL_TABLE3         TABLE          LOCAL_UNIFORM             0     131072
  16
LOCAL_TABLE4         TABLE          LOCAL_AUTO                0      65536
   8
LOCAL_TABLE2         TABLE          LOCAL_AUTO                0      65536
   8
4 rows selected

Question 2: The first extent taken by LOCAL_TABLE1 from the Local_Uniform
Tablespace is 16K (16384)  or 128K(131072)? why ?

 Now I am Allocating another extent for all above Tables.

 alter table local_table1 allocate extent;
 alter table local_table2 allocate extent;
 alter table local_table3 allocate extent;
 alter table local_table4 allocate extent;

 select SEGMENT_NAME, SEGMENT_TYPE, TABLESPACE_NAME, EXTENT_ID, BYTES, BLOCKS
     from user_extents
   where segment_name like 'LOCAL%';

 SEGMENT_NAME      SEGMENT_TYPE       TABLESPACE_NAME  EXTENT_ID  BYTES   BLOCKS
 --------------------------------- -------------- ------------------------------
 LOCAL_TABLE1         TABLE           LOCAL_UNIFORM      0     131072         16
 LOCAL_TABLE1         TABLE           LOCAL_UNIFORM      1     131072         16
 LOCAL_TABLE3         TABLE           LOCAL_UNIFORM      0     131072         16
 LOCAL_TABLE3         TABLE           LOCAL_UNIFORM      1     131072         16
 LOCAL_TABLE4         TABLE           LOCAL_AUTO         0      65536          8
 LOCAL_TABLE4         TABLE           LOCAL_AUTO         1      65536          8
 LOCAL_TABLE2         TABLE           LOCAL_AUTO         0      65536          8
 LOCAL_TABLE2         TABLE           LOCAL_AUTO         1      65536          8
8 rows selected

Question 3: All future Extents for all above Table would be based upon the LMT
i.e 128K for Local Uniform and 65K for Local Auto ignoring the actual storage
clause in the Table creation, right?

          Followup:

well, my question to you -- why do you care?  it just isn't relevant in a LMT??

but anyway -- I'll guess you have an 8k block size.  smallest segment is 2
blocks.  2 blocks is 16k, you asked for a 10k table so the data dictionary
records "16K" is the initial extent.  If you suck out my DDL and re-run me, i'll
go for 16k and let the LMT do what it will.

the closest extent size possible in a 8k blocksize database to 20k is 24k, hence
it used that.

USER_TABLES is showing you meta data -- what you requested more or less.
USER_EXTENTS is showing you reality.  If you exported either of these tables,
user_tables would be used to build the textual storage clause.  If you imported
the table that is currently in the 128k extent tablespace into a 64k tablespace
-- you would find the SAME metadata in user_tables, different REAL data in
user_extents.

After initial creation, where Oracle takes the initial, next, minextents and
computes how much to allocate up front -- these settings are utterly and
entirely and totally *ignored* for the life of the object.

      Rating:
      5
  October 24, 2002
Reviewer:
      A reader
Thanks for your prompt response, it was awesome.

      Rating:
      5
  October 24, 2002
Reviewer:  Harman
      Brar
Thanks for your prompt response, it was awesome.

      Rating:
      4
  October 25, 2002
Reviewer:  Sagi
       from India
Hi Tom,

I think I am clear now but anyway please see the commands exected below and
clarrify the

questions. Please

CREATE TABLESPACE TEST
DATAFILE 'C:\TEST.DBF' SIZE 10M
AUTOEXTEND ON NEXT 5M MAXSIZE 100M
EXTENT MANAGEMENT LOCAL UNIFORM 1M ;

SQL>  CREATE ROLLBACK SEGMENT TEST1
  2  TABLESPACE TEST
  3  STORAGE (INITIAL 1M NEXT 3M MINEXTENTS 10) ;
Rollback segment created.

SQL>   CREATE ROLLBACK SEGMENT TEST2
  2  TABLESPACE TEST
  3  STORAGE (INITIAL 3M NEXT 2M MINEXTENTS 5) ;

SQL>   CREATE ROLLBACK SEGMENT TEST3
  2  TABLESPACE TEST
  3  STORAGE (INITIAL 5M NEXT 5M MINEXTENTS 6) ;

Q1) So The Initial size for TEST1, TEST2 AND TEST would be someting like this:

TEST1
=====
- Allocate 10 Extents
- Total of 28M (1 + 3 + 3 + 3 + 3 + 3 + 3 + 3 + 3 + 3)
- If you Look in DBA_EXTENTS we see 28 Rows of Each Extent 1M (Because Using LMT
UNIFORM

1M).

TEST2
=====
- Allocate 5 Extents
- Total of 11M (3 + 2 + 2 + 2 + 2)
- If you Look in DBA_EXTENTS we see 11 Rows of Each Extent 1M (Because Using LMT
UNIFORM

1M).

TEST3
=====
- Allocate 6 Extents
- Total of 30M (5 + 5 + 5 + 5 + 5 + 5)
- If you Look in DBA_EXTENTS we see 11 Rows of Each Extent 1M (Because Using LMT
UNIFORM

1M).

Q2) Same Holds good for even other objects like TABLES etc. Right?

Q3) Now when transactions requesting space and say Extent-1 in TEST1 (RBS) is
Active it will

extend one more extent i.e. Extent-11, which would be of 1M (Because LMT UNIFORM
1M). Right?

Q4) For tables we can use "ALTER TABLE X ALLOCATE EXTENT", Similarly can we
manually extend

for RBS. Just to cross check.

Thanx for your patience and sorry for quoting you wrong for
"INITIAL+NEXT+MINEXTENTS".

Sorry.

Appreciating all your effors and hope you would answer to the above question.

Regards,
Sagi.

          Followup:

q1) yes
q2) yes
q3) yes
q4) drop + recreate is easiest.

October 26, 2002 kew -- Thanks for the question regarding "DBLinks
      between two DBes", version 7.3.4/8i

      You Asked
Backgroud :

I have two Oracle DB, 7.3.4 and Oracle 8. I want to access the
data in 7.3.4 via db link from Oracle 8 via some applications in
Oracle 8 . The two dbs are residing in different servers.

Q1) What is the best way to setup the dblink between these two
DB for the objective above ? The setup should be "secure" and
ease of maintenance.

Q2) I am thinking of creating private link that is owned by an
(or a fews) application account. But how can an user account
"switch user" to the owner of the private link ? Is this an good
option ?

Thanks in advance.

      and we said...
q1) you need to define "secure".  I myself like to use private
database links with proxy connections (you create it with a
username/password) and then build procedures to access the data.
 I control access to the link by granting execute on procedures.

q2) use procedures -- procedures run as "owner" or definer...
Review &
      Followup

      Rating: 4
DBLinks between
      two DBs  October 24, 2002
Reviewer:  Lalit
       from The Netherlands
Tom,

Currently I am trying to get data from a oracle database(7.3) to another
database (8.1.7). I am not sure which option I should go for. I can use the DB
links but DBAs here insists that we should not use db links. Can you through
some light on the disadvantages of usage of db links?

          Followup:

Can the DBA do that for you?  They are the one that seems to be adverse to it.

You can

o use dblinks
o the sqlplus COPY command (documented in the sqlplus guide)
o exp/imp

to name a few.

      Rating:
      5
But  October 25, 2002
Reviewer:  J
       from ma
o the sqlplus COPY command (documented in the sqlplus guide)
Don't you still need the dblink for this?

          Followup:

it does not use a dblink at all, no.

October 26, 2002 sinan -- Thanks for the question regarding "how
      can I organise a tree struct ?", version 3.3

      You Asked
Hello!

my table contains a,b fields. lets put in some values to
explain.

a  b
-- --
1  2
1  3
2  4
2  5
2  6
6  1 *

I want to construct a tree struct. but user couldn't enter
values like * specified records. how can I examine that.
In my records. I arrive from 1 to 6 and, from six to 2 and, from
2 to 1. I want to prevent this situation. user couldn't enter 6
1 record.

      and we said...
We can use triggers to validate this. In an after trigger, we'll
simply count the number of elements in the tree and if the
connect by fails -- we have a loop in the data and the
insert/update will fail.  It could look like this for example:

ops$tkyte@DEV816> create table t ( a int, b int );

Table created.

ops$tkyte@DEV816> create or replace package state_pkg
  2  as
  3      type rowidArray is table of rowid index by
                     binary_integer;
  4
  5      mod  rowidArray;
  6      empty rowidArray;
  7  end;
  8  /

Package created.

ops$tkyte@DEV816> create or replace trigger t_biu
  2  before insert or update on t
  3  begin
  4      state_pkg.mod := state_pkg.empty;
  5  end;
  6  /

Trigger created.

ops$tkyte@DEV816> create or replace trigger t_aiufer
  2  after insert or update on t for each row
  3  begin
  4      state_pkg.mod( state_pkg.mod.count+1 ) := :new.rowid;
  5  end;
  6  /

Trigger created.

ops$tkyte@DEV816> create or replace trigger t_aiu
  2  after insert or update on t
  3  declare
  4      l_cnt number;
  5  begin
  6      for i in 1 .. state_pkg.mod.count loop
  7          select count(*) into l_cnt
  8            from t
  9           start with rowid = state_pkg.mod(i)
 10          connect by prior b = a;
 11      end loop;
 12  end;
 13  /

Trigger created.

ops$tkyte@DEV816> insert into t values ( 1, 2 );

1 row created.

ops$tkyte@DEV816> insert into t values ( 1, 3 );

1 row created.

ops$tkyte@DEV816> insert into t values ( 2, 4 );

1 row created.

ops$tkyte@DEV816> insert into t values ( 2, 5 );

1 row created.

ops$tkyte@DEV816> insert into t values ( 2, 6 );

1 row created.

ops$tkyte@DEV816> insert into t values ( 6, 1 );
insert into t values ( 6, 1 )
            *
ERROR at line 1:
ORA-01436: CONNECT BY loop in user data
ORA-06512: at "OPS$TKYTE.T_AIU", line 5
ORA-04088: error during execution of trigger 'OPS$TKYTE.T_AIU'

Review &
      Followup

      Rating:
      5
Excellent!!!  April 18, 2001
Reviewer:
      Pavel Luzanov (pavel_luzanov@mail.ru  from Russia
All of Your's answers are excellent!
Its very help me in my job.
I will wait for Your book. It , by difinition, must be bestseller.

      Rating: 2
User Loop
        April 03, 2002
Reviewer:  She  from
      CT, USA
I took the same query and wanted to find all the parents of say 3 and got error
:
Connect by Loop in User Data
Is it possible I get that error because I already have loop in the data.
In the hierarchy I am seeing(i.e is parents of 3) there is no loop as I went and
physically checked the data.

But some bad data could already be there. How do I remove the bad data now ?

          Followup:

you have to query it out, find it, and correct it.  Nothing "magical" we can do
(other then discover that the problem exists)

      Rating: 5
Why do we need
      row level and statement level trigger?  October 25, 2002

Reviewer:  A reader
Tom,

In this case why do we need two statement level trigger and a rowlevel trigger?
Can we not do, jut using the rowlevel trigger? Is this to avoid the mutation
issue?

Thanks

          Followup:

yup, you need to see the table AFTER the modifications are done (must be an
AFTER trigger).  You need the row trigger to capture the id's of the rows
modified.  You need the before trigger to reset the package state in case some
previous statement err'ed out halfway through and left state variables about.

October 26, 2002 -- Thanks for the question regarding "Single SQL
      statement in a procedure or multiple lines of code?", version 8.1.7

      You Asked
Tom,

Your web cast presentation is very good and Informative. Thanks for
providing at your website.

Coming back to your suggestion:

"If you can do it in a single SQL do it...else some PL/SQL......."

The question I have today is, If I can do my requirement in a single
complex query say

 for eg select decode(A, (select * from B where ...))

(similar to analytical, cube, rollup's in a procedure, i have to
use either execute immediate or use open < > for 'select statement ..'

So when can i draw a line which is the best option? writing a single
complex SQL in Procedure or n lines of procedural code?

execute immediate 'SQL statement ' does more recursive SQL isn't it.
As along with Parse/fetch/execute it will look for objects whether they exist or
not and also the privilege sets associated
where as a procedural line of code is in compiled form though it still
does Parse/fetch/execute....

Thanks,

      and we said...
You would go for the one that uses dynamic sql -- you are doing the work in a
single sql statement.  Forget for a moment whether you do:

for x in ( select ....)

or

open c for string
loop
  fetch c into ...
  exit when c%notfound

You want to do as much in the select as you can.

You want to use the "for x in ( select ...." whenever you can and the ref
cursors only when you have to.  But here, using some things in plsql before 9i,
you MUST use ref cursors so it is all right.

Pretty much always go for the sql.

execute immediate sql_statment does a parse each time is all -- a soft parse
after the first hard parse.  using session_cached_cursors can help offset this
dramatically.

Review &
      Followup

      Rating:
      5
  October 26, 2002
Reviewer:
      A reader
Thanks a lot!

October 26, 2002 prashant -- Thanks for the question regarding
      "client server problem", version 9.2.0

      You Asked
hi tom,

    I have an Oracle 9i client installed on a local machine. The Oracle 9i
database server is remotely located (about 500 kms away). I have access to
certain tables and its data on the server.  I would like to retain this data on
the client side and this data should be updated whenever the data changes on the
server side.
    I desire this because I don't want to query the server again and again as
time is a constraint in my case.

Is this possible without creating a seperate database on the client side or
without replicating the database?

thanks in advance
prashant.

      and we said...
nope, you sort of need a database and replication going.

I query databases that are 2,500 miles away, about 4200 kms or more.  distance
doesn't have anything to do with it - networking does.  Perhaps what you need is
an application server generating HTML near the database providing you with a
nice interface to the data, you know, sort of like Amazon does for us or Yahoo.

October 26, 2002 -- Thanks for the question regarding "Hot Backup
      concept and Changing from Rule to Cost based DB", version 8.1.7

      You Asked
Hi Tom,

1.Can you please explain in detail like what happens when taking hot backups
from ALTER TABLESPACE <TABLESPACE> BEGIN BACKUP till END BACKUP commands.my
specific doubt lies in when the user gives begin backup command, the data blocks
that are changed for a datafile that is in the backup mode will be moved into
redo log files and that is why more redo is produced. but when will those blocks
be copied again into datafile.

2. what are the things that should be taken care when we change a database from
RULE based optimizer to COST based optimizer.

regards,
Vijaya Chander V.S

      and we said...
Hey -- I just talked about this in the web presentation I did.  If you goto
asktom.oracle.com, you can hear me answer this in the one hour presentation
(along with 11 other questions) or you can get my slides and just try to think
about what I might have said.

You can read:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:271815712711
as well -- but basically -- you are mistaken about the "data block that are
change for a datafile are moved to the redo log".  We write to the datafiles
CONSTANTLY during backup.  We never need to read the redo log files after an
"end backup" to get the changes.

2) make sure you understand how and what you need to analyze and you will
discover this easily during your full system test, run against a full scale
instance of course.  (that part in bold being the most important thing you
NEED to do)

October 26, 2002 Antoine -- Thanks for the question regarding
      "tuning ejb, jdo or other", version 8.1.7

      You Asked
Hi Tom,

when you have to tune the db and the sql is hide in ejb or jdo your often say
that it's hard to tune the database.

Is it possible to give more explanation about that? Is it because you cannot use
tkprof or the only thing you can use are the V$ views.

Thanks again for your time!

Antoine

      and we said...
90% of all tuning is done at the application level.  It is your physical table
structures, your SQL, your algorithms.

That is where tuning really takes place.  Now, if I use CMP (container managed
persistence) with EJB's for example and something generates ALL of the sql for
me -- how much can I tune

o the sql
o the way the sql works
o the algorithms?

answer = not very much.  JDO is the same -- it wants to map between java classes
and relation data.  Sometimes that is just a square peg -- round hole problem.

You've seen me use things like analytic functions, connect bys, lots of cool sql
stuff to do things in micro seconds that would take minutes or hours
procedurally.  Do you think these code generators do that?  They work for very
basic things, but when they hit the wall -- bamm.  It's a wall.

It is because these things take the approach that "I have to protect my
developers from this massively complex database thing".  That always makes me
smile a bit.  Here we have people who are "java experts" but cannot learn SQL?
Hmmm - It took me alot longer to learn the basics of J2EE and Java then it did
to lean select, from, where, group by, order by, having, insert, update, delete.

So, that is why I say what I say.  Mostly it is because of the "we don't want to
deal with that nasty database thing".  Until our application is slow then --
then it must be the databases fault.  Arg.

Review &
      Followup

      Rating: 5
Agreed with a
      comment  October 26, 2002
Reviewer:  Stu
      Charlton  from Toronto, ON Canada
There's two reasons for using something like CMP in my experience:

1- "the database is a black box to store my objects"
2- productivity improvement  BUT understanding that it's an 80% solution (need
to tune the key SQL statements that are resource intensive, ensure
excessive/redundant calls are not being made, etc)

I used to be in camp #1 back in my object database days.  I've been in camp #2
for the last several years.

A CMP engine that doesn't allow you to provide hints, tweak the sql, etc. is
pretty useless.

Tom, have you looked at Oracle's new aquisition of Toplink?  I've used it in the
past with some success.  It saves tremendous amounts of time with the "drudge
work" of manually writing JDBC code for CRUD operations. It lets me see all of
the SQL generated, tweak that, use hints, it uses bind variables, or I can map
to a specific stored proc. It's mainly a way of eliminating excess JDBC code,
not so much about eliminating knowledge of SQL, Oracle, etc.   though I will
admit that in the past most people view these tools as "crutches" to ignore the
database. :(

The major tradeoff with this approach is that now you need to know  both Toplink
AND Oracle's nuances.  So why it may require less ongoing work, it requires a
higher learning curve.

          Followup:

Don't get me wrong -- I've used and will use code generators.  Things like
Toplink -- when used by a database savvy group of developers -- turn out lots of
good stuff.

Things like toplink -- when used by people who won't be bothered in any way
shape or form to realize that behind their application is a database and will
succeed or fail based on how it uses the database -- turns out non-workable
applications.  At the end of the day -- it doesn't matter how cool the code is
or how fast you got it done if it doesn't

  a) scale
  b) work right
  c) work fast

does it?

My point is -- the pursuit of your #1 has taken more then one large project down
the path of cancellation.  I'm working with a customer right now that is going
down that path. They want to be database "independent" even though they are:

a) locked into having a windows box in the environment regardless of what
hardware their customer uses

b) locked into using MS IE only -- no other browser will work

they have right now a 40 gig data "warehouse" (no typos there - 40gig).  Takes
many hours to load/unload/reload to finally build a cube for MS OLAP services
(database independent indeed!).  This is 40gig bear in mind.  They won't use
sqlldr (not independent), only their db independent ODBC loader that runs on
windows (loading over the network to unix -- great idea right).  They won't use
bitmapped indexes (even when a longer then 1 hour process finished in under 13
minutes simply by adding one) since not everyone has them.  Won't do a darn
thing IN the database (unload it all to files for java to work with in a db
independent fashion).  Load it all back into the db again (not using any
database tools!  gotta be independent you know).  All of this to unload once
again to MS olap services -- when in fact after looking at what they do with it
we could do it all in SQL in the database (no olap extensions needed in Oracle
to accomplish 100% of what they are doing).

Now, they want to scale this up to the terabyte range.  I just have to laugh --
it'll never happen.  We estimate it'll take about 55 days to run.  They want to
redo this process every month -- they cannot even LOAD in a month!  Told em we
could rewrite the entire thing from scratch (ETL and UI) and run it a couple of
dozen times in less time then it would take them to load once.  We'll see what
happens.

      Rating: 5
just
      frightening  October 29, 2002
Reviewer:  Stu
      Charlton  from Toronto, ON, Canada
Tom, your 30-gig warehouse story is frightening.  But so true.

I was architect last year converting an COBOL flat-indexed file green-screen
application to a J2EE/Toplink/Oracle 9i. I had taught their dev team Java/J2EE
over 2 months, and was leading up the effort to port the 1.5 million lines of
code.

There was tremendous pressure not to over-rely on Oracle because "we might want
to switch after". They spent months before I arrived wondering why their queries
took so long (instead of chopping the cursor off at 20 rows for a list, they
would fetch 200k+ rows each screen), and didn't like the use of ROWNUM in the
where clause because it was Oracle-proprietary (thankfully JDBC's setMaxRows()
worked fine too).

And they didn't want to use SQLLDR properly to import the data, prefering to use
their RAD 4GL tool to dump the flat indexed files into Oracle (which took 50+
hours and required downtime).  This was for *MAYBE* 3 gigs of data.

There's something to be said about prioritization... as you say, a) works right,
b) works fast, c) scales.  But many political games wind up with "priority
inversion"!

Cheers, and thanks for the great site, and all of your efforts.

October 27, 2002 Vladimir -- Thanks for the question regarding
      "Order of mandatory and optional columns in a row", version 8.1.7

      You Asked
Will be the performance increased if mandatory columns are first in the row and
all optional fields follow mandatory ones?
Which statement shall I create table with
I.
Create table a
(a number not null,
c number not null,
d number not null,
b number,
e number)
Or
II.
Create table a
(a number not null,
b number ,
c number not null,
d number not null,
e number)
?
Thank you.

      and we said...
There can be storage efficiencies yes.

Trailing null columns -- they consume zero bytes of storage.

Interior null columns (column B in the second example) will consume at least 1
byte of storage.

Whether this is material or not (whether it really means any savings whatsoever)
will be a function of how many bytes/ row you could actually expect to save this
way.
Review
      & Followup

      Rating: 3
Thank
      you  October 28, 2002
Reviewer:  Vladimir
       from Albany, NY
Thank you for clarifying this.

October 27, 2002 D -- Thanks for the question regarding "Removing
      Duplicates by keeping unique records.", version 8174

      You Asked
Tom - I have a 50 Million row table with 96% duplicates in it due to an
application bug.  My task is to remove the duplicates.  It seems to me that with
such a large percentage of duplicates, the play is to do something like this -
create table newtable as select unique (a,b,c,d) from largetable and then rename
the tables and put the constraints back.
Is this the best way to do it?  I've noticed a lot of hits on "duplicates" on
your sight focus on creating a table of dups.  Is this because there are no
tricks for creating a table of unique records quickly other than using unique or
distinct?

I was wondering how one would do this with analytical functions for example.

Here's an example from your site of using analytical functions for dups -

delete from t
  where rowid in ( select rid
                     from ( select rowid rid,
                                   row_number() over
                                          ( partition by x order by rowid ) rn
                              from t
                           )
                    where rn > 1 )
/

Does it make sense to try this or a variation of analytical functions for
uniques?

      and we said...
Well, this is actually very similar to the "purge" question I was asked on my
live "asktom" last week (if you would like to hear a replay -- goto
asktom.oracle.com and click on the search database link there.  it's online).  I
covered three ways to do this.

If you knew about this ahead of time (eg: purging is part of your processing), I
suggested partitioning.  That doesn't apply here.

If you could afford some limited "downtime" (eg: read only access to the data
for some period), then my suggestion was to use CREATE TABLE AS SELECT to copy
the data you wanted to KEEP somewhere, get rid of the old table data (either
truncate or drop it) and put the new data in its place (either insert /*+ append
*/ or rename the table).

If 96% of the data is going away -- I would use truncate + insert /*+ append */
so I didn't have to deal with indexes, triggers, grants, etc, etc, etc....

The 3rd option was how to do it "online" in 9i and up in a manner that would
reclaim the space.

So, I would be doing a CTAS as you described, followed by a truncate and a
insert /*+ append */.

analytics are not very efficient for de-duping of data. I would use the
min(rowid) or max(rowid)

delete from t
  where rowid not in ( select max(rowid) from t group by <key columns> )

or

delete from t
 where rowid <> ( select max(rowid) from t t2 where t2.<keys> = t.<keys> );

Review &
      Followup

      Rating:
      5
Great!  October 27, 2002
Reviewer:  D
      C  from Hartford, CT
Clears that up.  As it turns out, selecting the unique records was pretty quick
after all.  So I will go watch a replay of your recent presentation.  Thanks
Tom.

October 27, 2002 aman -- Thanks for the question regarding
      "Loading data from external file", version 8.0.4

      You Asked
Hi,
   I have 50000 rows in an excel worksheet with the following
description(Customer_number, Customer_name, Application_date, Amount). I would
like to load data from excel worksheet into the Oracle 8 using SQL. Can you tell
me how to load a data without losing any information while transfering the data
from excel into the oracle8?

Thanks.

      and we said...
The easiest way I know:

o choose file save as "Comma delimited" and save the file as a CSV file

o use sqlldr to load the data with a control file like:

LOAD DATA
INTO TABLE TNAME
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
(customer_number,customer_name,application_date,amount)

and a command like

sqlldr userid=u/p control=thatfile.ctl data=whatever.csv

you might have to use a date format on the application_date -- depending on how
excell saves it and your default date format, something like:

LOAD DATA
INTO TABLE TNAME
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
(customer_number,customer_name,
application_date date "dd/mm/yyyy",
amount)

Review &
      Followup

      Rating: 4
I do it this
      way  October 27, 2002
Reviewer:  Melvin
       from Dubai
I do it the following ways
type =concatenate(Insert into <table name> values
select the field with the mouse <tab> , <tab>
select the second field................ last field <tab> );
and just select the formula and drag it till you cover the entire rows requires.
Select the entire row cut and paste this into a notepas and save this file and
execute this at the SQL prompt .

          Followup:

yuck -- fastest way to kill your shared pool that is.  please add

alter session set cursor_sharing=force;

at the beginning of that script to "autobind".

      Rating: 4
I do it this
      way  October 27, 2002
Reviewer:  Melvin
       from Dubai
I do it the following ways
type =concatenate(Insert into <table name> values
select the field with the mouse <tab> , <tab>
select the second field................ last field <tab> );
and just select the formula and drag it till you cover the entire rows requires.
Select the entire row cut and paste this into a notepad and save this file and
execute this at the SQL prompt .

October 27, 2002 Frank -- Thanks for the question regarding
      "B-Tree index and Reverse Key Index", version 9i.2

      You Asked
Hi Tom,

From your book, it seems that the reserve key index would only be beneficial in
an OPS environment. That implies in a non-OPS environment, it won't help to
reduce the skewness of the index tree and therefore it's not applicable.

On the regular B-tree index, I understand when you create an index the b-tree is
balanced automatically no matter the index column is sorted or not. However, the
index tree will not re-balance itself for the updating, insertion and deletion
after the index is created, obviously for performance reason.

Therefore, the index should be disabled/dropped before a massive insertion
operation and the index created or recreated after the opetion, not only for the
efficiency of the insertion but also for after operations since the insertion
may result in an un-balanced index tree.

Please correct me if I am wrong on any of these points.

Thanks,

Frank

      and we said...
Depends on how you use the index.

The tree does in fact re-balance itself with update/insert/deletes -- that is
what the B in B*Tree stands for. The data structure is self balancing.

During a large bulk load -- one might disable an index

o to speed the load
o because the index rebuild can be done in parallel and NOLOGGING instead of
serially with lots of log
o and the rebuilt index would be in "prime" form -- as clean as can be.

A reverse key index can be useful in non-ops / non-RAC environments as well.
Consider the case where

o you have a table with a primary key ID
o id is populated by an increasing sequence 1, 2, 3, 4, .....
o you delete old records from time to time (not all of them, but some)
o you always either FULL SCAN this table or do a keyed read (where id = :x) and
never do range scans (where id between :x and :y)

Here, the index will end up with lots of delete leaf rows over time.  By that I
mean you inserted 1, 2, 3, 4, 5, .... N and go back later and delete 2, 4, 6, 8
(all of the even numbers).  Well, since you will NEVER insert or update a row to
have an id that falls between 1 and 3 -- you will NEVER reuse that space
released by the number 2 (or 4, or 6 or 8 and so on).  So, in this exaggerated
case -- the index would end up with lots and lots of empty space over time.

On the other hand, if you used a reverse key index -- there is no reason why the
slot used by 2 wouldn't be reused -- because 2 isn't between 1 and 3 anymore --
its just somewhere.  The data is getting inserted all over the place in the
index structure and the space has a high probability of reuse.

Does that mean you should all run out and use reverse key primary key indexes on
tables with ID's populated by sequences?  NO (very loudly NO).  Fact is -- many
tables do not suffer from "deletes" (if you think about it -- deletes are very
very rare) and the COALESCE function of the alter index can be quite good at
getting this space back.
Review & Followup

      Rating: 5
Very good
      example but it would be based on one condition  October 27,
      2002
Reviewer:  Frank  from Canada
Thanks Tom. As always your answer is very helpful. However, the example would be
based on one condition--deletions don't shack up the index tree unless a whole
block is deleted. What I mean is the deletion of 2, 4, 6, ... from the index
will not resulting in an automatic moving of 3,5,7, ... I think this point is
mentioned/implied in your book or Lewis.

Would alter index coalesce make the space holes reusable?

          Followup:

Correct -- I'm using the degenerate case where the deletes are very widespread
and don't end up in deleting all entries on the leaf nodes.  As long as one
entry is there -- that block will stay in that position in the index and if you
never insert/update data to be near that entry (near the number "3" for
example), it'll be a mostly empty block forever.  (my book definitely goes into
this - in the myths section on indexes)

Index coalescing:

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96521/indexes.htm#4443
is definitely something that would help this particular situation by coalescing
adjacent index blocks in the same branches -- freeing up those almost empty
blocks for reuse elsewhere in the index structure.

October 27, 2002 Doug -- Thanks for the question regarding "Can
      PL/SQL be pre-compiled by users? P-code?", version 8174

      You Asked
Tom - a number of packages and PL/SQL procedures that come with oracle have
pre-compiled code in them when you try to look at the source like

DBMS_SHARED_POOL
contains this text string from user_source -
307 8 0
24d 8 9
1f1 1 7
1fe 7 0
35 1 4
43 4 0
244 1 8
3a 4 0
16 2 0
1c1 1 5
6 2 0

So.. I have no way of knowing what the real statements are..
Can we do this with our own packages?  Someone was telling me he was just on an
account where they did.   Also, what do you call this stuff, p-code?

      and we said...
It is not precompiled -- it is "wrapped".  It is a method to obscure the code,
to prevent prying eyes from reading it!

This is not p-code, just obscured code.

You can wrap your code using the wrap routine.  JUST MAKE SURE you keep a copy
of the source somewhere.  There is NO "unwrap" program out there.

See

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a77069/c_wrap.htm#1859
for details.

Review &
      Followup

      Rating: 5
Ah.. Thanks so
      much!!  October 27, 2002
Reviewer:  Doug
       from CT, USA
That's one of the things that's been kind of bugging me for quite some time..

October 27, 2002 Jerry -- Thanks for the question regarding
      "Application Servers and Transactions", version 8.1.7

      You Asked
I was reading recently on your site about parameterized views. They seem like
they could help with some design issues on our PeopleSoft HR database. However,
I am confused about something, and even though it relates to PeopleSoft, maybe
you could help me understand something.

Our PSFT system is 3 tier, with the app server middle layer handling the
connections to the DB, with a browser front end. If I call (through
"PeopleCode") dbms_application_info.set_client_info, alter session, or other
such thing, how do application servers in general, maintain session information
when only one or two Oracle user processes is started ?  It seems like the
sessions are co-mingled.

Along these lines come questions about locking, commits, etc. I sort of envision
the app server as "one big session", and am wondering how transactional
capability is handled among different users all using the same app server.

Thanks for any insights.

      and we said...
Well, with peoplesoft, SAP, Siebel and the like -- all bets are OFF.

They do middle tier cacheing

They do not use the database for much of anything (database indedependent you
know)

SAP does it's own locking

Peoplesoft does tons of cacheing (hence many accesses are not even made against
the database).

You would really have to get into the guts of how peoplesoft, SAP, etc *wrote*
their apps in order to do anything like this (to even understand if you CAN do
something like this)

October 27, 2002 Dhananjaya -- Thanks for the question regarding
      "PL/SQL Error Message", version 8.1.7

      You Asked
Tom,

   My question goes like this

   I have package like

   CREATE OR REPLACE PACKAGE PKG_TEST
   AS
     TYPE RD_TEST IS RECORD ( P_AMOUNT VARHCAR2);

     TYPE RC_TEST IS REF CURSOR RETURN PKG_TEST.RD_TEST;

     PROCEDURE SP_TEST (P_RETCUR IN OUT PKG_TEST.RC_TEST);

   END PKG_TEST;

   CREATE OR REPLACE PACKAGE BODY PKG_TEST
   AS

     PROCEDURE SP_TEST( P_RETCUR  IN OUT PKG_TEST.RC_TEST)
     AS
        BEGIN

          OPEN P_RETCUR FOR
             SELECT SUBSTR(TO_CHAR(NVL(TOTAL_VALUE,0),'00000.00'),2,6))
               FROM <TABLE_NAME>
              WHERE <CONDITION>;

     END SP_TEST;

   END PKG_TEST;

   when the package is executed it throws error

   pls-00307 too many declarations of 'TO_CHAR' match this call

   but if i replace the select with

   SELECT SUBSTR(TO_CHAR(DECODE(TOTAL_VALUE,NULL,0,TOTAL_VALUE),'00000.00'),2,6)

   it works fine.

   Am i missing some thing over here..please explain this.

   Thank you.

      and we said...
Sorry -- but I don't believe you that it "compiles" fine (unless of course the
query is dynamic -- it is really a string and not static as your "example" has.
your example is no where near ready to compile so I'm guessing you've totally
over simplified it)

I'll guess: total_value is a varchar2 in the database, you are using a STRING to
store a number (bad bad bad choice.  Use numbers to store numbers, dates for
dates and varchar2 for STRINGS)

Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table x;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table x ( total_value varchar2(25) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure p
  2  as
  3  begin
  4      for x in (select to_char(nvl(total_value,0),'00000.00') y from x )
  5      loop
  6          null;
  7      end loop;
  8  end;
  9  /

Warning: Procedure created with compilation errors.

ops$tkyte@ORA817DEV.US.ORACLE.COM> show err
Errors for PROCEDURE P:

LINE/COL ERROR
-------- -----------------------------------------------------------------
4/15     PL/SQL: SQL Statement ignored
4/22     PLS-00307: too many declarations of 'TO_CHAR' match this call
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table x;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table x ( total_value number );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure p
  2  as
  3  begin
  4      for x in (select to_char(nvl(total_value,0),'00000.00') y from x )
  5      loop
  6          null;
  7      end loop;
  8  end;
  9  /

Procedure created.

The decode trick just coerced the datatype to be a number so to_char knew what
to do.

you could just as easily:

SELECT SUBSTR(TO_CHAR(NVL( to_number(TOTAL_VALUE) ,0),'00000.00'),2,6))

or, if still in development use the correct and proper datatype in the FIRST
place -- numbers for numbers, dates for dates, strings for strings!!!!

October 27, 2002 Brian -- Thanks for the question regarding "Query
      to Tie Requests to the CC Manager that runs it.", version 8.0.5

      You Asked
I've been beating myself over the head trying to link the FND tables together so
that I can find the names of the requests that a certain cc manager run.  Any
advice or links to charts of these tables' interconnectedness?
Thanks,
Brian

      and we said...
I asked Joseph Bovaird, our resident apps guru, and he says:

Below is a query that should return the info in your customer's question.  Also,
all the TRMs for the apps are now online at
http://etrm.oracle.com,/
but they will need a metalink
account to access them (which they should have!).

select q.concurrent_queue_name,
       p.user_concurrent_program_name
  from fnd_concurrent_requests r,
       fnd_concurrent_programs_tl p,
       fnd_concurrent_processes pr,
       fnd_concurrent_queues q
 where q.concurrent_queue_id = pr.concurrent_queue_id
   and r.controlling_manager = pr.concurrent_process_id
   and r.concurrent_program_id = p.concurrent_program_id

Cheers,
Joseph.

Review &
      Followup

      Rating:
      4
  October 28, 2002
Reviewer:  Brian
      Mullin  from Emeryville, CA
Thanks!  connecting those tables was a doozy.

October 27, 2002 David -- Thanks for the question regarding
      "Trigger to insert id from sequence", version 8.1.6

      You Asked
Hi Tom.
I have a question on the sample code from p. 42 of your book. Its the trigger
code that inserts the PK value from a sequence on insert.
I'm getting an error on ":new.pk".
Is this a working code sample or just code to "illustrate an idea"?
If no, can you provide a link for a working example?

BTW - is this the proper forumn to ask book related questions?

P.S. The book is great - love it!

      and we said...
it was "real", you need to have a table that has a column called PK (pk isn't
like a special reserved term or anything -- it was the name of a column)

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( pk number primary key,
other_data int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create sequence pk_seq;

Sequence created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create trigger t_trigger before insert on t
for each row
  2  begin
  3          select pk_seq.nextval into :new.pk from dual;
  4  end;
  5  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 0, 55 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

        PK OTHER_DATA
---------- ----------
         1         55

ops$tkyte@ORA817DEV.US.ORACLE.COM>

You may feel free to ask questions about the books I've worked on here, yes.
thanks for the feedback.

Review &
      Followup

      Rating: 5
Bada-Bing
      Bada-Bang!  October 28, 2002
Reviewer:  David
      Schwartz  from New Jersey, USA
Thanks Tom. It worked out great.

October 28, 2002 -- Thanks for the question regarding "Getting the
      user updating the data through EJB's - using generic a login", version
      8.1.7

      You Asked
We have an application based on EJB's sitting on ORACLE 9 IAS 1.0.2.2.1, writing
data to an Oracle database.  When the user logins to the application, the users
uses his login/password to login to the application.
Once the login/password is verified the user is connected by the application
server through a generic login to the application.  The application is using
connection pooling to logon to the database.  The application connects to the
database in MTS mode
Ex.
user name = endUser1
generic login the application server uses = genericAppUser

All tables in the database have 4 standard columns, the data for which is
updated through a trigger

CREATE TABLE TESTING (
  ID                NUMBER        NOT NULL,
  LAST_UPDATE_DATE  DATE          NOT NULL,
  LAST_UPDATE_USER  VARCHAR2 (30)  NOT NULL,
  CREATION_DATE     DATE          NOT NULL,
  CREATION_USER     VARCHAR2 (30)  NOT NULL,
  CONSTRAINT PK_TESTING
  PRIMARY KEY ( ID ) );

CREATE OR REPLACE TRIGGER tiu_testing
   BEFORE INSERT OR UPDATE
   ON testing
   FOR EACH ROW
BEGIN
   IF inserting THEN
         :new.creation_date := sysdate;
      :new.creation_user := nvl(:new.creation_user, user);
         :new.last_update_date := sysdate;
      :new.last_update_user := nvl(:new.last_update_user, user);
   END IF;
   IF updating THEN
         :new.last_update_date := sysdate;
      :new.last_update_user := nvl(:new.last_update_user, user);
   END IF;
END;
/

When the user saves any data into the database the
creation_user/last_update_user is set to genericAppUser.  Is there anyway we can
change the creation_user/last_update_user from genericAppUser to endUser1 (may
be by setting USER = endUser1, instead of genericAppUser )

Thanks and Regards

      and we said...
The best answer is to use proxy authentication (user authenticates to app, app
authenticates to database but passes the "real" username along)

http://otn.oracle.com/docs/products/ias/doc_library/90200doc_otn/core.902/a90146/fundamen.htm#1006392
Short of that -- whenever you grab a connection from the connection pool -- you
can use an APPLICATION context to securely set an application "state" that would
include the "real" username that your trigger can then reference.

See

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96540/statements_53a.htm#2060929
for info on "application contexts"

Review &
      Followup

      Rating: 5
Getting the
      user updating the data through EJB's - using generic a
      login  October 26, 2002
Reviewer:
      A reader

      Rating: 5
Two links when
      followed lead to many.  October 27, 2002
Reviewer:
      Brijesh  from USA
Two links when followed lead to many.

and finally I am lost and boggled my head.

Thanks you Mr. Tom for making us know that such a feature exists in 9iAS and
9iDB.

The first link gives us introduction to proxy authentication and just leads us
to
create context package.

Ok i've created a package and also a context, but how should I use it ?

SQL> create or replace package test_pkg
  2  as
  3  test_empno number;
  4  end test_pkg;
  5  /

Package created.

SQL> create or replace package body test_pkg
  2  as
  3  begin
  4   test_empno := 7857;
  5  end;
  6  /

Package body created.

SQL> create or replace context test_context using test_pkg;

Context created.

SQL> create or replace view test_secure_view
  2  as
  3  select * from emp where empno = sys_context('test_context', 'test_empno')
  4  /

View created.

How can I pass the application user name from the middle tier (or any tier)
application server to

the database.

Mr. Tom it would be very helpful if you could provide some example on how to
acheive this.

Thnaks.

          Followup:

You would:

create or replace procedure set_username( p_user in varchar2 )
as
begin
   if ( user = 'YOUR_SPECIAL_SINGLE_ACCOUNT' )
   then
       dbms_session.set_context( 'my_ctx', 'user_name', p_user );
   end if;
end;
/

create or replace context my_ctx using set_username
/

Now, your Java code, when it grabs a connection from the pool would issue

  begin set_username( ? ); end;

and pass in the "real" application username (it should know it, it validated it
right)....

So, your trigger can access:

  select sys_context( 'my_ctx', 'user_name' ) from dual;

in order to find out the identity of the "real" user.  Since the single account
your java connection pool uses OWNS the set_username procedure and you never let
ANYONE ELSE USE IT (and if they did, it wouldn't work cause they won't be logged
in as the "right user") - there is no chance of "username spoofing" going on.

That is what I was trying to convey with:

...
you can use an APPLICATION context to securely set an application "state" that
would include the "real" username that your trigger can then reference.
...

      Rating: 5
Thanks Mr.
      Tom  October 28, 2002
Reviewer:
      A reader
Thank you Mr. Tom for the example,
it is really helpful.

Just 2 clarifications

1st one is below

SQL> show user
USER is "SCOTT"
SQL> exec dbms_session.set_context ('my_ctx', 'user_name', 'app_user')
BEGIN dbms_session.set_context ('my_ctx', 'user_name', 'app_user'); END;

*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at "SYS.DBMS_SESSION", line 78
ORA-06512: at line 1

SQL> conn sys as sysdba
Enter password: ****
Connected.

SQL> grant execute on dbms_session to scott;

Grant succeeded.

SQL> connect scott
Enter password: *****
Connected.
SQL>  exec dbms_session.set_context ('my_ctx', 'user_name', 'app_user')
BEGIN dbms_session.set_context ('my_ctx', 'user_name', 'app_user'); END;

*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at "SYS.DBMS_SESSION", line 78
ORA-06512: at line 1

SQL> exec set_username('app_user')

PL/SQL procedure successfully completed.

SQL>select sys_context ('my_ctx', 'user_name') from dual;

SYS_CONTEXT('MY_CTX','USER_NAME')
---------------------------------
app_user

Above when I execute dbms_session.set_context from the sql prompt ,
it gives me error: insufficient privileges, but when I execute the same
procedure from my set_username procedure it gets executed without any error.

Does it mean that I can use dbms_session only from named blocks
and not from autonomous blocks ?

2nd one
Will this work with BC4J also ?

Thanks.

          Followup:

You are seeing EXACTLY what should happen.

A context may be set ONLY by that one procedure.

That one procedure does whatever check it needs to to make sure setting the
context is permitted.

If we let you just call dbms_session from the command line -- the application
context would be setable by ANYONE -- making it useless from a security
perspective.

You need to use the context in the way I demonstrated -- being set by a
procedure, that procedure is callable by YOUR magic schema only (hence, it is
secure, no one can "spoof" it).

BC4J is just a "layer" on top of the database, a JDBC mapping layer.  Yes, this
works with BC4J.

October 28, 2002 Tina -- Thanks for the question regarding "How to
      avoid compiling 8.1.7.2 ProC code when upgrade to 9i?", version 8.1.7.2

      You Asked
How to avoid compiling 8.1.7.2 ProC code when upgrade to 9i?
You answered this question in yesterday's webcast, but I missed a little bit of
it.  Also, I have been told that the 9i SQL is different than 8i so the imbedded
SQL in the app would be using the 9i SQL even if we had the 8.1.7.2 directory
still intact that it compiled with.   We have 32 bit application that will not
be ready to be compiled with 64 bit for two years, however, we would like to go
ahead and install 9i since we have many new conversions this next two years.
Thanks!

      and we said...
Ok, lets say you have a machine A with the client code. And a machine B with the
server.

If the server is version x.y - does the client have to be x.y?  No -- it does
not.  It can be pretty much any supported version.

So, the client code, if left in its current ORACLE_HOME, does not need to be
recompiled.  It does however need to use Oracle Net to connect to the database
-- it must use the networking layer.  In the two machine configuration -- that
is clear.

However, suppose you have Machine A with the client code AND database.  Here you
need two oracle_homes -- one with the existing 8i client code you already have
and a new one with the 9i code and database.  Here the clients oracle home will
be the 8i oracle home.  It will use Oracle net to connect to the 9i datatabase
(use a connect string) even though we are on the same box.  This works just as
well.

it is only when you want to take advantage of new 9i features (which would
require a recompile of the source code -- you are changing the source code)
would you need to upgrade.  If you use dynamic sql -- you might not even have to
do it then -- the sql will be passed transparetnly to the server, the client
will never look at it.  Only if you access new 9i datatypes like TIMESTAMP and
such might you have issues with a dynamic program that would necessitate
recompiling.
Review & Followup

      Rating: 5
clarification
      needed  October 25, 2002
Reviewer:  Tina
      McAllister  from Heathrow, FL, USA
What I'm asking about is not the client code but the server application code
that is running on the HPUX machine.  we are connecting directly to the database
using the ProC functions.

          Followup:

the CLIENT is the pro*c code.  The pro*c code is the CLIENT.

If you leave the 8i oracle home there

And you leave the oracle home set to the 8i oracle home when you run the pro*c

And you connect over Oracle*net (using user/pass@database)

then -- you need not recompile.

If you

o remove the oracle home for 8i OR
o you set the oracle home to the 9i oracle home when running the 8i code OR
o you try to connect without using Oracle Net

you will by definition need to recompile everything.

      Rating: 3
32bit app on 64
      database, and my milage varied  October 28, 2002

Reviewer:  David  from Melbourne
a) The question says they have "32 bit application that will not be ready to be
compiled with 64 bit for two years" I thought you could built a 32 bit
application and still connect to a 64 database version - aren't there libraries
to do so?
b) My experience on using different client and server versions has been
disappointing with an application which uses lots of Method 4 dynamic Pro*C SQL
- it gets garbled results & I don't know why.  For 9.2 client talking to 8i
server it has worked.

          Followup:

a) yes, and I told them exactly HOW TO DO THAT.

keep the 8i oracle home.
have the proc code point to that when you run it.
done.

b) I would say you have a bug in your code then.  I use mixed clients and
servers constantly, continously and for many many many years. (and yes, I use
tons of dynamic sql method 4 myself.  I've never had an issue.  did you know
that in Oracle -- ALL SQL, 100% of it, is dynamic under the covers.  The
database has no clue if you are using static sql or dynamic sql in your pro*c)

October 28, 2002 nisha -- Thanks for the question regarding "Spell
      the number", version Oracle 8i

      You Asked
Hi,
 I am trying to spell the number which is greater than "5373484" . (I tried
"select to_char( to_date(5373484,'J'),'Jsp') from dual;  "  with no Problem).

Is there any functions available for me ?.
 Thanks.

      and we said...
Sure, with a little effort -- you can use the J -> Jsp trick and do it yourself.

tkyte@TKYTE816> create or replace
  2  function spell_number( p_number in number )
  3  return varchar2
  4  as
  5      type myArray is table of varchar2(255);
  6      l_str    myArray := myArray( '',
  7                             ' thousand ', ' million ',
  8                             ' billion ', ' trillion ',
  9                             ' quadrillion ', ' quintillion ',
 10                             ' sextillion ', ' septillion ',
 11                             ' octillion ', ' nonillion ',
 12                             ' decillion ', ' undecillion ',
 13                             ' duodecillion ' );
 14
 15      l_num   varchar2(50) default trunc( p_number );
 16      l_return varchar2(4000);
 17  begin
 18      for i in 1 .. l_str.count
 19      loop
 20          exit when l_num is null;
 21
 22          if ( substr(l_num, length(l_num)-2, 3) <> 0 )
 23          then
 24             l_return := to_char(
 25                             to_date(
 26                              substr(l_num, length(l_num)-2, 3),
 27                                'J' ),
 28                         'Jsp' ) || l_str(i) || l_return;
 29          end if;
 30          l_num := substr( l_num, 1, length(l_num)-3 );
 31      end loop;
 32
 33      return l_return;
 34  end;
 35  /

Function created.

ops$tkyte@DEV816>
ops$tkyte@DEV816> select
  2  spell_number( 12345678901234567890123456789012345678 )
  3  from dual;

SPELL_NUMBER(1234567890123456789012345678901234567
--------------------------------------------------
Twelve undecillion Three Hundred Forty-Five decill
ion Six Hundred Seventy-Eight nonillion Nine Hundr
ed One octillion Two Hundred Thirty-Four septillio
n Five Hundred Sixty-Seven sextillion Eight Hundre
d Ninety quintillion One Hundred Twenty-Three quad
rillion Four Hundred Fifty-Six trillion Seven Hund
red Eighty-Nine billion Twelve million Three Hundr
ed Forty-Five thousand Six Hundred Seventy-Eight

ops$tkyte@DEV816>
ops$tkyte@DEV816> declare
  2      l_num number;
  3      l_str varchar2(255);
  4      l_spelled varchar2(4000);
  5  begin
  6      for i in 1 .. 10
  7      loop
  8          l_num := random.rand() ||
  9                   random.rand() ||
 10                   random.rand();
 11          l_str :=
 12              to_char( l_num, '999,999,999,999,999,999' ) ;
 13          l_spelled := spell_number( l_num );
 14
 15          dbms_output.put_line
 16          ( l_str ||  ' -- ' || l_spelled );
 17      end loop;
 18  end;
 19  /
312,051,345,415,411 -- Three Hundred Twelve
trillion Fifty-One billion Three Hundred
Forty-Five million Four Hundred Fifteen thousand
Four Hundred Eleven
25,131,160,032,468 -- Twenty-Five trillion One
Hundred Thirty-One billion One Hundred Sixty
million Thirty-Two thousand Four Hundred
Sixty-Eight
210,202,025,424,018 -- Two Hundred Ten trillion
Two Hundred Two billion Twenty-Five million Four
Hundred Twenty-Four thousand Eighteen
28,221,169,384,374 -- Twenty-Eight trillion Two
Hundred Twenty-One billion One Hundred Sixty-Nine
million Three Hundred Eighty-Four thousand Three
Hundred Seventy-Four
148,881,759,131,997 -- One Hundred Forty-Eight
trillion Eight Hundred Eighty-One billion Seven
Hundred Fifty-Nine million One Hundred Thirty-One
thousand Nine Hundred Ninety-Seven
178,761,729,314,960 -- One Hundred Seventy-Eight
trillion Seven Hundred Sixty-One billion Seven
Hundred Twenty-Nine million Three Hundred Fourteen
thousand Nine Hundred Sixty
7,789,111,094,989 -- Seven trillion Seven Hundred
Eighty-Nine billion One Hundred Eleven million
Ninety-Four thousand Nine Hundred Eighty-Nine
137,652,698,329,777 -- One Hundred Thirty-Seven
trillion Six Hundred Fifty-Two billion Six Hundred
Ninety-Eight million Three Hundred Twenty-Nine
thousand Seven Hundred Seventy-Seven
155,311,009,417,668 -- One Hundred Fifty-Five
trillion Three Hundred Eleven billion Nine million
Four Hundred Seventeen thousand Six Hundred
Sixty-Eight
139,292,540,822,249 -- One Hundred Thirty-Nine
trillion Two Hundred Ninety-Two billion Five
Hundred Forty million Eight Hundred Twenty-Two
thousand Two Hundred Forty-Nine

PL/SQL procedure successfully completed.

ops$tkyte@DEV816>

followup to comment about 1000

Correct, the code is updated to reflect that.  forgot to take into consideration
the possibility of 000 as part of a number.  We just need to ignore that -- the
if statement added takes care of that.

Thanks.
Review
      & Followup

      Rating:
      5
  March 16, 2001
Reviewer:
      A reader

      Rating:
      5
  April 30, 2001
Reviewer:  Helena
      Markova  from Bratislava, Slovakia

      Rating: 2
Problem with
      spell_number function  April 30, 2001
Reviewer:
      Nusrat  from Mumbai, India
the spell_number fuction does not work
when 1000 is passed :
i get the foll. message :-
  1* select spell_number(1000) from dual
SQL> /
select spell_number(1000) from dual
       *
ERROR at line 1:
ORA-01854: julian date must be between 1 and 5373484
ORA-06512: at "PAYROLL.SPELL_NUMBER", line 19
ORA-06512: at line 1

      Rating:
      5
Excellent  January 22, 2002
Reviewer:
      A reader
I want to suggest you should make a better search, or clasify to make easy to
look in the search archive.

Because I look with
- translate number
- number words
etc.

but I only found this article looking with 'JSP'
Thanks.

          Followup:

hmm, I don't see how "translate number" would find this, we are not translating
a number.

Number words eithers.

But, I always find this one by looking for "spell a number" -- thats what you
want to do -- "spell a number".

I don't know how I could have made a search that would find this based on your
search strings -- sorry.

I find the interMedia text search to be pretty capable in this area myself.

      Rating: 5
Better then
      examples I have seen elsewhere  January 22, 2002

Reviewer:  Jim  from Australia
Liked it better then "spell a number" examples I have seen elsewhere.

FUP to search facility, I think the serach facility is pretty good...

      Rating: 4
spell
      sound  January 23, 2002
Reviewer:
      A reader
do you now any facility so i can here a sound for the output

          Followup:

Not a specific one, that would be something specific to your client operating
system.  I am aware of many tools to convert text to speech....

      Rating: 4
Excelent SQL
      Statment   January 23, 2002
Reviewer:  Mohammed
      Al-moayed  from Yemen
well, good I  like these SQl , which add to my Skills new information , but
Colud you please Explain to me what
JSP Stand for

          Followup:

J is the format for a Julian date -- turns a number into a date or vice versa.

  1* select to_char( sysdate, 'j' ) from dual
ops$tkyte@ORA817DEV.US.ORACLE.COM> /

TO_CHAR
-------
2452298

ops$tkyte@ORA817DEV.US.ORACLE.COM> select to_date( 2452299, 'j' ) from dual;

TO_DATE(2
---------
24-JAN-02

sp is a format for SPELLING a julian date:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select to_char(sysdate,'jsp') from dual;

TO_CHAR(SYSDATE,'JSP')
------------------------------------------------------------------------------
two million four hundred fifty-two thousand two hundred ninety-eight

I just used that fact to spell much larger numbers then a julian date can hold

      Rating:
      5
  January 23, 2002
Reviewer:
      Claudio  from Argentina

      Rating:
      3
  January 23, 2002
Reviewer:  Asif
      Merchant  from NY
May be this will be of some use...

create or replace function Digits3_To_Word (P_number in number)
return varchar2 as
-- Pass only three digits
-- This function return word equivalent to its number
-- Accepted values are 0 to 999
--
L_Return2 varchar2(500) := NULL ;

i Number := 0 ;
j Number := 0 ;
k Number := 0 ;

V_inp_Number Number := 0 ;
V_temp Varchar2(50) := NULL;

type OneArray is table of varchar2(50);
type TenArray is table of varchar2(50);
OneStr OneArray := OneArray('One ',
'Two ',
'Three ',
'Four ',
'Five ',
'Six ',
'Seven ',
'Eight ',
'Nine ',
'Ten ',
'Eleven ',
'Twelve ',
'Thirteen ',
'Fourteen ',
'Fifteen ',
'Sixteen ',
'Seventeen ',
'Eighteen ',
'Nineteen ');
TenStr TenArray := TenArray('',
'Twenty ',
'Thirty ',
'Forty ' ,
'Fifty ',
'Sixty ',
'Seventy ',
'Eighty ',
'Ninety ');

BEGIN

V_inp_Number := P_Number ;
if V_inp_number > 999 then
V_inp_number := 0 ;
end if ;

V_temp := LPAD(to_char(V_inp_number),3,0) ;

-- Find Hundredth position
i := to_number(substr(V_temp,1,1));
if i > 0 then
L_Return2 := OneStr(i)||'Hundred ' ;
end if ;

-- Find last 2 digits
i := to_number(substr(V_temp,2,2));
j := to_number(substr(V_temp,2,1));
k := to_number(substr(V_temp,3,1));
if i > 0 and i < 20 then
L_Return2 := L_Return2 || OneStr(i) ;
end if ;
if j >= 2 then
L_Return2 := L_Return2 || TenStr(j) ;
if k > 0 then
L_Return2 := L_Return2|| OneStr(k) ;
end if;
end if;

Return L_Return2 ;
EXCEPTION WHEN OTHERS
THEN NULL ;
END ;
/

create or replace function NUM_TO_WORD (P_inp_num in NUmber)
return Varchar2 as

-- Functions called : Digits3_To_Word

L_Return Varchar2(1000) ;

m Number := 0 ;

V_inp_Num Number := 0 ;
V_inp_Int Number := 0 ;
V_inp_dec Number := 0 ;
V_Char Varchar2(100) ;
V_Temp Number := 0 ;

Type LionArray is table of Varchar2(500) ;
In_str LionArray :=
LionArray ( '',
'Thousand ' ,
'Million ' ,
'Billion ' ,
'Trillion ' ,
'Quadrillion ' ,
'Quintillion ' ,
'Sextillion ' ,
'Septillion ' ,
'Octillion ' ,
'Nonillion ' ,
'Decillion ' ,
'Undecillion ' ,
'Duodecillion ');
Begin
if p_inp_num < 0 then
V_inp_Num := (P_inp_Num * -1) ;
else
V_inp_Num := P_inp_Num;
end if;
V_inp_Int := Trunc(V_inp_Num) ;
V_inp_Dec := V_inp_Num - V_inp_Int ;
if V_inp_Dec > 0 then
V_inp_dec := V_inp_dec * 100 ;
V_inp_Dec := to_number(substr(to_char(V_inp_Dec),1,2)) ;
end if ;

V_char := to_char(V_inp_Int) ;

for m in 1..In_Str.Count Loop
exit when V_char is NULL ;
V_Temp := To_Number(Substr(V_char,(Length(V_char)-2), 3));

if V_temp > 0 then
L_Return := Digits3_To_Word(V_Temp) || In_str(m) || L_Return ;
end if ;

V_char := Substr(V_char,1,(Length(V_Char)-3));

End Loop ;

if V_inp_Int > 0 then
L_Return := 'Dollar '||L_Return ||' and ';
elsif V_inp_Int = 0 then
L_return := 'Dollar Zero and ' ;
end if ;

-- Concatenate the word for decimal digits
if V_inp_Dec > 0 then
L_return := L_return || Digits3_To_Word(V_inp_Dec) ||' Cents';
elsif V_inp_Dec = 0 then
L_Return := L_return ||' Zero Cents';
End if ;
-- -------------------------------

Return L_return;
Exception when others then
return NULL ;
END;
/

          Followup:

don't see how that is better or even as good as "jSP" with a simple function??

I like to write as little as I have to, to use as much as is builtin as
possible.

this:

to_char( to_date( three_digit_number,'J' ), 'JSP' )

just seems so much easier then digits3_to_word doesn't it?

      Rating: 4
Want
      more  January 23, 2002
Reviewer:
      A reader
Hi,

Is it possible to spell all the digits after
decimal point and know how many exact digits
after point? Like:

23456.123423...

Efficient(as you always did) way please.

Thanks

          Followup:

Well, looking at the logic below -- it should be easy for you to accomplish
this.

the number of digits after?

greatest(length(x)-instr(x||'.','.'),0)
is one way,

greatest(length(x-trunc(x))-1,0)

another.  spelling the decimal part should be easy for you to accomplish
depending on the format you want to use.  all of the pieces are here for you.

      Rating: 3
Intelligent
      query!  January 23, 2002
Reviewer:  Sazzadur
      Rahman Tusar  from Bangladesh
select to_char( to_date(5373484,'J'),'Jsp') from dual is a very intelligent
query to spell out a number.

      Rating: 3
The above
      posting...  January 24, 2002
Reviewer:
      A reader
Compact it is, efficient it is, clever it is, cryptic it most certainly is.
Intelligent?. Considering the alternatives, I'd certainly use it though...

          Followup:

Well, if you understand the formats -- cryptic it isn't -- I suppose if you
didn't understand what 'J' and 'JSP' formats did, it would be but... isn't:

   y += x >> 1;

cryptic?  Well, not to a C programmer it isn't.  I guess its what you know....

      Rating:
      4
  January 24, 2002
Reviewer:
      A reader
Great !!!!

Does this JSP works for decimals also
ex 23456.66

          Followup:

it works for Julian dates -- you can use it as PART of your solution as I did.

      Rating: 5
Follow up -
      Intelligent Query & Decimals  January 24, 2002

Reviewer:  Jim  from Australia
"select to_char( to_date(l_my_variable,'J'),'Jsp')
from dual "

Is fine when the number is less than or equal to 5373484
for numbers bigger then that you will have to use a
solution like Tom's

Re decimals questions - you will have to handle the decimal
yourself. The to_date converts the input to a Julian
date. You will get an error trying to convert
1234.34 to a julian date.
Since the to_char JSP option is julian spell
then I am certain that trying to
to_char(1234.56, 'JSP') will also return an error.
So you will have to split your value into the whole number part and the decimal
part.

You could then use the functions to spell the whole
number part and concatenate it with the decimal part.

Fairly straight forward to do in PL/SQL

      Rating:
      5
decimals  January 24, 2002
Reviewer:
      Barbara Boehmer  from Norwalk, CA USA
Here is a modification of Tom's function to include decimal places, just
concatenating the original with the word point followed by each successive digit
spelled out:

SQL> create or replace
  2  function spell_number( p_number in number )
  3  return varchar2
  4  -- original by Tom Kyte
  5  -- modified to include decimal places
  6  as
  7      type myArray is table of varchar2(255);
  8      l_str    myArray := myArray( '',
  9                             ' thousand ', ' million ',
 10                             ' billion ', ' trillion ',
 11                             ' quadrillion ', ' quintillion ',
 12                             ' sextillion ', ' septillion ',
 13                             ' octillion ', ' nonillion ',
 14                             ' decillion ', ' undecillion ',
 15                             ' duodecillion ' );
 16      l_num varchar2(50) default trunc( p_number );
 17      l_return varchar2(4000);
 18  begin
 19      for i in 1 .. l_str.count
 20      loop
 21          exit when l_num is null;
 22
 23          if ( substr(l_num, length(l_num)-2, 3) <> 0 )
 24          then
 25              l_return := to_char(
 26                              to_date(
 27                               substr(l_num, length(l_num)-2, 3),
 28                                 'J' ),
 29                          'Jsp' ) || l_str(i) || l_return;
 30          end if;
 31          l_num := substr( l_num, 1, length(l_num)-3 );
 32      end loop;
 33
 34      -- beginning of section added to include decimal places:
 35      if to_char( p_number ) like '%.%'
 36      then
 37          l_num := substr( p_number, instr( p_number, '.' )+1 );
 38          if l_num > 0
 39          then
 40              l_return := l_return || ' point';
 41              for i in 1 .. length (l_num)
 42              loop
 43                  exit when l_num is null;
 44                  if substr( l_num, 1, 1 ) = '0'
 45                  then
 46                      l_return := l_return || ' zero';
 47                  else
 48                      l_return := l_return
 49                      || ' '
 50                      || to_char(
 51                             to_date(
 52                             substr( l_num, 1, 1),
 53                               'j' ),
 54                         'jsp' );
 55                  end if;
 56                  l_num := substr( l_num, 2 );
 57              end loop;
 58          end if;
 59      end if;
 60      -- end of section added to include decimal places
 61
 62      return l_return;
 63  end spell_number;
 64  /

Function created.

SQL> column number_spelled format a55 word_wrapped
SQL> select spell_number( 1234567890123456789.0123456789012345678 )
  2         as number_spelled
  3  from   dual
  4  /

NUMBER_SPELLED
-------------------------------------------------------
One quintillion Two Hundred Thirty-Four quadrillion
Five Hundred Sixty-Seven trillion Eight Hundred Ninety
billion One Hundred Twenty-Three million Four Hundred
Fifty-Six thousand Seven Hundred Eighty-Nine point zero
one two three four five six seven eight nine zero one
two three four five six seven eight

SQL> set serveroutput on
SQL> declare
  2      l_num number;
  3      l_str varchar2(255);
  4      l_spelled varchar2(4000);
  5  begin
  6      for i in 1 .. 5
  7  loop
  8          -- uses random package by Tom Kyte:
  9          l_num := random.rand() ||
 10                   random.rand() ||
 11                   random.rand() ||
 12                   '.'           ||
 13                   random.rand() ||
 14                   random.rand();
 15
 16          l_str :=
 17              to_char( l_num, '999,999,999,999,999,999.999999999999999999' )
;
 18          l_spelled := spell_number( l_num );
 19
 20          dbms_output.put_line
 21          ( l_str ||  ' -- ' || l_spelled );
 22      end loop;
 23  end;
 24  /
12,950,208,415,891.291723034800000000 -- Twelve
trillion Nine Hundred Fifty billion Two Hundred Eight
million Four Hundred Fifteen thousand Eight Hundred
Ninety-One point two nine one seven two three zero
three four eight
2,911,782,023,376.154431611000000000 -- Two trillion
Nine Hundred Eleven billion Seven Hundred Eighty-Two
million Twenty-Three thousand Three Hundred Seventy-Six
point one five four four three one six one one
230,111,308,428,449.206853487000000000 -- Two Hundred
Thirty trillion One Hundred Eleven billion Three
Hundred Eight million Four Hundred Twenty-Eight
thousand Four Hundred Forty-Nine point two zero six
eight five three four eight seven
22,772,324,131,461.593213475000000000 -- Twenty-Two
trillion Seven Hundred Seventy-Two billion Three
Hundred Twenty-Four million One Hundred Thirty-One
thousand Four Hundred Sixty-One point five nine three
two one three four seven five
130,622,360,517,691.106893185700000000 -- One Hundred
Thirty trillion Six Hundred Twenty-Two billion Three
Hundred Sixty million Five Hundred Seventeen thousand
Six Hundred Ninety-One point one zero six eight nine
three one eight five seven

PL/SQL procedure successfully completed.

      Rating: 4
How about other
      language?  June 10, 2002
Reviewer:  Hing-chau
      Leung  from Hongkong
Your solution is excellent. I just wonder is there any similar solution for
other language.

          Followup:

No, it only works for english, sorry.

      Rating: 5
Nice ... but
      what about UK English?  June 11, 2002
Reviewer:
      Charlie  from UK
Here in the UK we deal with numbers slightly differently, inserting the word
"and" between the hundreds and tens/units values throughout.

Hence 766,411 is
seven hundred <b>and</b> sixty-six thousand, four hundred <b>and</b> eleven.

Is there any way to do this, or do we have to suffer the rather grating
Americanism ;-)

Thank you,

Charlie E.

          Followup:

Ok, here is the somewhat pedantic but english-ized (or would that be
"english-ised") version ;)

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace
  2  function spell_number( p_number in number )
  3  return varchar2
  4  as
  5      type myArray is table of varchar2(255);
  6      l_str    myArray := myArray( '',
  7                             ' thousand ', ' million ',
  8                             ' billion ', ' trillion ',
  9                             ' quadrillion ', ' quintillion ',
 10                             ' sextillion ', ' septillion ',
 11                             ' octillion ', ' nonillion ',
 12                             ' decillion ', ' undecillion ',
 13                             ' duodecillion ' );
 14
 15      l_num   varchar2(50) default trunc( p_number );
 16      l_return varchar2(4000);
 17  begin
 18      for i in 1 .. l_str.count
 19      loop
 20          exit when l_num is null;
 21
 22          if ( substr(l_num, length(l_num)-2, 3) <> 0 )
 23          then
 24             l_return := replace(
 25                           to_char(
 26                             to_date(
 27                              substr(l_num, length(l_num)-2, 3),
 28                                'J' ),
 29                  'Jsp' ) || l_str(i), 'Hundred', 'Hundred AND') || l_return;
 30          end if;
 31          l_num := substr( l_num, 1, length(l_num)-3 );
 32      end loop;
 33
 34      return l_return;
 35  end;
 36  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> column n format a60
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select
  2  spell_number( 12345678901234567890123456789012345678 ) n
  3  from dual;

N
------------------------------------------------------------
Twelve undecillion Three Hundred AND Forty-Five decillion Si
x Hundred AND Seventy-Eight nonillion Nine Hundred AND One o
ctillion Two Hundred AND Thirty-Four septillion Five Hundred
 AND Sixty-Seven sextillion Eight Hundred AND Ninety quintil
lion One Hundred AND Twenty-Three quadrillion Four Hundred A
ND Fifty-Six trillion Seven Hundred AND Eighty-Nine billion
Twelve million Three Hundred AND Forty-Five thousand Six Hun
dred AND Seventy-Eight

      Rating:
      4
  June 12, 2002
Reviewer:  Sagi
       from India.
Once again thank you tom.

Regards,

      Rating: 3
Re: Nice ...
      but what about UK English?   June 12, 2002

Reviewer:  Charlie E  from England (where we invented
      English)
Well, we English are renowned for being pedantic (well, I am, anyway).

However, there is still a tiny problem with trailing 'AND's wherever there is no
tens/units value ...

SQL> select  spell_number(100200) from dual;

SPELL_NUMBER(100200)
-------------------------------------------
One Hundred AND thousand Two Hundred AND

SQL> select spell_number(100) from dual;

SPELL_NUMBER(100)
-------------------------------------------
One Hundred AND

Charlie

          Followup:

trivial change:

c/'Hundred', 'Hundred AND'/'Hundred ', 'Hundred AND '/

just add a space

      Rating: 5
other
      languages  June 13, 2002
Reviewer:  Barbara
      Boehmer  from Norwalk, CA USA
Tom,

I have seen frequent requests on the OTN forums and elsewhere for a function to
spell a number in another format or another language.  I and various other
people have posted various solutions.  Most of the ones that I have posted have
been based on modifying your original function.  I have listed a couple of them
below.  The first one is a modification of your original function that is
intended to spell monetary amounts in Indian format.  The second one is a
function that converts the output from your function to French.  I would imagine
that one or the other of these methods could be used to produce output in just
about any language.  I would be interested in hearing any comments from you
(Tom) or any of your readers as to whether there is a better way.  Also, if
anyone sees any errors in the French words or Indian format, please let me know.

Thanks,
Barbara

create or replace function spell_indian_money
    ( p_number in number )
    return varchar2
as
    type myArray is table of varchar2(255);
    l_str    myArray := myArray( ' Thousand ',
                                 ' Lakh ',
                                 ' Crore ',
                                 ' Arab ',
                                 ' Kharab ',
                                 ' Shankh ' );
    l_num varchar2(50) default trunc( p_number );
    l_return varchar2(4000);
begin
    if ( substr(l_num, length(l_num)-2, 3) <> 0 )
    then
        l_return := to_char(
                        to_date(
                         substr(l_num, length(l_num)-2, 3),
                           'J' ),
                    'Jsp' );
    end if;
    l_num := substr( l_num, 1, length(l_num)-3 );

    for i in 1 .. l_str.count
    loop
        exit when l_num is null;
        if ( substr(l_num, length(l_num)-1, 2) <> 0 )
        then
            l_return := to_char(
                            to_date(
                             substr(l_num, length(l_num)-1, 2),
                               'J' ),
                        'Jsp' ) || l_str(i) || l_return;
        end if;
        l_num := substr( l_num, 1, length(l_num)-2 );
    end loop;

    if to_char( p_number ) like '%.%'
    then
        l_num := substr( round( p_number, 2), instr( p_number, '.' )+1 );
        if l_num > 0
        then
            l_return := l_return || ' And '
            || to_char(
                   to_date(
                    l_num,
                     'J' ),
               'Jsp' )
            || ' Paise';
        end if;
    end if;

    return l_return;
end spell_indian_money;
/

create or replace
function spell_number_french( p_number in number )
return varchar2
as
begin
    return replace( replace( replace( replace( replace(
           replace( replace( replace( replace( replace(
           replace( replace( replace( replace( replace(
           replace( replace( replace( replace( replace(
           replace( replace( replace( replace( replace(
           replace( replace( replace( replace( replace(
           replace( replace( replace( replace( replace(
           replace( replace( replace( replace( replace(
           replace(
           lower( spell_number( p_number ))
           , 'duodecillion', 'bidecillion'     )
           , 'quintillion' , 'cintillion'      )
           , 'billion'     , 'milliard'        )
           , 'thousand'    , 'mille'           )
           , 'hundred'     , 'cent'            )
           , 'ninety'      , 'quatre-vingt-dix')
           , 'eighty'      , 'quatre-vingt'    )
           , 'seventy'     , 'soixante-dix'    )
           , 'sixty'       , 'soixante'        )
           , 'fifty'       , 'cinquante'       )
           , 'forty'       , 'quarante'        )
           , 'thirty'      , 'trente'          )
           , 'twenty'      , 'vingt'           )
           , 'nineteen'    , 'dix-neuf'        )
           , 'eighteen'    , 'dix-huit'        )
           , 'seventeen'   , 'dix-sept'        )
           , 'sixteen'     , 'seize'           )
           , 'fifteen'     , 'quinze'          )
           , 'fourteen'    , 'quatorze'        )
           , 'thirteen'    , 'treize'          )
           , 'twelve'      , 'douze'           )
           , 'eleven'      , 'onze'            )
           , 'ten'         , 'dix'             )
           , 'nine'        , 'neuf'            )
           , 'eight'       , 'huit'            )
           , 'seven'       , 'sept'            )
           , 'five'        , 'cinq'            )
           , 'four'        , 'quatre'          )
           , 'three'       , 'trois'           )
           , 'two'         , 'deux'            )
           , 'one'         , 'un'              )
           , 'dix-six'     , 'seize'           )
           , 'dix-cinq'    , 'quinze'          )
           , 'dix-quatre'  , 'quatorze'        )
           , 'dix-trois'   , 'treize'          )
           , 'dix-deux'    , 'douze'           )
           , 'dix-un'      , 'onze'            )
           , '-un '        , '-une '           )
           , 'un cent'     , 'cent'            )
           , 'un mille'    , 'mille'           )
           , 'une'         , 'un' );
end spell_number_french;
/

          Followup:

Very cool -- thanks.  Cannot vouch for the actual output -- the concept is sound

      Rating: 5
The last word
      on 'spell the number' (hopefully)  June 14, 2002

Reviewer:  Ed Sleath  from Cheshire, UK
My countryman was a bit off the mark with his analysis.  We don't put the "and"
in after the hundreds - it's more that we put the "and" in before the tens and
units.

So, for example, this year is "two thousand and two".

So, here's another modification to Barbara and Tom's function to allow for this:

CREATE OR REPLACE FUNCTION spell_number(
   p_number   IN   NUMBER
)
   RETURN VARCHAR2 AS
   TYPE myarray IS TABLE OF VARCHAR2(255);

   l_str      myarray
      := myarray('',
            ' thousand ',
            ' million ',
            ' billion ',
            ' trillion ',
            ' quadrillion ',
            ' quintillion ',
            ' sextillion ',
            ' septillion ',
            ' octillion ',
            ' nonillion ',
            ' decillion ',
            ' undecillion ',
            ' duodecillion '
         );
   l_num      VARCHAR2(50)   DEFAULT ABS(TRUNC(p_number));
   l_words    VARCHAR2(4000);
   l_return   VARCHAR2(4000);
BEGIN

   FOR i IN 1 .. l_str.COUNT LOOP
      EXIT WHEN l_num IS NULL;

      IF (SUBSTR(l_num, LENGTH(l_num) - 2, 3) <> 0) THEN

         l_words := TO_CHAR(TO_DATE(SUBSTR(l_num, LENGTH(l_num) - 2, 3), 'J'),
'Jsp');

         IF MOD(l_num, 100) > 0 THEN
            l_words :=
               SUBSTR(l_words, 1, INSTR(l_words, ' ', -1)) || 'and ' ||
                  SUBSTR(l_words, INSTR(l_words, ' ', -1) + 1);
         END IF;

         l_return := l_words || l_str(i) || l_return;

      END IF;

      l_num := SUBSTR(l_num, 1, LENGTH(l_num) - 3);

   END LOOP;

   l_return := LTRIM(l_return, 'and ');

   IF p_number <= -1 THEN
      l_return := 'minus '||l_return;
   ELSIF p_number < 0 THEN
      l_return := 'zero';
   END IF;

   RETURN l_return;
END;
/

Frankly, I can't see much practical use for this, but it's an interesting
technical exercise.

Incidentally (for Barbara), don't the French say "soixante-et-onze" rather than
"soixante-onze"?

Ed

      Rating: 5
response to Ed
      Sleath  June 14, 2002
Reviewer:  Barbara
      Boehmer  from Norwalk, CA USA
Ed,

Thanks for your feedback.

I think the most common practical usage for spelling numbers in various
languages and formats is for automated check writing, especially payroll.

As to whether to say one hundred one or one hundred and one (as in the movie
title "One Hundred and One Dalmations"), I have heard them both ways and seen
them both ways.  It may be that both are correct.  I don't know if there is a
definitive source that can be consulted.  It may be that which is correct
depends on the country or region that you are using it in.

As to whether 71 should be soixante-onze or soixante et onze, I have also seen
and heard that both ways.  I found soixante-onze on one website that lists
French numbers and soixante et onze on another.  It has been too long since my
French classes, for me to remember which way I was taught at the time.  I asked
a co-worker from France and she says soixante et onze.  A friend in Canada says
soixante-onze.  I have also heard that there may be a difference in the usage
depending on whether you are saying 71 things or counting 70, 71, 72, etc.
According to one website, when French is spoken in Switzerland or Belgium, they
use an entirely different word for 70.  Once again, lacking a definitive source
for clarification, it may be that both are correct, or which is correct is
dependent upon the country or region of usage.

Based on your suggestion, I modified the function to use soixante et onze.  I
also made a few other little related corrections below that.  I have listed the
corrected spell_number_french function and test below.  However, the
spell_number function that is being used within the spell_number_french function
is the one that I provided in my response entitled decimals, which does not
include the and's, because, if I used the one that includes and's, then I would
also have to convert the and's to et's and eliminate them from where they don't
belong.  As far as I know, and according to one website, in French, 101 is cent
un, not cent et un.

Barbara

create or replace
function spell_number_french( p_number in number )
return varchar2
as
begin
    return replace( replace( replace( replace( replace(
           replace( replace( replace( replace( replace(
           replace( replace( replace( replace( replace(
           replace( replace( replace( replace( replace(
           replace( replace( replace( replace( replace(
           replace( replace( replace( replace( replace(
           replace( replace( replace( replace( replace(
           replace( replace( replace( replace( replace(
           replace( replace( replace( replace( replace(
           lower( spell_number( p_number ))
           , 'duodecillion'      , 'bidecillion'     )
           , 'quintillion'       , 'cintillion'      )
           , 'billion'           , 'milliard'        )
           , 'thousand'          , 'mille'           )
           , 'hundred'           , 'cent'            )
           , 'ninety'            , 'quatre-vingt-dix')
           , 'eighty'            , 'quatre-vingts'   )
           , 'seventy'           , 'soixante-dix'    )
           , 'sixty'             , 'soixante'        )
           , 'fifty'             , 'cinquante'       )
           , 'forty'             , 'quarante'        )
           , 'thirty'            , 'trente'          )
           , 'twenty'            , 'vingt'           )
           , 'nineteen'          , 'dix-neuf'        )
           , 'eighteen'          , 'dix-huit'        )
           , 'seventeen'         , 'dix-sept'        )
           , 'sixteen'           , 'seize'           )
           , 'fifteen'           , 'quinze'          )
           , 'fourteen'          , 'quatorze'        )
           , 'thirteen'          , 'treize'          )
           , 'twelve'            , 'douze'           )
           , 'eleven'            , 'onze'            )
           , 'ten'               , 'dix'             )
           , 'nine'              , 'neuf'            )
           , 'eight'             , 'huit'            )
           , 'seven'             , 'sept'            )
           , 'five'              , 'cinq'            )
           , 'four'              , 'quatre'          )
           , 'three'             , 'trois'           )
           , 'two'               , 'deux'            )
           , 'one'               , 'un'              )
           , 'dix-six'           , 'seize'           )
           , 'dix-cinq'          , 'quinze'          )
           , 'dix-quatre'        , 'quatorze'        )
           , 'dix-trois'         , 'treize'          )
           , 'dix-deux'          , 'douze'           )
           , 'dix-un'            , 'onze'            )
           , '-un '              , '-une '           )
           , 'un cent'           , 'cent'            )
           , 'un mille'          , 'mille'           )
           , 'une'               , 'un'              )
           , 'soixante-onze'     , 'soixante et onze')
           , 'quatre-vingts-'    , 'quatre-vingt-'  )
           , '-un'               , ' et un'          )
           , 'quatre-vingt et un', 'quatre-vingt-un');
end spell_number_french;
/

SQL> set serveroutput on
SQL> begin
  2      for i in 1..101
  3      loop
  4          dbms_output.put_line( spell_number_french( i ) );
  5      end loop;
  6  end;
  7  /
un
deux
trois
quatre
cinq
six
sept
huit
neuf
dix
onze
douze
treize
quatorze
quinze
seize
dix-sept
dix-huit
dix-neuf
vingt
vingt et un
vingt-deux
vingt-trois
vingt-quatre
vingt-cinq
vingt-six
vingt-sept
vingt-huit
vingt-neuf
trente
trente et un
trente-deux
trente-trois
trente-quatre
trente-cinq
trente-six
trente-sept
trente-huit
trente-neuf
quarante
quarante et un
quarante-deux
quarante-trois
quarante-quatre
quarante-cinq
quarante-six
quarante-sept
quarante-huit
quarante-neuf
cinquante
cinquante et un
cinquante-deux
cinquante-trois
cinquante-quatre
cinquante-cinq
cinquante-six
cinquante-sept
cinquante-huit
cinquante-neuf
soixante
soixante et un
soixante-deux
soixante-trois
soixante-quatre
soixante-cinq
soixante-six
soixante-sept
soixante-huit
soixante-neuf
soixante-dix
soixante et onze
soixante-douze
soixante-treize
soixante-quatorze
soixante-quinze
soixante-seize
soixante-dix-sept
soixante-dix-huit
soixante-dix-neuf
quatre-vingts
quatre-vingt-un
quatre-vingt-deux
quatre-vingt-trois
quatre-vingt-quatre
quatre-vingt-cinq
quatre-vingt-six
quatre-vingt-sept
quatre-vingt-huit
quatre-vingt-neuf
quatre-vingt-dix
quatre-vingt-onze
quatre-vingt-douze
quatre-vingt-treize
quatre-vingt-quatorze
quatre-vingt-quinze
quatre-vingt-seize
quatre-vingt-dix-sept
quatre-vingt-dix-huit
quatre-vingt-dix-neuf
cent
cent un

PL/SQL procedure successfully completed.

      Rating: 5
Spell Number is
      Great!!  September 17, 2002
Reviewer:
      yendra  from Jakarta, Indonesia
This function is great.I use in purchase order to convert
example 1000000 to one million.
But if i have 10000000.12, it doesn't show me what i want.
I want to show the cent so it show one million and twelve cent.
Can help me???? I need this function.

          Followup:

think about it.

I can spell

1000000 = One Million

I can spell

12 = Twelve

You have 1000000.12 -- so, maybe....

SPELL_NUMBER(TRUNC(:X))||'DOLLARSAND'||SPELL_NUMBER((:X-TRUNC(:X))*100)||'CENTS'
---------------------------------------------------------------------------------
-------------------
One million dollars and Twelve cents

      Rating: 4
Its Amazing but
      not works for -ve values  October 28, 2002

Reviewer:  Kamran Laeeq  from Pakistan
Dear Sir,
Its Amazing and working for any digit except -ve values can any amendment be
made to make it useful for -ve intergers also

          Followup:

and what the heck is -ve numbers.  (I'm guessing you are making me play a word
game and the word you are trying to get me to guess is "negative")

Think about it, what could you do to get it to return Negative One Million.
Maybe use abs() on the number when assigning to l_num and conditionally
concatenating on the word "negative" or whatever you like at the end?

      Rating: 3
spell number
      > 38 digits  April 08, 2003
Reviewer:  ma
       from bristol, uk
hello here's my tuppence worth,

the following will spell number > 38 digits and spell point zero at the end

create or replace function spell_number(p_number in varchar2) return varchar2 as
  --
  type string_array is table of varchar2(255);
  --
  v_string  string_array := string_array('',
                                         ' thousand ',      ' million ',
                                         ' billion ',       ' trillion ',
                                         ' quadrillion ',   ' quintillion ',
                                         ' sextillion ',    ' septillion ',
                                         ' octillion ',     ' nonillion ',
                                         ' decillion ',     ' undecillion ',
                                         ' duodecillion ',  ' tridecillion ',
                                         ' quaddecillion ', ' quindecillion ',
                                         ' sexdecillion ',  ' septdecillion ',
                                         ' octdecillion ',  ' nondecillion ',
                                         ' dedecillion ');
  v_number  varchar2(255);
  v_return  varchar2(4000);
begin
  if instr(p_number, '.') = 0 then
    v_number := p_number;
  else
    v_number := substr(p_number, 1, instr(p_number, '.')-1);
  end if;
  --
  if v_number = '0' then
    v_return := 'zero';
  else
    for i in 1 .. v_string.count
    loop
      exit when v_number is null;
      --
      if (substr(v_number, length(v_number)-2, 3) <> 0) then
        v_return := to_char(to_date(substr(v_number, length(v_number)-2, 3),
'j'), 'jsp') ||
                    v_string(i) ||
                    v_return;
      end if;
      v_number := substr(v_number, 1, length(v_number)-3);
    end loop;
  end if;
  --
  -- to include decimal places.
  --
  if p_number like '%.%' then
    v_number := substr(p_number, instr(p_number, '.')+1);
    v_return := v_return ||' point';
    for i in 1 .. length(v_number)
    loop
      exit when v_number is null;
      if substr(v_number, 1, 1) = '0' then
        v_return := v_return ||' zero';
      else
        v_return := v_return ||' '||
                    to_char(to_date(substr(v_number, 1, 1), 'j'), 'jsp');
      end if;
      v_number := substr(v_number, 2);
    end loop;
  end if;  -- include decimal places.
  return v_return;
end spell_number;
/

select 11,
spell_number('9948443321800876587765443737476566352652356988626255324.09927764674
24')
from dual

nine septdecillion nine hundred forty-eight sexdecillion four hundred
forty-three quindecillion three hundred twenty-one quaddecillion eight hundred
tridecillion eight hundred seventy-six duodecillion five hundred eighty-seven
undecillion seven hundred sixty-five decillion four hundred forty-three
nonillion seven hundred thirty-seven octillion four hundred seventy-six
septillion five hundred sixty-six sextillion three hundred fifty-two quintillion
six hundred fifty-two quadrillion three hundred fifty-six trillion nine hundred
eighty-eight billion six hundred twenty-six million two hundred fifty-five
thousand three hundred twenty-four point zero nine nine two seven seven six four
six seven four two four

select 11, spell_number('324.0')
from dual

three hundred twenty-four point zero

select 11, spell_number('.0')
from dual

point zero

      Rating: 3
spell number
      > 38 digits version 2  April 08, 2003
Reviewer:
      ma  from bristol, uk
sorry, here's an update

create or replace function spell_number(p_number in varchar2) return varchar2 as
  --
  type string_array is table of varchar2(255);
  --
  v_string  string_array := string_array('',
                                         ' thousand ',      ' million ',
                                         ' billion ',       ' trillion ',
                                         ' quadrillion ',   ' quintillion ',
                                         ' sextillion ',    ' septillion ',
                                         ' octillion ',     ' nonillion ',
                                         ' decillion ',     ' undecillion ',
                                         ' duodecillion ',  ' tridecillion ',
                                         ' quaddecillion ', ' quindecillion ',
                                         ' sexdecillion ',  ' septdecillion ',
                                         ' octdecillion ',  ' nondecillion ',
                                         ' dedecillion ');
  v_number  varchar2(255);
  v_return  varchar2(4000);
begin
  if instr(p_number, '.') = 0 then
    v_number := p_number;
  else
    v_number := substr(p_number, 1, instr(p_number, '.')-1);
  end if;
  --
  if v_number = '0'
  or v_number is null then
    v_return := 'zero';
  else
    for i in 1 .. v_string.count
    loop
      exit when v_number is null;
      --
      if (substr(v_number, length(v_number)-2, 3) <> 0) then
        v_return := to_char(to_date(substr(v_number, length(v_number)-2, 3),
'j'), 'jsp') ||
                    v_string(i) ||
                    v_return;
      end if;
      v_number := substr(v_number, 1, length(v_number)-3);
    end loop;
  end if;
  --
  -- to include decimal places.
  --
  if p_number like '%.%' then
    v_number := substr(p_number, instr(p_number, '.')+1);
    v_return := v_return ||' point';
    for i in 1 .. length(v_number)
    loop
      exit when v_number is null;
      if substr(v_number, 1, 1) = '0' then
        v_return := v_return ||' zero';
      else
        v_return := v_return ||' '||
                    to_char(to_date(substr(v_number, 1, 1), 'j'), 'jsp');
      end if;
      v_number := substr(v_number, 2);
    end loop;
  end if;  -- include decimal places.
  return v_return;
end spell_number;
/

select
spell_number('9948443321800876587765443737476566352652356988626255324.09927764674
24')
from dual;

> nine septdecillion nine hundred forty-eight sexdecillion four hundred
forty-three quindecillion three hundred twenty-one quaddecillion eight hundred
tridecillion eight hundred seventy-six duodecillion five hundred eighty-seven
undecillion seven hundred sixty-five decillion four hundred forty-three
nonillion seven hundred thirty-seven octillion four hundred seventy-six
septillion five hundred sixty-six sextillion three hundred fifty-two quintillion
six hundred fifty-two quadrillion three hundred fifty-six trillion nine hundred
eighty-eight billion six hundred twenty-six million two hundred fifty-five
thousand three hundred twenty-four point zero nine nine two seven seven six four
six seven four two four

select spell_number('.0')
from dual;

> zero point zero

select spell_number('1')
from dual;

> one

select spell_number('1.0')
from dual

> one point zero

      Rating:
      5
Terrific  May 08, 2003
Reviewer:
      Walker  from Maryland, USA
Terrific Solution.
Thanks to everyone for their reviews.

      Rating: 4
spellnum for
      german language  August 14, 2003
Reviewer:  Maik
      Rabe  from Stuttgart, Germany
Hi,

i have write a stored procedure for all the people, that need this function in
german language! The parameter is a number type, so the following is poosible
too:

SQL> select spellnum(10/4) from dual;

SPELLNUM(10/4)
---------------------------------------------
Zwei Komma Fünf
(Two Point Five)

CREATE OR REPLACE function spellnum(val in number)
return varchar2
is
v_val varchar2(200);
v_text varchar2(4000);
v_exp_text varchar2(6);
v_teiltext varchar2(200);
commapos pls_integer;
exppos pls_integer;
commasep char;
tsd_stelle pls_integer;
i pls_integer DEFAULT 1;
c char(3);
function get_text(ziffer in varchar2)
return varchar2
is
begin

case trim(ziffer)
    when commasep then return ' Komma';
    when '0' then return ' Null';
    when '1' then return ' Ein';
    when '2' then return ' Zwei';
    when '3' then return ' Drei';
    when '4' then return ' Vier';
    when '5' then return ' Fünf';
    when '6' then return ' Sechs';
    when '7' then return ' Sieben';
    when '8' then return ' Acht';
    when '9' then return ' Neun';
    else return ' Fehler_Text';
    end case;
end;

function get_hunderter_text(ziffer in varchar2)
return varchar2
is
v_text_hunderter varchar2(150);
v_text_zehner varchar2(150);
v_text_einer varchar2(150);
begin

--Hunderter-Stelle

If (substr(ziffer,1,1)!='0') then
   v_text_hunderter := get_text(substr(ziffer,1,1))||'hundert';

   --wenn nachfolgender Einer/Zehner-Wert, dann 'und' ranhängen
   if (substr(ziffer,2,1) != '0' or substr(ziffer,3,1) != '0') then
         v_text_hunderter := v_text_hunderter||'und';
      end if;

   end if;

--nun die Einer
if (substr(ziffer,3,1)!='0') then
     v_text_einer := get_text(substr(ziffer,3,1));

   --wenn im Bereich 10..19, dann Einer-Text auf 4 Stellen beschneiden
(Sieben->Sieb|zehn)
   if (substr(ziffer,2,1) = '1') then
     v_text_einer := substr(v_text_einer,1,5);
     end if;

   --wenn Zehner >=20, dann 'und' zwischen Zehner und Einer einfügen
   if (substr(ziffer,2,1) not in ('0','1')) then
     v_text_einer := v_text_einer||'und';
     end if;

   --aus Ein wird hier Eins, wenn kein Zehner vorhanden
   if (substr(ziffer,2,1) = '0' and substr(ziffer,3,1)='1') then
     v_text_einer := v_text_einer||'s';
     end if;

  end if;

--nun die Zehner
if (substr(ziffer,2,1) != '0') then
   v_text_zehner := substr(get_text(substr(ziffer,2,1)),1,5)||'zig';
   end if;

--10 und 20 korrigieren
if (v_text_zehner=' Einzig') then v_text_zehner := ' Zehn'; end if;
if (v_text_zehner=' Zweizig') then v_text_zehner := ' Zwanzig'; end if;

--11 und 12 konvertieren
if (v_text_einer||v_text_zehner=' Ein Zehn') then v_text_zehner := ' Elf';
v_text_einer:= null; end if;
if (v_text_einer||v_text_zehner=' Zwei Zehn') then v_text_zehner := ' Zwölf';
v_text_einer:= null; end if;

--Groß/Klein- und Zusammenschreibung abhängig vom vorausgehenden Text einstellen

if ( v_text_hunderter is not null) then
   v_text_zehner := trim(lower(v_text_zehner));
   v_text_einer := trim(lower(v_text_einer));
   end if;

if ( v_text_einer is not null) then
   v_text_zehner := trim(lower(v_text_zehner));
   end if;

return v_text_hunderter||v_text_einer||v_text_zehner;

end;

function get_tausender_text(stelle in pls_integer, val in pls_integer)
return varchar2
is
ret_text varchar2(100);
begin

  case stelle
    when 0 then return null;
    when 1 then ret_text := ' Tausend';
    when 2 then ret_text := ' Million';
    when 3 then ret_text := ' Milliarde';
    when 4 then ret_text := ' Billion';
    when 5 then ret_text := ' Billiarde';
    when 6 then ret_text := ' Trillion';
    when 7 then ret_text := ' Trilliarde';
    when 8 then ret_text := ' Quadrillion';
    when 9 then ret_text := ' Quadrilliarde';
    when 10 then ret_text := ' Quintillion';
    when 11 then ret_text := ' Quintilliarde';
    when 12 then ret_text := ' Sextillion';
    when 13 then ret_text := ' Sextilliarde';
    when 14 then ret_text := ' Septillion';
    when 15 then ret_text := ' Septilliarde';
    when 16 then ret_text := ' Oktillion';
    when 17 then ret_text := ' Oktilliarde';
    when 18 then ret_text := ' Nonillion';
    when 19 then ret_text := ' Nonilliarde';
    when 20 then ret_text := ' Dezillion';
    when 21 then ret_text := ' Dezilliarde';
        else ret_text := ' mal Zehn
hoch'||get_hunderter_text(lpad(3*stelle,3,'0'));
    end case;

if (val > 1) then
  ret_text := replace(ret_text,'ion','ionen');
  ret_text := replace(ret_text,'iarde','iarden');
  end if;

return ret_text;
end;

begin

--Null-Wert sofort als 'unbekannt' zuück
if (val is null) then return 'unbekannt'; end if;

--Auslesen des aktuellenDezimalzeichens
commasep:=substr(to_char(1/2),1,1);

--Wert als Text und commapos bestimmen
  v_val      := trim(to_char(val));
commapos  := instr(v_val,commasep);

--Bei Exponentialwert Exp-Teil extrahieren
exppos := instr(v_val,'E');
if (exppos > 0) then
  v_exp_text :=substr(v_val, exppos+1);
  v_val := substr(v_val,1,exppos-1);
  v_val := rtrim(v_val,'0');
  end if;

--Betrag auswerten und String anpassen
if (substr(v_val,1,1)='-') then
  v_text := 'Minus';
  v_val     := substr(v_val,2);
  commapos  := commapos - 1;
  end if;

--String auffüllen auf durch 3 teilbare Stellenzahl mit führenden Nullen
if (commapos < 1) then
  v_val := lpad(v_val, (trunc(length(v_val)/3)+1)*3,'0')||commasep;
  else
  v_val      :=
lpad(v_val,length(v_val)+(trunc((commapos-1)/3)+1)*3-abs(commapos-1),'0');
  end if;

commapos  := instr(v_val,commasep);

--Vorkommastellen
while (i<=commapos-1) loop
  c := substr(v_val,i,3);

  v_teiltext := get_hunderter_text(c);
  if (v_teiltext is not null) then
    tsd_stelle := trunc((commapos-1-i)/3);
    if (tsd_stelle >1 and v_teiltext=' Eins') then
      v_teiltext:=' Eine';
      end if;
    if (tsd_stelle = 1 and v_teiltext=' Eins') then
      v_teiltext:=' Ein';
      end if;
    v_text := v_text||v_teiltext||get_tausender_text(tsd_stelle, to_number(c));
    end if;

  i := i + 3;

  end loop;

--Vorkomma-Null
if (v_text is null or v_text = 'Minus') then
  v_text:= trim(v_text||' Null');
  end if;

--Nachkommastellen

--wenn Nachkommastellen vorhanden, dann aufzaehlen
if (commapos<length(v_val)) then

  --durchlaufen der Nachkommastellen
  for i in commapos .. length(v_Val) loop
    v_teiltext := get_text(substr(v_val, i,1));
    if v_teiltext=' Ein' then v_teiltext:=' Eins'; end if;
    v_text := v_text||v_teiltext;
    end loop;

  end if;

--wenn Exponentialdarstellung, dann exp-Wert angeben
if (v_exp_text is not null) then

  if (v_text=' Eins') then v_text:=' Ein'; end if;

  v_text := v_text ||' mal Zehn hoch';

   --Betrag auswerten und String anpassen
  if (substr(v_exp_text ,1,1)='-') then
    v_text := v_text || ' Minus';
    end if;

  v_text := v_text || get_hunderter_text(substr(v_exp_text,2));
  end if;

return trim(v_text);

end;
/

Hope, that this procedure will be used!

Maik

      Rating:
      5
  August 25, 2003
Reviewer:
      A reader
No review. Just thanks. well done everyone.
James

October 28, 2002 Bruce -- Thanks for the question regarding "How
      many buckets to specify for histograms?", version 8.1.6

      You Asked
When analyzing a table to setup histograms, how does Oracle determine the number
of buckets?  I notice different numbers in user_histograms for a column than the
number of buckets I specify in the command.  For example, I do some analysis and
determine that 68 buckets are needed for a reference table with about 23,000
rows.  Then I execute this command:

analyze table geo_xref_tb
compute statistics
for columns map_ref size 68

and then looking at the data dictionary, I see this

select table_name,column_name,count(*)
from user_histograms
where table_name = 'GEO_XREF_TB'
group by table_name,column_name
having count(*) > 2

TABLE_NAME                     COLUMN_NAME       COUNT(*)
------------------------------ --------------- ----------
GEO_XREF_TB                    MAP_REF                 64

It seems Oracle has decided that it doesn't need all the 68 that I specified.  I
have not come across the situation where Oracle has increased the number of
buckets beyond what I specify.  Most often Oracle and I either have the same
number or are +/- 1.  Am I counting the number of buckets wrong or chalk it up
to "the database knows better than I do"?

Since it knows what its doing and its numbers are all that count, is it better
to set the size to the maximum 254 all the time and let Oracle figure out what
it needs?

Thank-you for your help,
Bruce

      and we said...
It is a normal, expected outcome -- it did what you asked.  What you are seeing
is the different outcome you get when you specify a number of buckets that is
LESS then the number of distinct values.

Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select rownum from all_users
where rownum <= 10;

10 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select 5 from all_objects;

22819 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table t compute statistics for
columns x size 10;

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select column_name, endpoint_number,
endpoint_value
  2    from user_tab_histograms
  3   where table_name = 'T';

COL ENDPOINT_NUMBER ENDPOINT_VALUE
--- --------------- --------------
X                 1              1
X                 2              2
X                 3              3
X                 4              4
X             22824              5
X             22825              6
X             22826              7
X             22827              8
X             22828              9
X             22829             10

10 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table t delete statistics;

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table t compute statistics for
columns x size 8;

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select column_name, endpoint_number,
endpoint_value
  2    from user_tab_histograms
  3   where table_name = 'T';

COL ENDPOINT_NUMBER ENDPOINT_VALUE
--- --------------- --------------
X                 7              5
X                 8             10
X                 0              1

ops$tkyte@ORA817DEV.US.ORACLE.COM>

So, did it do the right thing?  Yes, it did.  I asked for 8 and appear to have
gotten 3 but I really got 8 (like I asked for).  It is just that redundant ones
(that could be inferred) were left out.

There is a support note, available on metalink as well, explains it in detail:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3126073805757
read through that...

Review &
      Followup

      Rating: 4
Good
      discussion  July 05, 2002
Reviewer:  Bruce
       from Edmonton, Canada

      Rating: 5
number of
      default buckets  October 28, 2002
Reviewer:
      A reader
Hi

in 8.1.7 doc it says that the default number of buckets when we generate
histograms is 75

cut & pasted from doc:
Specify the maximum number of buckets in the histogram. The default value is 75,
minimum value is 1, and maximum value is 254

But this is wrong right? I think the number of buckets is 1.

By default when we compute statistics what does it do?

for table or for all columns?
for indexes? for all indexed columns?

what are the default values for the for clause? Cant find it anywhere in the
document

cheers

          Followup:

sigh, try it and see?

ops$tkyte@ORA920.US.ORACLE.COM> drop table t;

Table dropped.

ops$tkyte@ORA920.US.ORACLE.COM> create table t as select owner from all_objects;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> analyze table t compute statistics
  2  for table
  3  for all columns;

Table analyzed.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select count(*)
  2    from user_tab_histograms
  3   where table_name = 'T';

  COUNT(*)
----------
        33

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>

ops$tkyte@ORA920.US.ORACLE.COM> analyze table t delete statistics;

Table analyzed.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> analyze table t compute statistics
  2  for table
  3  for all columns size 1;

Table analyzed.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select count(*)
  2    from user_tab_histograms
  3   where table_name = 'T';

  COUNT(*)
----------
         2

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>

Apparently, it is more then 1.....  documentation is accurate on this point.

      Rating: 3
SIZE clause in
      ANALYZE  December 03, 2003
Reviewer:
      A reader  from NJ, USA
Tom,

1.Can you pls. clarify what SIZE clause in ANALYZE means. I read the Oracle
documentation and is not clear. Also, in your first example, you mentioned that
it shows 8.
2. If I analyze any non-integer column, how do I get the range of values in each
bucket. ie. If I analyze the integer column, the ENDPOINT_VALUE column shows the
column values and the difference between ENDPOINT_NUMBER column between the
previous and current row will give the number of values in that range. However,
when I analyzed and created a histogram on VARCHAR column, the
ENDPOINT_ACTUAL_VALUE was null.
3. Can you eloborate on this as to how to interpret the output from
_TAB_HISTOGRAM?.

October 28, 2002 -- Thanks for the question regarding "rollup or
      cube would calculate the total?", version 8.1.7

      You Asked
Hi Tom

I have this small sql to calculate the datafile size and total

spool &&temp_path\datafiles_size_info.log
prompt ********************************
prompt Datafile Size
prompt ********************************
column file_name format a50
column bytes format 999999
break on report
COMPUTE sum LABEL 'TOTAL' OF SIZE/MB ON REPORT
select
tablespace_name,
file_name,
bytes/1024/1024 "SIZE/MB",
autoextensible "AUTOEXT"
from dba_data_files
/

as you can see I have to use compute, I tried with rollup and cube but I got
funny results... I did with analytic functions But I had to use an extra column
which is not needed. How can I accmplish this in a single SQL?

Cheers

      and we said...
You need to use GROUPING to filter the rows with a having clause (to get just
the details AND the summary you want).  Also, you can use grouping as I have to
get the word "total" printed out (shows you how to determine whether a row is a
rollup row or a detail row)

ops$tkyte@ORA817DEV.US.ORACLE.COM> select
  2  decode( grouping(tablespace_name), 0, tablespace_name, 'TOTAL:' )
tablespace_name,
  3  decode( grouping(file_name), 0, file_name, rpad('-',40,'-') ) file_name,
  4  sum(round(bytes/1024/1024,2)) "SIZE/MB",
  5  decode( grouping(tablespace_name), 0, max(autoextensible), null ) "AUTOEXT"
  6  from dba_data_files
  7  group by rollup(tablespace_name,file_name)
  8  having (grouping( tablespace_name ) = 0 and grouping( file_name ) = 0)
  9      or (grouping( tablespace_name ) = 1 and grouping( file_name ) = 1)
 10  /

TABLESPACE_NAME      FILE_NAME                                   SIZE/MB AUT
-------------------- ---------------------------------------- ---------- ---
DRSYS                /d04/oradata/ora817dev/drsys.dbf                 34 YES
RBS_TS_01            /d01/oradata/ora817dev/rbs_ts_01.dbf           51.1 YES
RBS_TS_02            /d02/oradata/ora817dev/rbs_ts_02.dbf          338.1 YES
RBS_TS_03            /d03/oradata/ora817dev/rbs_ts_03.dbf          116.1 YES
RBS_TS_04            /d04/oradata/ora817dev/rbs_ts_04.dbf           95.1 YES
RBS_TS_05            /d01/oradata/ora817dev/rbs_ts_05.dbf           25.1 YES
SYSTEM               /d01/oradata/ora817dev/system01.dbf             334 YES
TESTING              /tmp/testing.dbf                                 46 YES
USERS                /d04/oradata/ora817dev/users.dbf               2000 YES
TOTAL:               ----------------------------------------     3039.5

10 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>

Review &
      Followup

      Rating: 5
I dont
      understand grouping  March 26, 2002
Reviewer:
      lsc  from madrid, kpmg
Hi Tom

Thanks for your replay and my late response! (Or late question)

I suspected I had to use grouping but I just cant understand how to use it even
with your example I am puzzled how grouping works, may you provide a simpler
example which shows how grouping works? (I think it is used to filter out the
NULL rows or is just one of its functions?)

cheers

          Followup:

It is not being used to filter out NULL rows.  It is being used to keep the
aggregates we want.

Consider:

  1  select deptno, job, sum(sal), grouping(deptno), grouping(job)
  2  from emp
  3* group by rollup( deptno, job )
scott@ORA817DEV.US.ORACLE.COM> /

    DEPTNO JOB         SUM(SAL) GROUPING(DEPTNO) GROUPING(JOB)
---------- --------- ---------- ---------------- -------------
        10 CLERK           1300                0             0
        10 MANAGER         2450                0             0
        10 PRESIDENT       5000                0             0
        10                 8750                0             1
        20 ANALYST         6000                0             0
        20 CLERK           1900                0             0
        20 MANAGER         2975                0             0
        20                10875                0             1
        30 CLERK            950                0             0
        30 MANAGER         2850                0             0
        30 SALESMAN        5600                0             0
        30                 9400                0             1
                          29025                1             1

13 rows selected.

When the grouping "bits" if you will are 0,0 -- we have a detail row.  This is a
non-aggregated row at that level (it shows the sum(sal) at the DEPTNO/JOB level)

When we wee a bit mask of "0,1", we have an aggregated result -- the JOB
dimension was collapsed out -- we get a sum(sal) by DEPTNO, not dept/job.

When we see a bit mask of "1,1" we have another aggregated result -- that is
simply in this case the SUM(SAL) over all record (like a grand total).

You use the grouping to see the level of detail a given record is at.

      Rating: 5
rollup or
      cube  October 28, 2002
Reviewer:
      A reader
Hi

I have following query which returns some sum, I would like to know if I can use
rollup or cube to calculate sub-totals, I tried both rollup and cube but got
massive number of rows back!

select product_id,
       substr(codigo, instr(codigo, '_', 1) + 1, 2) TIPO_INCIDENCIA,
       estado,
       numero_incidencia
  from (select
            product_id,
            estado,
            codigo,
            row_number() over (partition by product_id, estado, substr(codigo,
instr(codigo, '_', 1) + 1, 2)
                               order by product_id) numfila,
            count(product_id) over (partition by product_id, estado,
substr(codigo, instr(codigo, '_', 1) + 1, 2))
            numero_incidencia,
            case
            when fecha_actualizacion = fecha_apertura then avg(sysdate -
fecha_apertura) over
                                       (partition by product_id, estado,
substr(codigo, instr(codigo, '_', 1) + 1,2))
        else avg(fecha_actualizacion - fecha_apertura) over (
                 partition by product_id, estado, substr(codigo, instr(codigo,
'_', 1) + 1,2))
            end media_tiempo
          from tiempos_medios_pvcs
         where product_id in ('ADDA'))
 where numfila = 1
order by 1, 2, 3
/

PRODUCT_ID  TIPO ESTADO                   NUMERO_INCIDENCIA
----------- ---- ------------------------ -----------------
ADDA        PR   $MOVED                                   2
ADDA        PR   ANULADO                                 16
ADDA        PR   CERRADA                                 27
ADDA        RU   ANULADO                                  9
ADDA        RU   CERRADA                               1142

What I would like to do is add an extra row in each different TIPO so I get the
subtotals (145 for PR and 1152 for RU). Is this possible with rollup, cube?

          Followup:

ops$tkyte@ORA920.LOCALHOST> select product_id, tipo, nvl(estado, '**total**'),
sum(numero_incidencia)
  2    from ( select * from t )
  3   group by rollup( product_id, tipo, estado )
  4  having grouping(product_id) = 0 and grouping(tipo) = 0
  5  /

PROD TI NVL(ESTADO SUM(NUMERO_INCIDENCIA)
---- -- ---------- ----------------------
ADDA PR MOVED                           2
ADDA PR ANULADO                        16
ADDA PR CERRADA                        27
ADDA PR **total**                      45
ADDA RU ANULADO                         9
ADDA RU CERRADA                      1142
ADDA RU **total**                    1151

7 rows selected.

ops$tkyte@ORA920.LOCALHOST>

replace my select * from t with your query and thats it

October 28, 2002 Justin -- Thanks for the question regarding
      "Loading Data through C ", version 8.1.7

      You Asked
Hi Tom,

Lets say if i have a flat text file and i want to load it through C.
I can do it through SQL LOADER, as i am new to C, can you please provide some
simple examples of loading data through C.. your help will be greatly
appreciated..

Thanks..

      and we said...
the easiest way to load it in C would be:

void main()
{
   system( 'sqlldr u/p emp.ctl' );
}

Now, thats not what you were looking for however -- the answer to this quesiton
is don't use C

o since you don't know C
o sqlldr is very capable
o sqlldr will beat your C code for performance and options
o even I who knows C really really well would not choose C for loading data

If you really want to do it in C, you will first need to gain a mastery of C and
then using Pro*C is easy from there.

Review &
      Followup

      Rating:
      3
  October 20, 2002
Reviewer:
      A reader
thanks tom.

      Rating: 5
load data from
      c using sqlldr  October 28, 2002
Reviewer:
      A reader
Hi tom,

 I have linux 73 and oracle 8.1.7
I have to load the data every night.
I am using sqlldr

 now I don't know java. But I can do "C"
also after loading the data, I have to parse the "LOG file"
"BAD file" and if some problem (like 0 rows loaded or # errors > 50 then what to
write in the syslog)

 can you plase tell me how to program that !

how to call sqlldr from c and write to the syslog ?

thanks,

          Followup:

If you know C, you know how to do this already!

system to run sqlldr (as above)

fopen, fgets and such to read the log file.

But.... it would probably be easier with a shell script using grep and such....

October 28, 2002 Subramaniam -- Thanks for the question regarding
      "ARCH Destination Failure", version 8.1.5

      You Asked
Hi Tom,

I need your help in the following:

I am using oracle 8i (rel. 8.1.5) database  with archieve log mode on sun
server. My question is,

1. If the arch file destination fails what will happen, means, physically HD
fails,
2. In that case how can I shut down the DB. Normal mode is not working, If I use
Abort mode the message came instance aborted, but if I see the process all the
orcale background process are running.
3. Daily am shutting down the DB @10.30pm, and do the cold backup's @11.30 pm
and restarting DB @ 6.30 am. using scripts. Is it ok? Suppose if the DB is not
shutting down properly the backup will start @ 11.30 pm and another script will
try to start DB @ 6.30 am, is it feasible?

TIA,

S.SenthilKumar

      and we said...
1) database stops

2) you should not be observing that unless they are stuck in a disk wait (which
if you've had hardware failures -- would be understandable).  If stuck in a disk
wait, they'll be there forever (the OS won't kill them)

3) TERRIBLE, HORRIBLE, excessively bad practice.

I hate to shutdown a database -- ever.

You are in archive log mode.  you should do HOT backups.  They are no harder to
do, no harder to restore from.

Do you have someone there at 10:30pm to shutdown the database?  If not, what
happens when it won't shutdown?  bummer.

Do you have someone there at 6:30am to start it up again?  If not, what happens
when it won't startup?  bummer.

This is WHEN, not IF, WHEN it won't startup/shutdown.

USE HOT.
Review
      & Followup

      Rating: 5
Cold
      backups?  October 28, 2002
Reviewer:  Trevor

Tom,
    If you get a minute, I would be very interested in knowing why you don't
like cold backups?
Regards
Trevor

          Followup:

Cold backups involve shutting down the database -- it shouldn't be shutdown.
You empty the buffer cache.  You empty the shared pool -- its all gotta be
filled back up again.

Worse yet -- no one does ATTENDED cold backups.  Guess what two statements get
stuck more then any others????

a) shutdown
b) startup

hmmm-- see also

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3151992035532

      Rating: 5
Cold
      backups?  October 28, 2002
Reviewer:  Trevor

Tom,
    If you get a minute, I would be very interested in knowing why you don't
like cold backups?
Regards
Trevor

October 28, 2002 Ami -- Thanks for the question regarding
      "Extents", version 8.1.6

      You Asked
Dear Tom,
    Hi, Please go thro' the following sql statements

PROJ>create table test ( a int, b char default '*')
  2  storage ( initial 40k next 40k pctincrease 50 minextents 5);

PROJ>
Wrote file afiedt.buf
  1  select extent_id, bytes, blocks
  2           from user_extents
  3           where segment_name = 'TEST'
  4             and segment_type = 'TABLE'
  5* ORDER BY EXTENT_ID
PROJ>/

   EXTENT_ID        BYTES       BLOCKS
------------ ------------ ------------
           0        40960            5
           1        40960            5
           2        81920           10
           3       122880           15
           4       163840           20

5 rows selected.

I have a db block size of 8kb, can you figure out for me,
how are the extents allocated, what are the factors that
oracle considers while allocating extents.

Thanks for your help

Ami

      and we said...
You have Oracle8i release 2.

Please stop -- just stop -- using dictionary managed tablespaces.  You will find
the mysteries of extents just "disappear" when you use locally managed
tablespaces!!!  You'll never never never use:

next
pctincrease
maxextents

ever again!!!!  No more fragmentation!!!  They are great.

In your case,

initial = 40k  (extent 1)

next    = 40k  (extent 2)

next next = 40+20 = 60k but we want number of blocks to be divisible by 5
generally so it went to 80k (10 blocks)

next next next = 60+30k = 90k but 120k is 15 8k blocks so we like that one
better....

next next next = 90+45k = 135k but 160k is 20 8k blocks so we like that one
better...

See

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c02block.htm#2846

it is actually documented in a very excellent manual that everyone should read
from cover to cover - the concepts guide!!

Review &
      Followup

      Rating: 4
thank you so
      much  October 28, 2002
Reviewer:  ami
      sutaria  from ahemedabad, India
Dear Tom,
   As a frequent visitor to this site, I know the reaction
 from you was expected, but this is just to clear the doubts.
  Interviews still pose questions on that.

Thanks for the great site. I hope you continue this great
work.


Subject | Article | Last Updated (East coast time)
Wrapper | article only | Oct 29, 2002 06:32 am
unable to insert text into clob column | article only | Oct 29, 2002 07:00 am
Bulk Binds | article only | Oct 29, 2002 07:09 am
Tracing for multiple sessions for single user | article only | Oct 29, 2002 01:48 pm
Inserting data from comma delimited text file into a table. | article only | Oct 29, 2002 02:30 pm
index usage | article only | Oct 30, 2002 06:37 am
about intelligent agent | article only | Oct 30, 2002 06:40 am
V$ Objects For DDL Statement | article only | Oct 30, 2002 06:45 am
Piggyback | article only | Oct 31, 2002 08:25 am
Tabular data | article only | Oct 31, 2002 08:32 am
Key pain areas for dba!! | article only | Oct 31, 2002 09:03 am
Dbms_Flashback | article only | Oct 31, 2002 04:23 pm
Trigger Code Control Sequencer Causing Deadlock | article only | Oct 31, 2002 04:29 pm
package privilege | article only | Oct 31, 2002 04:31 pm
How to enforce conditional unique on multiple columns | article only | Oct 31, 2002 08:40 pm
Cursor in Database Trigger | article only | Oct 31, 2002 08:59 pm
Use of Forms Commit | article only | Nov 01, 2002 06:37 am
Pl/sql table type declaration approaches, which is better. | article only | Nov 01, 2002 06:49 am
Export direct =y | article only | Nov 01, 2002 03:35 pm
Why is it said that global indexes are suitable for OLTP apps, and local indexes are suitable for DSS i.e. data warehousing apps | article only | Nov 01, 2002 03:44 pm
char(1) versus varcahr2(1) | article only | Nov 01, 2002 03:49 pm
Oracle DBA | article only | Nov 01, 2002 04:10 pm
Boolean Function | article only | Nov 01, 2002 05:41 pm
Generic Connectivity | article only | Nov 01, 2002 05:42 pm
10053 trace inconsistent with NOTE 68992.1 | article only | Nov 02, 2002 08:21 am
Cursor Reparse -- "freedom" of moving back and forth a result set | article only | Nov 02, 2002 06:36 pm
Optimize Time Consumming Function | article only | Nov 03, 2002 08:39 am
Partition views | article only | Nov 03, 2002 08:44 am
redo generation | article only | Nov 03, 2002 09:29 am
doubt in REFCURSOR | article only | Nov 03, 2002 09:34 am
Using a trigger to insert a sequence value in PK column | article only | Nov 03, 2002 11:16 am
Wait event: DB file sequential read | article only | Nov 03, 2002 11:21 am
Select top 10 values without rowid | article only | Nov 03, 2002 09:23 pm
Binary Tree | article only | Nov 04, 2002 08:21 am
Difference Between Cumulative and Incremental Export | article only | Nov 04, 2002 08:24 am
SQL ANALYZ | article only | Nov 04, 2002 08:37 am
pfile and undo tablespace | article only | Nov 04, 2002 08:48 am
Oracle concepts | article only | Nov 04, 2002 09:16 am
Insert /*+append */ statement | article only | Nov 04, 2002 09:39 am
row(s) 1 - 39 of 39
October 29, 2002 Ashiq -- Thanks for the question regarding "Wrapper", version 8.1.7
You Asked
Hi Tom,

     Oracle allowing us to wrap our codes for procedure,packages,etc..Why its
not allowing us to wrap our codes of triggers.Any specific reasons for
this??Since we used to write some  business logic in triggers.

Thanx,
and we said...
put the business logic in a package (where all code of any length rightly
belongs) and the problem is solved.

Triggers just are not "wrappable" but - since packages are and all real code
belongs in a package -- this is really OK.
Review & Followup
Rating: 4
Wrapable Views? October 29, 2002
Reviewer: Marc Blum from Aachen, Germany
Is there any possibility to hide the view text? Some very komplex views contain
highly sensible business logic. So how may I hide the view definition from
curious eyey?

Followup:
no way in the world.

it'll always be in the v$ tables -- even if you obscured it in the dictionary.

even if you used stored procedures with ref cursors and mangled the SQL in the
procedure - it would pop right up in the v$ tables.

I hope all views contain highly SENSIBLE business logic ;)  I would hate to see
the view that didn't!!!!

October 29, 2002 Rozario -- Thanks for the question regarding "unable to insert text into clob column", version 9.0.1
You Asked
I am trying to insert text data into a Clob column from SQLworksheet with the
following command
Insert into tabname values(1,'....text longer than 4K............');
The text is longer than 4K. Neither do I get any error message nor is the data
getting inserted. The system behaves as if the execute button has not been
clicked. When I enter the next command "select count(*) from tabname", I get the
output 0 rows selected.
I though that in Oracle 9i I can do direct insert without going thro a procedure
& DBMS_LOB.write etc. I need to do a simple SQL insert as my email parser can
execute only simple sql commands
and we said...
You really need to use bind variables and a proper programming language.  You
won't get very far at all with sqlworksheet or a simple thing that just lets you
execute UNBOUND (no bind variables!!) statements.

In fact, you could write this all in java entirely in the database -- you can
load the java mail api's right in there and get full access to already parsed
emails using pop, imap, whatever.

You can use simple SQL to insert a clob -- however CHARACTER STRING LITERALS in
sql are, have been and will continue to be limited wisely to 4k in size.  You
need to use bind variables for anything larger (well -- you need to use bind
variables period!!!)
Review & Followup
Rating: 5
Prompt reply and unambiguous October 29, 2002
Reviewer: Rozario P Mariadassou from Bangkok, Thailand
Hi Tom
Thanks for a prompt and categorical reply. Actually I was using SQL worksheet
only for testing. The actual command will be executed by a third party email
parser. However, I got yr point and will look for a soln on the email parser
side.

October 29, 2002 Kevin -- Thanks for the question regarding "Bulk Binds", version 8i
You Asked
Would like to look at an example of a program where data is pulled from one
table using a cursor and then inserted into another table using BULK BINDING.

If you could show what the program looks like before and then after using BULK
BINDING.

Assumption is that both tables have the same columns. This is for review before
updating the production table.
and we said...
Here is an example that reads from EMP and inserts into EMP2.  It array fetches
upto 5 rows at a time and inserts them.  You could of course use a number much
larger then 5 - i did that cause EMP is small.  Also, you might use arrays of
different types (arrays of dates and numbers) to avoid the implicit conversions
I'm doing in the demo.  You really only need 3 array types in general:

declare
    type myArray is table of varchar2(4000) index by binary_integer;

    l_enames myArray;
    l_empnos myArray;
    l_sals   myArray;

    cursor c1 is select empno, ename, sal from emp;
begin
    open c1;

    loop
        fetch c1 bulk collect into l_empnos, l_enames, l_sals
                  LIMIT 5;

        dbms_output.put_line( l_empnos.count || 'rows');

        .... your code here .....

        forall i in 1 .. l_empnos.count
            insert into emp2( empno, ename, sal )
            values
            ( l_empnos(i), l_enames(i), l_sals(i) );

        exit when c1%notfound;
    end loop;
    close c1;
end;
/

followup to comment one below

Well, you don't mention what % decrease in runtime 1 minute represents?

Using BULK binds will decrease the number of context switches between PLSQL and
the SQL engine.  In the "best case" this could be upto 5 times faster -- IF the
sql statements in question "run at the speed of light".  In most cases, this
will just be "faster".

For example, lets say in your example above -- the "insert into select"
statement consumes 80% of the runtime (just hypothesizing here). The BEST we
could expect would be less then a 20% decrease in runtime (the bulk operation is
NOT affecting the "insert into select" operation.

Your logic is:

update EVERY row in the table f_itmdss (fixed cost, bulk binds don't affect)

commit (fixed cost, bulk binds don't affect)

insert into that table select from another table (fixed cost, bulk binds don't
affect)

run a query (that look hugely expensive to run) updating each row in f_itmdss we
get a hit on. -- this is the only logic that will be affected by the bulk binds.

The context switch between PLSQL and SQL in this case represents an extremely
SMALL percentage of the total run time.

I can suggest a CHANGE in your existing logic that will most likely help you
here....

1) lose that commit -- it looks like a bug to me actually.  Why commit part way
through a transaction?  If the insert into select fails -- your f_itmdss table
will be left "nulled out".

2) change the logic to be:

insert all new rows from v_all_shops, f_item
update every row with a correlated subquery

instead of

update every row
insert new
update selected rows procedurally

The following two statements appear to me to be your procedural logic:

       insert into f_itmdss(item_no,organ_id)
       (select a.organ_id, b.item_no
          from v_all_shops a, f_item b
         where a.status='AC'
           and NOT exists (select 'X'
                             from f_itmdss
                            where item_no=b.item_no
                              and organ_id=a.organ_id));

        update f_itmdss
           set (ld_sales_qty,ld_sales_val,mtd_sales_qty,mts_sals_val) =
               (SELECT nvl(SUM(QUANTITY_SOLD),0),
                       nvl(SUM(NET_SALES_AMT),0),
                       NULL,
                       NULL
                  FROM D_SALESTX
                 WHERE DATE_KEY=TRUNC(SYSDATE-1)
                   and product_key = f_itmdss.item_no
                   and store_key = f_itmdss.organ_id );

That will add all new rows and then update EVERY row in the f_itmdss table
setting the 4 columns appropriately.
Review & Followup
Rating: 3
Bulk Updating without significant performance improvment August 02, 2001
Reviewer: Ringo Li from Hong Kong
I tried this bulk approach but the gain in performance didn't that great in our
testing

Original function:
Function F_UpdItemDSS (Vp_Date Date)
  Return Number
  As
    Vl_Result Number;
    Vl_Count  Number;
    Cursor C_SalesTx Is
       SELECT PRODUCT_KEY,STORE_KEY,SUM(QUANTITY_SOLD) QTY_SOLD,
          SUM(NET_SALES_AMT) SALES_AMT FROM D_SALESTX
          WHERE DATE_KEY=TRUNC(SYSDATE-1) GROUP BY PRODUCT_KEY, STORE_KEY;
  Begin
    Update f_itmdss
       set ld_sales_qty =0,
           ld_sales_val =0,
           MTD_SALES_QTY=NULL,
           MTD_SALES_VAL=NULL;
    commit;

    insert into f_itmdss(item_no,organ_id)
       (select a.organ_id, b.item_no from v_all_shops a, f_item b
          where a.status='AC' and
          NOT exists (select 'X' from f_itmdss where item_no=b.item_no
                      and organ_id=a.organ_id));

    For V_C_SalesTx In C_SalesTx Loop
      Update f_itmdss
        set ld_sales_qty=V_C_SalesTx.Qty_Sold,
            ld_sales_val=V_C_SalesTx.Sales_Amt
        Where item_no = V_C_SalesTx.Product_Key
            and organ_id = V_C_SalesTx.Store_Key;

=============
The Change:
==============
 Function F_UpdItemDSS (Vp_Date Date)
  Return Number
  As
    Vl_Result Number;
    Vl_Count  Number;
    type name_varray is table of varchar2(12) index by binary_integer;
    type number_varray is table of number(12,2) index by binary_integer;
    Cursor C_SalesTx Is
       SELECT PRODUCT_KEY,STORE_KEY,SUM(QUANTITY_SOLD) QTY_SOLD,
          SUM(NET_SALES_AMT) SALES_AMT FROM D_SALESTX
          WHERE DATE_KEY=TRUNC(SYSDATE-1) GROUP BY PRODUCT_KEY, STORE_KEY;
    v_PRODUCT_KEY  name_varray;
    v_STORE_KEY    name_varray;
    v_QTY_SOLD     number_varray;
    v_SALES_AMT    number_varray;
  Begin
    Update f_itmdss
       set ld_sales_qty =0,
           ld_sales_val =0,
           MTD_SALES_QTY=NULL,
           MTD_SALES_VAL=NULL;
    commit;

    insert into f_itmdss(item_no,organ_id)
       (select a.organ_id, b.item_no from v_all_shops a, f_item b
          where a.status='AC' and
          NOT exists (select 'X' from f_itmdss where item_no=b.item_no
                      and organ_id=a.organ_id));

    Open C_Salestx;
    Fetch C_Salestx bulk collect into v_PRODUCT_KEY, v_STORE_KEY, v_QTY_SOLD,
v_SALES_AMT;
    FORALL indx IN v_PRODUCT_KEY.first .. v_PRODUCT_KEY.last
       Update f_itmdss
          set ld_sales_qty=v_QTY_SOLD (indx),
              ld_sales_val=v_SALES_AMT (indx)
          Where item_no = v_PRODUCT_KEY (indx)
              and organ_id = v_STORE_KEY (indx);

We process around 5xxx rows but the gain is time is approx 1 minutes. Did I miss
something here ?

Rating: 5
regarding context switches in 9i October 29, 2002
Reviewer: A reader
Hi

since 9i SQL and PL/SQL uses same engine now so i guess bulk bind is not that
useful as it was in 8i (if reducing context switch was it´s goal)?

Followup:
same PARSER -- same sql parser.

still a context switch.

bulk binds are still and will be a good thing when you can use them.

October 29, 2002 Karen -- Thanks for the question regarding "Tracing for multiple sessions for single user", version 8.1.6
You Asked
I was asked to review performance issues with a Microsoft Visual C++/Delphi 7
application written to access an Oracle 8.1.6 database running on AIX (sorry
don't know the version).  I've got two related issues.

Once the user is logged in, OCX and various controls on the screens make
database connections to retrieve/process data.  In other words, one process ends
up with multiple sessions.  So, here's the problem... since there are multiple
sessions that are created by the various controls, if I want to trace the
activity of a single user I'd have to trace multiple sessions.  But when I start
a trace, all of the sessions aren't there yet (they open as various controls
make database connections/requests).  Each session has the same process but the
number of sessions related to a single process can range from 1-n (usually 5-6
max).  I was wondering if there's a way to trace all activity against a process
instead of tracing by session?  I can't recall seeing anything like that, but
it's quite maddening to try and trace sessions that come and go!

The second related issue is that one of the major performance killers is the
lack of use of bind variables.  I've never used their development tools and so
I'm unable to give them concrete examples of how to use bind variables in their
development lingo.  They also could benefit by writing procedures to return
resultsets in many cases.  Can you point me to a link or two where I can find
some related code examples to show them how to 1) use bind variables and 2) call
procedures which return resultsets in Microsoft Visual C++ syntax.

I've been able to find "snippets" but not enough good full examples to really
help.  I've done alot of looking and hoped you might just have some resources
close at hand so I could curtail this tedious search!

Many thanks in advance!
and we said...
What a terrible application design -- Oracle is not sqlserver, we do not have
their inherit limitations.  My first goal would be to try and figure out how to
fix the application so as to NOT open a session per little button or widget on a
screen.  Talk about wasting resources!

There is no concept of a "process" here.  We have a client opening umpteen
sessions and they are (as far as we can tell) 100% unrelated to eachother.

You could use a logon trigger perhaps, something like:

create trigger sql_trace after logon on database
begin
   if ( user = 'USER YOU WANT TO TRACE' )
   then
     execute immediate 'alter session set sql_trace=true';
   end if;
end;
/

make sure the OWNER of that trigger has "ALTER SESSION" granted directly to them
(not via a role).  I've used this "trick" many times in the past.

See
http://asktom.oracle.com/~tkyte/ResultSets/index.html
for all of the examples I have with ref cursors (using binds)...

MS Visual C++ is a language -- I assume they are using ODBC -- if so, they could
jsut read the documentation, ODBC fully supports the concept of binding.

(they have a HUGE performance killer in the form of all of those sessions --
thats a terrible, horrible -- but very "ms'ish" for whatever reason -- approach)
Review & Followup
Rating: 2
All users log in with same ID!! October 27, 2002
Reviewer: Karen Morton from Kodak, TN
Ooops....forgot to mention that all users of the app log in using the same
Oracle ID....I know...another "bad" idea, huh?  That's part of my problem....I
can't figure out "who" to trace.  If I put a trigger to trace USER, I trace
everybody.  But...how can I tell the trigger who they are?  Any ideas?  I can
usually figure out who they are by looking at v$session OS user and machine
columns...maybe use that info?

Also, I'd agree that the multiple sessions approach is very "ms'-ish" but am
being told there's no way around multiple sessions.  Do you suppose that's
really true?

Followup:
I do not believe that is true (the multiple sessions).  I believe it is
configurable, but not being an MS developer at all (prefering the ability to put
my code on any operating system) - I cannot answer completely.

You can try the discussion groups on otn.oracle.com -- there are ones that
center around things like this.

Without the application code helping you out (eg: being able to set a property
somewhere that says -- hey guys, after you connect, please turn on tracing), I
don't know what to tell you.  Given the design of the application -- i doubt
they gave any consideration to things like this and won't be setup to do it at
all.

(and yes, that "same oracle id" thing, gotta love that -- not)

Rating: 4
No choice = stubborn October 27, 2002
Reviewer: Jim from Portland, OR USA
You are experiencing the "tyranny of the specification".  Clearly this app has
an MS Sqlserver bent to it. 1 user  with multiple concurrent connections and
probably no transactions - "autocommit mode".   This is to get around the
locking limitations of sqlserver (server side cursors bad).  If one used
transactions multiple connections per user would drive a developer batty and end
up probably locking yourself up.  Abomination.

The developers need to spend a little time learning what a database is for and
why autcommit is not a good concept.

Rating: 4
Thoughts confirmed October 27, 2002
Reviewer: Karen Morton from Kodak, TN
Oracle did come along after SQL Server had already been used as the primary
database environment in this case.  I knew that there were differences, but I'm
admittedly surprised at how basically devastating following one development path
for one database can be so utterly performance killing when you try to implement
that design on another.  It's just hard to tell someone that they need to
re-design an entire app to accommodate their database choice!  Oh well....  This
has just helped me to confirm some re-design suggestions I thought needed to be
made.  Thanks

Rating: 5
Partial solution - IP address October 28, 2002
Reviewer: andrew from ca, usa
If you have the luxury of having a know IP to do the testing from, then you can
test for that IP in the logon trigger.
if SYS_CONTEXT ('USERENV','IP_ADDRESS') = '1.2.3.4' then...

Followup:
Excellent idea!  Variation on the "if user = the user"

Rating: 3
You could use IP address to enable tracing October 28, 2002
Reviewer: msc from Estonia
---
Ooops....forgot to mention that all users of the app log in using the same
Oracle ID....I know...another "bad" idea, huh?  That's part of my problem....I
can't figure out "who" to trace.  If I put a trigger to trace USER, I trace
everybody.
---

You could use SYS_CONTEXT ('USERENV', 'IP_ADDRESS') to enable SQL tracing in ON
LOGON trigger when client with specified IP address connects.

Rating: 3
October 29, 2002
Reviewer: Sagi from India
Hi All there,

A few of you gave a solution that is:

if SYS_CONTEXT ('USERENV','IP_ADDRESS') = '1.2.3.4' then...

But the problem is this works in Windows Environment. If you are on Unix
normally you to a telnet session. So the IP_ADDRESS would be same...

Regards,
Sagi

Followup:
did you even read the question?  its all about microsoft -- the cause of all of
all of the heartburn is because its all about microsoft.  If this was a unix
based application -- we would not even be having this discussion!

Rating: 3
October 29, 2002
Reviewer: Sagi from India
Hi Tom and Others,

I hope none is angry with me. I got lost in the middle of the question while
giving my comment. Sorry about that.

Yeap, SYS_CONTEXT ('USERENV', 'IP_ADDRESS') Would work since its MS.

Sorry for that once again.

Regards,
Sagi

Rating: 4
Delphi specific comment got lost? October 29, 2002
Reviewer: Holger Baer from Germany
Tom,

I read this thread earlier today and there was a Delphi specific comment on how
to turn on monitoring there. Now I can not see it any more - what happened?

Followup:
i think you mean this:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4254158186083
it was about delphi and tracing and recently updated

October 29, 2002 Nag -- Thanks for the question regarding "Inserting data from comma delimited text file into a table.", version 8.1.7
You Asked
Tom

A month back, inadvertently I delted some data from production and committed.
But fortunately for me, I had saved that data in a text file, (comma
delimited)(round about 1000 rows).

I tried using the following ways:

1.Sql loader:  but I didnt have access to the server on which the database
resides. I only had access to the network server through which we connect to the
database server( the database and network servers are unix servers, where as my
client is WIN2000).

2.COPY  the insert sql against the all the rows and insert all the rows.

No.1 didnt work due to the explained reasons.

No.2 worked , but when I copied all the 1000 insert statements, and pasted at
the sqlplus prompt, it was just doing 50 to 60 inserts and breaking. Why is this
so?

So I copied 50 insert statements at a time and pasted at the sqlplus prompt ,
and committed .

to sum up I have the following questions.

1.Was that a buffer problem with the sqlplus, that it did not all me to paste
all the 1000 insert statements. If you had 1000 insert statements in a file ,
how will you execute them at the sqlplus prompt.

2.Can you tell me a real convenient method to overcome these kind of situations
using pl/sql ( from both client and server sides).

3.Can we used the sqlloader only from the database server side.

Thank you
and we said...
number one should work -- sqlldr works just dandy over the network.

number two -- I don't know why your cut and paste buffer was getting blown out
but if you had them in a file -- why didn't you just:

SQL> @filename

that would work straight out.

PLSQL won't be too much help here.  All you really needed was sqlldr -- it would
have done it just fine.  Note that if you didn't have sqlldr and this was
important data -- why didn't you just give it to the admin on the server and ask
them to blow it in real quick?  Thats part of their job in a way -- to make sure
your business stays up and running after all.
Review & Followup
Rating: 5
October 13, 2001
Reviewer: A reader
While doing SQL> @filename

We should not have the semi colon after the end of each insert statement , Iam I
right.

Followup:
you should if you want to run it.  You either need:

insert into t values ( 1 );
insert into t values ( 2 );

or you need:

insert into t values ( 1 )
/
insert into t values ( 2 )
/

either one works.

BTW -- just had an after thought on this topic as well.  You'll love a new
feature in 9i called a flashback query.  Using that, this person would have been
able to open up a query AFTER they committed that "flashed back" to the point in
time right before they DELETED the data -- and just query it back out of the
database!  Instant recovery, no need to reload.

Rating: 5
load_data - that was pl/sql solution to load data , wasnt it October 13, 2001
Reviewer: Nag
'pl/sql will not be much help ', Well, I believe not, becasue ,I just found
something on your website,  you yourself  have advetised and advocated it
strongly in
http://asktom.oracle.com/pls/ask/f?p=4950:8:184310::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:464420312302,%7Binsert%7D%20and%20%7Bdelimited%7D
( The name of the  function is  load_data)

What purpose does load_data serve then.

In what context did you provide this solution(load_data) , and in what context
you said that pl/sql will not be much help ful.

Followup:
Well, you said

"but I didnt have access to the server on which the database resides."

that sort of precludes using that utility as it relies 100% on utl_file and
utl_file can ONLY read files on the server.

So, for you -- plsql won't be much help here as I said.

Load data is only useful if you have the file on the server.

Rating: 5
Fascinating feature October 13, 2001
Reviewer: Nag
BTW -- just had an after thought on this topic as well.  You'll love a new
feature in 9i called a flashback query.  Using that, this person would have been
able to open up a query AFTER they committed that "flashed back" to the point in
time right before they DELETED the data -- and just query it back out of the
database!  Instant recovery, no need to reload.

Tom, mindboggling ,overwhelming are the words which instatly come to my mind.

1.Can you illustrate the scenario described above, and also the sql clause which
will help flash back.

2.If oracle goes at this pace, it will be very difficult to catchup and be
uptodate. You comments please.

3.How is the flashback query managed internally. You have to answer this.

Followup:
1) Here is an annotated demo of this feature.  You can flashback either based on
the SCN (system change number) or based on time (time is within a 5 minute
window however -- not exact.  SCN is exact)

scott@ORA9I.WORLD> @demo012
=================================================================

We'll begin by creating a table of "keep scns"

As we run through our updates, we'll remember the scn in place when
when we began/ended.  We can use this to flash back to various points
in time

scott@ORA9I.WORLD>
scott@ORA9I.WORLD>
scott@ORA9I.WORLD> create table keep_scn( msg varchar2(25), scn number );

Table created.

=================================================================

Now, we'll update and commit the EMP table manmy times in a loop.  We
remember the system change number before we start and when we are done.
this way, we can flash back to the beginning, middle or end

remember each commit in the system will up the SCN, its our "clock"

scott@ORA9I.WORLD>
scott@ORA9I.WORLD> begin
  2      insert into keep_scn values ( 'start',
dbms_flashback.get_system_change_number );
  3
  4      for i in 1 .. 100
  5      loop
  6          update SCOTT.EMP set sal = sal * 1.01;
  7          commit;
  8      end loop;
  9
 10      insert into keep_scn values ( 'stop',
dbms_flashback.get_system_change_number );
 11      commit;
 12  end;
 13  /

PL/SQL procedure successfully completed.

=================================================================

Now, lets start flashing back.  We'll start with a flash back to the
beginning first:

scott@ORA9I.WORLD>
scott@ORA9I.WORLD> variable x refcursor
scott@ORA9I.WORLD> set autoprint on
scott@ORA9I.WORLD>
scott@ORA9I.WORLD>
scott@ORA9I.WORLD> declare
  2      l_scn number;
  3  begin
  4      select scn into l_scn from keep_scn where msg = 'start';
  5      dbms_flashback.enable_at_system_change_number( l_scn );
  6      open :x for select ename, sal from SCOTT.EMP;
  7      dbms_flashback.disable;
  8  end;
  9  /

PL/SQL procedure successfully completed.

ENAME             SAL
---------- ----------
SMITH             800
ALLEN            1600
WARD             1250
JONES            2975
MARTIN           1250
BLAKE            2850
CLARK            2450
SCOTT            3000
KING             5000
TURNER           1500
ADAMS            1100
JAMES             950
FORD             3000
MILLER           1300

14 rows selected.

=================================================================

Now we'll get the SCN that was in effect in the "middle" of update
and view the data at that point in time

scott@ORA9I.WORLD>
scott@ORA9I.WORLD> declare
  2      l_scn number;
  3  begin
  4      select trunc(avg(scn)) into l_scn from keep_scn;
  5      dbms_flashback.enable_at_system_change_number( l_scn );
  6      open :x for select ename, sal from SCOTT.EMP;
  7      dbms_flashback.disable;
  8  end;
  9  /

PL/SQL procedure successfully completed.

ENAME             SAL
---------- ----------
SMITH         1302.67
ALLEN         2605.36
WARD          2035.47
JONES         4844.33
MARTIN        2035.47
BLAKE         4640.79
CLARK         3989.45
SCOTT         4885.04
KING          8141.76
TURNER        2442.51
ADAMS         1791.19
JAMES          1546.9
FORD          4885.04
MILLER        2116.85

14 rows selected.

=================================================================

Now we'll get the SCN that was in effect at the end
and view the data at that point in time

scott@ORA9I.WORLD> declare
  2      l_scn number;
  3  begin
  4      select scn into l_scn from keep_scn where msg = 'stop';
  5      dbms_flashback.enable_at_system_change_number( l_scn );
  6      open :x for select ename, sal from SCOTT.EMP;
  7      dbms_flashback.disable;
  8  end;
  9  /

PL/SQL procedure successfully completed.

ENAME             SAL
---------- ----------
SMITH         2163.82
ALLEN         4327.72
WARD          3381.09
JONES         8046.79
MARTIN        3381.09
BLAKE         7708.73
CLARK         6626.78
SCOTT         8114.43
KING         13524.08
TURNER        4057.24
ADAMS         2975.28
JAMES         2569.53
FORD          8114.43
MILLER        3516.27

14 rows selected.

=================================================================

lastly, we'll flash back back to the start, open a cursor on the
data and then use that cursor to restore our data to that point in
time.  we'll rollback the work in effect

scott@ORA9I.WORLD>
scott@ORA9I.WORLD> declare
  2      cursor emp_cur is select empno, sal from SCOTT.emp;
  3      l_rec emp_cur%rowtype;
  4      l_scn number;
  5  begin
  6      select scn into l_scn from keep_scn where msg = 'start';
  7      dbms_flashback.enable_at_system_change_number( l_scn );
  8      open emp_cur;
  9      dbms_flashback.disable;
 10      loop
 11          fetch emp_cur into l_rec;
 12          exit when emp_cur%notfound;
 13          update SCOTT.emp set sal = l_rec.sal where empno = l_rec.empno;
 14      end loop;
 15      close emp_cur;
 16      commit;
 17  end;
 18  /

PL/SQL procedure successfully completed.

=================================================================

Just to see that it worked...

scott@ORA9I.WORLD>
scott@ORA9I.WORLD> select ename, sal from SCOTT.emp;

ENAME             SAL
---------- ----------
SMITH             800
ALLEN            1600
WARD             1250
JONES            2975
MARTIN           1250
BLAKE            2850
CLARK            2450
SCOTT            3000
KING             5000
TURNER           1500
ADAMS            1100
JAMES             950
FORD             3000
MILLER           1300

14 rows selected.

2) Well, thats a fact of everyday life for everything.  I teach a course
internally on new 9i features -- just a bullet on each with a demo here and
there to make it real (like the one above).  Its about 170 slides and takes
eight solid hours and it only touches on new features (and doesn't get them
all).  There is alot in htere thats for sure.

3) its done the same exact way we do a consistent read -- using the UNDO we have
to store to rollback queries in the first place.  In 9i, there are UNDO
tablespaces (no need for rollback segments anymore) where by you set up an undo
retention period of N minutes and that dictates how far back you can "flash
back" to.

Rating: 4
Flashback queries followup October 17, 2001
Reviewer: Rich from Ottawa, Ontario, Canada
Hi Tom,

Several places I've read mention something along the lines of "go back the
number of MINUTES you want".

Is the flashback functionality useful for reviewing data over a longer period of
time?  Eg: how many sales orders were there on the system at this time last
month.

If not, why not?

Thanks.

Followup:
The flashback query uses the UNDO information (aka rollback data).  In 9i, you
would set up an UNDO tablespace with a retention period.  This retention period
is  specified in minutes and tells us how long to hang onto rollback for (no
need for rollback segments, just an undo tablespace).

If you have the room for a months worth of UNDO online -- you could do it (i'll
not be able to test that for you however ;)

The flashback query is a very complete solution for a "smallish" window of time,
hours of time.  You could use it in conjunction with fine grained auditing
(another new 9i feature where by we capture the SQL, the bind variables AND the
SCN when you want us to).  With that information, you can see what they saw
since you can flashback.  So, its very useful for watching someone you suspect.
It is very useful to recover data you didn't mean to drop and so on.

If I had a need to go back to last month, I would consider a tool such as
Workspace Management.  Using that, I can simply create a "branch" of my data
every month and I'll have the ability to go back to that point in time whenever
I want.  This is accomplished by versioning records.  You can use this to
automagically get an audit trail if you like (we can keep the entire change
history of a record), you can use this to create versions of your data to "what
if", you can use this to create point in time snapshots of your data.  Your
application doesn't know this is happening (totally transparent).  See
http://download-west.oracle.com/otndoc/oracle9i/901_doc/appdev.901/a88806/toc.htm
for details.  It would be more applicable to what you wanted to do and would not
require having 30 days of UNDO online.

Rating: 5
October 18, 2001
Reviewer: A reader
excellent explanation on flashback query

Rating: 5
RE: Flashback queries followup October 17, 2001 October 18, 2001
Reviewer: Rich from Ottawa, Ontario, Canada
Thanks for the excellent explanation and alternate solution.

Rating: 5
concat first and second fields May 22, 2002
Reviewer: Robert from Scott AFB
Tom,

Can you concatenate the 1st two fields in a comma delimited infile and insert
them into the first column with sql*loader?

Rating: 4
October 29, 2002
Reviewer: A reader
you said
"This retention period
is  specified in minutes and tells us how long to hang onto rollback for (no
need for rollback segments, just an undo tablespace)."
in the Oracle documantaion they say you specify the retention in seconds is that
correct ?

Followup:
correct, typo on my part -- it is seconds.

October 30, 2002 NiTIN -- Thanks for the question regarding "index usage ", version 8.1.6
You Asked
There is a question regarding Index usage .
We are calculating index usage using
( table fetch by rowid / (table fetch by rowid + table scan rows gotten)
Iam told that the standard value of the index usage should be > 50 though i beg
to differ.
to achive this it is suggested to increase the value of
hash_multiblock_read_count in init.ora .
I am unable to understand how we can get the value of index usage more than 50
though presently we are getting value of 10.

If you could clarify the necessity for a higher value of index usage and how it
would impact my database performance.
Also Please suggest how can i increase the value of the same
and we said...
Geez, I think that ratio should be 80, no wait 72.345, no wait, it should really
be 100, umm, let me rethink that -- wait 5 is very good.

You are suffering from what is known as CTD.  Compulsive tuning disorder.

have you identified a performance issue?  If not, stop, you are done tuning.

if you have identified that you have an honest performance issue, you need to
identify the CAUSE of the performance issue.  Stop what you are doing and find
the root cause.  Don't look for "symtoms" and treat those.  That would be like
going to a doctor and saying "I have a headache and blurry vision" and having
them say "will take two aspirin and see the eye doctor for glasses" -- instead
of looking for the root cause (a brain tumor perhaps) and fixing it.

FULL SCANS ARE NOT EVIL (table scan rows gotten).

INDEXES DO NOT IMPLY, INFER, MEAN, or CONVEY "fast = true" (in fact the opposite
could be quite correct)

This ratio is as bad as "cache hit ratios".  I can make the above ratio become
any number I want -- does that mean my system is OK?  No, it doesn't.  See
www.hotsos.com and read the paper Are you still using cache hit ratios?

and just global change CACHE HIT to "index usage"

(also, by upping the hash multiblock read count - what effect would that have on
table scan rows gotten???)
Review & Followup
Rating: 4
This is what i wanted to hear actually September 26, 2002
Reviewer: Nitin from India
Just Clarification.

Followup:
Cool, then pass this onto the person who just wants to adjust ratios.  They need
help!

Rating: 4
rofl September 26, 2002
Reviewer: Dennis from Missouri, USA
Don't we all need help??  I'm a crazy mad fool!

Dennis

Rating: 4
Millsap's When to Use Index October 29, 2002
Reviewer: Mohsin from Unguja
Hi Tom:

I read and re-read the above paper from Cary Millsap.
Is his recommendation valid in a purely CBO env? From what I can see, it is
valid only in an RBO env.

Am I correct or have I missed the point completely ?

Your help will be greatly appreciated.

Mohsin

Followup:
I don't see the tie in between the referenced paper "still using cache hit
ratios" and indexes?  can you be more precise?

October 30, 2002 kobe -- Thanks for the question regarding "about intelligent agent", version 8.1.6
You Asked
Can you tell me what is "Intelligent Agent"?
Thanks a lot.
and we said...
The Intelligent Agent is a process that runs on remote node
within the
enterprise. The Agent's purpose is to provide the DBA with a
resource for
performing remote support for discovered Oracle services running
on the system.
The Agent can support more than one service on a single node.
For example, if
the node has two databases installed, a single Agent can support
both database
as well as the database listener, etc.

The agent is responsible for many tasks including:

  - Executing Jobs and Events on behalf of the Enterprise
Manager Console.
  - Providing a 'lights out' functionality for administring
services.
  - Returning the results of Jobs/Events to the correct
administrator.
  - Handling SNMP requests including trap generation.
Review & Followup
Rating: 3
Thank you . March 12, 2001
Reviewer: kobe from ShenZhen ,China
Thank you for your response,which makes me know what is Intelligent Agent.
Your service is so good .

Rating: 4
What makes Agent not start July 08, 2002
Reviewer: Abdal-Rahman Agha from Saudi Arabia
Hi Tom,

  When does Intelligent Agent not start in the services window. I mean if the
Agent gots the error:
Could not start the OracleOraHome81Agent service on Local Computer. Error
3221356562:

... and the error: O/S Error in starting service OracleOraHome81 Agent.

Thanks.

Followup:
could be this:

Article-ID:         <Note:201274.1>
Alias:              SOL:OSS218766
Circulation:        PUBLISHED (EXTERNAL)
Folder:             em.Iagent.V8
Topic:              Articles to be Refiled
Title:              Starting Intelligent Agent Fails with Error 3221356562
Document-Type:      PROBLEM
Updated-Date:       26-JUN-2002 12:40:42
Attachments:        NONE
Content-Type:       TEXT/X-HTML
Products:           242;
Platforms:          GENERIC;

    * fact: Oracle Intelligent Agent
    * fact: MS Windows NT
    * symptom: Error 3221356562 - Could not start the <oracle_home_name> agent
service
    * symptom: NL-00425: bad continuation line
* cause: SQLNET.ORA file is corrupt.

fix:

Examination of the SQLNET.ORA file reveals the header lines are not commented
out.

Perform the following on the database server:
1.  Edit the SQLNET.ORA file and comment out the header lines
2.  Start the agent.

Rating: 4
Configuring the Oracle Intelligent Agent October 29, 2002
Reviewer: Steve from Mtl
Hi Tom,

Platform: Solaris 8
Oracle:   9iR2 64 bit

We are using the Sun Management Center as the master SNMP agent
on our Node. The Sun MC console and server are on a separate node.

I am trying to have the Oracle Intelligent Agent talk to
the Sun MC master agent on our node which will then relay
the SNMP traps the Sun MC console/server node. Needless to
say, I am having a bit of trouble getting it going.
(I read the docs, it's still not clear for me).

First, the SUN MC documentation, these is a section titled:

   "Configuring a Legacy SNMP Agent as a Subagent of a
    Sun Management Center Agent"

Basically, we need to edit a Sun MC config file and add a block
similar to the following:

sa2 = {
 type = legacy
 persist = false
 snmpPort = "20001"
 errorAction = restart
 startCommand = "/usr/lib/snmp/mibiisa -p %port"
 stopCommand = "kill -9 %pid"
 pollInterval = 60
 pollHoldoff = 60
 oidTrees = 1.3.6.1.2.1
 snmpVersion = SNMPv1
 securityLevel = noauth
 securityName = espublic
 }

So the way I understand it, we need to put the commands
necessary to start the Oracle Intelligent agent in the above file.

Questions:
==========

1. There seem to be 3 processes on the Oracle side:

     start_peer -m   (start master peer only)
     start_peer -e   (start encap_peer only)
     start_peer -n   (start snmpd only).

   Are some/all of these 3 process the intelligent Agent or
   am I way out in left field here?

2. What is the difference between "agentctl start" and the start_peer script?

3. If I am using Sun MC as the master agent, which one
   of the above 3 would I need to start only the Oracle
   Intelligent agent? None? Some? All?

4. Could you give me a one liner as to the purpose of each of the above
   3 processes. (e.g. one is acting as master agent, ...)

5. What would be the start-up command I could use in Sun MC config file
   to start the Oracle Intelligent agent only.

Thanks Tom

Followup:
Please contact support for setup and configuration type information.  I've never
configured the SNMP stuff personally (i don't get an opportunity to install and
configure every product on every platform)

Rating: 5
November 05, 2003
Reviewer: A reader
Tom,

I have two installations of Oracle one is 9.2.0.1 and 9.2.0.3 on the same
server. I have started the agent of 9.2.0.3(only this agent is running ).

From my OEM management server when i try to discover node, it fails since it is
looking for agent of 9.2.0.1.

How can I modify so that OEM looks for agent that is running 9.2.0.3.

Thanks.

October 30, 2002 Alan -- Thanks for the question regarding "V$ Objects For DDL Statement", version 8.1.7
You Asked
Hi Tom,

Is there any v$ object that I can query to get the statistics of a DDL without
using TKPROF, to be specific CTAS with nologging? The reason why I'm asking this
is, I need to prove that DIRECT PATH INSERT is the same as with CTAS in an
archivelog mode, if we alter the table to nologging then do the direct load
(insert /*+ append */).

Thanks in advance.
and we said...
You can use runstats
http://asktom.oracle.com/~tkyte/runstats.html

ops$tkyte@ORA920.LOCALHOST> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5
  6      type rc is ref cursor;
  7      l_cur   rc;
  8  begin
  9      insert into run_stats select 'before', stats.* from stats;
 10
 11      l_start := dbms_utility.get_time;
 12      for i in 1 .. 1
 13      loop
 14                  insert /*+ append */ into t1 select * from all_objects;
 15      end loop;
 16      l_run1 := (dbms_utility.get_time-l_start);
 17      dbms_output.put_line( l_run1 || ' hsecs' );
 18
 19      insert into run_stats select 'after 1', stats.* from stats;
 20      l_start := dbms_utility.get_time;
 21      for i in 1 .. 1
 22      loop
 23                  execute immediate 'create table t2 as select * from
all_objects';
 24      end loop;
 25      l_run2 := (dbms_utility.get_time-l_start);
 26      dbms_output.put_line( l_run2 || ' hsecs' );
 27      dbms_output.put_line
 28      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 29
 30      insert into run_stats select 'after 2', stats.* from stats;
 31  end;
 32  /
398 hsecs
391 hsecs
run 1 ran in 101.79% of the time

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.LOCALHOST> select a.name, b.value-a.value run1, c.value-b.value
run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11   order by abs( (c.value-b.value)-(b.value-a.value))
 12  /

NAME                                 RUN1       RUN2       DIFF
------------------------------ ---------- ---------- ----------
...
LATCH.row cache enqueue latch       70985      70098       -887
LATCH.row cache objects            106079     104926      -1153
LATCH.library cache pin alloca       3262        850      -2412
tion

LATCH.library cache pin              6494       1715      -4779
LATCH.cache buffers chains         289971     284957      -5014
LATCH.shared pool                    7746       2462      -5284
LATCH.library cache                 10080       2932      -7148
LATCH.session allocation            33640       4778     -28862
STAT...redo size                    63068     104460      41392
...
95 rows selected.

ops$tkyte@ORA920.LOCALHOST>

CTAS did slightly more redo but the CTAS is less "latch intensive" which
probably doesn't matter in this case (you don't plan on running thousands of
these concurrently right..)
Review & Followup
Rating: 5
Additional Info Needed October 29, 2002
Reviewer: Alan from AL, USA
Can you provide to us what are the key things that we should look in the
run_stats and is there any docs that discusses/describes all the details of
run_stats. Thanks again Tom.

Followup:
The sole existing documentation for runstats is at the url referenced.  It is a
tiny script i wrote.

It simply shows three things:

a) elapsed time differences between two approaches.  This is good to know but
definitely NOT the important information

b) the difference in session stats (like parses, recursive sql calls) etc
between two approaches.  This is important -- the more you do, they more oracle
is made to do, the worse an approach is

c) the difference between the number of LATCHES takens.  This is important
stuff.  If you are comparing 2 approaches to a problem and you know the selected
approach will be run thousands of times a day by lots of users, you would go for
the one that takes less latches in general as latches are a serialization
device, serialization devices are also known as locks and lock inhibit
scalability and concurrency.

In the example above -- we were mainly interested in runtimes and stats
(unusual) for the sole reason that running insert /*+ APPEND */ vs CTAS is not
something you would do 10's of thousands of times in a day (maybe once a day or
a handleful of times but the latching won't be the big issue here, the stats and
runtimes are)

October 31, 2002 Fabio -- Thanks for the question regarding "Piggyback", version 9.2
You Asked
Helo, Tom.

Could you please explain what does the term "piggyback" apply for?

The meaning of this expression remains in the dark since i started studying
Oracle.

I've already asked it in some forums and I've got several different
explanations, so I'm a little confused.

Thanks for your help,

Cefers.br
and we said...
Main Entry: 1pig·gy·back
Pronunciation: 'pi-gE-"bak
Function: adverb
Etymology: alteration of earlier a pick pack, of unknown origin
Date: 1565
1 : up on the back and shoulders
2 : on or as if on the back of another; especially : on a railroad flatcar

It is used when one thing (process, routine, action, whatever) attaches itself
to another thing and they do the work together.

For example, Oracle will "piggyback commits" -- also known as group commits.
This means if 5 transactions all commit at about the same time -- instead of
doing 5 flushes of the redo log buffer to commit each one one at a time, we'll
piggyback (join together if you will) the 5 commits into one operation.
Review & Followup
Rating: 5
Impact October 22, 2002
Reviewer: Alexandre from Rio de Janeiro - Brazil
Is there any performance impact when "piggyback" happens due to conflict on
initialization parameters:
- FAST_START_IO_TARGET
- LOG_CHECKPOINT_INTERVAL
- LOG_CHECKPOINT_TIMEOUT
and checkpoint background process (when log switches)...

What view shows me "piggyback" is occuring on database?

Thanks a lot,

Followup:
No, none.  All of the commits would have to happen -- rather then do them one by
one by one -we just batch them up.

I'm not aware of any v$ information that shows the group commits, no.  In theory
you might be able to see this in a really high end system with tons of
concurrent activity by looking at redo writes and commits. If there was a lot of
piggybacking going on -- redo writes may be less then commits -- but it would be
really rare.

Rating: 4
make learning ORACLE a pleasure October 22, 2002
Reviewer: Ivy from NY, USA
Love love love this .....the way that you answer the question... by reading your
answer everyday not only gain the ORACLE knowledge but also learn your humor and
that's the way I like to learn everything in a very open minded way. very
appreciate the way that you teach us ORACLE.Thanks

Rating: 5
Piggyback October 22, 2002
Reviewer: Cefers.br (Fabio) from Brasil
Thanks for your clear explanation, Tom.

I understood it now.

Rating: 4
Redo entries!!! October 29, 2002
Reviewer: A reader
Tom,

Lets say i have redo entries in log buffer, if some other user issues commit,
then all the info from log buffer are sent to log files..Now if i commit after
sometime later, lets say after 30-40 secs, how will oracle record the entry in
log file..

Thanks

Followup:
it'll flush the rest of your stuff and the commit record for your transaction.

consider this -- redo log buffer = 512bytes.  You update 1meg of data...  the
redo buffer is constantly flushing itself well before you commit.  None of it is
"real" until your commit record makes it out there.

Rating: 4
October 29, 2002
Reviewer: A reader
Tom,

So will there be two entries for the same transaction one with commit and other
without it ??

Thanks.

Followup:
there could be thousands of redo entries for a transaction.  A commit record is
just a "type of entry".

October 31, 2002 -- Thanks for the question regarding "Tabular data", version 9.0.1
You Asked
Hi Tom

I have to provide, via the web, a "calendar" type view of some data.  I have two
table table_a which has location, meeting_date and category_id and table_b which
has category_id and category_name.  What I need to display is the months across
the top, the categories down the side and of course, the location and day in the
cells.

So:

             June              July             August
Sales        London 15/6      New York 22/7     Paris 13/8
                                                Barcelona 23/8
Marketing    Washington 22/6                    Toronto 25/8
etc etc.

Is there a way of doing this with one sql?  What I have at the moment does the
job, but it makes a couple of trips to the database does a couple of unions and
seems very messy!!!

Thanks

Steve
and we said...
Sure, grab my STRAGG user defined aggregate (search for stragg)

Change my separator in the code from ',' to chr(10) (new line) and:
ops$tkyte@ORA920.US.ORACLE.COM> column m1 format a20
ops$tkyte@ORA920.US.ORACLE.COM> column m2 format a20
ops$tkyte@ORA920.US.ORACLE.COM> column m3 format a20
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> variable start_month varchar2(20);
ops$tkyte@ORA920.US.ORACLE.COM> exec :start_month := '01-jun-2002'

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select category_name,
  2     stragg( decode( to_char(meeting_date,'MON'),
  3        to_char(add_months(to_date(:start_month,'dd-mon-yyyy'),0),'MON'),
  4               location || to_char(meeting_date,'dd/fmhh'), NULL)) m1,
  5     stragg( decode( to_char(meeting_date,'MON'),
  6        to_char(add_months(to_date(:start_month,'dd-mon-yyyy'),1),'MON'),
  7               location || to_char(meeting_date,'dd/fmhh'), NULL)) m2,
  8     stragg( decode( to_char(meeting_date,'MON'),
  9        to_char(add_months(to_date(:start_month,'dd-mon-yyyy'),2),'MON'),
 10               location || to_char(meeting_date,'dd/fmhh'), NULL)) m3
 11    from t1, t2
 12   where t1.category_id = t2.category_id
 13     and meeting_date between to_date( :start_month,'dd-mon-yyyy' )
 14               and add_months( to_date( :start_month, 'dd-mon-yyyy' ), 3 )
 15     and meeting_date <>
 16          add_months( to_date( :start_month, 'dd-mon-yyyy' ), 3 )
 17   group by category_name
 18  /

CATEGORY_NAME   M1                   M2                   M3
--------------- -------------------- -------------------- --------------------
Marketing       Washington22/6                            Toronto25/8
Sales           London15/6           New York22/7         Paris13/8
                                                          Barcelona23/8

If you needed more then 3 months, just copy the M3 column, change the 2 to 3 and
repeat.

Actually, you might change my separator to <br> and use:

  2     '<td>'||stragg( decode( to_char(meeting_date,'MON'),
  3        to_char(add_months(to_date(:start_month,'dd-mon-yyyy'),0),'MON'),
  4               location || to_char(meeting_date,'dd/fmhh'), NULL))
||'</td>'m1,

instead (or something similar -- should get you started tho)
Review & Followup
Rating: 5
Up the Ante September 25, 2002
Reviewer: Steve from UK
Tom

That was excellent - thank you.

Now, if I could impose on you a little bit more!!!  Suppose that table_a had a
meeting_start_date and a meeting_end_date, so that each meeting should appear in
every month that it is happening.  So for example, if the first London event in
June started on June 15th and ended August 4th then an entry for London should
appear in all three months.  The need to show the date would disappear in this
scenario though.

Would that be a "simple" progression of your example or something different?

Thanks

Steve

Followup:
ops$tkyte@ORA920.US.ORACLE.COM> select category_name,
  2     stragg( case when add_months(to_date(:start_month,'dd-mon-yyyy'),0)
  3                       between trunc(meeting_date,'month')
  4                   and add_months(trunc(meeting_end,'month'),1)-1/24/60/60
  5             then
  6                 decode( to_char(meeting_date,'MON'),
  7           to_char(add_months(to_date(:start_month,'dd-mon-yyyy'),0),'MON'),
  8                                 location || to_char(meeting_date,'dd/fmhh'),
  9                                 location || ' ..cont..' )
 10              else NULL
 11              end ) m1,
 12     stragg( case when add_months(to_date(:start_month,'dd-mon-yyyy'),1)
 13                       between trunc(meeting_date,'month')
 14                    and add_months(trunc(meeting_end,'month'),1)-1/24/60/60
 15             then
 16                 decode( to_char(meeting_date,'MON'),
 17           to_char(add_months(to_date(:start_month,'dd-mon-yyyy'),1),'MON'),
 18                                 location || to_char(meeting_date,'dd/fmhh'),
 19                                 location || ' ..cont..' )
 20              else NULL
 21              end ) m2,
 22     stragg( case when add_months(to_date(:start_month,'dd-mon-yyyy'),2)
 23                       between trunc(meeting_date,'month')
 24                    and add_months(trunc(meeting_end,'month'),1)-1/24/60/60
 25             then
 26                 decode( to_char(meeting_date,'MON'),
 27           to_char(add_months(to_date(:start_month,'dd-mon-yyyy'),2),'MON'),
 28                                 location || to_char(meeting_date,'dd/fmhh'),
 29                                 location || ' ..cont..' )
 30              else NULL
 31              end ) m3
 32    from t1, t2
 33   where t1.category_id = t2.category_id
 34     and meeting_date between to_date( :start_month,'dd-mon-yyyy' )
 35               and add_months( to_date( :start_month, 'dd-mon-yyyy' ), 3 )
 36     and meeting_date <>
 37          add_months( to_date( :start_month, 'dd-mon-yyyy' ), 3 )
 38   group by category_name
 39  /

CATEGORY_NAME   M1                   M2                   M3
--------------- -------------------- -------------------- --------------------
Marketing       Washington22/6       Washington ..cont..  Washington ..cont..
                                                          Toronto25/8

Sales           London15/6           New York22/7         Paris13/8
                                                          Barcelona23/8

Rating: 5
September 25, 2002
Reviewer: Steve from UK
Well I was almost getting there!!  ;-)

Is there any particular reason why you're using both CASE and DECODE statements?
 I thought you might use case exclusively?

Cheers

Steve

Followup:
Cause I've been using decode for 15 years and Case for lots less....

decode is a little more "terse" sometimes, mostly cause I just cut and pasted.

Rating: 5
September 25, 2002
Reviewer: Steve from UK
Sorry - but if t2 has an additional row - say Finance for which there are no
dates how can I show the column thus:

CATEGORY_NAME   M1        M2           M3
--------------- --------- ------------ ------------
Marketing       Washington             Toronto25/8
Sales           London    New York     Paris
                                       Barcelona23/8
Finance

Cheers

Steve

Followup:
outer join

 32    from t1, t2
 33   where t1.category_id(+) = t2.category_id
 34     and (meeting_date between to_date( :start_month,'dd-mon-yyyy' )
 35               and add_months( to_date( :start_month, 'dd-mon-yyyy' ), 3 )
 36     and meeting_date <>
 37          add_months( to_date( :start_month, 'dd-mon-yyyy' ), 3 )) or
meeting_date is null
 38   group by category_name

Rating: 5
September 26, 2002
Reviewer: Steve from UK
Excellent thank you.  It was the "or meeting_date is NULL" I was missing with
the outer join.  I think you closed your brackets too soon though:

32    from t1, t2
 33   where t1.category_id(+) = t2.category_id
 34     and (meeting_date between to_date( :start_month,'dd-mon-yyyy' )
 35               and add_months( to_date( :start_month, 'dd-mon-yyyy' ), 3 )
 36     and meeting_date <>
 37          add_months( to_date( :start_month, 'dd-mon-yyyy' ), 3 ) or
meeting_date is null)
 38   group by category_name

Rating: 5
Stragg October 29, 2002
Reviewer: Steve from UK
Hi Tom

We are aggreagting what seems to be a large amount of character data.  When
running a query using stragg I'm getting:
ERROR:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "USER.STRING_AGG_TYPE", line 24
ORA-06512: at line 1

no rows selected

I've even tried changing the VARCHAR2's in stragg to clob's but get the same
result.  Any ideas?

Thanks

Followup:
exceeded the 4000 character string limit in varchar2 when used in SQL.  SQL
understands "varchar2 means at most 4000 bytes".

You would have to change to a clob. goto groups.google.com and enter:

stragg clob dbms_lob group:comp.databases.oracle.* author:tkyte@oracle.com

in the search field.  I have a stragg that used clobs there. READ THE ENTIRE
THREAD please -- important caveats in there (eg: client must free clob else
you'll run out ultimately).

October 31, 2002 G -- Thanks for the question regarding "Key pain areas for dba!!", version 8.1.7
You Asked
Good Morning Tom!

Our company has moved the focus from management to Monitoring database.
Could u please list out some key pain areas for DBA; and the reports a database
admin will like to see for particular time period.

I have gone through your Expert one on one!! It’s really a great job. Have you
any plans to come with the second edition which talks specific to oracle 9i.

Any kind of help will be great.

Regards
G Singh

Tom,

Management of the database includes several task like taking backup, creating
user & other day to day DBA activity. On the other hand monitoring is giving him
an insight; what’s going in the database on a proactively basis. It’s just that
monitoring people will escalate the problem to the management team. Monitoring
will be done remotely not on site. Could you please tell me what are the key
areas in a database that should be monitored?  What kind of report will be dba’s
dream come true?

Any help will be great.

As every thing one can’t learn in book; Experience also counts a lot.

Regards
G Singh
and we said...
I would be using OEM and its monitoring capabilities -- or at least, some other
packaged off the shelf monitoring tool of choice.  In 2002, the system
management tools are fairly mature in this area, removing the need to script it
ourselves - and they all have hooks that allow you to extend them when needed.

That would be my suggestion -- you start looking at infrastructure tools -
systems management tools -- not at what queries you need to run/what things to
look for.
Review & Followup
Rating: 3
October 29, 2002
Reviewer: A reader

Rating: 5
OEM October 29, 2002
Reviewer: Connor McDonald from UK
Of course, with OEM, the time you save not writing SQL scripts is then spent
writing shell/batch scripts to keep the pesky agent for more than an hour at a
time...

:-)

Signed
Stuck-in-an-OEM-enabled-infrastructure

Rating: 5
Can statspack be used ? October 29, 2002
Reviewer: Arun Gupta from Harrisburg, PA USA
Tom
I am in an environment where client does not want to use OEM for reasons best
known to them. Though IA has been running on development and production servers
without problems for about 1.5 years now, they want to use third party tools for
monitoring. The problem is these tools do not generate any reports useful for
management. The information is for DBA use only. Someone came up the idea to use
statspack report for gathering database performance metrics. My questions is:

a) Does statspack have information which can be of use to management ?

Followup:
What information does "mgmt" want?

Rating: 5
Just a note for Conner... October 31, 2002
Reviewer: jan van mourik from Houston, TX
The 9i agents seems to be dependable, I haven't had trouble with them yet.
Getting them to run is easy too, the 8i agents gave me a lot of grieve in that
area, so much I kind of gave up on them. I even got the 9i agent to work on our
hp cluster :-)
Now I'm just trying to get *all* of our machines ready for 9i so I can run the
IA everywhere...

Rating: 5
question for jan van mourik October 31, 2002
Reviewer: Sean Bu from Atlanta
What is the OS? if it's Unix then:
what is the permission of dbsnmp? setuid root or setuid dba. Or non-setuid, do
jobs/events work for non-setuid? Are there other than accounts in the DBA group
exist?

October 31, 2002 Nag -- Thanks for the question regarding "Dbms_Flashback", version 8.1.7.2
You Asked
Tom

1.Are flash back queries useful for the developer or the DBA.

2.I observe that one needs the SCN  number to do a flash back query. How can I
as a developer get to know the SCN number of a transaction.

3.A single transaction can have multiple deletes and a single SCN number
identifying all of these deletes. What if I want to flash back only a single
individual delete.

Can yu explain with an example of flashback query.

Thank you
and we said...
1) both.  its a tool, it has uses for both.  eg: user says "i accidently deleted
all of my data at noon".  If user is developer, developer can flash back,
recover data and fix the problem.  If user is not database savy - DBA can flash
back, recover data and fix the problem.

The developer can use it in their applications.  "ok, here is the data BEFORE
you updated vs after your update".  A flash back query can help you there.

2)  dbms_flashback.get_system_change_number returns the current system SCN.  Log
Miner can be used to look back in time at various events to find SCN's as well.

3) You would flash back to the SYSTEM (not your transactions) SCN at that point
in time.  The SYSTEM has an SCN, your transaction has an SCN.  You care about
the SYSTEM SCN with flashback, not your transactions SCN.

And now for the demo.....

We'll begin by creating a table of "keep scns"

As we run through our updates, we'll remember the scn in place when
when we began/ended.  We can use this to flash back to various points
in time

tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM> create table keep_scn( msg varchar2(25), scn number
);

Table created.

tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM> set echo off
Enter to continue

=================================================================

Now, we'll update and commit the EMP table manmy times in a loop.  We
remember the system change number before we start and when we are done.
this way, we can flash back to the beginning, middle or end

remember each commit in the system will up the SCN, its our "clock"

tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM> begin
  2      insert into keep_scn values ( 'start',
dbms_flashback.get_system_change_number );
  3
  4      for i in 1 .. 100
  5      loop
  6          update SCOTT.EMP set sal = sal * 1.01;
  7          commit;
  8      end loop;
  9
 10      insert into keep_scn values ( 'stop',
dbms_flashback.get_system_change_number );
 11      commit;
 12  end;
 13  /

PL/SQL procedure successfully completed.

tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM> set echo off
Enter to continue

=================================================================

Now, lets start flashing back.  We'll start with a flash back to the
beginning first:

tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM> variable x refcursor
tkyte@TKYTE9I.US.ORACLE.COM> set autoprint on
tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM> declare
  2      l_scn number;
  3  begin
  4      select scn into l_scn from keep_scn where msg = 'start';
  5      dbms_flashback.enable_at_system_change_number( l_scn );
  6      open :x for select ename, sal from SCOTT.EMP;
  7      dbms_flashback.disable;
  8  end;
  9  /

PL/SQL procedure successfully completed.

ENAME             SAL
---------- ----------
SMITH             800
ALLEN            1600
WARD             1250
JONES            2975
MARTIN           1250
BLAKE            2850
CLARK            2450
SCOTT            3000
KING             5500
TURNER           1500
ADAMS            1100
JAMES             950
FORD             3000
MILLER           1300

14 rows selected.

tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM> set echo off
Enter to continue

=================================================================

Now we'll get the SCN that was in effect in the "middle" of update
and view the data at that point in time

tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM> declare
  2      l_scn number;
  3  begin
  4      select trunc(avg(scn)) into l_scn from keep_scn;
  5      dbms_flashback.enable_at_system_change_number( l_scn );
  6      open :x for select ename, sal from SCOTT.EMP;
  7      dbms_flashback.disable;
  8  end;
  9  /

PL/SQL procedure successfully completed.

ENAME             SAL
---------- ----------
SMITH         1289.77
ALLEN         2579.56
WARD          2015.32
JONES         4796.37
MARTIN        2015.32
BLAKE         4594.84
CLARK         3949.95
SCOTT         4836.67
KING          8867.24
TURNER        2418.33
ADAMS         1773.46
JAMES         1531.58
FORD          4836.67
MILLER        2095.89

14 rows selected.

tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM> set echo off
Enter to continue

=================================================================

Now we'll get the SCN that was in effect at the end
and view the data at that point in time

tkyte@TKYTE9I.US.ORACLE.COM> declare
  2      l_scn number;
  3  begin
  4      select scn into l_scn from keep_scn where msg = 'stop';
  5      dbms_flashback.enable_at_system_change_number( l_scn );
  6      open :x for select ename, sal from SCOTT.EMP;
  7      dbms_flashback.disable;
  8  end;
  9  /

PL/SQL procedure successfully completed.

ENAME             SAL
---------- ----------
SMITH         2163.82
ALLEN         4327.72
WARD          3381.09
JONES         8046.79
MARTIN        3381.09
BLAKE         7708.73
CLARK         6626.78
SCOTT         8114.43
KING         14876.49
TURNER        4057.24
ADAMS         2975.28
JAMES         2569.53
FORD          8114.43
MILLER        3516.27

14 rows selected.

tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM> set echo off
Enter to continue

=================================================================

lastly, we'll flash back back to the start, open a cursor on the
data and then use that cursor to restore our data to that point in
time.  we'll rollback the work in effect

tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM> declare
  2      cursor emp_cur is select empno, sal from SCOTT.emp;
  3      l_rec emp_cur%rowtype;
  4      l_scn number;
  5  begin
  6      select scn into l_scn from keep_scn where msg = 'start';
  7      dbms_flashback.enable_at_system_change_number( l_scn );
  8      open emp_cur;
  9      dbms_flashback.disable;
 10      loop
 11          fetch emp_cur into l_rec;
 12          exit when emp_cur%notfound;
 13          update SCOTT.emp set sal = l_rec.sal where empno = l_rec.empno;
 14      end loop;
 15      close emp_cur;
 16      commit;
 17  end;
 18  /

PL/SQL procedure successfully completed.

tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM> set echo off
Enter to continue

=================================================================

Just to see that it worked...

tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM> select ename, sal from SCOTT.emp;

ENAME             SAL
---------- ----------
SMITH             800
ALLEN            1600
WARD             1250
JONES            2975
MARTIN           1250
BLAKE            2850
CLARK            2450
SCOTT            3000
KING             5500
TURNER           1500
ADAMS            1100
JAMES             950
FORD             3000
MILLER           1300

14 rows selected.

tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM>
tkyte@TKYTE9I.US.ORACLE.COM> set echo off

Table dropped.
Review & Followup
Rating: 4
Reader January 27, 2002
Reviewer: Reader
Tom,
In 9.0.1, I can get the system SCN number dynamically
from  dbms_flashback.get_system_change_number.

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

SQL> select dbms_flashback.get_system_change_number from dual;

GET_SYSTEM_CHANGE_NUMBER
------------------------
                   79414

SQL> alter system switch logfile;

System altered.

SQL>  select dbms_flashback.get_system_change_number from dual;

GET_SYSTEM_CHANGE_NUMBER
------------------------
                   79415

How can I get this system SCN number in earlier
RDBMS versions (8.1.7)

Thanks

Followup:
totally, wholly undocumented and not supported method:

create global temporary table t ( x number );
create or replace function get_scn return number
as
    pragma autonomous_transaction;
    l_scn number;
begin
    insert into t values ( userenv('commitscn') ) returning x into l_scn;
    commit;
    return l_scn;
end;
/
select get_scn from dual;

search www.google.com under the usenet newsgroups for commitscn to get more
details on this quirky little function.

Rating: 5
Tom, you are THE BEST January 27, 2002
Reviewer: Suresh from New York

Rating: 5
Reader January 28, 2002
Reviewer: Reader
Thanks very much for the code and
the search URL as well

Rating: 3
dbms_flashback July 20, 2002
Reviewer: Chunhai He from USA
I can not try your demo.
The details are as follow.
What is requirement in order to use dbms_flashback
I use Oracle 9i version 1. I grant dbms_flashback to scott
  1  declare
  2          l_scn number;
  3      begin
  4          select scn into l_scn from keep_scn where msg = 'start';
  5          dbms_flashback.enable_at_system_change_number( l_scn );
  6          open :x for select ename, sal from SCOTT.EMP;
  7          dbms_flashback.disable;
  8*     end;
  9  /
declare
*
ERROR at line 1:
ORA-01466: unable to read data - table definition has changed
ORA-06512: at line 6

Followup:
looks like you just created the scott.emp table.  wait for about 5 minutes.

Rating: 5
Why is this 5 Mnt Wait ... July 21, 2002
Reviewer: A reader
Hi Tom,

Why Should i wait before i issue the Enable_At_time/scn ??

Regards,
Ganesh R

Followup:
No, you did some DDL on scott.emp, that is what that is saying.  You are
flashing back too far for that object.  You don't have the entire sqlplus
session (like I do) so I'm not sure what you really did or didn't do.

Rating: 3
Why is this 5 Mnt Wait -- July 21, 2002
Reviewer: Chunhai He from USA
Tom-
I did one more time. It still does not work.
This time I did not create any table. I did not have any
DDL.
I set  ALTER SYSTEM SET UNDO_RETENTION = 1800;
SQL> select * from emp;
select * from emp
              *
ERROR at line 1:
ORA-01466: unable to read data - table definition has changed

Followup:
You are trying to flash back to before the table was created.

Rating: 5
scn getting changed even no body is there.. August 23, 2002
Reviewer: Ramesh from Atlanta, GA, USA
after creating the get_scn function

even though except me no body is there, It is getting incremented without
commit.. incremented not by 1 it is incremented by sometimes it is 5 or 6 or 8

Why ?
Is it expected behavior ?

Followup:
somebody is commiting then.  look to your job queues or other background
processes

Rating: 3
no body is committing except in get_scn function. August 25, 2002
Reviewer: Ramesh from Atl, GA
No body is committing except  in get_scn function.
if only in get_scn funcion is getting committed then increment should be one..
Is there any Oracle Back groud process doing increment of SCN ?

08:23:17 sysadm@BWYHRDEM SQL> select username from v$session where
status='ACTIVE'
08:23:45   2  and username is not null;

USERNAME
------------------------------
SYSADM

Elapsed: 00:00:00.00
08:23:52 sysadm@BWYHRDEM SQL> show parameters job

NAME                                 TYPE    VALUE
------------------------------------ ------- ------------------------------
job_queue_interval                   integer 60
job_queue_processes                  integer 0
08:23:58 sysadm@BWYHRDEM SQL>
08:23:58 sysadm@BWYHRDEM SQL> select get_scn from  dual;

   GET_SCN
----------
    852968

Elapsed: 00:00:00.01
08:24:03 sysadm@BWYHRDEM SQL> /

   GET_SCN
----------
    852974

Elapsed: 00:00:00.02
08:24:05 sysadm@BWYHRDEM SQL> /

   GET_SCN
----------
    852980

Elapsed: 00:00:00.01

Followup:
Must be.  and don't do just ACTIVE, look at all sessions. If you have other
sessions connected and they just happen to commit, they'll bump the SCN (heck,
just a session exiting from SQL*plus will bump it since plus commits on exit).

Why are you worried about it?  The SCN is a number(38).  It would take
3162315320785266140457397288 years to exhaust them -- even generating many
hundreds per second (that is 1,000 / second forever ).

Rating: 4
Only one session August 25, 2002
Reviewer: Ramesh from Atl,GA
I am not worrying about the SCN limit..
But the thing is , even though I am only man connected in the database ( No body
is connected)..
HOw the SCN got incremented more than one.. ( Please try in your system)

14:41:39 sysadm@BWYHRDEM SQL> select username, status from v$session;

USERNAME                       STATUS
------------------------------ --------
                               ACTIVE
                               ACTIVE
                               ACTIVE
                               ACTIVE
                               ACTIVE
                               ACTIVE
SYSADM                         ACTIVE

7 rows selected.

Elapsed: 00:00:00.02
14:41:47 sysadm@BWYHRDEM SQL> select get_scn from dual;

   GET_SCN
----------
    852994

Elapsed: 00:00:00.04
14:41:55 sysadm@BWYHRDEM SQL>
14:41:56 sysadm@BWYHRDEM SQL> /

   GET_SCN
----------
    853000

Elapsed: 00:00:00.02
14:41:57 sysadm@BWYHRDEM SQL> /

   GET_SCN
----------
    853006

Elapsed: 00:00:00.01
14:41:58 sysadm@BWYHRDEM SQL> /

   GET_SCN
----------
    853012

Elapsed: 00:00:00.02

Followup:
did you notice that it always goes by 6.

Mine goes by two (as I expect).

Not actually seeing the code you are running, not knowing the version, the OS,
etc.... Nope, cannot answer you.  I wouldn't worry about it personally (or you
could use log miner yourself to see whats up)

Rating: 4
scn incrementing 6 August 25, 2002
Reviewer: Ramesh from Atl,GA
yes you are right..
It is getting incremented by 6.. Why ?

OS : Sun OS 5.8
DB 8.1.7.3

Theory says its shoud be incremented by 1.. But it is getting incremented by 6
appp02:BWYHRDEM:/homes/oracle> sqlplus

SQL*Plus: Release 8.1.7.0.0 - Production on Sun Aug 25 21:06:21 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Enter user-name: sysadm
Enter password:

Connected to:
Oracle8i Enterprise Edition Release 8.1.7.3.0 - Production
With the Partitioning option
JServer Release 8.1.7.3.0 - Production

21:06:27 sysadm@BWYHRDEM SQL>
21:06:28 sysadm@BWYHRDEM SQL> drop table scn_t;

Table dropped.

Elapsed: 00:00:00.28
21:06:35 sysadm@BWYHRDEM SQL> create table scn_t( scn_n number, scn_d date);

Table created.

Elapsed: 00:00:00.03
21:06:54 sysadm@BWYHRDEM SQL> begin
21:07:00   2  for i in 1..10
21:07:13   3  loop
21:07:15   4  insert into scn_t select get_scn, sysdate from dual;
21:08:03   5  end loop;
21:08:07   6  end;
21:08:09   7  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.12
21:08:11 sysadm@BWYHRDEM SQL> select scn_n, to_char(scn_d, 'hh:mi:ss') scn_time
from scn_t;

     SCN_N SCN_TIME
---------- --------
    853737 09:08:10
    853743 09:08:10
    853749 09:08:10
    853755 09:08:11
    853761 09:08:11
    853767 09:08:11
    853773 09:08:11
    853779 09:08:11
    853785 09:08:11
    853791 09:08:11

10 rows selected.

Elapsed: 00:00:00.01

Even though SCN has very large limit.. Just curious why it is incremented by 6

Followup:
theory (and practice) says 2, but -- you are on 8173 - which I no longer have.
try it on another supported release like 8174 (what I used).

Still, haven't seen YOUR get_scn ;)  cut and paste -- if I had a nickel for
everytime someone cut and pasted my code (but it is exactly the same they say)
and they MODIFIED it somehow -- well, I'd be very rich.

still -- hardly curious myself but -- did you even try logminer???  you seem
very curious yourself -- log miner might tell you LOTS.

8172:

ops$tkyte@ORA8I.WORLD> create global temporary table t ( x number );

Table created.

ops$tkyte@ORA8I.WORLD> create or replace function get_scn return number
  2  as
  3      pragma autonomous_transaction;
  4      l_scn number;
  5  begin
  6      insert into t values ( userenv('commitscn') ) returning x into l_scn;
  7      commit;
  8      return l_scn;
  9  end;
 10  /

Function created.

ops$tkyte@ORA8I.WORLD> select get_scn from dual;

       GET_SCN
--------------
 6539836230307

ops$tkyte@ORA8I.WORLD> select get_scn from dual;

       GET_SCN
--------------
 6539836230309

ops$tkyte@ORA8I.WORLD> select get_scn from dual;

       GET_SCN
--------------
 6539836230311

ops$tkyte@ORA8I.WORLD> select get_scn from dual;

       GET_SCN
--------------
 6539836230313

ops$tkyte@ORA8I.WORLD> select get_scn from dual;

       GET_SCN
--------------
 6539836230315

8174:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create global temporary table t ( x number );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function get_scn return
number
  2  as
  3      pragma autonomous_transaction;
  4      l_scn number;
  5  begin
  6      insert into t values ( userenv('commitscn') ) returning x into l_scn;
  7      commit;
  8      return l_scn;
  9  end;
 10  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select get_scn from dual;

           GET_SCN
------------------
     6532309038283

ops$tkyte@ORA817DEV.US.ORACLE.COM> select get_scn from dual;

           GET_SCN
------------------
     6532309038285

ops$tkyte@ORA817DEV.US.ORACLE.COM> select get_scn from dual;

           GET_SCN
------------------
     6532309038287

ops$tkyte@ORA817DEV.US.ORACLE.COM> select get_scn from dual;

           GET_SCN
------------------
     6532309038289

ops$tkyte@ORA817DEV.US.ORACLE.COM> select get_scn from dual;

           GET_SCN
------------------
     6532309038291

Rating: 5
error while using dbms_flashback August 26, 2002
Reviewer: Ashwani Singh from India
Dear Tom,
    The example given by u is great. I my case using dbms_flashback is ginving
me an error.

"PLS-00201: identifier 'DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER' must be
declared"

Kindly, provide some solutions for the above stated problem,

Regards and thanx in advance,

Ashwani

Followup:
sounds like you are using 8i and this is new 9i functionality.

Rating: 5
Problem with DBMS_FLASHBACK August 26, 2002
Reviewer: aa from India
Hi Tom ,
I am using Oracle8i Enterprise Edition Release 8.1.7.0.0 .
WHEN i run this block of code

  1  declare
  2          l_scn number;
  3      begin
  4          select scn into l_scn from keep_scn where msg  = 'start';
  5          dbms_flashback.enable_at_system_change_number( l_scn );
  6          open :x for select ename, sal from SCOTT.EMP;
  7          dbms_flashback.disable;
  8*     end;
  9  /
        dbms_flashback.enable_at_system_change_number( l_scn );
        *
ERROR at line 5:
ORA-06550: line 5, column 9:
PLS-00201: identifier 'DBMS_FLASHBACK.ENABLE_AT_SYSTEM_CHANGE_NUMBER' must be
declared
ORA-06550: line 5, column 9:
PL/SQL: Statement ignored
ORA-06550: line 7, column 9:
PLS-00201: identifier 'DBMS_FLASHBACK.DISABLE' must be declared
ORA-06550: line 7, column 9:
PL/SQL: Statement ignored
Get this error.
Does Oracle  8.1.7.0 support DBMS_FLASHBACK package.
I could not find out this package in Oracle Documentation(Oracle Supplied
Packages).
Thanks & regards,
A Reader

Followup:
it is new with 9i (read the first comment in this article, i answer there how to
get this info in 8i -- however FLASHBACK QUERY itself is new with 9i)

Rating: 5
scn incrementing by 2 August 26, 2002
Reviewer: Ramesh from ATL,GA
I tried with log_miner..
Now i am seeing increment of two only..
371720560 INSERT insert into "SYSADM"."SCN_T"("SCN_N","SCN_D") values
(371720559,TO_DATE('26-AUG-2002 14:19:38', 'DD-MON-YYYY HH24:MI:SS'));

 371720561 START set transaction read write;

 371720561 INSERT insert into "SYSADM"."T"("X") values (371720561);

 371720561 UPDATE update "SYSADM"."T" set "X" = 371720561 where ROWID =
'AAALIfAAjAAABRxAAQ';

 371720562 COMMIT commit;
increment 1 for start and increment for commit.

thanks for your quick response..
You are the best..

Ramesh

Rating: 5
why this number is getting increased when there is no dml taking place October 30, 2002
Reviewer: A reader
Hi Tom I've just a new session and queied scn number. It is getting incremented,
It is only me who is connected to the database as it on my local PC.

SQL*Plus: Release 9.2.0.1.0 - Production on Wed Oct 30 14:28:16 2002

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

Connected to:
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production

SQL> select dbms_flashback.get_system_change_number from dual;

GET_SYSTEM_CHANGE_NUMBER
------------------------
                19272452

SQL> /

GET_SYSTEM_CHANGE_NUMBER
------------------------
                19272453

SQL> /

GET_SYSTEM_CHANGE_NUMBER
------------------------
                19272453

SQL> /

GET_SYSTEM_CHANGE_NUMBER
------------------------
                19272453

SQL> /

GET_SYSTEM_CHANGE_NUMBER
------------------------
                19272453

SQL> /

GET_SYSTEM_CHANGE_NUMBER
------------------------
                19272454

SQL> /

GET_SYSTEM_CHANGE_NUMBER
------------------------
                19272454

SQL> /

GET_SYSTEM_CHANGE_NUMBER
------------------------
                19272454

SQL> /

GET_SYSTEM_CHANGE_NUMBER
------------------------
                19272454

SQL> /

GET_SYSTEM_CHANGE_NUMBER
------------------------
                19272454

SQL> /

GET_SYSTEM_CHANGE_NUMBER
------------------------
                19272454

SQL> /

GET_SYSTEM_CHANGE_NUMBER
------------------------
                19272454

SQL> /

GET_SYSTEM_CHANGE_NUMBER
------------------------
                19272454

SQL> /

GET_SYSTEM_CHANGE_NUMBER
------------------------
                19272455

SQL> /

GET_SYSTEM_CHANGE_NUMBER
------------------------
                19272455

SQL> /

GET_SYSTEM_CHANGE_NUMBER
------------------------
                19272455

SQL>

Followup:
job queues, advanced queues, background processes.  They are all out there.

Rating: 5
Simply Wonderful! July 30, 2003
Reviewer: Jim from MA

Rating: 3
possible cause of ORA-01466: November 06, 2003
Reviewer: dzmnd from the Netherlands
The following information I found on METALINK might explain why some people get
"ORA-01466: unable to read data - table definition has changed"

Limitations of Flashback query:
--------------------------------

1. Specifying a time will only find the flashback copy to the nearest five
minute interval. This is also true of the get_system_change_number.

(
http://metalink.oracle.com/metalink/plsql/ml2_documents.showDocument?p_database_id=NOT&p_id=174425.1
Try waiting five minutes after creating the table.

October 31, 2002 Leah -- Thanks for the question regarding "Trigger Code Control Sequencer Causing Deadlock", version version 8.1.5
You Asked
We have two tables with the following definitions (I am omitting
some of the fields for brevity):

create table shop
(   shop_id   number(20), /* primary key */
    order_seq_nbr number(20) );

create table order
(   sid     number(20), /* primary key - from legacy design */
    shop_id number(20), /* foreign key to shop_id of shop
table*/
    order_number number(20) );

The trigger definition is as follows:

create or replace trigger trigger_name
before insert order
for each row
declare
   v_ord_seq_nbr  shop.order_seq_nbr%type;
begin
   select s.order_seq_nbr from shop s
     into v_ord_seq_nbr
   where s.shop_id = :new.shop_id
   for update;
   :new.order_number := v_ord_seq_nbr + 1;
   update shop s
     set s.order_seq_nbr = v_ord_seq_nbr
   where s.shop_id = :new.shop_id;
end;

This trigger is causing deadlocks on shop table and consequently
on order and other related tables. I can see that the for update
may be causing the locks but it seems like if it was taken out,
this should be okay.
I have recommended using a sequencer but from business
requirements, potentially the number of shops can grow to
hundred thousands and to keep a sequencer per shop was a
concern. The order number has to be unique for each shop.

Thanks in advance,
Leah
----------------------------------------------
Hi Tom,
I believe the business requirement is that an order number
should not be unique for each shop. I didn't say the requirement
correctly above. These shops are small businesses and would
normally have order numbers starting from a min value. So shop 1
can have order numbers 1, 2, 3, etc. and shop 2 can have thesame
order numbers as well.

Thanks,
Leah
and we said...
Yes -- but do thet HAVE to.

I would 100% infinitely definitely totally prefer a sequence in
all cases.  But....  saying you really won't do that.... here is
how to do this.

The answer is an autonomous transaction.  It'll greatly increase
your concurrency here as well -- no longer will you totally
serialize on the SHOP table.  The transactions to get and
increment a value in there will be very small, short and fast.
Here is how it would look:

ops$tkyte@ORA8I.WORLD> create table shop
  2  ( shop_id        number primary key,
  3    order_seq_nbr  int default 0
  4  )
  5  organization index
  6  /

Table created.

I recommand an IOT for this -- index organized table.  No need
for a "real" table in this case

ops$tkyte@ORA8I.WORLD> create or replace
  2  function get_seqno( p_shop_id in number )
  3  return int
  4  is
  5      PRAGMA AUTONOMOUS_TRANSACTION;
  6      l_seqno number;
  7  begin
  8      update shop
  9         set order_seq_nbr = order_seq_nbr + 1
 10       where shop_id = p_shop_id
 11      RETURNING order_seq_nbr into l_seqno;
 12
 13      if (sql%rowcount = 0)
 14      then
 15          l_seqno := 1;
 16          insert into shop ( shop_id, order_seq_nbr )
 17          values ( p_shop_id, l_seqno );
 18      end if;
 19
 20      COMMIT;
 21
 22      return l_seqno;
 23  end;
 24  /

Function created.

The pragma makes this an autonomous transaction.  This is a
child transaction wholly independent from the parent - it might
as well be in another session.  We must commit before we return
but it ONLY commits the work it does -- not what the parent has
done or is doing.  Thats the key here. We can call the above
from a trigger even though it commits -- because its autonomous.
 This does mean however that a ROLLBACK in the parent WILL NOT
rollback this update -- your "order numbers" will not be gap
free anymore (they should not need be anyway).

Now for the trigger:

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> create table orders
  2  ( shop_id   number,
  3    orderno   int,
  4    data      varchar2(25)
  5  )
  6  /

Table created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> create or replace trigger trigger_name
  2  before insert on orders
  3  for each row
  4  begin
  5      :new.orderno := get_seqno( :new.shop_id );
  6  end;
  7  /

Trigger created.

and a quick test:

ops$tkyte@ORA8I.WORLD> insert into orders ( shop_id, data )
  2  values ( 1, 'Hello' );

1 row created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> insert into orders ( shop_id, data )
  2  values ( 1, 'Hello There' );

1 row created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> insert into orders ( shop_id, data )
  2  values ( 1, 'How are you' );

1 row created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> insert into orders ( shop_id, data )
  2  values ( 2, 'goodbye!' );

1 row created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> select * from orders;

   SHOP_ID    ORDERNO DATA
---------- ---------- -------------------------
         1          1 Hello
         1          2 Hello There
         1          3 How are you
         2          1 goodbye!

This should remove any deadlocks you are getting around the
SHOP table due to people going after different rows (by shop_id)
in a random order.  It'll also increase the concurrency of your
application as well.

The correct answer to this question however is STILL a sequence.
 An order number is an order number is an order number.  It is
just a number, not a hit counter -- if I get orders 5, 55,
10032, 234243, and 4234523 -- is that any different then if I
get orders 1, 2, 3, 4, and 5?
Review & Followup
Rating: 5
This really does happen... October 15, 2001
Reviewer: Steve Rock from Phoenix AZ, USA
I totally agree that a sequence is the best answer, however...

Like the original requester, I too encountered this identical requirement that
would not change no matter how hard we pushed back or demonstrated it to not be
of significant business value.  The user committee emphatically stated that they
MUST have site specific order numbers.  Eventually, they indicated that the
staff in the field sites liked to line up their printed orders in sequence on
their work area and would not be able to spot a gap if they were not in
sequence.

The fact that we were giving them an automated desk that displayed all their
orders for them was not sufficient to sway them.  Value is in the eye of the
beholder...

Thanks again for your clearly stated solutions!

Rating: 5
Sequence in parent - GAP FREE and INCREMENTING... October 30, 2002
Reviewer: pasko from HH,Germany
Hi Tom ,
I saw the following  code in Oracle 9i2 Schema Examples .

CREATE TABLE orders
( order_id NUMBER(12)
, order_date TIMESTAMP WITH LOCAL TIME ZONE
  CONSTRAINT order_date_nn NOT NULL
, order_mode VARCHAR2(8)
, customer_id NUMBER(6) CONSTRAINT order_customer_id_nn NOT NULL
, order_status NUMBER(2)
, order_total NUMBER(8,2)
, sales_rep_id NUMBER(6)
, promotion_id NUMBER(6)
, CONSTRAINT order_mode_lov
  CHECK (order_mode in ('direct','online'))
, constraint order_total_min
  check (order_total >= 0)
) ;

CREATE UNIQUE INDEX order_pk
ON orders (order_id) ;

CREATE TABLE order_items
( order_id NUMBER(12)
, line_item_id NUMBER(3) NOT NULL
, product_id NUMBER(6) NOT NULL
, unit_price NUMBER(8,2)
, quantity NUMBER(8)
) ;

CREATE UNIQUE INDEX order_items_pk
ON order_items (order_id, line_item_id) ;

ALTER TABLE order_items
ADD ( CONSTRAINT order_items_pk PRIMARY KEY (order_id, line_item_id)
);

ALTER TABLE order_items
ADD ( CONSTRAINT order_items_order_id_fk
FOREIGN KEY (order_id)
REFERENCES orders(order_id)
ON DELETE CASCADE
enable novalidate
) ;

CREATE OR REPLACE TRIGGER insert_ord_line
BEFORE INSERT ON order_items
FOR EACH ROW
DECLARE

new_line number;

BEGIN

SELECT (NVL(MAX(line_item_id),0)+1) INTO new_line
FROM order_items
WHERE order_id = :new.order_id;

:new.line_item_id := new_line;

END;
/

I think from this Example :

order_items.line_item_id
would always be INCREMENTING and GAP FREE
for each  order_id.

Is that so , regardless of how many people are inserting Orders ?

Regards

Followup:
it would be incrementing within a ORDER_ID.

it might or might not be gap free -- depends, if I delete a row, gaps = yes.

It is (IMHO)

a) a bad practice

b) subject to some nastly concurrency issues -- two people inserting a line item
for the same order at the same time will collide.  One will continue on -- the
other will be blocked until the first commits.  At that point, the second user
will get an "error ora-00001"

I would simply use a line_order_sequence sequence personally.  It matters not if
the line item numbers start at 1 and go up for each order or start at 10000001
and go up - as long as they are "sortable".

October 31, 2002 Charlie -- Thanks for the question regarding "package privilege", version 8.1.7
You Asked
Hi Tom,

I wrapped several stored procedures in a package and grant execute privilege on
this package to a role, then everybody having this role would be able to run ANY
PROCEDURES IN THIS PACKAGE.  This is not exactly what I want.  Is there any
CLEAN way to do the following:

create or replace package pkg_my
as
    procedure a;
    procedure b;
    procedure c;
end;

grant execute on pkg_my.A to USER1;  -- illegal
grant execute on pkg_my.B to USER2;  -- illegal
....

I want to be able to assign different execute privilege against different
procedures IN A PACKAGE to different users.

Thanks,
and we said...
You'll need two packages in order to segregate them out.

The PACKAGE it THE OBJECT.  It is what can be granted on (consider the confusion
of:

create package p
as
  procedure a;
  procedure a( x in date );
  procedure a( x in number );
end;

grant execute on p.a to ...

now, what does that mean?  In anycase, you cannot do it.
Review & Followup
Rating: 5
how to create two package ? October 30, 2002
Reviewer: A reader
I already have one package with 10 procs.

 how can I create another pkg that so that I can grant only
5 of them to the another user ?

Followup:
umm, just create another package?  not sure what you are asking here really.

October 31, 2002 Lucy -- Thanks for the question regarding "How to enforce conditional unique on multiple columns", version 8.1.6
You Asked
Hi, Tom,
I have a table
create table project (project_ID number primary key,
                teamid number,
                job varchar2(100),
                status number(1));

status=1 means it is an active project, otherwise it is archived,
I was told to enforce a unique rule: the job has to be unique in the same teamid
for the active projects, it means teamid and job have to be unique while
status=1, what is the best way to do this?

What happens if the job can be NULL?

Thanks
and we said...
Here is one method.  We can use a function based index on a function:

ops$tkyte@ORA8I.WORLD> create or replace function my_unique_function( p_teamid
in number, p_job in varchar2, p_status in number ) return varchar2
  2  DETERMINISTIC
  3  as
  4  begin
  5      if ( p_status = 1 )
  6      then
  7          return p_teamid || '/' || p_job;
  8      else
  9          return NULL;
 10      end if;
 11  end;
 12  /

Function created.

Now, we have a function that if status = 1, it will return the TEAMID || '/'
|| JOB (if job is null-you'll get TEAMID || '/' and thats what will be uniqued).
 If status is NULL or not equal to one, we return NULL.

This is nice since an index entry that is entirely NULL is not in the index (we
don't put fully null entries in our b*trees), we'll only actually index the data
we need to check

Now, we can create our table and index:

ops$tkyte@ORA8I.WORLD> create table project
  2  (project_ID number primary key,
  3   teamid number,
  4   job varchar2(100),
  5   status number(1)
  6  );
Table created.

ops$tkyte@ORA8I.WORLD> create unique index project_idx on project( substr(
my_unique_function(teamid,job,status), 1, 110 ) )
  2  /

Index created.

See
http://osi.oracle.com/~tkyte/article1/index.html
for why I used substr there and to discover more about function based indexes in
general

ops$tkyte@ORA8I.WORLD> insert into project values( 1, 10, 'a', 1 );
1 row created.

ops$tkyte@ORA8I.WORLD> insert into project values( 2, 10, 'a', 1 );
insert into project values( 2, 10, 'a', 1 )
*
ERROR at line 1:
ORA-00001: unique constraint (OPS$TKYTE.PROJECT_IDX) violated

that shows that we cannot insert the same TEAMID/JOB with a status of 1...
Lets see what happens when we update that status and try again:

ops$tkyte@ORA8I.WORLD> update project set status = 2 where project_id = 1 and
teamid = 10 and status = 1;

1 row updated.

ops$tkyte@ORA8I.WORLD> insert into project values( 2, 10, 'a', 1 );

1 row created.

as expected, it works... Now, lets put some more data in there:

ops$tkyte@ORA8I.WORLD> insert into project values( 3, 10, 'a', 2 );
1 row created.

ops$tkyte@ORA8I.WORLD> insert into project values( 4, 11, 'a', 1 );
1 row created.

ops$tkyte@ORA8I.WORLD> insert into project select rownum+50, 10, 'a', 2 from
all_users where rownum <= 100;

100 rows created.

ops$tkyte@ORA8I.WORLD> select count(*) from project;

  COUNT(*)
----------
       104

ops$tkyte@ORA8I.WORLD> select count(*) from project where status = 1;

  COUNT(*)
----------
         2
So, of 104 rows in the table, 2 have a status of 1.  Lets see what impact that
has on our index we are using:

ops$tkyte@ORA8I.WORLD> analyze index project_idx validate structure;
Index analyzed.

ops$tkyte@ORA8I.WORLD> select lf_rows from index_stats;

   LF_ROWS
----------
         2

Our index has but 2 entries -- only the rows that NEED to be validated will be
in our index.

Now, if you do not have the enterprise edition, you do not have function based
indexes.  In that case, the easiest thing for you to do would be to add another
column and a trigger to maintain it plus a unique constraint.

Here is an example of that:

we will use a table called project table

ops$tkyte@ORA8I.WORLD> create table project_TABLE
  2  (project_ID number primary key,
  3   teamid number,
  4   job varchar2(100),
  5   status number(1),
  6   extra number
  7  );

Table created.

That extra column will hold the primary key whenever status is NOT equal to 1,
that'll make it unique.  It'll be NULL otherwise.  We'll create a view that our
applications will use -- don't want them to see this extra column:

ops$tkyte@ORA8I.WORLD> create or replace view project
  2  as
  3  select project_id, teamid, job, status from project_table
  4  /

View created.

ops$tkyte@ORA8I.WORLD> create unique index project_idx on project_TABLE( teamid,
job, extra )
  2  /

Index created.

now, our unique index is on teamid, job, extra.  When status = 1, extra is
null so teamid, job must be unique.  Whan status is not 1, extra will be the
primary key and hence will be unique so that teamid/job don't have to be

ops$tkyte@ORA8I.WORLD> create or replace trigger project_trigger
  2  before insert or update of status on project_TABLE
  3  for each row
  4  begin
  5          if :new.status = 1
  6          then
  7                  :new.extra := null;
  8          else
  9                  :new.extra := :new.project_id;
 10          end if;
 11  end;
 12  /

Trigger created.

that trigger does the work for us, making sure that extra is set properly.  If
you run the above example -- the results would be the same..
Review & Followup
Rating: 5
You are great August 24, 2001
Reviewer: A reader
Currently I am using a trigger solution, but I love the 1st method.

Thanks

Rating: 5
Enforcing conditional uniqueness on multiple columns August 29, 2001
Reviewer: Padders from UK
Tom, this is very imaginitive use of FBI. However, would you agree that...

CREATE UNIQUE INDEX ON SUBSTR (DECODE (status, 1, teamid || '/' || job, NULL),
1, 110);

...be logically equivalent to PL/SQL function and more efficient?

Followup:
Yes, it would be another way to implement the function based index.  It would be
slightly more efficient but since this function is only invoked on INSERT and
UPDATE of that column -- I found the PLSQL function to be more "self
documenting".

Either approach would work.

Rating: 5
More on...unique condition on multiple columns August 29, 2001
Reviewer: Padders from UK
Tom - thanks for quick answer. I agree that PL/SQL function permits
more documentation. However this rather goes against your general
advice of language choice i.e. sql, plsql, java sp, c. In many cases
I suspect index naming convention would be sufficient to describe
its purpose.

I am now thinking that delimiter is a problem. If two jobs exists
with matching name except that one job has leading character which
happens to match delimiter then either version of index will not
permit combination. Perhaps it would be safer to use two column
version as in...

CREATE UNIQUE INDEX index_name ON table_name (
  DECODE (status, 1, teamid),
  DECODE (status, 1, job));

CREATE UNIQUE INDEX index_name ON table_name (
  CASE WHEN status = 1 THEN teamid END,
  CASE WHEN status = 1 THEN job END);

Followup:
Agreed -- either one of these would be superior to the suggested PLSQL
implementation.  I would use

CREATE UNIQUE INDEX index_name ON table_name (
  DECODE (status, 1, teamid, NULL ),
  DECODE (status, 1, job, NULL ));

in hindsight.

Rating: 5
More on...unique condition on multiple columns August 29, 2001
Reviewer: Padders from UK
Tom - thanks for quick answer. I agree that PL/SQL function permits
more documentation. However this rather goes against your general
advice of language choice i.e. sql, plsql, java sp, c. In many cases
I suspect index naming convention would be sufficient to describe
its purpose.

I am now thinking that delimiter is a problem. If two jobs exists
with matching name except that one job has leading character which
happens to match delimiter then either version of index will not
permit combination. Perhaps it would be safer to use two column
version as in...

CREATE UNIQUE INDEX index_name ON table_name (
  DECODE (status, 1, teamid),
  DECODE (status, 1, job));

CREATE UNIQUE INDEX index_name ON table_name (
  CASE WHEN status = 1 THEN teamid END,
  CASE WHEN status = 1 THEN job END);

Followup:
In hindsight I fully agree that the implementation of:

CREATE UNIQUE INDEX index_name ON table_name (
  DECODE (status, 1, teamid, NULL ),
  DECODE (status, 1, job, NULL ));

would be the best overall answer in terms of performance.

Rating: 5
Where do I find the definition of teh function based index August 29, 2001
Reviewer: Arup Nanda from Norwalk, CT, USA
As always, Tom, you hit a bull's eye when it comes to pin a question to ground!
However, a little question to creep in here..after declaring a function based
index where do I find the definition later in the dictionary? The
user_ind_columns view states the name of the column for that index something
like SYS_NC0005$. Where do I find the function that the index uses?

Followup:
user_ind_expression contains this info:

ops$tkyte@ORA8I.WORLD> create table project (project_ID number primary key,
  2                  teamid number,
  3                  job varchar2(100),
  4                  status number(1));

Table created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> CREATE UNIQUE INDEX project_idx ON project (
  2    DECODE (status, 1, teamid, NULL ),
  3    DECODE (status, 1, job, NULL ));

Index created.

ops$tkyte@ORA8I.WORLD>
ops$tkyte@ORA8I.WORLD> select column_expression, column_position from
user_ind_expressions
  2  /

COLUMN_EXPRESSION                        COLUMN_POSITION
---------------------------------------- ---------------
DECODE("STATUS",1,"TEAMID",NULL)                       1
DECODE("STATUS",1,"JOB",NULL)                          2

Rating: 4
How to enforce conditional unique on multiple columns July 15, 2002
Reviewer: Jamil Shaibani from Saudi
Hi Tom
   I have try to use your funcation my_unique_function
while I am creating the unique index project_idx on project(
substr(my_unique_function(teamid,job,status), 1, 110 ) )
  but I am getting this message
ORA-00439 FEATURE NOT  ENABLED: FUNCATION - BASED INDEXES

thanks alot
Jamil

Followup:
you need Oracle8i enterprise edition for function based indexes.

Rating: 5
Can I apply this techniques on FK? October 29, 2002
Reviewer: Tudy from USA
Great answer!
I always want to put a conditional FK on a column.
For example, if there is one more requirement which is
" if the status is 1 the teamid should have a FK references teams.ID.
I thought I can simpley do ths following, but it did not work. Tom, can you tell
me why?

ALTER TABLE project ADD  CONSTRAINT FK_project_team
 FOREIGN KEY (DECODE ( status, 1 , teamid )
  REFERENCES team (id) ;

Followup:
fkeys must be on real columns.  You would need to maintain a shadow column for
this to work:

create table U$YOUR_REAL_TABLE( ......, status, teamid_shadow references team,
.... );

create view YOUR_REAL_TABLE
as
select ...., status, .....  /* no shadow columns */
 from U$YOUR_REAL_TABLE;

create trigger t on u$your_real_table before insert or update for each row
begin
   if :new.status := 1 then :new.teamid_shadow := :new.teamid;
   else :new.teamid_shadow := NULL;
   end if;
end;

Rating: 4
Can function based index boost performance on big table October 30, 2002
Reviewer: A reader
Hi Tom,
         I have table which has 200 million rows . I update this table 2 times a
day in batch Job .Every time I only update  2K - 3K records only .
         Here is the part of the table ..
CREATE TABLE item_arc
    (
     end_date                       DATE,
     parent_serial_num              VARCHAR2(30),
     item_serial_num                VARCHAR2(30)
     item_location                  VARCHAR2(80),
     description                       VARCHAR2(30),
    );

  Here is the cursor I use to identify those 2000 - 3000
records . ..
   select rowid,item_serial_num from  item_arc
        where end_date is null and
              item_serial_num = parent_serial_num ;

  Can function based index be used here and if so does it improve any
performance ? .
    I would really appriciate if you can provide code for the function based
index here .

Thanks

Followup:
If you indexed:

create index item_arc_idx on item_arc
( decode( item_serial_num,
              parent_serial_num, decode(end_date,
                                            NULL,1,
                                            NULL),
              NULL ) );

and then

update item_arc
   set ....
 where decode( item_serial_num,
              parent_serial_num, decode(end_date,
                                            NULL,1,
                                            NULL),
              NULL ) = 1;

then it would most likely use the index (index selects 2-3k rows from the 200
million row table) and the index will only have 2-3k entries in it at any point
in time since the values in the function are either 1 or NULL and entirely NULL
entries are not recorded in b*tree indexes.

ops$tkyte@ORA920.US.ORACLE.COM> CREATE TABLE item_arc
  2      (
  3       end_date                       DATE,
  4       parent_serial_num              VARCHAR2(30),
  5       item_serial_num                VARCHAR2(30),
  6       item_location                  VARCHAR2(80),
  7       description                       VARCHAR2(30)
  8      );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create index item_arc_idx on item_arc
  2  ( decode( item_serial_num,
  3                parent_serial_num, decode(end_date,
  4                                              NULL,1,
  5                                              NULL),
  6                NULL ) );

Index created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into item_arc select NULL, 'ABC', 'ABC',
'xxx', 'yyy'
  2    from all_objects
  3   where rownum <= 3000;

3000 rows created.
these are indexed...

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into item_arc select NULL, 'XYZ', 'ABC',
'xxx', 'yyy'
  2    from all_objects
  3   where rownum <= 3000;

3000 rows created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into item_arc select sysdate, 'ABC',
'ABC', 'xxx', 'yyy'
  2    from all_objects
  3   where rownum <= 3000;

3000 rows created.

those 6k are not

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> analyze index item_arc_idx validate structure;

Index analyzed.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select name, lf_rows from index_stats;

NAME                              LF_ROWS
------------------------------ ----------
ITEM_ARC_IDX                         3000

and that shows that is the case...

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> analyze index item_arc_idx compute statistics;

Index analyzed.

ops$tkyte@ORA920.US.ORACLE.COM> exec dbms_stats.set_table_stats( user,
'ITEM_ARC', numrows => 200000000, numblks => 2000000 );

PL/SQL procedure successfully completed.

to make it look like your environment -- index with 3k entries, table with
200,000,000 rows....

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> alter session set query_rewrite_enabled = true;

Session altered.

ops$tkyte@ORA920.US.ORACLE.COM> alter session set query_rewrite_integrity =
trusted;

Session altered.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> set autotrace on
ops$tkyte@ORA920.US.ORACLE.COM> update item_arc
  2     set end_date = sysdate
  3   where decode( item_serial_num,
  4                parent_serial_num, decode(end_date,
  5                                              NULL,1,
  6                                              NULL),
  7                NULL ) = 1;

3000 rows updated.

Execution Plan
----------------------------------------------------------
   0      UPDATE STATEMENT Optimizer=CHOOSE (Cost=19 Card=2000000
Bytes=86000000)
   1    0   UPDATE OF 'ITEM_ARC'
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'ITEM_ARC' (Cost=19 Card=2000000
Bytes=86000000)
   3    2       INDEX (RANGE SCAN) OF 'ITEM_ARC_IDX' (NON-UNIQUE) (Cost=9
Card=800000)

Statistics
----------------------------------------------------------
        109  recursive calls
       8528  db block gets
       1302  consistent gets
          0  physical reads
    1158396  redo size
        791  bytes sent via SQL*Net to client
       1020  bytes received via SQL*Net from client
          4  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
       3000  rows processed

ops$tkyte@ORA920.US.ORACLE.COM> set autotrace off

ops$tkyte@ORA920.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM> analyze index item_arc_idx validate structure;

Index analyzed.

ops$tkyte@ORA920.US.ORACLE.COM> select name, lf_rows, del_lf_rows from
index_stats;

NAME                              LF_ROWS DEL_LF_ROWS
------------------------------ ---------- -----------
ITEM_ARC_IDX                         3000        3000

and the index is for all intents and purposes "empty" - just waiting to be
filled with new data tomorrow...

Rating: 5
Saved my day November 01, 2002
Reviewer: A reader
Tom,
   You are the best .
Thanks

October 31, 2002 Debasish -- Thanks for the question regarding "Cursor in Database Trigger", version 8.1.7
You Asked
Dear Tom,
It's my hard luck to get you after trying long time, to day i am verry happy to
get you.

I have written a database trigger which contains a cursor query and after
querying the valid data it will be updated corresponding field upon the
condition.

I have two table

table Transaction

SQL> desc transaction
 Name                                      Null?    Type
 ----------------------------------------- -------- -------------
 CO_CD                                     NOT NULL CHAR(4)
 VNO                                       NOT NULL CHAR(5)
 VDT                                       NOT NULL DATE
 DR_CR                                     NOT NULL CHAR(1)
 GL_CD                                     NOT NULL CHAR(5)
 S_GRP                                     NOT NULL CHAR(4)
 AMT                                       NOT NULL NUMBER(12,2)

SQL> desc ac_Bal;
 Name                                      Null?    Type
 ----------------------------------------- -------- ------------------
 GL_CD                                     NOT NULL CHAR(5)
 S_GRP                                     NOT NULL CHAR(4)
 MNTH                                      NOT NULL DATE
 BAL                                       NOT NULL NUMBER(12,2)
 CO_CD                                     NOT NULL CHAR(4)

CREATE OR REPLACE PACKAGE TRANS1 AS
  TYPE mycur IS REF CURSOR;

  PROCEDURE TRANS(p_cursor in out mycur,
             DD IN DATE,
             CDD IN CHAR,
             CSD IN CHAR,MCOCD IN CHAR);
END TRANS1;
---------------------------------
CREATE OR REPLACE PACKAGE BODY TRANS1 AS

    PROCEDURE TRANS(p_cursor in out mycur,
             DD IN DATE,
             CDD IN CHAR,
             CSD IN CHAR,MCOCD IN CHAR)
     IS
  BEGIN

 open p_cursor for
     SELECT *
     FROM system.AC_BAL
    where MNTH>=dd AND GL_CD=CDD AND S_GRP=CSD AND CO_CD=MCOCD;

 EXCEPTION
   WHEN OTHERS THEN
     p_errorcode:= SQLCODE;

  END TRANS;

END TRANS1;

----------------------
CREATE OR REPLACE TRIGGER TRANS3 AFTER INSERT ON TRANSACTION
    FOR EACH ROW
declare
    D NUMBER;
    c2 trans1.mycur;
    BB system.AC_BAL%ROWTYPE;
BEGIN
   TRANS1.TRANS(C2,:NEW.VDT,:NEW.GL_CD,:NEW.S_GRP,:NEW.CO_CD);

   LOOP
    FETCH C2 INTO BB;
    UPDATE system.AC_BAL SET BAL=BAL+1 WHERE CURRENT OF C2;
-      ============================ the problem in this statement
   END LOOP;
   close c2;
end;

When I put The Statement UPDATE then SQL Give me compilation error but I I
compile this without Update statement that time this procedure create
successfully. but I want to update the AC_BAL Table every time when one
transaction has made in Transaction table.

please help me.

with best worm.
regrads
Debasish Ghosh
Calcutt,India
and we said...
before I even finish reading this question - I'm going to comment on your
exception block.  I HATE IT.  why why why would you catch and effectively IGNORE
an error.  that is a bug just waiting to happen.  You should let exceptions
PROPAGATE until someone can catch it and deal with it.  You are not dealing with
the exception -- you are ignoring it.  You will have bugs due to this (errors
that are blissfully ignored!)

anyway....

there are two issues -- one "logical" and one "physical"

Logical error -- The ref cursor is not FOR UPDATE.  WHERE CURRENT OF works only
with FOR UPDATE cursors.

Physical error -- where current of works ONLY with static named cursors, not ref
cursors.
ops$tkyte@ORA920.LOCALHOST> create or replace package demo_pkg
  2  as
  3          type rc is ref cursor;
  4
  5          procedure p1( p_cursor in out rc );
  6  end;
  7  /

Package created.

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> create or replace package body demo_pkg
  2  as
  3          procedure p1( p_cursor in out rc )
  4          is
  5          begin
  6                  open p_cursor for select rowid r, ename from emp FOR
UPDATE;
  7          end;
  8  end;
  9  /

Package body created.

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> declare
  2          l_cursor demo_pkg.rc;
  3          l_rid    rowid;
  4          l_var    varchar2(30);
  5  begin
  6          demo_pkg.p1( l_cursor );
  7          fetch l_cursor into l_rid, l_var;
  8          update emp set ename = lower(ename) WHERE rowid = l_rid;
  9  end;
 10  /

PL/SQL procedure successfully completed.

demonstrates how you might accomplish this.
Review & Followup
Rating: 5
System owned objects October 29, 2002
Reviewer: Lewis from St Pete, FL USA
I noticed the table was owned by system and wanted to point out that wasn't a
very good practice.  Create any other user and put the object there.

Rating: 4
Ref. Cursor in Database Trigger October 31, 2002
Reviewer: Debasish Ghosh from Calcutta, W.B, INDIA
Hi Tom-- Thanks for your assistance on this.  The proof you supplied was useful
but one of your comments tipped me in the right direction-- now I am writing
cursor in trigger in this way you please tell me which one is much reliable.
access cursor from trigger through procedure or direct into the trigger.
Thanks again!

below put my present trigger. this is working well.

create or replace trigger TRANS3 after insert on system.transaction
    for each row
------------------------------
declare
    type l_cursor     is REF CURSOR;
    cur l_cursor;
    l_rid         rowid;
    l_bal         number(12,2);
begin
    open cur for select rowid,bal from system.ac_Bal
    where co_cd=:new.co_cd and gl_Cd=:new.gl_cd and
        s_grp=:new.s_grp and
    tr_Date>=to_date(:new.vdt,'dd-mm-yyyy') FOR UPDATE;
    loop
        fetch cur into l_rid, l_bal;
        exit when cur%NOTFOUND;
        update system.ac_bal set  bal=l_Bal+:new.amt  where rowid=l_rid;
end loop;
close cur;
end;

Followup:
(the other reader is right -- DO NOT USE SYSTEM, DO NOT USE SYS.  they are
special, they are ours (oracle's) do NOT use them for your stuff under ANY
circumstances -- never)

well, here, you could use the UPDATE WHERE CURRENT OF syntax if you like.

But really, this should just be:

update ac_bal
   set bal = bal + :new.amt
 where co_cd = :new.co_cd
   and gl_cd = :new.gl_cd
   and s_grp = :new.s_grp
   and tr_date >= to_date( :new.vdt,'dd-mm-yyyy');

period -- no loop, no procedural code, just update.

November 01, 2002 Bhavesh -- Thanks for the question regarding "Use of Forms Commit", version 5.0.6.8.0
You Asked
Hi Tom,
    I have something to check out with you in forms 5.x. Suppose I have created
a menu where by user is privileged to open few forms. Now take the case that
there are two master forms say form A and form B. What the user does is that he
opens form A enters some data. He has yet to enter some of the mandatory fields
but all of a sudden he  realizes that he has forgot to enter something in form
B. He opens form B without closing the form A containing some data. Now he
enters the data in form B and saves it. At this time forms_commit is invoked and
since form A is also open with incomplete entries, it prompts some error
message(s). Though form B is saved but still we see  error messages due to form
A. Is there any way by which we can ensure that only the current form is made to
save ?
Thanks a lot in advance.
Regards

Bhavesh
and we said...
Depends on how you open the form.  If you use CALL_FORM, they share a session.
If, on the other hand, you use OPEN_FORM -- you can specify that a new session
is created for the new form.

See OPEN_FORM.
Review & Followup
Rating: 3
Additional Query July 02, 2002
Reviewer: Barathi A C from India
what is the purpose of using 'Forms_DDL('Commit')?

This way of commiting creates problems in concurrent access?

Pls throw a light on this..

Followup:
It commits in the database (the transaction is over) but Forms did not know
about it.  Hence, forms will think all data that was POSTED to the db is still
posted but not yet committed.  Also, all changed data that forms BELIEVES is
locked will no longer be locked.

It is a bad idea to use forms_ddl to commit like that.  Use the forms methods so
everything works nicely.

Rating: 5
what do you mean by "forms methods " July 03, 2002
Reviewer: A reader
can you please elaborate...

Followup:
COMMIT_FORM;

Rating: 4
July 07, 2002
Reviewer: Barathi A C from India
Thanx a lot Tom!

Rating: 3
Why this doesn't works ? July 16, 2002
Reviewer: Riaz Shahid from PRAL,Islamabad,Pakistan
Hello Tom !

I have a procedure in form like:

procedure aa is
begin
update mytab set flag='Y'
where claim_no=:myblock.claim_no;
end;

But when i see that in MyTab, the flag is not updated...why it is so? The flag
is updated only when i leave the form and then re-enter. What is the cause for
that...I want flag to be updated immediately. Do i need a database trigger or
procedure for that ?

Followup:
If you are looking at MYTAB from another session using sqlplus for example --
you won't see the changes until you COMMIT in your form.  Other sessions cannot
see uncommited work

Rating: 5
commit and commit_form July 16, 2002
Reviewer: Thx
Tom

when I code a client side procedure in my forms. Do I have to include an
explicit commit or commot_form inthe procedure(as I'm updating and deleting
using this procedure), or do I have to commit the form using the menu.

If I use commit in my client side procedure does it have any side effects.

What is the diff between commit and commit_form.

Followup:
"have to" -- no

"should" -- insufficient data.

Do you want your END USER to be in control of the transaction?  If so - answer =
no.

If you want your CODE to end the transaction -- answer = commit_form (so forms
will POST any other changes and then commit).  If you just "commit", you might
not post changes that forms did.  See the first followup -- we talk about it
there.

Rating: 3
Same session update not happening October 29, 2002
Reviewer: praora from India
I have a database procedure called by forms which also has a cursor with a for
update of ... nowait clause. Inside the procedure I am updating one column of
one table(out of three) using the where current of clause of the update
statement. I am spooling the results on to a txt file using utl_file for
debugging.
Now though there is a commit_form issued, the records get updated only when the
user exists out of the form and re-enter it again.But if he proceeds ahead with
the same transaction the updates are not committed.

Followup:
commit_form only does something if you CHANGED DATA IN FORMS, using forms.

you called a procedure, forms is totally unaware there are outstanding
modifications

You should use:

  POST_FORM;
  commit;

instead when you do some updates via forms and some "by yourself"

Rating: 3
Open_Form in Separate Session October 30, 2002
Reviewer: Srikant from India
Hi Tom,

  In my application to avoid the above said problem when users work
simulatenously with multiple forms, I am using Open_Form with SESSION, so that
each form is opened in a separate session and I won't have any problem while
commiting one form.  My doubts are: -

1. If I am opening each form in a separate session, will that affect the
performance in any way.  Do you prefer this?  If not, what is the work around?

2. I have a library that is attached to all the forms and the library has a
package.  The main idea was to share the package variables across the
application in all the forms.  But since I am opening each form in a separate
session, each form will have a separate copy of the Library (Please correct me
if I am wrong) and hence I am not able to access the value across the forms.  Is
there any work around for this other than using Global variables?  What do you
suggest.

  Kindly advice.  Thanks.

Srikant

Followup:
1) i don't like it -- you are opening a SESSION (eg: consuming lots of extra
resources).  I personally haven't seen an application that needs more then one
-- very very rarely TWO sessions.

I cannot imagine why in a forms application (heads down data entry stuff) you
need more then one transaction going -- I'd really need to know much more about
WHY the you believe the application needs this sort of complexity.  As an end
User I would be seriously wondering why my changes on one screen could never be
visible on another until i committed them.  Just not natural.

There may well be a good reason you need to, I just cannot imagine it yet.

2) sounds right -- you are in different sessions totally, different states, no
sharing for you.

Rating: 3
My problem not understood clearly October 31, 2002
Reviewer: Praora from India
Mr. Tom,
Following my earlier posting, the suggestion you made helped a bit but did not
solve my prob.
I have trn 1 : Some updations in form. A call to a database procedure that does
some updations in the backend. User commits. Clears the form. In the same
session,
trn 2: A similar transaction like trn 1. Commits , clears the form again in the
same session.
Now after trn 2 he exits the form.
If I see the database all changes due to trn 1 and trn 2 made in the form are
committed. But changes made by the database procedure for trn 2 are only
committed.
I tried post; call procedure; commit;
No change.
So i put a commit inside my database procedure . No change.
I am banging my head for the past 2 weeks . Please help.

Followup:
Sorry, I'll be blunt -- but I "don't believe what you say is happening happens"

You would have to tell me step by step by step how to reproduce this.  If
exiting the form makes the "data suddenly appear", the above steps you describe
are not physically possible.

Rating: 3
Please tell me if the info below suffices October 31, 2002
Reviewer: praora from India
Mr. Tom,
As requested by you here are the steps I have in the form.
This is a master detail form. I need to do updates to other tables when a
specific column in the base table of the detail block is updated. I have a
before update (row level) trigger which updates a package. I use this package in
my procedure to update the other tables. This is the situation.
The steps taken by me earlier are: I have verified the package which has a plsql
table. The package gets filled up fine through my before update trigger.
In the procedure(under discussion) I have a cursor with a for update of a
specific column with a nowait clause. The table (under discussion) is updated
using the where current of clause.In the process of debugging at one point I
removed the for update clause and used the primary key columns to update the
table .I used the returning clause on this update statment and spooled the
results to a log file. I found that the update statement did not fail as the
returning clause gave me the correct value.But even then only the last
transaction values got committed. So I reverted to the earlier situation,and
here I stand.
The business flow and the pk , fk etc could not be explained here as it would be
complicated.
Thanks in advance

Followup:
and how are you committing?  commit_form or forms_ddl('commit') or commit; in
plsql.

I believe you are using commit_form, the form had nothing to commit in fact and
hence did not commit and therefore your changes are not "visible" until after
you exit then form which ends your transaction and commits for you.

Rating: 5
It is something else November 01, 2002
Reviewer: praora from india
Mr. Tom,

I just believed what you said and started my debugging in a different fashion.
The problem is different.
I have solved it.
Thanks a lot.
I will not need any reply for my ealier post.

November 01, 2002 Arvind -- Thanks for the question regarding "Pl/sql table type declaration approaches, which is better.", version 8.1.7
You Asked
Hi Tom,
If we have some plsql table type o/p parameters in some oracle stored procedure
like as follows:

create table emp( empname varchar2(80));

create or replace package pck1 is
   type tp_varchar2 is table of varchar2(100) index by binary_integer;
   procedure proc( var1  out tp_varchar2);
end;

create or replace package pck2 is
   type tp_varchar2 is table of emp.empname%type index by binary_integer;
   procedure proc( var1  out tp_varchar2);
end;

The only change is this, that in first package I have fixed length of 100 for
plsql table type while in other I have table field length which is dynamic.

Lets assume while returning data o/p variable var1 is returning max data  length
50 in all records in plsql table. So will it use the same amount of memory while
returning data in both approaches or it will be different.
which one is better in your opinion, since by using first approach I can use
only one declared type in other o/p parameters also where returning length is <=
100, but by using second approach I need define as many plsql table types as
many number of plsql table o/p parameters.

Thanks for any help.
Arvind Kapil Sharma
and we said...
Me, I would just use varchar2(some_big_number) once.  The data in the table is
allocated using varying length strings.  varchar2(100), (20), (10000) -- they
will consume the same space if you put the same strings in.

ops$tkyte@ORA9I.WORLD> !cat test.sql
declare
        type array is table of varchar2(&1) index by binary_integer;

        l_data array;
begin
        for i in 1 .. 1000
        loop
                l_data(i) := rpad( '*', 20, '*' );
        end loop;
end;
/
@mystat pga

ops$tkyte@ORA9I.WORLD> @test 100
old   2:        type array is table of varchar2(&1) index by binary_integer;
new   2:        type array is table of varchar2(100) index by binary_integer;

PL/SQL procedure successfully completed.

old   4: and lower(a.name) like '%' || lower('&1')||'%'
new   4: and lower(a.name) like '%' || lower('pga')||'%'

NAME                                VALUE
------------------------------ ----------
session pga memory                 237344
session pga memory max             237344

ops$tkyte@ORA9I.WORLD> Disconnected from Oracle9i Enterprise Edition Release
9.0.1.2.0 - Production
With the Partitioning option
JServer Release 9.0.1.2.0 - Production

Wed Aug 14 13:43:28 EDT 2002
(tkyte@aria-dev) /export/home/tkyte
> plus

SQL*Plus: Release 9.0.1.0.0 - Production on Wed Aug 14 14:57:00 2002

(c) Copyright 2001 Oracle Corporation.  All rights reserved.

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

ops$tkyte@ORA9I.WORLD> @test 30
old   2:        type array is table of varchar2(&1) index by binary_integer;
new   2:        type array is table of varchar2(30) index by binary_integer;

PL/SQL procedure successfully completed.

old   4: and lower(a.name) like '%' || lower('&1')||'%'
new   4: and lower(a.name) like '%' || lower('pga')||'%'

NAME                                VALUE
------------------------------ ----------
session pga memory                 237344
session pga memory max             237344

ops$tkyte@ORA9I.WORLD> Disconnected from Oracle9i Enterprise Edition Release
9.0.1.2.0 - Production
With the Partitioning option
JServer Release 9.0.1.2.0 - Production

Wed Aug 14 13:43:28 EDT 2002
(tkyte@aria-dev) /export/home/tkyte
> plus

SQL*Plus: Release 9.0.1.0.0 - Production on Wed Aug 14 14:57:06 2002

(c) Copyright 2001 Oracle Corporation.  All rights reserved.

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

ops$tkyte@ORA9I.WORLD> @test 10000
old   2:        type array is table of varchar2(&1) index by binary_integer;
new   2:        type array is table of varchar2(10000) index by binary_integer;

PL/SQL procedure successfully completed.

old   4: and lower(a.name) like '%' || lower('&1')||'%'
new   4: and lower(a.name) like '%' || lower('pga')||'%'

NAME                                VALUE
------------------------------ ----------
session pga memory                 237344
session pga memory max             237344
Review & Followup
Rating: 5
Thats what I was expecting August 14, 2002
Reviewer: Arvind K Sharma from USA
Thanks tom.
Really it solved some of the doubts in my mind.

Take care
Arvind K Sharma

Rating: 5
Does 'session pga memory' shrink ? November 01, 2002
Reviewer: Adrian Billington from UK
Tom

I was always under the assumption that using the DELETE method on a collection
did not release the memory, whereas assigning an empty collection to a currently
populated one released the memory. However, Steven Fueurstein's latest OPP3 says
that DELETE of all elements of a collection returns all the memory back.

How do you measure this? I had a go as follows, but it doesn't seem to do what I
was expecting it to do:-

920> declare
  2
  3     g_mem     pls_integer;
  4     g_mem_max pls_integer;
  5
  6     type typ_ibt_bin is table of varchar2(30)
  7        index by binary_integer;
  8
  9     ibt_bin       typ_ibt_bin;
 10     ibt_bin_empty typ_ibt_bin;
 11
 12     procedure snap is
 13     begin
 14        select (select value from v$mystat
 15                where statistic# = (select statistic# from v$statname where
name = 'session pga memory'))
 16        ,      (select value from v$mystat
 17                where statistic# = (select statistic# from v$statname where
name = 'session pga memory max'))
 18        into g_mem, g_mem_max
 19        from dual;
 20        --dbms_output.put_line('Memory is '||to_char(g_mem));
 21        --dbms_output.put_line('Memory max is '||to_char(g_mem_max));
 22     end snap;
 23
 24     procedure show_mem is
 25        v_mem pls_integer := g_mem;
 26        v_mem_max pls_integer := g_mem_max;
 27     begin
 28        snap();
 29        dbms_output.put_line('Memory has increased by '||to_char(g_mem -
v_mem)||' bytes.');
 30        dbms_output.put_line('Memory max has increased by
'||to_char(g_mem_max - v_mem_max)||' bytes.');
 31     end show_mem;
 32
 33  begin
 34
 35     /* Now let's start... */
 36     snap();
 37
 38     /* Get memory usage above default session allocation... */
 39     select name bulk collect into ibt_bin from dba_source;
 40     show_mem();
 41
 42     /* Any released ? (should be no)... */
 43     ibt_bin.delete(150);
 44     show_mem();
 45
 46     /* Any released ? (I thought it should be no, OPP3 says it should)... */
 47     ibt_bin.delete;
 48     show_mem();
 49
 50     /* Any released ? (I thought it should be)... */
 51     ibt_bin := ibt_bin_empty;
 52     show_mem();
 53
 54  end;
 55  /
Memory has increased by 5864796 bytes.
Memory max has increased by 6585692 bytes.
Memory has increased by 0 bytes.
Memory max has increased by 0 bytes.
Memory has increased by 0 bytes.
Memory max has increased by 0 bytes.
Memory has increased by 0 bytes.
Memory max has increased by 0 bytes.

PL/SQL procedure successfully completed.

Can v$mystat provide the answer as I was expecting?

Regards

Adrian

Followup:
If you have my book "expert one on one", I have a discussion on this and why pga
memory in general won't go down (it would not be worth the effort -- it won't
really "shrink" at the OS level regardless).  We can "force it" but in general,
you are only making yourself feel better but the system is running slower for
it.

Here is an example, if you want the details, my book goes into why shrinking PGA
memory in dedicated server isn't worth the effort:

ops$tkyte@ORA920.US.ORACLE.COM> declare
  2          type array is table of varchar2(4000) index by binary_integer;
  3
  4          l_data array;
  5
  6          procedure show_pga_usage( p_msg in varchar2 )
  7          is
  8          begin
  9          dbms_output.put_line( p_msg );
 10          for x in (      select a.name, b.value
 11                    from v$statname a, v$mystat b
 12                   where a.statistic# = b.statistic#
 13                     and a.name like '%pga%' )
 14          loop
 15                  dbms_output.put_line( to_char( x.value, '999,999,999' ) ||
' ' || x.name );
 16          end loop;
 17          end;
 18  begin
 19          show_pga_usage( 'starting' );
 20
 21          for i in 1 .. 1000
 22          loop
 23                  l_data(i) := rpad( '*', 4000, '*' );
 24          end loop;
 25          show_pga_usage( 'after filling array: ' );
 26
 27          for i in 1 .. l_data.count
 28          loop
 29                  if ( mod( i, 500 ) = 0 )
 30                  then
 31                          show_pga_usage( 'after deleting ' || i || '
entries' );
 32                  end if;
 33                  l_data.delete(i);
 34          end loop;
 35          show_pga_usage( 'after deleting ALL entries' );
 36  end;
 37  /
starting
401,444 session pga memory
401,444 session pga memory max
after filling array:
5,888,396 session pga memory
5,888,396 session pga memory max
after deleting 500 entries
5,888,396 session pga memory
5,888,396 session pga memory max
after deleting 1000 entries
5,888,396 session pga memory
5,888,396 session pga memory max
after deleting ALL entries
5,888,396 session pga memory
5,888,396 session pga memory max

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> @mystat pga
ops$tkyte@ORA920.US.ORACLE.COM> select a.name, b.value
  2  from v$statname a, v$mystat b
  3  where a.statistic# = b.statistic#
  4  and lower(a.name) like '%' || lower('&1')||'%'
  5  --and b.value > 0
  6  /
old   4: and lower(a.name) like '%' || lower('&1')||'%'
new   4: and lower(a.name) like '%' || lower('pga')||'%'

NAME                                VALUE
------------------------------ ----------
session pga memory                5888396
session pga memory max            5888396

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> declare
  2          type array is table of varchar2(4000) index by binary_integer;
  3
  4          l_data array;
  5
  6          procedure show_pga_usage( p_msg in varchar2 )
  7          is
  8          begin
  9          dbms_output.put_line( p_msg );
 10          for x in (      select a.name, b.value
 11                    from v$statname a, v$mystat b
 12                   where a.statistic# = b.statistic#
 13                     and a.name like '%pga%' )
 14          loop
 15                  dbms_output.put_line( to_char( x.value, '999,999,999' ) ||
' ' || x.name );
 16          end loop;
 17          end;
 18  begin
 19          show_pga_usage( 'starting' );
 20
 21          for i in 1 .. 1000
 22          loop
 23                  l_data(i) := rpad( '*', 4000, '*' );
 24          end loop;
 25          show_pga_usage( 'after filling array: ' );
 26
 27          for i in 1 .. l_data.count
 28          loop
 29                  if ( mod( i, 500 ) = 0 )
 30                  then
 31                          show_pga_usage( 'after deleting ' || i || '
entries' );
 32                          dbms_session.FREE_UNUSED_USER_MEMORY;
 33                          show_pga_usage( 'after deleting ' || i || ' entries
and calling "free memory"' );
 34                  end if;
 35                  l_data.delete(i);
 36          end loop;
 37          show_pga_usage( 'after deleting ALL entries' );
 38  end;
 39  /
starting
5,888,396 session pga memory
5,888,396 session pga memory max
after filling array:
5,888,396 session pga memory
5,888,396 session pga memory max
after deleting 500 entries
5,888,396 session pga memory
5,888,396 session pga memory max
after deleting 500 entries and calling "free memory"
3,194,204 session pga memory
5,888,396 session pga memory max
after deleting 1000 entries
3,194,204 session pga memory
5,888,396 session pga memory max
after deleting 1000 entries and calling "free memory"
467,156 session pga memory
5,888,396 session pga memory max
after deleting ALL entries
467,156 session pga memory
5,888,396 session pga memory max

PL/SQL procedure successfully completed.

Rating: 5
That's got it... November 01, 2002
Reviewer: Adrian Billington from UK
Tom

Of course! By freeing the memory with DBMS_SESSION (OK I would never use this in
"real" code), it at least enabled me to verify that the DELETE method (all
elements only) and empty collection assignment both work in the same way.

Thanks.

Adrian

November 01, 2002 -- Thanks for the question regarding "Export direct =y", version 8.1.7.0
You Asked
Hi Tom

When I say direct=y in the export the processing is much faster.
What r the scanerious where i should not use direct=y

Regards
and we said...
A direct path export should not be considered the converse of a direct path load
using SQLLDR (for those of you familiar with that option). A direct path export
does not read directly from the datafiles and write to the DMP file. SQLLDR does
write directly to the datafiles from the DAT files. Export in direct path mode
simply bypasses the SQL evaluation buffer (where clause processing, column
formatting and such). 90% of the path is the same, EXP is still reading buffers
into the buffer cache, doing the same consistent read processing and so on.

The speedup of the direct path export can be large however. The 10% of the
processing that it cuts out, accounts for a much larger percentage of the
run-time. For example, I have just exported about 100meg of data, and 1.2
million records. The direct path export took about one minute. The conventional
path export on the other hand took three minutes. There is unfortunately no
corresponding 'direct path import'. Import uses conventional SQL to insert the
data back into tables. SQLLDR is still the way to go for high performance data
loads.

It should be noted that in direct path mode, you cannot use the QUERY= parameter
to select a subset of rows. This makes sense given that DIRECT=Y is simply a way
to bypass the SQL evaluation buffer and that is where the where clause
processing would normally take place.

followup to comment two below

Yes, you can move the dmp file -- the file format is the same regardless of the
direct= setting.
Review & Followup
Rating: 4
Export Direct = y July 13, 2001
Reviewer: Bibeka Nanda Patnaik from India
Thanks Tom,
Really it's very nice the way You explained Here and it helped me a lot

Rating: 5
Cross Platform export import using Direct=y July 13, 2001
Reviewer: Sudip from Calcutta, India
Can export import using direct = y be used
across platforms i.e. export on NT and import on Unix?

Rating: 5
Errors after importing a table from a dmp file created using direct=y option November 01, 2002
Reviewer: Yogeeraj from Mauritius
hi,

I exported my database using the following script:
===========================================================
#!/bin/sh
#
###############################################################
#         Script Name: cmtdbexp_full.sh                #
#         Description: Export all tables in CMTDB Database #
#                          to DISK                         #
#                      By: YD-14/12/2001-created script    #
#           Date Modified: YD-14/12/2001-Created script
###############################################################
#
LOG_PATH=/BACKUP/export/logfiles
ORACLE_HOME=/d01/app/oracle/product/8.1.7
DMP_PATH1=/BACKUP-SLX1/export
ACC_PASS=system/xxxxxx
export dt=`date +%Y-%m%d`

export LOG_PATH DAT ACC_PASS DMP_PATH1 ORACLE_HOME
#
$ORACLE_HOME/bin/exp $ACC_PASS filesize=1024M
file=\($DMP_PATH1/dbexp"$dt"FULLa.dmp,$DMP_PATH1/dbexp"$dt"FULLb.dmp,
$DMP_PATH1/dbexp"$dt"FULLc.dmp,
$DMP_PATH1/dbexp"$dt"FULLd.dmp,$DMP_PATH1/dbexp"$dt"FULLe.dmp\) buffer=409600
log=$LOG_PATH/dbexp"$dt"FULL.log full=Y grants=Y rows=Y compress=N direct=Y
#
# AUTHOR - YD
===========================================================

and tried to import one table from the exported dmp using the script below:
============================================================
#!/bin/sh
#
dt=2002-1031
DMP_PATH1=/u02/backup-l1000/export
imp system/xxxx
file=\($DMP_PATH1/dbexp"$dt"FULLa.dmp,$DMP_PATH1/dbexp"$dt"FULLb.dmp,$DMP_PATH1/d
bexp"$dt"FULLc.dmp,$DMP_PATH1/dbexp"$dt"FULLd.dmp,$DMP_PATH1/dbexp"$dt"FULLe.dmp\
)  constraints=n indexes=n buffer=409600 log=impxlarge32.log grants=n
fromuser=swprod touser=swprod ignore=y tables='(FDT850)'
#
# AUTHOR - YD
============================================================

I am getting the following errors:
============================================================
Import: Release 8.1.7.4.0 - Production on Fri Nov 1 14:53:01 2002

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Connected to: Oracle8i Enterprise Edition Release 8.1.7.4.0 - 64bit Production
With the Partitioning option
JServer Release 8.1.7.4.0 - 64bit Production

Export file created by EXPORT:V08.01.07 via direct path
import done in US7ASCII character set and US7ASCII NCHAR character set
IMP-00046: using FILESIZE value from export file of 1073741824
. importing SWPROD's objects into SWPROD
. . importing table                       "FDT850"    5103464 rows imported
IMP-00051: Direct path exported dump file contains illegal column length
IMP-00008: unrecognized statement in the export file:
  M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?
M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?^A
IMP-00008: unrecognized statement in the export file:
  ^KM-~M-^?^C
IMP-00008: unrecognized statement in the export file:
  ^_M-~M-^?M-~M-^?M-~M-^?M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?
^F
IMP-00008: unrecognized statement in the export file:
  3M-~M-^?^B
IMP-00008: unrecognized statement in the export file:
  ^K^C
IMP-00008: unrecognized statement in the export file:
  ^F^C
IMP-00008: unrecognized statement in the export file:
  ^F^C
IMP-00008: unrecognized statement in the export file:
  M-~M-^?M-~M-^?^B
IMP-00008: unrecognized statement in the export file:
  ^C
IMP-00008: unrecognized statement in the export file:
  M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?M-~M-^?
M-~M-^?^C
============================================================

Please guide me on how to troubleshoot this.

thank you in advance

Regards
Yogeeraj
PS. Can you please tell us about your forthcoming book and if possible about a
tentative date...thanks

Followup:
I'll have to refer you to support on that -- if the dmp file was transferred
using binary ftp -- that would look like a "product issue".

book, in the spring -- performance/best practices/scalability related.

November 01, 2002 Sam -- Thanks for the question regarding "Why is it said that global indexes are suitable for OLTP apps, and local indexes are suitable for DSS i.e. data warehousing apps", version 8.1.7
You Asked
Tom

Why is it said that global indexes are suitable for OLTP apps, and local indexes
are suitable for DSS i.e. data warehousing apps.

What is the reasoning behind it.

Thank you
and we said...
do you have my book?  I go into many of these considerations.  Here are some
short extracts:

...
Data Warehousing and Global Indexes

It is my opinion that these two things are mutually exclusive. A data warehouse
implies certain things; large amounts of data coming in and going out, high
probability of a failure of a disk somewhere, and so on. Any data warehouse that
uses a 'sliding window', where old data (say anything over five years old) is
'slid' off one end of a table and new data is added to the other end, would want
to avoid the use of global indexes.
.... (the reason is that partitioning maintenance would invalidate the global
index and you do lots of partition level operations in a data warehouse, you
would have to rebuild them often)

....
OTLP and Global Indexes

An OLTP system is characterized by many small to very small read and write
transactions occurring frequently. Sliding windows of data is not something you
are concerned about in general. Fast access to the row, or couple of rows, you
need is paramount. Data integrity is vital. Availability is very important as
well.

Global indexes make sense in OLTP systems. Table data can only be partitioned by
one key ? one set of columns. You may need to access the data in many different
ways however. You might partition EMPLOYEE data by location. You still need fast
access to EMPLOYEE data however by:

o DEPARTMENT ? Departments are geographically dispersed, there is no
relationship between a department and a location.
o EMPLOYEE_ID ? While an employee ID will determine a location, you don't want
to have to search by EMPLOYEE_ID and LOCATION, hence partition elimination
cannot take place on the index partitions. Also EMPLOYEE_ID by itself must be
unique.
o JOB_TITLE
o And so on...
......

(so, you need global indexes in order to effectively do index partition
elimination to avoid having to scan N index structures)
Review & Followup
Rating: 5
How much the local indexes will degrade OLTP performance? November 01, 2002
Reviewer: Leo from USA
We have an OLTP system (8.1.7) and some tables are over 10G.  We are planning to
migrate it to 9.2 with exp/imp and take this opportunity to partition all the
large tables.  After the migration, we can archive the historical data by moving
the partitions with old data out of the system.  As you said, for OLTP systems,
global indexes are better.  However, with global indexes, archive process will
invalidate all the global indexes.  For local indexes, the archive process will
be much easier.  I am wondering have you benchmarked how much the local indexes
will degrade the OLTP system performance.  If the effect is not significant, we
still prefer local indexes.

Followup:
it is NOT something you can benchmark *in general*.

Partitioning is a physical design thing.  There could be OLTP apps that benefit
HUGELY from local indexes.  Others that die because of it.  Depends on the app,
depends on how you use it.

The problem is in OLTP type apps -- you generally come at the data from various
different keys over time.  You can only partition by one key really so if you
come at the data via two different keys in different apps -- one will have to
index range scan N index partitions to find its answer (the other can use
partition elimination).  It really depends.

Now, in 9i there is "maintain those global indexes for me" option to the
partition operations.  You might consider looking into that.

Rating: 5
November 01, 2002
Reviewer: Leo
Thanks.

November 01, 2002 javed -- Thanks for the question regarding "char(1) versus varcahr2(1)", version
You Asked
Q.1-     What is difference between Data Type  ?

CHAR(1)           NOT NULL
VARCHAR2(1)        NOT NULL

Q.2-    What are the no of  Byter required for each Type ?
and we said...
In the above example, they are effectively the same.

A CHAR is stored with a leading byte count and is blank padded
to its maximum length (so the leading byte count for ALL rows is
either 0 - null, or the max length).

A VARCHAR is stored with a leading byte count and is NOT blank
padded to its maximum length.

If the max length = 1 and they are NOT NULL, then a CHAR and
VARCHAR2 are exactly the same, each will consume 2 bytes of
storage.
Review & Followup
Rating: 5
For key columns November 01, 2002
Reviewer: A reader
Dear Tom,
One third party implementation has CHAR defined for all key columns, and not
VARCHAR2. It is also recommending changing to CHAR for the key columns in all
the interface applications. I know this is a diastrous recommendation, wrt. the
size of indexes and finally performance associated with these indexes. But, is
there any truth in this whatsoever? I would use VARCHAR2 even for a single CHAR
value, where do we have to use CHAR at all?
Regards

Followup:
You need to do what your 3rd party vendor tells you in this case.  Thats they
only way their application will work.

Their recommendation is in order to avoid the "non-join" that will otherwise
happen.  If you have:

ops$tkyte@ORA920.US.ORACLE.COM> create table t1( x char(4) );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into t1 values ( 'a' );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create table t2( a char(4), b varchar2(4), c
varchar2(4) );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into t2 values ( 'a', 'a', 'a   ' );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select 'A', t1.*, t2.* from t1, t2 where t1.x =
t2.a
  2  union all
  3  select 'B', t1.*, t2.* from t1, t2 where t1.x = t2.b
  4  union all
  5  select 'C', t1.*, t2.* from t1, t2 where t1.x = t2.c
  6  /

' X    A    B    C
- ---- ---- ---- ----
A a    a    a    a
C a    a    a    a

see how the join of the char(4) to the varchar2(4) that was NOT blank padded
"fails".  That is what will happen to you!

You don't really have a choice here, short of not using that product.  It is not
disasterous, just not as good as it could be.

(a varchar2(1) and char(1) consume exactly the same storage in exactly the same
fashion by the way...)

November 01, 2002 Abhijeet -- Thanks for the question regarding "Oracle DBA", version 8.1.5
You Asked
Dear Mr. Tom,

        To my following question :

Dear Mr. Tom,

I would like to know, after creation of an Oracle Database, how
to configure it so that it starts default with the operating
system startup. Also what configuration has to be made for the
LISTENERS. Does every database should have a separate LISTENER.
How to start multiple databases at the same time.

How to shutdown individual databases, if multiple databases are
running.

Your reply was whether I had any O.S. in my mind.

Yes, I would like to have information on UNIX and Windows NT
Operating System.

Thanking You,

Waiting for reply.

Reagrds,
Abhijeet.
and we said...
On NT, this is all done via services.  When you create a new
instance and database, they are set to autostart on reboot via a
service.  You may configure it so that they are to be started
manually and then you can use the control panel to start/stop
them.  Optionally, you may use the command line, eg:

c:\> net start oracleservice<ORACLE_SID>
c:\> net stop oracleservice<ORACLE_SID>

On Unix, it is done via scripts, see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:387218751430
That also starts the answer the multiple listeners question.
Every database does not NEED a separate listener, every database
can HAVE a separate listener.  That is up to you.  On NT  the
listeners will again be started as services, on Unix, the above
URL shows how you would autostart them.

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1481804901409
for how you might setup a listener.ora to allow for >1
listener on a machine.  Typically, you would use the net8
assistant to set this all up for you.
Review & Followup
Rating: 5
database creation assistant error November 01, 2002
Reviewer: P from LA
hi tom,
I am getting following error when I execute
dbca or netca

$ dbca
jre was not found in /u01/ORACLE/jre/1.1.8/bin/../bin/i586/green_threads/jre

what could be wrong?

thank you

Followup:
sounds like your path isn't set up properly.  contact support for configuration
and installation assistance.

Rating: 5
could you please put it out there November 01, 2002
Reviewer: P from LA
hi tom,
I am trying this on my home linux box ...could you please put it out there so if
anyone know about this they might help

Appreciate your help
P

Followup:
put "it" out there...

hmm -- what is "it"?

Rating: 4
P, check November 01, 2002
Reviewer: Bricklen from Victoria, Canada
http://groups.google.com/groups?hl=en&group=comp.databases.oracle
and verify (providing that you are using 8i) that you followed the instructions.
The following link is a simple walk-through of the installation.

http://jordan.fortwayne.com/oracle/817.html
hth

November 01, 2002 Vladimir -- Thanks for the question regarding
      "Boolean Function", version Oracle 8i

      You Asked
I written a Java Class that return boolean!!!, in Oracle 8i I
written a function that return boolean and call this boolean
Class without problem but when I call this function from Oracle
SQL*PLUS like :
select function_created_in_Oracle(param1, param2) from dual
I received this message:
ORA-00932: inconsistent datatypes
when I change Java Class to return not boolean but string!!!
it's work without problem!!, I think that Oracle doesn't support
function that return boolean

Thanks and regards
Vladimir Rojas

      and we said...
Boolean is not a SQL type -- there is no datatype "boolean" that
you can use to create a table with.  In SQL -- only the
datatypes that you can use to create a table with may be
returned.

You should return 1/0 for TRUE/FALSE.  In SQL there is no
boolean type -- hence it is not allowed.

We support boolean types in PL/SQL as a programming construct
and in Java as a programming construct.  In straight SQL --
there is no such construct.
Review & Followup

      Rating:
      5
DBA  May 14, 2002
Reviewer:  Robert
      Wood  from Coldwater, MS
Hi Tom,

That is exactly what I was looking for.

Thanks,

Robert.

      Rating: 3
Performance of
      booleans.  November 01, 2002
Reviewer:
      Kashif  from Houston, TX
Hi Tom,

I tried searching the archives but couldn't find specific info on the
performance of boolean functions/variables. If I have the option of returning a
boolean versus returning a character/number, which should I use? Why? Consider:

--
select 'T'
into var1
from dual
where exists (...);
return (var1);
--
vs.
--
select count (*)
into num_var
from dual
where exists (...)
return (num_var > 0);
--

Any benchmarks/examples you can provide will be helpful. Thanks in advance.

Kashif

P.S. What does IMHO stand for?

          Followup:

IMHO = In My Humble Opinion

IMHO -- you can use

http://asktom.oracle.com/~tkyte/runstats.html
as a test harness to see which is better!  ;)

I'm going to guess -- it'll be a "photo finish"

November 01, 2002 Singarevelu -- Thanks for the question regarding
      "Generic Connectivity", version 8.1.6

      You Asked
Hello Tom ,

I'am using Oracle 8i on unix Release 2 . There is feature called
Generic Connectivity .

My Requirement :

I need to check for a existence of a value in DB2 Database .
If that value is present ,
I need go ahead an insert values into my Oracle Table
Else
 throw out an error .

Can this be done using generic connectivity ?????

Or

Is Gateway the only option.

      and we said...
The generic connectivity is the underlying technology used by
the gateways.  you need a gateway to DB2 or you can drop the DB2
jdbc drivers into the database and log in from a java stored
procedure over jdbc (I've done that, it works).
Review &
      Followup

      Rating: 1
Generic
      Connectivity  January 31, 2002
Reviewer:  Tom
      Mulyca  from West Point, NY
I use a Sybase runtime ASA7 db with ODBC access for read only. I want to synch
tables from Sybase to my Oracle 8i db.
GAteway for Sybase didn't work but, at least there was a disk to install it. I
can find that generic connectivity comes with 9iAS but, after installing that I
am still clueless as to how to get a connection going thru my existing ODBC
connection to the Sybase ASA db. Direct instructions for est. that link would
have been helpful.

I do this all the time with Access but, this is really hidden in Oracle. Then
again, I  am a neophyte at this.
Guess I'll go read your book with a fine cigar and see if I can find it there.
Thanks for the site.

          Followup:

Well, the generic connectivity is a framework (underlying) technology.  You
would need to use the appropriate gateway (
http://www.oracle.com/gateways/
 with it.

With 8i, you could always load a type4 jdbc (thin) driver into Oracle for Sybase
and write a java stored procedure that does this.

You could use ODBC and connect to both databases to syncronize them as well.

      Rating:
      3
  November 01, 2002
Reviewer:  Pushparaj
      Arulappan  from NJ, USA
Tom,

We do have PG4APPC gateway in our environment and we use
this APPC connectivity to interact with CICS transactions in Mainframe.

Since we have a gateway to communicate Mainframe already, can I now query a DB2
table from oracle directly using
HS packages and if so could you please show or direct some
examples.

Thanks
Pushparaj

          Followup:

To use APPC, you need a gateway to DB2 (its not ODBC).  The gateways come with
9iAS -- you would need to contact your sales contact in order to get it if you
don't already have it.

November 02, 2002 Miroslav -- Thanks for the question regarding
      "10053 trace inconsistent with NOTE 68992.1", version 8.1.7

      You Asked
Dear Ser:

The note 68992.1 says that selectivity is calculated as:

Sel = 1/max[NDV(t1.c1),NDV(t2.c2)] *
           ( (Card t1 - # t1.c1 NULLs) / Card t1) *
           ( (Card t2 - # t2.c2 NULLs) / Card t2)

When I look at a 10053 trace for a simple join of two tables, the join predicate
selectivity ("sel") does not match the formula above.  Specifically, the trace
contains:

Join cardinality: 3 = outer (2) * inner (5) * sel (3.3333e-001) [flag=0]

According to the explanation of 10053, I would expect "sel" to be 1/5=2.0e-001,
but the trace shows 3.3333e-001.  (I will upload the trace file separately).

Finally, do you have any recommendation for helping the optimizer not
underestimate number of rows (overestimate selectivity) when the predicates on
different tables are correlated?  I'm aware that building a compound index can
help for columns in one table.

      and we said...
Well, you do not have a test for me, so I made up one

---------------------------------------------------------------------------
drop table emp;
drop table dept;

create table emp as select * from scott.emp;
create table dept as select * from scott.dept;
create index emp_deptno_idx on emp(deptno);
alter table dept add constraint dept_pk primary key(deptno);

analyze table emp compute statistics for table for all indexes for all indexed
columns;
analyze table dept compute statistics for table for all indexes for all indexed
columns;

set autotrace traceonly explain
select * from emp, dept where emp.deptno = dept.deptno;
set autotrace off

alter session set events '10053 trace name context forever, level 1';

select  * from emp, dept where emp.deptno = dept.deptno;
-----------------------------------------------------------------------

Now, the NDV for emp is 3, NDV for dept is 4.
card of t1 is 14, number of nulls is 0
card of t2 is 4, number of nulls is 0

Hence the selectivity is:

select 1/ greatest(3,4) * ((14-0)/14) * ((4-0)/4)
from dual;

1/GREATEST(3,4)*((14-0)/14)*((4-0)/4)
-------------------------------------
                                  .25

and I got:

Join cardinality:  14 = outer (4) * inner (14) * sel (2.5000e-01)  [flag=0]

which is exactly right.

I'll make an "educated" guess.  The table with 5 values -- it has 3 distinct
values:

  1  select 1/ greatest(2,3) * ((2-0)/2) * ((5-0)/5)
  2* from dual
ops$tkyte@ORA817DEV.US.ORACLE.COM> /

1/GREATEST(2,3)*((2-0)/2)*((5-0)/5)
-----------------------------------
                         .333333333

the numbers come out perfect in that case.

I don't know what you mean by that last part exactly -- examples are great in
cases like that.
Review & Followup

      Rating: 5
Problem
      grasping the definitions of selectivity and
      cardinality  November 01, 2002
Reviewer:
      A reader  from Canada
Hi Tom:

I am having a mental block trying to understand the terms cardinality and
selectivity as applied to a table and an index.
Any explanation or pointers to existing doc/ref material will be greatly
appreciated. An example from you usually is right on the mark and will be
greatly appreciated. I searched the archives but could not find anything on this
subject and this thread was the closest you came to explaining these terms.

Thanks a 10**6

          Followup:

selectivity is a measure of how "selective" something is (oh good -- I start
with a recursive definition...)..  Take the ALL_OBJECTS view for example,
suppose you:

create table t as select * from all_objects where owner in ( 'SYS', 'SYSTEM' );
create index t_idx1 on t(object_id);
create index t_idx2 in t(owner);

The index t_idx1 will be very "selective" in general.  object id is a very
selective column in T (in fact it is UNIQUE).  Using that index for a given
object_id = :n will return 1 record.

The index t_idx2 on the other hand is not very "selective".  owner has but two
values -- using that index for a given OWNER = :n will return LOTS of records.

Hence t_idx1 is a selective index, t_idx2 is a non-selective index.  The
selectivity of t_idx1 is better then t_idx2....

cardinality is the number of elements in something (eg: if select count(*) from
t returns 1000 -- the cardinality of T is 1000, it has 1000 rows).

selectivity of a join is computed based on number of distinct values ( NDV ) and
cardinalities of the underlying columns.

      Rating: 2
Join
      selectivity  November 02, 2002
Reviewer:
      Wolfgang Breitling  from Calgary, Canada
I found that in many simple cases the join selectivity formula given in note
68992.1 leads to the number seen in the 10053 trace. However, in many cases,
especially more complex ones, it seems obvious that the cbo uses other formulas.
Just as it does for other selectivity formulas (e.g. the one for range
selectivities). I can give the following real example, slightly simplified to
fit in 1000 words. All the pieces are from the same 10053 trace.
This is the sql. I left out the list of select columns, the group by and order
by clause and any non-join predicates:
SELECT ...
from PS_PAY_CALENDAR A
  , PS_JOB B
  , PS_RETROPAY_EARNS C
  , PS_RETROPAY_RQST D
  , PS_RETROPAYPGM_TBL E
where B.COMPANY=A.COMPANY
  and B.PAYGROUP=A.PAYGROUP
  and C.EMPLID=B.EMPLID
  and C.EMPL_RCD#=B.EMPL_RCD#
  and D.RETROPAY_SEQ_NO=C.RETROPAY_SEQ_NO
  and E.RETROPAY_PGM_ID=D.RETROPAY_PGM_ID
  and E.OFF_CYCLE=A.PAY_OFF_CYCLE_CAL

The following table lists the column NDVs, 1/NDVs and 1/max(NDV):
                                          NDV1      NDV2        1/NDV1
1/NDV2    1/max(NDV1,NDV2)
B.COMPANY          A.COMPANY                10        11    1.0000E-01
9.0909E-02        9.0909E-02
B.PAYGROUP         A.PAYGROUP               14        15    7.1429E-02
6.6667E-02        6.6667E-02
C.EMPLID           B.EMPLID              12328     41639    8.1116E-05
2.4016E-05        2.4016E-05
C.EMPL_RCD#        B.EMPL_RCD#               1         1    1.0000E+00
1.0000E+00        1.0000E+00
D.RETROPAY_SEQ_NO  C.RETROPAY_SEQ_NO     13679     14532    7.3105E-05
6.8814E-05        6.8814E-05
E.RETROPAY_PGM_ID  D.RETROPAY_PGM_ID        15        14    6.6667E-02
7.1429E-02        6.6667E-02
E.OFF_CYCLE        A.PAY_OFF_CYCLE_CAL       1         2    1.0000E+00
5.0000E-01        5.0000E-01

There is a difference in how join selectivity is calculated for ps_job (alias B)
depending where in the join order it is:

Join order[2]: PS_PAY_CALENDAR [ A] PS_RETROPAYPGM_TBL [ E] PS_RETROPAY_RQST [
D] PS_JOB [ B] PS_RETROPAY_EARNS [ C]
Now joining: PS_RETROPAYPGM_TBL [ E] *******
Join cardinality:  45 = outer (6) * inner (15) * sel (5.0000e-001)  [flag=0]
Now joining: PS_RETROPAY_RQST [ D] *******
Join cardinality:  41037 = outer (45) * inner (13679) * sel (6.6667e-002)
[flag=0]
Now joining: PS_JOB [ B] *******
Join cardinality:  79458326 = outer (41037) * inner (319483) * sel (6.0606e-003)
 [flag=0]
Now joining: PS_RETROPAY_EARNS [ C] *******
Join cardinality:  31403 = outer (79458326) * inner (239142) * sel (1.6526e-009)
 [flag=0]
All selectivities are as expected: 1/A.PAY_OFF_CYCLE_CAL, 1/E.RETROPAY_PGM_ID,
1/A.COMPANY * 1/A.PAYGROUP, and 1/B.EMPLID * 1/B.EMPL_RCD# * C.RETROPAY_SEQ_NO

But later on the cbo comes up with these selectivities:
Join order[10]: PS_PAY_CALENDAR [ A] PS_RETROPAY_RQST [ D] PS_JOB [ B]
PS_RETROPAYPGM_TBL [ E] PS_RETROPAY_EARNS [ C]
Now joining: PS_RETROPAY_RQST [ D] *******
Join cardinality:  82074 = outer (6) * inner (13679) * sel (1.0000e+000)
[flag=0]
Now joining: PS_JOB [ B] *******
Join cardinality:  187294627 = outer (82074) * inner (319483) * sel
(7.1429e-003)  [flag=0]
Now joining: PS_RETROPAYPGM_TBL [ E] *******
Join cardinality:  93647314 = outer (187294627) * inner (15) * sel (3.3333e-002)
 [flag=0]

There is no join predicate between tables A and D, so the first selectivity is
1. The second should be 1/A.COMPANY * 1/A.PAYGROUP = 6.0606e-003 as before, but
it is 7.1429e-003 which seems to be 1/B.COMPANY * 1/B.PAYGROUP. The 3rd one
follows the formula again: 1/E.RETROPAY_PGM_ID * 1/A.PAY_OFF_CYCLE_CAL. There is
no selectivity for joining C because pruning kicked in.

Looking at all the permutations in the trace and the corresponding join
selectivities, the difference appears to be related to the position of tables B
and E in the join order. If E precedes B then the expected formula is used. If B
precedes E then not.
Any comments

          Followup:

comments are: I get headaches looking at traces like this.  Personally -- I have
so very very very rarely ever used 10053 traces (i mean -- only when forced by
others to use them).  I generally send them onto the guys who wrote them for
interpretation.

Things change -- algorithms change -- I wish we didn't even "psuedo document"
via support notes what you might expect to find in these things.

I guess I'm sort of saying "to me, it is not relevant.... To support or
development in order to fix a bug, it would be super relevant.  There are so
many bigger fish to fry..."

      Rating: 4
Wow, that was
      quick  November 02, 2002
Reviewer:  Wolfgang
      Breitling  from Calgary, Canada
I agree, to a degree. I became interested in the CBO's selectivity calculations
trying to understand why it comes up with some of the ridiculously low
cardinality estimates ( like 1 when in reality there are 80,000+ ) which then
lead to disastrous access plans that take hours, provided they finish at all,
instead of minutes or seconds.

I didn't really expect an explanation. I mostly wanted to point out that the CBO
and its estimation engine are a very complex pieces of software and the rules
and calculations that are pseudo-documented, as you say, in notes are applicable
verbatim only in basic and simple cases. Also, as you said, things change and
some of the notes are several years old, probably written in Oracle 7 days. Some
e.g. 104817.1 "Discussion on Oracle Joins - Costs - Algorithms & Hints" are
getting pulled "due to problems with the content found during review" according
to Helen Schoone in a recent response on metalink.

November 02, 2002 Robert -- Thanks for the question regarding
      "Cursor Reparse -- "freedom" of moving back and forth a result set",
      version 8.1.7.2

      You Asked
Tom,
My question is about fetch to the end...re-parse..and refetch. And say Im using
NDS.

OPEN l_rc FOR 'select * from emp where deptno = 1234';
LOOP
  FETCH l_rc INTO l_emprec;
  EXIT WHEN l_rc%NOTFOUND;

-- I need to see if anyone in the cursor is a MANAGER
-- If Yes, do something..like raise an EXCEPTION and quit.
-- If No, no one is MANAGER, do something else like UTL_FILE the result.

Q1) Now, when I fetch to the END checking for MANAGER and if I determine there's
none, there is no way to go back to the 1st record, I have to OPEN l_rc
FOR...again, to get my result set back, correct ?

Q2) This will be a "soft" parse, correct ?

Q3) If the dyn. SQL is big and complex...but you know the select COLUMNS, thus
can TYPE a record and an INDEX_BY table...will you consider sticking each
fetched record into the table FIRST, do the checking....and the rest...instead
of re-OPEN the cursor ?
(If you don't like this index_by table way...do pls suggest one)

Basically Im talking about the "freedom" of moving back and forth a result set,
checking for conditions etc.
Much Thanks !

      and we said...
You are taking the wrong approach then.

You could do this:

select *
  from emp
 where deptno = :x
   and exists ( select NULL
                  from emp
                 where mgr in ( select empno
                                  from emp
                                 where deptno = :x ) )

or

scott@ORA920.US.ORACLE.COM> variable x number
scott@ORA920.US.ORACLE.COM> exec :x := 10

PL/SQL procedure successfully completed.

scott@ORA920.US.ORACLE.COM> select empno, ename, max(is_mgr) over ()
  2    from ( select empno, ename,
  3                  (select 1 from emp E2 where e2.mgr = emp.empno and rownum=1
) is_mgr
  4             from emp
  5           where deptno = :x
  6             and ename like '%A%'
  7         )
  8  /

     EMPNO ENAME      MAX(IS_MGR)OVER()
---------- ---------- -----------------
      7782 CLARK                      1

scott@ORA920.US.ORACLE.COM>
scott@ORA920.US.ORACLE.COM> exec :x := 20

PL/SQL procedure successfully completed.

scott@ORA920.US.ORACLE.COM> /

     EMPNO ENAME      MAX(IS_MGR)OVER()
---------- ---------- -----------------
      7876 ADAMS

scott@ORA920.US.ORACLE.COM>

That shows that in deptno=10 and ename like '%A%' -- that there is a MANAGER in
the result set (1 came back).  In deptno=20 and ename like '%A%' there is NOT a
manager in the result set.

You only need peek at the first row.

You want to do this in a single query.

q1) yes -- but remember unless you set isolation=serializable it could be a
DIFFERENT result set!

q2) yes

q3) only if there was a small number of records -- well under 1000, say 500 or
less. but then again, I would use a single sql statement and avoid the
procedural processing anyway.

November 03, 2002 Elie -- Thanks for the question regarding
      "Optimize Time Consumming Function", version 9.2.0

      You Asked
Hi Tom.

I have a plsql package that is being used to standardize
a set of address information and, subsequently, use these
standardized addresses to create so called "match strings"
which are, in turn, inserted into a table for later
processing in our data warehouse.

Although the package works OK, it takes about 5 hours to standardize
100,000 address records.  Since we typically need to standardize
millions of records, this package needs to be optimized.

As the package contains several functions and procedures, I was
not sure which of these is the "bottleneck".  And so, I made use
of the dbms_utility.get_time function and found out that only one
of my functions is spending 99% of the processing time.

Here's the code for this bottleneck function.

================================

function buildmatchstr(matchtype in number) return varchar2 is

    matchstr            varchar2(200);
    proc                varchar2(30);
    error_text            varchar2(512);
    a_start                number;
    crlf                varchar2(1) := chr(10);

begin

    proc := 'BUILDMATCHSTR';

    a_start := dbms_utility.get_time;

    matchstr := null;

    if (matchtype = 1) then
      if (mstr1 is not null) and (mstr2 is not null) and (mstr3 is not null)
then
          matchstr := mstr3 || substr(mstr2,1,10) || substr(mstr1,1,100);
            matchstr := substr(matchstr,1,100);
        end if;
    elsif (matchtype = 2) then
      if (mstr1 is not null) and (mstr2 is not null) and (mstr3 is not null)
then
          matchstr := mstr3 || substr(mstr2,1,10) || substr(mstr1,1,15);
        end if;
    elsif (matchtype = 3) then
      if (mstr1 is not null) and (mstr2 is not null) and (mstr3 is not null)
then
          matchstr := mstr3 || substr(mstr2,1,5) || substr(mstr1,1,10);
        end if;
    elsif (matchtype = 11) then
      if (mstr4 is not null) and (mstr2 is not null) and (mstr3 is not null)
then
        matchstr := mstr3 || substr(mstr2,1,10) || substr(mstr4,1,100);
            matchstr := substr(matchstr,1,100);
        end if;
    elsif (matchtype = 12) then
      if (mstr4 is not null) and (mstr2 is not null) and (mstr3 is not null)
then
          matchstr := mstr3 || substr(mstr2,1,10) || substr(mstr4,1,15);
        end if;
    elsif (matchtype = 13) then
      if (mstr4 is not null) and (mstr2 is not null) and (mstr3 is not null)
then
          matchstr := mstr3 || substr(mstr2,1,5) || substr(mstr4,1,10);
        end if;
    elsif (matchtype = 21) then
      if (mstr1 is not null) and (mstr5 is not null) and (mstr6 is not null)
then
          matchstr := mstr6 || substr(mstr5,1,10) || substr(mstr1,1,100);
            matchstr := substr(matchstr,1,100);
        end if;
    elsif (matchtype = 22) then
      if (mstr1 is not null) and (mstr5 is not null) and (mstr6 is not null)
then
          matchstr := mstr6 || substr(mstr5,1,5) || substr(mstr1,1,15);
        end if;
    elsif (matchtype = 23) then
      if (mstr1 is not null) and (mstr5 is not null) and (mstr6 is not null)
then
          matchstr := mstr6 || substr(mstr5,1,5) || substr(mstr1,1,10);
        end if;
    elsif (matchtype = 31) then
      if (mstr4 is not null) and (mstr5 is not null) and (mstr6 is not null)
then
          matchstr := mstr6 || substr(mstr5,1,10) || substr(mstr4,1,100);
            matchstr := substr(matchstr,1,100);
        end if;
    elsif (matchtype = 32) then
      if (mstr4 is not null) and (mstr5 is not null) and (mstr6 is not null)
then
          matchstr := mstr6 || substr(mstr5,1,5) || substr(mstr4,1,15);
        end if;
    elsif (matchtype = 33) then
      if (mstr4 is not null) and (mstr5 is not null) and (mstr6 is not null)
then
          matchstr := mstr6 || substr(mstr5,1,5) || substr(mstr4,1,10);
        end if;
    elsif (matchtype = 41) then
      if (mstr1 is not null) and (mstr7 is not null) and (mstr8 is not null)
then
          matchstr := mstr7 || mstr8 || substr(mstr1,1,10);
        end if;
    elsif (matchtype = 42) then
      if (mstr1 is not null) and (mstr7 is not null) and (mstr8 is not null)
then
          matchstr := mstr7 || mstr8 || substr(mstr1,1,5);
        end if;
    elsif (matchtype = 43) then
      if (mstr4 is not null) and (mstr7 is not null) and (mstr8 is not null)
then
          matchstr := mstr7 || mstr8 || substr(mstr4,1,10);
        end if;
    elsif (matchtype = 44) then
      if (mstr4 is not null) and (mstr7 is not null) and (mstr8 is not null)
then
          matchstr := mstr7 || mstr8 || substr(mstr4,1,5);
        end if;
    elsif (matchtype = 51) then
      if (mstr1 is not null) and (mstr9 is not null) and (mstr10 is not null)
and ((mstr7 <> mstr9) or (mstr8 <> mstr10)) then
          matchstr := mstr9 || mstr10 || substr(mstr1,1,10);
        end if;
    elsif (matchtype = 52) then
      if (mstr1 is not null) and (mstr9 is not null) and (mstr10 is not null)
and ((mstr7 <> mstr9) or (mstr8 <> mstr10)) then
          matchstr := mstr9 || mstr10 || substr(mstr1,1,5);
        end if;
    elsif (matchtype = 53) then
      if (mstr4 is not null) and (mstr9 is not null) and (mstr10 is not null)
and ((mstr7 <> mstr9) or (mstr8 <> mstr10)) then
          matchstr := mstr9 || mstr10 || substr(mstr4,1,10);
        end if;
    elsif (matchtype = 54) then
      if (mstr4 is not null) and (mstr9 is not null) and (mstr10 is not null)
and ((mstr7 <> mstr9) or (mstr8 <> mstr10)) then
          matchstr := mstr9 || mstr10 || substr(mstr4,1,5);
        end if;
    elsif (matchtype = 61) then
      if (mstr1 is not null) and (mstr11 is not null) then
          matchstr := mstr11 || mstr1;
            matchstr := substr(matchstr,1,100);
        end if;
    elsif (matchtype = 62) then
      if (mstr1 is not null) and (mstr11 is not null) then
          matchstr := mstr11 || substr(mstr1,1,15);
        end if;
    elsif (matchtype = 63) then
      if (mstr4 is not null) and (mstr11 is not null) then
          matchstr := mstr11 || mstr4;
            matchstr := substr(matchstr,1,100);
        end if;
    elsif (matchtype = 64) then
      if (mstr4 is not null) and (mstr11 is not null) then
          matchstr := mstr11 || substr(mstr4,1,15);
        end if;
    elsif (matchtype = 71) then
      if (mstr1 is not null) and (mstr12 is not null) then
          matchstr := mstr12 || mstr1;
            matchstr := substr(matchstr,1,100);
        end if;
    elsif (matchtype = 72) then
      if (mstr1 is not null) and (mstr12 is not null) then
          matchstr := mstr12 || substr(mstr1,1,15);
        end if;
    elsif (matchtype = 73) then
      if (mstr4 is not null) and (mstr12 is not null) then
          matchstr := mstr12 || mstr4;
            matchstr := substr(matchstr,1,100);
        end if;
    elsif (matchtype = 74) then
      if (mstr4 is not null) and (mstr12 is not null) then
          matchstr := mstr12 || substr(mstr4,1,15);
        end if;
    elsif (matchtype = 81) then
      if (mstr1 is not null) then
          matchstr := substr(mstr1,1,100);
        end if;
    elsif (matchtype = 82) then
      if (mstr4 is not null) then
          matchstr := substr(mstr4,1,100);
        end if;
    end if;

    buildmatchstr_time := buildmatchstr_time + (dbms_utility.get_time -
a_start);
    bm_num := bm_num + 1;

    return matchstr;

exception
    when others then
        sqlerrnum := sqlcode;
        sqlerrmsg := sqlerrm;
        error_text := 'OTHERS EXCEPTION: '||proc||crlf||
                  'SQL ERROR: '||to_char(sqlerrnum)||crlf||
                  substr(sqlerrmsg,1,255);
        dbms_output.put_line(substr(error_text,1,250));

end buildmatchstr;

================================

As you can see, the function does quite a bit of processing.

The variables mstr1 through mstr12 are all global to the package body,
and so, do not need to be passed into the function.  Only the variable
matchtype is passed in.  This variable comes from a table called
MATCHKEY_CNTL.

As mentioned above, this function is taking up 99% of the processing time.

To optimize this, I attempted to compact most of my plsql code (including this
function)
into a single SQL statement.

I have seen you do this several times on your website.

I also attempted to use a direct path insert (via the APPEND) hint in order to
reduce
redo log space as well as further speed up the process.

The table into which match strings are being inserted, LOC_MATCHKEYS, is in
NOLOGGING mode.

Here then is the SQL statement I created that calls the above function.

================================

    -- Begin insert operation

    ins_start := dbms_utility.get_time;

    sql_stmt := '['||proc||']: INSERT INTO LOC_MATCHKEYS';

    insert /*+ APPEND */ into athena.loc_matchkeys ( loc_id,
                             match_type,
                             client_id,
                             match_key )
    select    z.loc_id,
        z.matchtype,
        z.client_id,
        z.matchstr
    from
    (select    x.matchtype,
        y.loc_id,
        y.client_id,
        buildmatchstr(y.mstr1,
                  y.mstr2,
                  y.mstr3,
                  y.mstr4,
                  y.mstr5,
                  y.mstr6,
                  y.mstr7,
                  y.mstr8,
                  y.mstr9,
                  y.mstr10,
                  y.mstr11,
                  y.mstr12,
                  x.matchtype) matchstr
       from
        (select matchtype
           from athena.matchkey_cntl
          where client_id = 6
          group by matchtype
          order by matchtype
        ) x,
        (select a.loc_id,
            a.client_id,
            buildcompanystr(upper(a.name))            mstr1,
            buildaddrstr(upper(a.bill_addr1))        mstr2,
            a.bill_postal_code_id                mstr3,
            (case
                when a.do_bus_as is not null
                then
                    buildcompanystr(upper(a.do_bus_as))
                else
                    null
                end
            )                        mstr4,
            (case
                when a.ship_addr1 is not null
                then
                    buildaddrstr(upper(a.ship_addr1))
                else
                    null
                end
            )                        mstr5,
            a.ship_postal_code_id                mstr6,
            buildaddrstr(upper(a.bill_city))        mstr7,
            upper(a.bill_stateprov_id)            mstr8,
            buildaddrstr(upper(a.ship_city))        mstr9,
            upper(a.ship_stateprov_id)            mstr10,
            (case
                when a.client_id = 6
                then
                    ryder_zoe_dim_val_func(a.bill_postal_code_id,'branch')
                else
                    null
                end
            )                        mstr11,
            (case
                when a.client_id = 6
                then
                    ryder_zoe_dim_val_func(a.bill_postal_code_id,'cbu')
                else
                    null
                end
            )                        mstr12
           from athena.locations a,
            athena.tmp_id b
          where a.loc_id = b.loc_id
            and b.process_flag is null
        ) y
    ) z
    where z.matchstr is not null;

    tot_ins_loc_matchkeys := sql%rowcount;

    tot_ins_loc_matchkeys_time := dbms_utility.get_time - ins_start;

    wmsg := '['||proc||']: TOTAL INSERTED LOC_MATCHKEYS
RECORDS='||to_char(tot_ins_loc_matchkeys)||
        ' AND TOOK '||to_char(round(tot_ins_loc_matchkeys_time/6000,2))||'
MINUTES';
    dbms_output.put_line(wmsg);

    tot_process_time := dbms_utility.get_time - tot_start;

    wmsg := '['||proc||']: TOTAL PROCESS TIME =
'||to_char(round(tot_process_time/6000,2))||' MINUTES';
    dbms_output.put_line(wmsg);

================================

I created a new buildmatchstr function that requires that I pass it
the (formally global) mstr1 through mstr12 variables as well as the
matchtype variable.

If I am interpreting this SQL statement correctly, the buildmatchstr function
is being iterated over each and every LOC_ID value retrieved from the "y"
in-line view, which is what needs to happen.

As I am fairly new to creating such "in-line view" SELECTs, I was
very surprised that the statement actually works.

However, this statement still takes an hour to process 100,000 records,
with my buildmatchstr function being the time consumming culprit.

And so, I decided to utilize yet another tip I learned from your website
and that is to incorporate the buildmatchstr function totally within the
SQL statement.  This way, I thought, I would avoid a lot of context
switching between the plsql and SQL engines, and so, save time.

Here, then, is my final code.

================================

    -- Begin insert operation

    ins_start := dbms_utility.get_time;

    sql_stmt := '['||proc||']: INSERT INTO LOC_MATCHKEYS';

    insert /*+ APPEND */ into athena.loc_matchkeys ( loc_id,
                             match_type,
                             client_id,
                             match_key )
    select    z.loc_id,
        z.matchtype,
        z.client_id,
        z.matchstr
    from
    (select    x.matchtype,
        y.loc_id,
        y.client_id,
        (case
            when ( (x.matchtype = 1) and (y.mstr1 is not null) and (y.mstr2 is
not null) and (y.mstr3 is not null) ) then
                substr(y.mstr3 || substr(y.mstr2,1,10) || substr(y.mstr1,1,100),
1, 100)
            when ( (x.matchtype = 2) and (y.mstr1 is not null) and (y.mstr2 is
not null) and (y.mstr3 is not null) ) then
                y.mstr3 || substr(y.mstr2,1,10) || substr(y.mstr1,1,15)
            when ( (x.matchtype = 3) and (y.mstr1 is not null) and (y.mstr2 is
not null) and (y.mstr3 is not null) ) then
                y.mstr3 || substr(y.mstr2,1,5) || substr(y.mstr1,1,10)
            when ( (x.matchtype = 11) and (y.mstr4 is not null) and (y.mstr2 is
not null) and (y.mstr3 is not null) ) then
                substr(y.mstr3 || substr(y.mstr2,1,10) || substr(y.mstr4,1,100),
1, 100)
            when ( (x.matchtype = 12) and (y.mstr4 is not null) and (y.mstr2 is
not null) and (y.mstr3 is not null) ) then
                y.mstr3 || substr(y.mstr2,1,10) || substr(y.mstr4,1,15)
            when ( (x.matchtype = 13) and (y.mstr4 is not null) and (y.mstr2 is
not null) and (y.mstr3 is not null) ) then
                y.mstr3 || substr(y.mstr2,1,5) || substr(y.mstr4,1,10)
            when ( (x.matchtype = 21) and (y.mstr1 is not null) and (y.mstr5 is
not null) and (y.mstr6 is not null) ) then
                substr(y.mstr6 || substr(y.mstr5,1,10) || substr(y.mstr1,1,100),
1, 100)
            when ( (x.matchtype = 22) and (y.mstr1 is not null) and (y.mstr5 is
not null) and (y.mstr6 is not null) ) then
                y.mstr6 || substr(y.mstr5,1,5) || substr(y.mstr1,1,15)
            when ( (x.matchtype = 23) and (y.mstr1 is not null) and (y.mstr5 is
not null) and (y.mstr6 is not null) ) then
                y.mstr6 || substr(y.mstr5,1,5) || substr(y.mstr1,1,10)
            when ( (x.matchtype = 31) and (y.mstr4 is not null) and (y.mstr5 is
not null) and (y.mstr6 is not null) ) then
                substr(y.mstr6 || substr(y.mstr5,1,10) || substr(y.mstr4,1,100),
1, 100)
            when ( (x.matchtype = 32) and (y.mstr4 is not null) and (y.mstr5 is
not null) and (y.mstr6 is not null) ) then
                y.mstr6 || substr(y.mstr5,1,5) || substr(y.mstr4,1,15)
            when ( (x.matchtype = 33) and (y.mstr4 is not null) and (y.mstr5 is
not null) and (y.mstr6 is not null) ) then
                y.mstr6 || substr(y.mstr5,1,5) || substr(y.mstr4,1,10)
            when ( (x.matchtype = 41) and (y.mstr1 is not null) and (y.mstr7 is
not null) and (y.mstr8 is not null) ) then
                y.mstr7 || y.mstr8 || substr(y.mstr1,1,10)
            when ( (x.matchtype = 42) and (y.mstr1 is not null) and (y.mstr7 is
not null) and (y.mstr8 is not null) ) then
                y.mstr7 || y.mstr8 || substr(y.mstr1,1,5)
            when ( (x.matchtype = 43) and (y.mstr4 is not null) and (y.mstr7 is
not null) and (y.mstr8 is not null) ) then
                y.mstr7 || y.mstr8 || substr(y.mstr4,1,10)
            when ( (x.matchtype = 44) and (y.mstr4 is not null) and (y.mstr7 is
not null) and (y.mstr8 is not null) ) then
                y.mstr7 || y.mstr8 || substr(y.mstr4,1,5)
            when ( (x.matchtype = 51) and (y.mstr1 is not null) and (y.mstr9 is
not null) and (y.mstr10 is not null) and ((y.mstr7 <> y.mstr9) or (y.mstr8 <>
y.mstr10)) ) then
                y.mstr9 || y.mstr10 || substr(y.mstr1,1,10)
            when ( (x.matchtype = 52) and (y.mstr1 is not null) and (y.mstr9 is
not null) and (y.mstr10 is not null) and ((y.mstr7 <> y.mstr9) or (y.mstr8 <>
y.mstr10)) ) then
                y.mstr9 || y.mstr10 || substr(y.mstr1,1,5)
            when ( (x.matchtype = 53) and (y.mstr4 is not null) and (y.mstr9 is
not null) and (y.mstr10 is not null) and ((y.mstr7 <> y.mstr9) or (y.mstr8 <>
y.mstr10)) ) then
                y.mstr9 || y.mstr10 || substr(y.mstr4,1,10)
            when ( (x.matchtype = 54) and (y.mstr4 is not null) and (y.mstr9 is
not null) and (y.mstr10 is not null) and ((y.mstr7 <> y.mstr9) or (y.mstr8 <>
y.mstr10)) ) then
                y.mstr9 || y.mstr10 || substr(y.mstr4,1,5)
            when ( (x.matchtype = 61) and (y.mstr1 is not null) and (y.mstr11 is
not null) ) then
                substr(y.mstr11 || y.mstr1, 1, 100)
            when ( (x.matchtype = 62) and (y.mstr1 is not null) and (y.mstr11 is
not null) ) then
                y.mstr11 || substr(y.mstr1,1,15)
            when ( (x.matchtype = 63) and (y.mstr4 is not null) and (y.mstr11 is
not null) ) then
                substr(y.mstr11 || y.mstr4, 1, 100)
            when ( (x.matchtype = 64) and (y.mstr4 is not null) and (y.mstr11 is
not null) ) then
                y.mstr11 || substr(y.mstr4,1,15)
            when ( (x.matchtype = 71) and (y.mstr1 is not null) and (y.mstr12 is
not null) ) then
                substr(y.mstr12 || y.mstr1, 1, 100)
            when ( (x.matchtype = 72) and (y.mstr1 is not null) and (y.mstr12 is
not null) ) then
                y.mstr12 || substr(y.mstr1,1,15)
            when ( (x.matchtype = 73) and (y.mstr4 is not null) and (y.mstr12 is
not null) ) then
                substr(y.mstr12 || y.mstr4, 1, 100)
            when ( (x.matchtype = 74) and (y.mstr4 is not null) and (y.mstr12 is
not null) ) then
                y.mstr12 || substr(y.mstr4,1,15)
            when ( (x.matchtype = 81) and (y.mstr1 is not null) ) then
                substr(y.mstr1,1,100)
            when ( (x.matchtype = 82) and (y.mstr4 is not null) ) then
                substr(y.mstr4,1,100)
            else
                null
            end
        )    matchstr
       from
        (select matchtype
           from athena.matchkey_cntl
          where client_id = 6
          group by matchtype
          order by matchtype
        ) x,
        (select a.loc_id,
            a.client_id,
            buildcompanystr(upper(a.name))            mstr1,
            buildaddrstr(upper(a.bill_addr1))        mstr2,
            a.bill_postal_code_id                mstr3,
            (case
                when a.do_bus_as is not null
                then
                    buildcompanystr(upper(a.do_bus_as))
                else
                    null
                end
            )                        mstr4,
            (case
                when a.ship_addr1 is not null
                then
                    buildaddrstr(upper(a.ship_addr1))
                else
                    null
                end
            )                        mstr5,
            a.ship_postal_code_id                mstr6,
            buildaddrstr(upper(a.bill_city))        mstr7,
            upper(a.bill_stateprov_id)            mstr8,
            buildaddrstr(upper(a.ship_city))        mstr9,
            upper(a.ship_stateprov_id)            mstr10,
            (select    branch_code
               from    athena.ryder_zoe_dim
              where    zip_code_id = a.bill_postal_code_id)    mstr11,
            (select    cbu_code
               from    athena.ryder_zoe_dim
              where    zip_code_id = a.bill_postal_code_id)    mstr12
           from athena.locations a,
            athena.tmp_id b
          where a.loc_id = b.loc_id
            and b.process_flag is null
        ) y
    ) z
    where z.matchstr is not null;

    tot_ins_loc_matchkeys := sql%rowcount;

    tot_ins_loc_matchkeys_time := dbms_utility.get_time - ins_start;

    wmsg := '['||proc||']: TOTAL INSERTED LOC_MATCHKEYS
RECORDS='||to_char(tot_ins_loc_matchkeys)||
        ' AND TOOK '||to_char(round(tot_ins_loc_matchkeys_time/6000,2))||'
MINUTES';
    dbms_output.put_line(wmsg);

    tot_process_time := dbms_utility.get_time - tot_start;

    wmsg := '['||proc||']: TOTAL PROCESS TIME =
'||to_char(round(tot_process_time/6000,2))||' MINUTES';
    dbms_output.put_line(wmsg);

================================

Question
========
Do you think that I am going about this (trying to speed up my code
and reduce redo log space issues) in the best way?  Can you suggest
a better way to do this?

Note that, in a previous attempt, I have re-written my package to make
use of the BULK COLLECT and FORALL keywords and saw only a small
improvement (2%) in speed.

And I have thought of creating a function based index with respect to
the above buildmatchrstr function.  But I am not sure this is possible
because the data that buildmatchstr works on does not reside in any
specific Oracle table.  In order to create any type of index, the index
must be created for a table.

One thought I had was that if I could somehow replace the above umongous CASE
statement
with a global temporary table and then use Oracle's SQL engine to do something
like
(in psuedo code):

    select    substr(y.mstr3 || substr(y.mstr2,1,10) || substr(y.mstr1,1,100),
1, 100)
      from    athena.global_temp_table
     where    (x.matchtype = 1)
       and    (y.mstr1 is not null)
       and    (y.mstr2 is not null)
       and    (y.mstr3 is not null)

And do this for each of the "x.matchtype" conditions ( = 1, = 2, ..., = 82 ).

Perhaps then the code would be much faster.

Your help is much appreciated.

Thank you.

      and we said...
Well, I think we can make that tons of procedural code table driven which could
help speed it up.  Looking at the "patterns" I see -- there are basically 4
different string concat's -- with 4 different "if" conditions.  By setting up a
control record -- we can get the code down to this:

ops$tkyte@ORA920.US.ORACLE.COM> create or replace type ctlRec
  2  as object
  3          ( field1     int,
  4            len1       int,
  5            field2     int,
  6            len2       int,
  7            field3     int,
  8            len3       int,
  9            field4a    int,
 10            field4b    int,
 11            field5a    int,
 12            field5b    int,
 13            maxl       int
 14          )
 15  /

Type created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create or replace type ctlRecs as table of
ctlRec
  2  /

Type created.

we'll use those data structures to control what fields we check for nullness,
lengths and such...

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create or replace package my_pkg
  2  as
  3          function matchString( matchtype in number ) return varchar2;
  4  end;
  5  /

Package created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create or replace package body my_pkg
  2  as
  3
  4  type array is table of varchar2(4000) index by binary_integer;
  5
  6  g_ctlData ctlRecs;
  7  g_mstr       array;
  8

Instead of 10 variables mstr1 .. mstr10, we'll want them in an array g_mstr(1)
.. g_mstr(10) so it can work generically:

  9  function matchString( matchType in number ) return varchar2
 10  is
 11          l_ctlData ctlRec := g_ctlData(matchType);
 12  begin
 13
 14          if ( matchType <= 44 )
 15          then
 16                  if ( g_mstr( l_ctlData.field1 ) is not null AND
 17                       g_mstr( l_ctlData.field2 ) is not null AND
 18                       g_mstr( l_ctlData.field3 ) is not null )
 19                  then
 20                          return substr(
 21                           g_mstr( l_ctlData.field1 ) ||
 22                           substr( g_mstr( l_ctlData.field2 ), 1,
l_ctlData.len2 ) ||
 23                           substr( g_mstr( l_ctlData.field3 ), 1,
l_ctlData.len3 ), l_ctlData.maxl );
 24                  end if;
 25          elsif ( matchType <= 54 )
 26          then
 27                  if ( g_mstr( l_ctlData.field1 ) is not null AND
 28                       g_mstr( l_ctlData.field2 ) is not null AND
 29                       g_mstr( l_ctlData.field3 ) is not null AND
 30                           (g_mstr( l_ctlData.field4a ) <> g_mstr(
l_ctlData.field4b ) OR
 31                            g_mstr( l_ctlData.field5a ) <> g_mstr(
l_ctlData.field5b ))
 32                          )
 33                  then
 34                          return substr(
 35                           g_mstr( l_ctlData.field1 ) ||
 36                           substr( g_mstr( l_ctlData.field2 ), 1,
l_ctlData.len2 ) ||
 37                           substr( g_mstr( l_ctlData.field3 ), 1,
l_ctlData.len3 ), l_ctlData.maxl );
 38                  end if;
 39          elsif ( matchType <= 74 )
 40          then
 41                  if ( g_mstr( l_ctlData.field1 ) is not null AND
 42                       g_mstr( l_ctlData.field2 ) is not null  )
 43                  then
 44                          return substr( g_mstr( l_ctlData.field1 ) ||
g_mstr( l_ctlData.field2 ), 1, 100 );
 45                  end if;
 46          else
 47                  if ( g_mstr( l_ctlData.field1 ) is not null )
 48                  then
 49                          return substr( g_mstr( l_ctlData.field1 ), 1, 100
);
 50                  end if;
 51          end if;
 52          return null;
 53  end;
 54

so there are many many less conditional checks here -- you should ORDER the
"IF" statement above in order of "most probable" to "least probable" -- eg: if
most matchtypes are 80's and the second most are 50's  -- make that the FIRST if
condition

   if ( matchtype between 81 and 82 )
   then
       ...
   elsif ( matchtype between 51 and 54 )
   then
      .....

so the if statement short circuts ASAP

 55
 56  begin
 57          g_ctlData.extend(85);
 58          g_ctlData(1) := ctlRec( 3, 9999, 2, 10, 1, 100, null, null, null,
null, 9999  );
 59          g_ctlData(2) := ctlRec( 3, 9999, 2, 10, 1, 15, null, null, null,
null, 9999  );
 60          g_ctlData(3) := ctlRec( 3, 9999, 2, 5, 1, 10, null, null, null,
null, 9999  );
 61          g_ctlData(11) := ctlRec( 3, 9999, 2, 10, 4, 100, null, null, null,
null, 100  );
 62          g_ctlData(12) := ctlRec( 3, 9999, 2, 10, 4, 15, null, null, null,
null, 9999  );
 63          g_ctlData(13) := ctlRec( 3, 9999, 2, 5, 4, 10, null, null, null,
null, 9999  );
 64          g_ctlData(21) := ctlRec( 6, 9999, 5, 10, 1, 100, null, null, null,
null, 100  );
 65          g_ctlData(22) := ctlRec( 6, 9999, 5, 5, 1, 15, null, null, null,
null, 9999  );
 66          g_ctlData(23) := ctlRec( 6, 9999, 5, 5, 1, 10, null, null, null,
null, 9999  );
 67          g_ctlData(31) := ctlRec( 6, 9999, 5, 10, 4, 100, null, null, null,
null, 100  );
 68          g_ctlData(32) := ctlRec( 6, 9999, 5, 5, 4, 15, null, null, null,
null, 9999  );
 69          g_ctlData(33) := ctlRec( 6, 9999, 5, 5, 4, 10, null, null, null,
null, 9999  );
 70          g_ctlData(41) := ctlRec( 7, 9999, 8, 9999, 1, 10, null, null, null,
null, 9999  );
 71          g_ctlData(42) := ctlRec( 7, 9999, 8, 9999, 1, 5, null, null, null,
null, 9999  );
 72          g_ctlData(43) := ctlRec( 7, 9999, 8, 9999, 4, 10, null, null, null,
null, 9999  );
 73          g_ctlData(44) := ctlRec( 7, 9999, 8, 9999, 4, 5, null, null, null,
null, 9999  );
 74
 75          g_ctlData(51) := ctlRec( 9, 9999, 10, 9999, 1, 10, 7, 9, 8, 10,
9999  );
 76          g_ctlData(52) := ctlRec( 9, 9999, 10, 9999, 1, 5, 7, 9, 8, 10, 9999
 );
 77          g_ctlData(53) := ctlRec( 9, 9999, 10, 9999, 4, 10, 7, 9, 8, 10,
9999  );
 78          g_ctlData(54) := ctlRec( 9, 9999, 10, 9999, 4, 5, 7, 9, 8, 10, 9999
 );
 79
 80          g_ctlData(61) := ctlRec( 11, 9999, 1, 9999, null, null, null, null,
null, null, 100  );
 81          g_ctlData(62) := ctlRec( 11, 9999, 1, 15, null, null, null, null,
null, null, 9999  );
 82          g_ctlData(63) := ctlRec( 11, 9999, 4, 9999, null, null, null, null,
null, null, 100  );
 83          g_ctlData(64) := ctlRec( 11, 9999, 4, 15, null, null, null, null,
null, null, 9999  );
 84          g_ctlData(71) := ctlRec( 12, 9999, 1, 9999, null, null, null, null,
null, null, 100  );
 85          g_ctlData(72) := ctlRec( 12, 9999, 1, 15, null, null, null, null,
null, null, 9999  );
 86          g_ctlData(73) := ctlRec( 12, 9999, 4, 9999, null, null, null, null,
null, null, 100  );
 87          g_ctlData(74) := ctlRec( 12, 9999, 4, 15, null, null, null, null,
null, null, 9999  );
 88
 89          g_ctlData(81) := ctlRec( 1, 100, null, null, null, null, null,
null, null, null, 9999  );
 90          g_ctlData(82) := ctlRec( 4, 100, null, null, null, null, null,
null, null, null, 9999  );
 91  end my_pkg;
 92  /

Package body created.

ops$tkyte@ORA920.US.ORACLE.COM>

the above is known as "elaboration code", it'll run ONCE the first time the
package is touched in a session and load up our control records -- telling us
what fields to use in the g_mstr array and what the max lengths are...

Give that a whirl  - also consider NCOMPing the code (natively compiling it)..

Oh and check my ctlRec setup -- I didn't check and recheck the field numbers and
offsets -- make sure they are right!
Review &
      Followup

      Rating: 5
Optimize Time
      Consumming Function  November 03, 2002
Reviewer:
      Elie Grunhaus  from Rochester, NY USA
Thanks so much, Tom, for your hard work in re-writing my match string function.

I will give this a try and see if it speeds my processing.

I must confess, though, that I don't totally understand
what you've written in the package initialization section:

56  begin
 57          g_ctlData.extend(85);
 58          g_ctlData(1) := ctlRec( 3, 9999, 2, 10, 1, 100, null, null, null,
null, 9999  );

I guess my confusion really stems from my not really understanding the fields
you've defined in the ctlRec object:

ops$tkyte@ORA920.US.ORACLE.COM> create or replace type ctlRec
  2  as object
  3          ( field1     int,
  4            len1       int,
  5            field2     int,
  6            len2       int,
  7            field3     int,
  8            len3       int,
  9            field4a    int,
 10            field4b    int,
 11            field5a    int,
 12            field5b    int,
 13            maxl       int
 14          )
 15  /

Type created.

If you would kindly explain what these fields represent, it would greatly help
me.  You mentioned that I should check the "offsets" and such in the
initialization setup.  I'm not sure what offsets you are referring to.  I am
very new in the use of objects, and so, perhaps these "offsets" are positions
within the ctlRec object type you created?  I'm not sure.

Thank you again.

It is much appreciated.

          Followup:

Well, ignore for a moment that it is an "object".  Just think of it as a record.
 You have 4 ways to process your data basically:

if (matchtype = 1)
then
  if (mstr1 is not null) and (mstr2 is not null) and (mstr3 is not null)
  then
     matchstr := mstr3 || substr(mstr2,1,10) || substr(mstr1,1,100);
     matchstr := substr(matchstr,1,100);
  end if;
....
elsif (matchtype = 51)
then
   if (mstr1 is not null) and (mstr9 is not null) and (mstr10 is not null)
                   and ((mstr7 <> mstr9) or (mstr8 <> mstr10))
   then
      matchstr := mstr9 || mstr10 || substr(mstr1,1,10);
   end if;
...

elsif (matchtype = 61) then
  if (mstr1 is not null) and (mstr11 is not null) then
     matchstr := mstr11 || mstr1;
     matchstr := substr(matchstr,1,100);
  end if;
...
elsif (matchtype = 81)
then
  if (mstr1 is not null) then
      matchstr := substr(mstr1,1,100);
  end if;

Each of these inspects from 1 to 5 fields (hence field1 .. field5 in the
record).  These fields are easily indexed in an array (mstr1..mstr10 easily go
into an array 1 .. 10).  So all of the IF's are easily turned into:

if (matchtype = 1)
then
  if (array( ctl_record.field1 ) is not null)
      and ( array( ctl_record.field2)  is not null)
      and ( array( clt_record.field3)  is not null)
  then

that is -- field1, field2, field3, field4, field5 should all be matching to what
you were comparing in the IF statement before.  If you had:

  if ( mstr1 is not null and mstr2 is not null and mstr3 is not null )

we should find that the control record has 1, 2, 3 as indexes in it.

Now, I used 4a/4b and 5a/5b cause you compared field to field for those.

the len1, len2, len3 are used to do the substr's inside the IF statement.  I
used 9999 to represent "the whole string".  I simply used that to substr with.
the maxl field is when you chopped things off at 100 characters -- i do the same
(or use 9999 to return the entire string)

This is a very common programming technique in procedural languages. when you
can load up an array of records that help you turn hundreds of lines of
procedural *but very similar* code into a handful.

      Rating: 5
Optimize Time
      Consumming Function  November 03, 2002
Reviewer:
      Elie Grunhaus  from Rochester, NY USA
Thank you, Tom, for your quick and very educational followup to my question.

I will give your code re-write a shot.

It's wonderful having a helpful resource like you
so available.

November 03, 2002 -- Thanks for the question regarding "Partition
      views", version 8.1.7.3

      You Asked
Hi Tom,

I have huge tables (more than 10 Gigabytes of sizes).
We have Oracle 8.1.5 and we are going to upgrade to ORacle 92i SE.
I would like to partition these tables using the hash partition (this is the
best alternative for these tables), but partitioning is not available in SE.
So my questions are the follwings
1-What can I do to improve performance in the access of these huge tables if I
don't use partition views?
2-Is there anything similar to partitioning in the SE besides partition views?
3-Do you think that using partition views will complicate a lot the maintenance
of these tables?
4-Will the partition view work correctly if I create the FK and constraints over
the tables instead of the partition view?
5-Can I create triggers over and againts the partition view?

Thanks for your help.

      and we said...
Hmm, version in the question input fiels- 8173, but you are using 815 and going
to 9iR2... anyway

1) I would not recommend partition views -- they are deprecated features (eg:
not enhanced since version 7.3 when they were introduced).

There is nothing that says a 10gig table is inheritly slow.  A 10gig table that
is read via an index is AS FAST as a 1meg table read via an index.  It is 100% a
function of the number of rows returned.

If you full scan 10gig of data -- that'll take as long as full scanning 10gig of
data takes on your system.

2) no.  You have to remember -- the standard editions of databases (this is true
for IBM, Microsoft, Oracle, et. al) are designed for small workgroups, smaller
databases, less features.

3) I think using partition views could be a bad idea as they are not really the
direction we have moved in.  You can try them but -- given the amount of
information in this question -- I cannot say if they would "help", "hurt", "do
nothing at all for you"

4) don't know what you mean by that.  you create fk constraints over tables.
Having fk constraints on views is a new 9i feature for query rewrite only (part
of EE as well).

5) you need instead of triggers to do that.  They are part of 817 SE, EE and PE
and up.  But, if performance is the name of the game, triggers on views will be
something that adds overhead.

I have a feeling -- if you look at the cost -- the cost of the software to do
this will be less then the cost of a developer for less then one year of
development (to try and tackle this problem).  It is a "make vs buy" decision
and ultimately yours -- but, given the amount of time and energy you will
personally spend on this, looking at getting the software that does it for you
is something you should consider as well.
Review &
      Followup

      Rating:
      5
  November 02, 2002
Reviewer:
      A reader
Sorry for the mix of versions. This question is about another database that we
have which is 8.1.5 and we want to upgrade it (with the rest that we have in
8.1.7.3) to 9i2.
I understand your point and I think you are right, but the company doesn't have
the money to upgrade this database to EE.
I will follow your advice and don't use partition views. But I have a doubt, Is
there a size limit in which we can say that a table should be migrated to a
partition table? I mean these huge tables are growing every day and in some
point in the time they will have 15 or 20 gigabytes or more. Have you ever seen
an OLTP application with common tables of 40 Gigabytes of size? I think that in
this case (tables of 40 gig) if the table is analyzed and has the correct
indexes in order to support the application, the only problem that I can see is
connected with maintenance and backups. Am i right?
Thanks

          Followup:

Then I fail to see how the company has the money to pay someone to design and
implement software -- cause the cost of buying the feature is less then the cost
of thinking about how to do it yourself (and then doing it and then testing it
and then debugging it and then maintaining it)......  (always confused by this
-- plenty of money to DIY, but not enough to buy it for less -- and yes, I would
say this is less.  Lets say you paid list price and are running this on the
BIGGEST machine you can run SE on which is 4 cpus -- we are talking 160k which
is what you pay per year for a average paid employee in salary, taxes, benefits,
space to sit, hardware to use, training, hr overhead, etc etc etc).  Anyway.

I've seen applications with tables much larger then that -- yes.  Partitioning
for OLTP in general is for administrative and availability purposes -- not
performance (OLTP being characterized by lots of indexed reads and partitioning
doesn't really speed those up very much).

      Rating: 3
Can you
      explain...  November 03, 2002
Reviewer:
      Matt  from Australia
..your comments on partitioned table performance in more dtail please?

I have been thinking of partitioning some several of our tables in our system in
order to reduce the query time. These tables are all joined at various time in
the applications processing. I consider that joining small tables (or partitions
in this case ) would be faster that joining the unpartioned tables.

The system is a hybrid (more Decision Support than OLTP)

Is the above consistent with your comments? Or do I have to re-evaluate some of
my understanding of Oracle?

Regards

          Followup:

Can you explain in more detail why you think joining smaller partitions would be
faster then joining a single big pair of tables?

You don't have to re-evaluate your understanding of Oracle -- but you might want
to think about partitioning in general, understand the concepts behind it -- how
it works and then figure out in your mind if you believe it can make you go
faster.

Consider this example:

select *
   from t1, t2
  where t1.pk = t2.fk
    and t1.status = 5;

Now, suppose that there is no index on t1.status.  But, t1 is partitioned into N
partitions BY status.  This query will benefit greatly by partitioning on
status.

Now, assume t1 is not partitioned by status but is partioned by PK (range or
hash partitioned).  Now, we might be able to come up with a nicer parallel plan
(but were you going to use PQ?) but in general, there would be the same amount
of data to munge through.

Now, assume t1 is not partitioned by status or pk -- partitioning would
(hopefully) have no net effect on the query performance at all....

      Rating:
      3
Additional...  November 04, 2002
Reviewer:
      Matt  from Australia
"Can you explain in more detail why you think joining smaller partitions would
be faster then joining a single big pair of tables?"

I was thinking that reducing the quantity of data in the tables involved in the
join would decrease the amount of work that was involved - simply less physical
data to plough through.

In my mind I was envisaging some of the advantages that you were pointing out
(by partitioning the data in an intelligent way (given an understanding of the
app) I might be able to stick all the data involved in to partitions in such a
way as to make it easier for Oracle to combine the tables. You seem to be
indicating that this may not be the best thing to do.

I haven't considered PQ at this point.

I guess that you are right and I need to look into the partion functionality
(and PQ) and the app in a bit more detail and see what is most appropriate (and
benchmark).

November 03, 2002 Syed Jaffar -- Thanks for the question regarding
      "redo generation", version 8.1.7

      You Asked
Hi Tom,

We are runing Oracle 8i under HP unix platform, we have online transaction
system (OLTP) and our DB has three redo log groups,10MB each.  We are running
the database in archive log mode.  We have allocated 5 GB for archive log space.
 Normally, this space is moe than enough for our archive log files.  At the end
of every month, we take cold backups and removes these archive log files to free
up the space. Some time our programming staff run a maintenance job without
prior testing and job enter in to loop and generate the continous redolog and
hence utlize the 100% of archive log space which cause halt the database
operation.  Is there any way to know the rate of redo log geneated per minute to
trace such jobs and avoid halting instance.

Thanks in advance

      and we said...
Well, in Oracle9i you could use the resource manager to limit the amount of UNDO
generated -- hence limiting the amount of redo generated (not a 100% match
between capability but close).

You could use RESOURCE PROFILES to limit (govern) the amount of resources a user
can use as well. You can limit cpu_per_call/per_session/logical ios per
session/per call (logical IO's per call would probably be your best bet here).

You could also setup a "monitor" of your own either as a script, a dbms_job job
or an OEM report/event that would monitor v$sesstat looking for sessions
generating GOBS of redo that you would report out so you could proactively
"kill" them.

setting up a script to monitor your archive destination would be good as well.

I'll offer my advice/opinion here as well.  Archives need to be MOVED OFF OF THE
SYSTEM ASAP, if not moved, at least COPIED.  ARCHIVES are used with your backups
(which should be off of the system) to recover in the event of media failure.
If you suffer widespread media failure -- you'll wipe out your archives.  You
also want AT LEAST two copies of them.  I question your methods as they exist
right now.  You should be getting these archives off to tape -- preferably two
different tapes and then you can safely remove them from the system.
November 03, 2002 Arumugam -- Thanks for the question regarding
      "doubt in REFCURSOR", version 8.1.7

      You Asked
Hello TOM,
     1.     I have declared as a ref cursor and used it with OPEN FOR. How can i
use the variable for dynamically created table.
For example..

                TYPE my_ref IS REFCURSOR;
                my_cur my_ref;
                rec    dynamictable%rowtype; - How to declare for
                                                dynamic table.
           begin
              OPEN my_cur for strSql||dynamictable;
              FETCH my_cur into rec.
              etc.
           end;

regards,
siva

      and we said...
You cannot -- you do not know the number or type of the outputs -- hence you
CANNOT use a ref cursor.  If you have my book "Expert one on one Oracle" -- i
cover this in huge detail (when you can and cannot use NDS -- native dynamic
sql).

You will be using DBMS_SQL.  Search this site for

dump_csv

for examples of dynamically processing a query.
November 03, 2002 David -- Thanks for the question regarding "Using
      a trigger to insert a sequence value in PK column", version 8.1.6

      You Asked
Hi Tom.
I've been using the example from your book to insert the value from a sequence
using a trigger. It works great. Thanks.
How can I retrieve the value that was inserted - assuming I need the value to
insert into the related (child) tables?
For example, I insert an Invoice header row now need the sequence number to
insert into each related line item row.

      and we said...
easiest would be for the code to do it itself:

   insert into t values ( seq.nextval, .... );

   select seq.currval from dual; <=== returns your SESSIONS last Nextval from
seq

or to use the insert ... returning clause:

ops$tkyte@ORA920.US.ORACLE.COM> create table t ( x int primary key, y int );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create sequence seq;

Sequence created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create or replace trigger t_trigger before
insert on t
  2  for each row
  3  begin
  4          select seq.nextval into :new.x from dual;
  5  end;
  6  /

Trigger created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> declare
  2          l_new_key number;
  3  begin
  4          insert into t ( y ) values ( 55 ) returning x into l_new_key;
  5
  6          dbms_output.put_line( 'New key = ' || l_new_key );
  7  end;
  8  /
New key = 1

PL/SQL procedure successfully completed.

November 03, 2002 sudhakar -- Thanks for the question regarding
      "Wait event: DB file sequential read", version 9.2.0.1.0

      You Asked
I am using a database link to connect from one 9i database to another 9i
database.

I am frequently getting "db file sequential read" wait event at the linked
server. Due to that all my queries are having a slow response.

I tried to find the table that is causing the sequential read, it has 560,780
rows in it. Unfortunately, I am not able to make use of any primary key on it.

I have created a seperate tablespace for this table alone with 16k block size.
Still it is very slow. I have 1GB of RAM, Oracle SGA 400MB.

How should I make all my queries faster? Can you give me any suggestions on
this.

      and we said...
buy faster disks?

stripe the data across multiple disks?

db file sequential read is due to INDEXED reads by the way -- it is single block
IO, it is not the result of a full scan.  So.... you may well be looking at the
wrong place.  the p1, p2, p3 info in v$session_wait can be used to determine the
actual object being waited on.  A sequential read is usually a single-block
read, although it is possible to see sequential reads for more than one block
(See P3). This wait may also be seen for reads from datafile headers (P2=1
indicates a file header read) .

Block reads are fairly inevitable so the aim should be to minimise un-necessary
IO. This is best achieved by good application design and efficient execution
plans. Changes to execution plans can yield orders of magnitude changes in
performance. Tweaking at system level usually only achieves percentage gains.
The following points may help:

    * Check for SQL using unselective index scans

    * A larger buffer cache can (not will, "might") help

    * A less obvious issue which can affect the IO rates is how well data is
clustered physically. Eg: Assume that you frequently fetch rows from a table
where a column is between two values via an index scan. If there are 100 rows in
each index block then the two extremes are:
         1. Each of the table rows is in a different physical block (100 blocks
need to be read for each index block)
         2. The table rows are all located in the few adjacent blocks (a handful
of blocks need to be read for each index block)
      Pre-sorting or re-organising data can help to tackle this in severe
situations.

    * See if partitioning can be used to reduce the amount of data you need to
look at.

November 03, 2002 lakshmi -- Thanks for the question regarding
      "Select top 10 values without rowid", version 8.1.7

      You Asked
Hi Tom
         Is it possible to select top 10 values for a column from a table
without using rowid & rank() function in the select statement.

Thanks in advance

regards

Lakshmi

      and we said...
yes.

but it would be horribly slow.

  1  select sal from emp
  2* where 3 >= (select count(distinct sal) from emp e2 where e2.sal >= emp.sal
)
scott@ORA920.LOCALHOST> /

       SAL
----------
      2975
      3000
      5000
      3000

scott@ORA920.LOCALHOST>

there are the top 3 salaries (NOTE: not 3 rows, there are 3 top salaries and you
get them all)

Review &
      Followup

      Rating:
      4
  November 04, 2002
Reviewer:
      Lakshmi
Tom many thanks for your quick and accurate response .

regards

November 04, 2002 Mohammed -- Thanks for the question regarding
      "Binary Tree", version 8x and up

      You Asked
Hi Mr. Kyte ,
Finally I've got chance to ask my question,

I want to implement a binary tree in oracle, for that I have created a script
file
which creates the table and also loads some sample data.

DROP TABLE btree;

CREATE TABLE btree (
                      node        VARCHAR2(10),
                      parent_node VARCHAR2(10),
                      left_node   VARCHAR2(10),
                      right_node  VARCHAR2(10),
                      CONSTRAINT pk_btree PRIMARY KEY (node),
                      CONSTRAINT fk_btree FOREIGN KEY (parent_node) REFERENCES
btree(node)
                    );

-- this is the root node
INSERT INTO btree VALUES ('root',null,'root1','root2');

INSERT INTO btree VALUES ('root1','root',null,null);
INSERT INTO btree VALUES ('root2','root','root21','root22');

INSERT INTO btree VALUES ('root21','root2',null,null);
INSERT INTO btree VALUES ('root22','root2','root221','root222');

INSERT INTO btree VALUES ('root221','root22',null,null);
INSERT INTO btree VALUES ('root222','root22',null,null);

The tree structure whith the above values is below.

              0 -root
             / \
     root1- 0   0 -root2
               / \
      root21- 0   0 -root22
                 / \
       root221- 0   0 -root222
                  -- and so on

My requirement is when I query the records I should get records in the same
heirarchial structure
as above and If there is no record for left_node or right_node then it should
display as 'EMPTY'

in place of that.

For example the tree above it should display like this (assume 'E' for 'EMPTY')

              0
             / \
            /   \
           0     0
          /|     |\
         / |     | \
        E  E     0   0
      /|  /|    /|   |\
      E E E E   EE   0 0
     |\|\ |\|\  |\|\ |\|\
     EEEE EEEE  EEEE EEEE

Each level becoming a row in the heirarchy.

If you ask me why I want to implement a binary tree in the database, then my
answer will be
please follow this link below

http://marvel.oracle.com/pls/otn/f?p=17000:8:45972::NO::F17000_P8_DISPLAYID,F17000_P8_CRITERIA:15406838245,%7Bbinary%7D%20and%20%7Btree%7D
After waiting for a long time to put the question on your site, I've asked it
with devtrends,
and this is the answer which I've got, Mr. Kyte if I had to do "recursion" then
I would have completed it a long ago, I want to do it with queries as it would
be thoushands thime faster than procedural code.

I've read an article regarding PIVOT tables in oracle magzine.

http://otn.oracle.com/oramag/oracle/02-sep/o52sql.html
with the help of PIVOT tables we can bring the missing rows.
But still not getting the logic as to how to build the query with my table
structure ?

Although I've thought that the above table design would be appropriate, but I
may be wrong and it is open for any changes.

I'll be using jsp and jdbc, and in the jsp page I will execute the query create
an HTML Table with the same tree structure as above and display it.

I am sorry if my question is too long.

Thanks.

      and we said...
I worked with Cameron O'Rourke with this.

Our only conclusion is you need a procedural, recursive routine in order to do
this.  You have "missing" data -- rows that don't exist that you would like to
pretend exist.  That drives relational databases *nuts*.  We don't do that.

Hence, our collective suggestion as a procedure stands.  pivot tables -- i fail
to see how they will bring about the missing rows here (i tried, got a headache
on level 3, stopped trying -- said "hey, sometimes you know, the procedural
approach is totally appropriate)

Maybe what you really want -- just maybe -- is to serialize a java b*tree
structure of your own making in to the database and de-serialize it later.  See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1285601748584
for an example.

Review &
      Followup

      Rating: 5
All my doubts
      are clear now  November 03, 2002
Reviewer:
      A reader
Thanks Mr. Kyte for your response and time you spent on my question.

Well we have to accept that not everything can be done with queries, and
sometimes we
should resort to procedural code.

The link for serializing java objects is extremely useful, but not for this one
because the tree might grow indefinite and whenever I want to retreive something
I
will be reading the whole BLOB deserializing it and then traversing it, thats a
lot of overhead
and procedural approach looks better than this one. Correct me if I am wrong.

Just one more thing , from my pl/sql procedure how can I concatenate the rows
into a CLOB
and return it, so that I can read it from my jdbc code as a stream.
I can concatenate it to a varchar datatype also but it has a limit of 32k, so I
want ot use CLOB.

Thanks.

          Followup:

If you just want the top of the tree, not the entire thing then yes, do not
serialize it (else you need to get the whole thing)

As for the CLOB -- just change the return type to IN OUT clob, use
dbms_lob.createTemporary to create a CLOB -- dbms_lob.writeAppend to write to it
and have java then stream from it (don't forget to have java dbms_lob.close it
as well!)

      Rating: 5
I've forgot to
      put my name  November 03, 2002
Reviewer:
      Mohammed Khan
The above post is my me

November 04, 2002 -- Thanks for the question regarding "Difference
      Between Cumulative and Incremental Export", version 8.1.7

      You Asked
Hi Tom,

Thank you for taking up this question.

Can you please explain the difference between the cumulative and incremental
exports. When and why they are used. I was reading the the documentation and was
confused about these terms.

Regards,
Vijaya Chander V.S

      and we said...
Well, I never have and never will use them -- export has a very long history and
way way way back, it was considered a backup tool.  It is not a backup tool.

A full backup gets everything.
An incremental backup only exports tables that have been modified since the last
export (hence the term incremental)

If you don't understand it still -- that is good.  Don't use em!

Review &
      Followup

      Rating: 3
One
      Problem.........  October 30, 2002
Reviewer:
      Riaz Shahid  from PRAL, Islamabad, Pakistan
I want to have increamental backup of my database on daily basis. How i can do
this?

Regards

          Followup:

Read about RMAN and use RMAN -- not export/import (as they are terrible backup
tools)

      Rating: 5
Backup
      Server  November 04, 2002
Reviewer:  Riaz
      Shahid  from PRAL, Islamabad, Pakistan
Hello Tom !

I am using 8.1.7 on Windows 2000. I want to make a backup server so that if the
primary server's database crashes/fails, i can switch to backup server. What is
the right way to do that ?

Waiting for your expert opinion....

Riaz

          Followup:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/index.htm
and read about the standby database

November 04, 2002 Mohammed -- Thanks for the question regarding
      "SQL ANALYZ", version 8.1.7

      You Asked
>how can an index type(unique,bitmap,unsorted,reverse) better than the other?
>the proplem is that i have two large tables one with 500000(customer_data)
records
and the other with 3000000(water_consume) records
>ther is a two b-tree non unique indexs  built in the two tables
the select statment that joins the two tabels has a (not exist) condition .
>the first index is custoner_data_indx(delever_center ,town_center ,unit_quarter
,vellage ,region ,region ,
record_num ,record_id )
the second index is water_consume_indx(con_month ,customer_id )
>this statment is frequently used (in any transaction on the system we need to
run this query first and for 10 users it takes 7 seconds until replay for each
one (in forms 6i))
the statment is
SELECT *
    FROM user_data.customer_data c
    WHERE c.delever_center = 1
      AND c.town_center = 2
      AND c.unit_quarter = 9
      AND c.vellage = 7
      AND c.region = 7
      AND c.record_num = 7
      AND c.record_id BETWEEN 1 AND 1000
      AND NOT EXISTS (SELECT w.water_consume_id
                          FROM user_data.water_consume w
                          WHERE w.con_month <= '06-2002'
                            AND c.customer_id = w.water_consume_id);

>this query retrieves about 700 to 2000 record per execution.
>when using the explain plan clause and sql analyz version 2.2.0
(a tuning tool provided in the tuning package of the Oracle Enterprise Manager
version 2.2.0) it provides that the select statment already used the indexs with
range scans and then access the tables by rowid(no full scans)
>although the explain plan say that i do it good,i still have slow execution for
this statment.
>should i cash the tables or it is the type of the indexs?

>i think that using indexs insted of full table scans for these large tables is
a good idea

      and we said...
You are under the mistaken impression that

o indexes mean "faster"
o indexed reads are better then full scans
o a query that full scans is in need of tuning
o a query that isn't using indexes needs to be forced to use them

All of those statements are FALSE.

I have no idea what "sql analyze version 2.2.0" is.

the explain plan is not saying "you did it good", it is saying you are using
indexes which may be the worst thing in the world.

That query is sort of "scary".  The part that scares me is:

where w.con_month <= '06-2002'

reasons for fear:

a) you seem to be storing a date in a string
b) you have stored it in a way that is NOT sortable, consider:

ops$tkyte@ORA920.LOCALHOST> create table t ( x varchar2(10) );
Table created.

ops$tkyte@ORA920.LOCALHOST> insert into t values ( '07-1999' );
1 row created.

ops$tkyte@ORA920.LOCALHOST> insert into t values ( '04-2003' );
1 row created.

ops$tkyte@ORA920.LOCALHOST> select * from t where x < '06-2002';

X
----------
04-2003

You need to look at that.

If you are using the CBO -- you should try NOT IN instead of NOT EXISTS.  If it
uses an "ANTI JOIN" (search this site for

HASH_AJ

for an example of what I mean -- or download the AOTC slides I have on asktom's
home page this week -- i have an example in there as well) -- it could be much
faster.

Also -- try to make sure that the fields in C that you "where" on have an index
on the MOST selective fields.
November 04, 2002 Syed Jaffar -- Thanks for the question regarding
      "pfile and undo tablespace", version 9.2.0.1.0

      You Asked
Hi Tom,

We have recently migrated our DB from 8.1.7 to 9i release two. I had no problems
during migration as everything done perfectly.  However, I have the following
questions:

1.  After migrating, I dont want to use RBS tablespace as I want to use undo
tablespace management.  I have created undo tablespace.  My question is, I want
to remove the existing RBS tablespace to make use of undo tablespace.  I just
have to drop the RBS TBS?  Is there any problem if I do it?

2.  I want to make some changes in my init file. like, compatiable parameter,
auto_management, etc.  The only problem is, whatever I cahnge in init file,
after startup, it does affect to the DB.  I dont know from where PFILE is being
read? Remember, I have migrated from 8.1.7 to 9.2.  Where do can I find my
PFIEL.

3.  Can you please give me a link where I can find all the new feature of 9.2.
that explained with examples.

thanks in advance.

      and we said...
1) you need to set the UNDO init.ora related parameters (to use the undo
tablespace) and restart.  Then you can just drop the rbs's

2) spfile's RULE.  we are using a stored parameter file.  See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:855380423028
and use:

alter system set parameter = value SCOPE=SPFILE;

to change a parameter for the next reboot or

.... scope = both;

to change the instance and parameter file.

Review &
      Followup

      Rating: 5
very
      usefull  November 05, 2002
Reviewer:  syed
      jaffar hussain  from SAUDI ARABIA
Tom, just thanks for a good answers, by heart.

November 04, 2002 Sanjaya -- Thanks for the question regarding
      "Oracle concepts", version 8.1.7

      You Asked
Hi Tom,

I've got some conceptual problems. Need help to clarify.

1.) About the export utility.
    We can assign values for the parameter BUFFER. Where is this memory     is
allocated ? On the client where we run the export ? Or on the database
server ?

2. ) About the redo log buffer.
    The redo log buffer is flushed on each commit.(hope I'm correct.)
    Say we 5 transactions started simulataniously. One transaction is over early
and it's commited. Here whole redo log buffer is flushed ? Or
only the things realted to the commited transaction are flushed ?

3. ) Again about redo log buffer.
    This something I experienced. I had a database which had only 7K redo log
buffer. Then I increased it to 200K. Now performence shuld be increased. But the
clients complains now it's slower. Is my log buffer is too big ? And is there
any performence impact cause by too much buffer ?
My server is a RS6000 with 512MB memory. There is only one database on the
server.

Waiting for your clarification.
Thanks in advance.

-San

      and we said...
1) client -- its the buffer we fetch into in the client

2) the entire thing -- it is called a piggy back commit (search this site for
piggy back).  Your redo can and will be flushed way before you commit
frequently. It is very normal.  We actually flush:

o on commit
o when 1/3 full
o when 1 meg full
o every 3 seconds

whichever comes first.  And, we flush the entire thing.

3) I think you are mixing up a cause and effect.  I seriously DOUBT changing the
redo log buffer from 7k (which is tiny) to 200k caused this.

BEFORE CHANGING ANYTHING -- any setting -- YOU MUST, YOU HAVE TO, IT IS
IMPERATIVE you determine the cause of the problem.  Just changing things is
futile and a waste of time.

You need to collect metrics (clients are totally unreliable at telling us "it is
faster or it is slower" - they cannot really tell).  You need to determine
causes.  You need to fix cause.

Review &
      Followup

      Rating:
      5
  November 04, 2002
Reviewer:
      A reader
".  We actually flush:

o on commit
o when 1/3 full
o when 1 meg full
o every 3 seconds"

--Doesn't it mean that redo buffer >1MB is just waste of memory?

          Followup:

Nope -- not at all.

It is a buffer -- people are filling it, lgwr is emtying it.

Lets say you have lots of people doing stuff -- they generate 1meg of REDO.
it'll take some measurable amount of time to flush that redo to disk and get
confirmation back from the OS that the write succeeded (and don't forget, we
usually write to AT LEAST two places and if you are using data guard -- we'll be
writing over the NETWORK as well!)

So, while LGWR is flushing that 1m -- others are filling another 1m (or more)
area of the log buffer.  That is -- we do not stop accepting new entries while
flushing so we can be filling the log buffer even while lgwr is trying to empty
it!  If could well be that lgwr finds it has another 1meg of data to flush as
soon as it flushed the 1meg it was working on.

Now, a 1gig log buffer - wasteful. 5meg -- not wasteful.  Depends on your
system, size of transactions (eg: if your transactions are tiny and not high
volume -- 5meg would be overkill

      Rating:
      4
Thanks  November 04, 2002
Reviewer:
      Sanjaya Balasuriya  from Sri Lanka
Thanks for the explanation Tom.

-San

November 04, 2002 Om -- Thanks for the question regarding "Insert
      /*+append */ statement ", version 8.1.7

      You Asked
Hi Tom,

Thanks for all the help and support.

After reading an article from your site only,I started using insert /*+append */
command and find it very useful.

I have successfully copied million of records over a period of time.But today
when I issued this command,I got an error as stated below :-

SQL> insert /*+ append */ into dml_prod.h_tact_rate
  2  select * from dml_prod.h_tact_rate@cb10_to_cb18;
select * from dml_prod.h_tact_rate@cb10_to_cb18
                                   *
ERROR at line 2:
ORA-01652: unable to extend temp segment by  in tablespace
ORA-02063: preceding line from CB10_TO_CB18

Now,cb10 and cb18 are the instance name.I am trying to copy the records from the
table h_tact_rate in CB18 to the same table in CB10 database.

I had a look at the temporary segment in CB10 database and found that it's 100
free.Although the max byte avilable was .51 Mb.

I extended the datafile for 200Mb,still got the same error.Then I added an
additional datafile of about 200Mb to add to the space further.The max byte
available was around 300MB.

Still got the same problem.

Finally,I executed the same command without making use of append hint,but it
took lot of time.

Could you please comment on the above error and why temp segment was raised this
again and again even after adding sufficient amount of space in it.

Best regards,
Om

      and we said...
insert /*+ append */ writes ABOVE the HWM.  It is that the "temp" segment
mentioned was for the TABLE (where we write the new data), not the temporary
tablespace.

Bascially -- the tablespace the table H_TACT_RATE is in is out of space for new
extents -- no more room above the HWM.

The "regular" insert worked because it reused the space BELOW the HWM.

Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create tablespace testing
  2  datafile '/tmp/test.dbf' size 512k reuse
  3  autoextend OFF
  4  extent management local uniform size 64k
  5  /
Tablespace created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x char(2000) ) tablespace
testing;
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> begin
  2          loop
  3                  insert into t values ( 'x' );
  4                  commit;
  5          end loop;
  6  end;
  7  /
begin
*
ERROR at line 1:
ORA-01653: unable to extend table OPS$TKYTE.T by 8 in tablespace TESTING
ORA-06512: at line 3

ops$tkyte@ORA817DEV.US.ORACLE.COM> delete from t
  2  /
165 rows deleted.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;
Commit complete.

so, here we have a table that has NO room to grow above the HWM, but has room
for 165 rows BELOW the HWM...

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert /*+ append */ into t select 'x' from
dual@ora815;
insert /*+ append */ into t select 'x' from dual@ora815
                          *
ERROR at line 1:
ORA-01653: unable to extend table . by  in tablespace
ORA-02063: preceding line from ORA815

looks similar to your error.....  Similar enough that I would say this is
basically the same thing

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select 'x' from dual@ora815;
1 row created.

but that worked...

so, look at the tablespace the target table is in -- thats where I believe the
issue to be.


Subject | Article | Last Updated (East coast time)
Is it possible to priortise a process in database | article only | Nov 05, 2002 08:04 am
Can we use the database files from Unix box to NT box | article only | Nov 05, 2002 08:46 am
9i Database | article only | Nov 05, 2002 08:50 am
Performance Problem | article only | Nov 05, 2002 09:35 am
Predicting SQL execution time. | article only | Nov 05, 2002 10:32 am
Procedural architecture & performance | article only | Nov 05, 2002 02:30 pm
Reference relation between two tables in different databases using a DB link | article only | Nov 05, 2002 03:07 pm
CREATE OPERATOR | article only | Nov 05, 2002 03:11 pm
partitioning | article only | Nov 05, 2002 09:47 pm
Increase utl_file max _linesize parameter dymamically | article only | Nov 05, 2002 10:14 pm
Computing execution time for procedure, function or SQL statement | article only | Nov 06, 2002 06:30 am
Percentage of Exp or Imp got over | article only | Nov 06, 2002 06:41 am
Convert Long Column to Varchar2 | article only | Nov 06, 2002 03:35 pm
different cost | article only | Nov 06, 2002 04:20 pm
Oracle Expert | article only | Nov 06, 2002 04:29 pm
table/index storage,commit,database code | article only | Nov 06, 2002 07:32 pm
Where has all the tablespace gone and how can I recover it. | article only | Nov 07, 2002 08:04 am
bulk loading | article only | Nov 07, 2002 08:49 am
Using "PRAGMA automated_transactions " in forms 6i Poses problems | article only | Nov 07, 2002 08:50 am
Join table (many to many) query | article only | Nov 07, 2002 10:42 am
Determining which query caused deadlock. | article only | Nov 07, 2002 01:13 pm
create like user | article only | Nov 08, 2002 07:45 am
undo tablespace | article only | Nov 08, 2002 08:28 am
want to find a way to shift column values up or down | article only | Nov 08, 2002 10:53 am
New TYPE to encapsulate java BigDecimal | article only | Nov 08, 2002 01:13 pm
Calculating Weighted Percentile | article only | Nov 08, 2002 04:57 pm
LOADER: Data-conversion in the control-file | article only | Nov 08, 2002 05:00 pm
How to change a column name using SQL | article only | Nov 08, 2002 05:32 pm
Import with no data loaded. | article only | Nov 09, 2002 08:44 am
Regarding Index | article only | Nov 09, 2002 11:38 am
snmp | article only | Nov 09, 2002 03:05 pm
D/W Database size estimation and configuration | article only | Nov 09, 2002 07:05 pm
Object Views | article only | Nov 11, 2002 10:55 am
staging tables to main tables | article only | Nov 11, 2002 06:29 pm
row(s) 1 - 34 of 34
November 05, 2002 Om -- Thanks for the question regarding "Is it possible to priortise a process in database", version 8.1.7
You Asked
Hi Tom,

Thanks for always supporting us with your valuable comments.

As you know we make use of nice command in unix to give a process
higest priority.

Is it possible in oracle database to priortise a job or any process.

Best regards,
Om
and we said...
It is possible to prioritise a USER account and if you run the job as that
account -- it'll be prioritised.

See
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76956/dbrm.htm#5080
Review & Followup
Rating: 3
use with care? November 04, 2002
Reviewer: andrew from ca, usa
Tom
When I played with prioritization at the OS level (VMS) to give online users a
higher priority than batch (both doing DML on the same tables), the slower
running batch look out longer record locks which ultimately made things worse
for the online users. I guess that nothing changes using this approach?

Followup:
I'm not suggesting using OS level stuff -- in fact - i would strongly DISCOURAGE
such attempts as they generally fail horribly.

When you have conflicts on the same table -- the same rules we've been following
for years apply (with regards to concurrency).  Yes.

November 05, 2002 Awan -- Thanks for the question regarding "Can we use the database files from Unix box to NT box", version 8.1.7
You Asked
Dear Kyte:

You are doing a great work for all the Oracle DBAs from all around the world.
And we all admire u a lot.

My question is:

Can we use the database files from UNIX box to NT box. Suppose I have production
database on the Sun Solaris and I want to make the test database on NT box. So
can i use the same database files which exist on the UNIX box to NT box??

Please shed some light.

Regards,
Awan
and we said...
No, datafiles are not cross platform compatible as of 9iR2.

You would need to use a tool such as EXP and IMP and move the data from platform
to platform in a platform agnostic format.
Review & Followup
Rating: 2
November 04, 2002
Reviewer: Sagi from India
Hi Tom,

I am aware that datafile are not compatabile. I got one question because you
said "as of 9iR2".

You mean say if we have a DB (in 9iR2) on Some Unix Flavor can we clone the same
on Win-NT/2000 or vice versa?

Regards,
Sagi

Followup:
No, "as of 9iR2" means that "as of release 9iR2 you CANNOT move files from one
os to another".  meaning the ability to move datafiles across OS's does not
exist in that release and before.

Rating: 3
November 04, 2002
Reviewer: Reader from New York
What is the best approach to migrate a database(8.1.5) on Compaq(Digital Unix
4.0) to database(9iR2) on Sun Solaris(9)??

Followup:
depends on the size.

exp/imp might suffice. I prefer to use schema level exports (user by user) so
you can export and import in parallel.

bigger databases -- you might want to "dump and load" using sqlldr (direct path)
and then parallel index builds - things you cannot do with exp/imp.

Rating: 4
I did this on 8.1.7 November 04, 2002
Reviewer: Shrek from DC
However, I was able to clone a 8.1.7 database (using a cloning/renaming
technique in asktom). I FTP'd the database files from Win2000 to Linux 7.x. The
hardware was the same but the OS's were different. The big-endian vs.
little-endian difference shows up only if you move them from Intel to SUN
hardware or vice-versa. There may be other issues, but I haven't noticed them.

Followup:
don't do this with a "real" database.  Not supported, not supportable.

Rating: 5
Good Answer November 05, 2002
Reviewer: Awan from Pakistan

November 05, 2002 Senthil -- Thanks for the question regarding "9i Database", version 9.0.1
You Asked
Hi Thomas,

i just got an oppurtunity to work with 9i database. i would like to hear your
comments about 9i. do you know any new features, on top of your mind, that i
want to try.

as they said, have eliminated svrmgrl utility...

thanks,
senthil.
and we said...
Flash back queries.

Online table alterations (partition a non-partitioned table, turn a heap table
into an IOT and so on)

Restartable DML...

external tables (attach flat files of data and use sql on them)

Different block sizes in same database

Multi-table inserts

See the differences in plans for a an optimizer mode of first_rows(1), (20),
(200)  (optimize to get the first row, the first 20, the first 200 and so on)

User defined aggregate functions

Functions that work like tables -- not a function returning data, a function
that returns "rows" like a table would.

SQL/99 datetime support including timezones

XML datatype

Resizable SGA components

NCOMPed PLSQL code

just to start with....
Review & Followup
Rating: 3
June 08, 2001
Reviewer: A reader

Rating: 5
June 10, 2001
Reviewer: A reader

Rating: 5
June 10, 2001
Reviewer: Eric Grancher from Switzerland

Rating: 4
dba June 12, 2001
Reviewer: R.KAMALESWARAN from INDIA

dear tom

          i  have a two server i want to communicate with

both . example : now i working first server  but i want

find who will  be working in other server

a) i want to display  system name
b) i want to display what user useing sql 8.0

     (i want to saw in my server ) sir please give a example in pl*sql8.0(and
that commands)

                 thanking Q

                                ....R.KAMALESWARAN

Rating: 5
convert heap table to iot October 29, 2002
Reviewer: A reader
( turn a heap table into an IOT and so on)

Tom, i didn't know we could do this.. i am trying to convert a heap table into
iot but i don't think i have the syntax correct. Could you please provide the
correct syntax?
I tried this in both 9iR1 and 9iR2. Thanks.

SQL> alter table dev organization index;
alter table dev organization index
                   *
ERROR at line 1:
ORA-01735: invalid ALTER TABLE option

Followup:
it uses online redefines like this:

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> drop table t_interim;

Table dropped.

ops$tkyte@ORA920.LOCALHOST> drop table t;

Table dropped.

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> create table t NOLOGGING
  2  as
  3  select * from all_objects;

Table created.

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> alter table t add constraint t_pk primary
key(object_id);

Table altered.

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> set echo off
=================================================================

Here is the new table we want to reorg into , we are dropping some
NOT NULL constraints, creating a derived column flags from other columns
and getting rid of some columns -- renaming others (and partitioning it)

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> create table t_interim
  2  (
  3    object_id     number primary key,
  4    object_type   varchar2(18),
  5    owner         varchar2(30),
  6    oname         varchar2(30),
  7    created         date,
  8    last_ddl_time date,
  9    status         varchar2(7),
 10    flags         varchar2(5)
 11  )
 12  organization INDEX
 13  /

Table created.

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> set echo off
=================================================================

This block of code  starts moving the data over for us.  changes
to the base table are being recorded off to the side while this happens..

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> declare
  2           l_colmap varchar(512);
  3  begin
  4           l_colmap :=
  5            'object_id,
  6                 object_type,
  7                 owner,
  8                 object_name oname,
  9                 created,
 10                 last_ddl_time,
 11                 status,
 12             temporary || ''/'' || generated || ''/'' || secondary flags ';
 13
 14           dbms_redefinition.start_redef_table
 15           ( uname => user,
 16             orig_table => 'T',
 17             int_table => 'T_INTERIM',
 18             col_mapping => l_colmap );
 19  end;
 20  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> set echo off
=================================================================

Now we'll sync them -- to speed up the "finish".  The finish will
be a non-online operation -- so we want it to be fast

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> begin
  2      dbms_redefinition.sync_interim_table( user, 'T', 'T_INTERIM' );
  3  end;
  4  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> set echo off
=================================================================

Tables are synced, we are ready to rollover

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> begin
  2      dbms_redefinition.finish_redef_table( user, 'T', 'T_INTERIM' );
  3  end;
  4  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> desc t
 Name                                                  Null?    Type
 ----------------------------------------------------- --------
------------------------------------
 OBJECT_ID                                             NOT NULL NUMBER
 OBJECT_TYPE                                                    VARCHAR2(18)
 OWNER                                                          VARCHAR2(30)
 ONAME                                                          VARCHAR2(30)
 CREATED                                                        DATE
 LAST_DDL_TIME                                                  DATE
 STATUS                                                         VARCHAR2(7)
 FLAGS                                                          VARCHAR2(5)

ops$tkyte@ORA920.LOCALHOST> desc t_interim
 Name                                                  Null?    Type
 ----------------------------------------------------- --------
------------------------------------
 OWNER                                                 NOT NULL VARCHAR2(30)
 OBJECT_NAME                                           NOT NULL VARCHAR2(30)
 SUBOBJECT_NAME                                                 VARCHAR2(30)
 OBJECT_ID                                             NOT NULL NUMBER
 DATA_OBJECT_ID                                                 NUMBER
 OBJECT_TYPE                                                    VARCHAR2(18)
 CREATED                                               NOT NULL DATE
 LAST_DDL_TIME                                         NOT NULL DATE
 TIMESTAMP                                                      VARCHAR2(19)
 STATUS                                                         VARCHAR2(7)
 TEMPORARY                                                      VARCHAR2(1)
 GENERATED                                                      VARCHAR2(1)
 SECONDARY                                                      VARCHAR2(1)

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> spool off

Rating: 5
Thanks a lot October 29, 2002
Reviewer: A reader
Thanks so much, Tom. I went over dbms_redefinition package but it wasn't so
obvious. I appreciate your detailed solution.

Rating: 5
Great Example !! October 30, 2002
Reviewer: pasko from HH,GERMANY
Hi Tom ,
thanks for a Great Example right there!

One question though :
I note the mapping for flags column:
from
flags  varchar2(5)
to
temporary || ''/'' || generated || ''/'' || secondary flags '
giving

TEMPORARY  VARCHAR2(1)
GENERATED  VARCHAR2(1)
SECONDARY  VARCHAR2(1)

What could  i do to map for example , from flags as it is above to :

TEMPORARY  VARCHAR2(200)
GENERATED  interval day to second
SECONDARY  sys.AnyData

Thanks in advance.

Followup:
Well, I can convert to interval day to second, but not so to sys.anydata:

ops$tkyte@ORA920.US.ORACLE.COM> create table t ( a int primary key, x
varchar2(1), y varchar2(1), z varchar2(1) );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create table t_tmp ( a int primary key, x
varchar2(200), y interval day to second, z char(1) );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into t values ( 1, 'x', '1', 'z' );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> declare
  2          l_colmap varchar2(512);
  3  begin
  4          l_colmap := 'a, x, cast( numtodsinterval(y,''day'') as interval
day(2) to second(6) ) y, z';
  5
  6          dbms_redefinition.start_redef_table
  7          ( uname => user, orig_table => 'T', int_table => 'T_TMP',
col_mapping => l_colmap );
  8
  9          dbms_redefinition.finish_redef_table( user, 'T', 'T_TMP' );
 10  end;
 11  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> drop table t_tmp;

Table dropped.

ops$tkyte@ORA920.US.ORACLE.COM> create table t_tmp ( a int primary key, x
varchar2(200), y interval day to second, z sys.anyData );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> declare
  2          l_colmap varchar2(512);
  3  begin
  4          l_colmap := 'a, x, y, sys.anydata.convertVarchar2(z)';
  5
  6          dbms_redefinition.start_redef_table
  7          ( uname => user, orig_table => 'T', int_table => 'T_TMP',
col_mapping => l_colmap );
  8
  9          dbms_redefinition.finish_redef_table( user, 'T', 'T_TMP' );
 10  end;
 11  /
declare
*
ERROR at line 1:
ORA-12015: cannot create a fast refresh materialized view from a complex query
ORA-06512: at "SYS.DBMS_REDEFINITION", line 8
ORA-06512: at "SYS.DBMS_REDEFINITION", line 146
ORA-06512: at line 6

ops$tkyte@ORA920.US.ORACLE.COM>

One of the restrictions is:

o Tables with user-defined types (objects, REFs, collections, typed tables)
cannot be online redefined.

Rating: 3
October 30, 2002
Reviewer: Sagi from India
Hi Tom,

As usual a great example to enlighten us.

You said

"Now we'll sync them -- to speed up the "finish".  The finish will be a
non-online operation -- so we want it to be fast"

I could not get what you ment by this? You mean to say that we can use the

dbms_redefinition.finish_redef_table

without calling

dbms_redefinition.sync_interim_table

But then since the FINISH_REDEF_TABLE is a non-online, its advisable to first
SYNC and call the FINISH routing?

Thanx in advance for help.

Regards,
Sagi

Followup:
Yes, exactly -- you can call finish without calling sync, finish will call sync.

Rating: 4
Please Explain November 04, 2002
Reviewer: Bharath from USA
>>Functions that work like tables -- not a function >>returning data, a function

>>that returns "rows" like a table would.

Can You please  give us an example for this.

Followup:
search for

PIPELINED

on this site for lots of them.

November 05, 2002 Ramachandran -- Thanks for the question regarding "Performance Problem", version 8.1.7
You Asked
Hi tom

We are updating a particular column of a table containing 700.000 records.  The
update statement contains a call to function.

Something like

Update table table1 set col1=func1();

The v$waitstat undo blocks is having heavy count of more than
29,000.

So i am planning to monitor V$session_Wait and V$sql_area.

Am i going in right direction?

Can you suggest me is there better way to start monitor.

Regards
Lakshminarasimhan
and we said...
29,000 is a pretty trivial number (small).

is col1 indexed?  if so, this'll take a while.

will updating col1 cause rows to migrate?  if so, this'll take a while.

how fast is func1?  Let's say it is really fast -- runs in 1/100th of a second.
You have a minimum of 700,000/100 = 7000 seconds or about 2hours.

So -- have you done the math to see how fast this can *reasonably* be expected
to run??
Review & Followup
Rating: 4
any other way November 05, 2002
Reviewer: Lakshminarasimhan R from Dubai
HI tom

thanks for the help.  in fact the function which is used in the update statement
is third party's and hence we couldnt see that.  (they have wrapped it). is
there any other way to detect the function is the spoilspot? since source is not
with us we cannot enable the procedure tracking also.

Followup:
run the function in a loop yourself with some inputs that are valid and time it.

Do this:

alter session set sql_trace=true;
alter session set timed_statistics=true;
update table table1 set col1='xxxxxxx' where rownum < 1000
rollback;
update table table1 set col1=func1() where rownum < 1000;
rollback;

and run tkprof on the trace files.  The difference between the two -- that'll be
the overhead of the function.

November 05, 2002 Dawn -- Thanks for the question regarding "Predicting SQL execution time.", version 9.2.0
You Asked
Hello Tom

I have heard (using OEM) you can see how long an SQL statement has left untill
the end of execution, which means Oracle has already calculated the execution
plan for the statement, and worked out how long it thinks it will take.

Can I get the total estimated execution time for an sql statement without
actually having to execute it. Can I use Explain Plan or query certain views to
obtain this info. If I can how do I do this? (As I do not always have access to
OEM).

I know how to use explain plan and query the associated plan table, but there
does not seem to be any information related to how long the query will take.

Any help would be greatly appreciated

Many Thanks

Dawn
and we said...
You heard right but your cause and effect conclusion is wrong.

In 9iR1 and up -- the CBO will attempt to compute a cost IF you are using the
resource manager and you have "excecution time estimation" limits on.

What OEM does though is that is uses v$session_longops to review how far along
long running statements are.  In there, it can see what the statement is doing
-- how long it has been doing it and how much more it has to go.  It calculates
the time based on recently execution history.

Search this site for

v$session_longops

for examples using this table.
November 05, 2002 Chris -- Thanks for the question regarding "Procedural architecture & performance", version 8.1.6
You Asked
I have a list of procedures which perform various operations on tables. For
example I may have a USER table and an accompanying save_user proc and get_user
proc, which save and get individual records. This is repeated across many
tables.
All the procs are standalone and don't reference each other and are called
individually, not in groups.

These procs can then be called by a .NET app.

There are several options open to me. Please can you tell me which would be best
in your opinion AND which would give the best performance:

1. Lots of individual procedures -
Yuk - very messy & hard to maintain but probably quick as only the proc is
loaded into memory when the functionality is needed.

2. One huge package containing lots of procedures -
Not really a serious consideration, but added for completeness.

3. Lots of packages containing one procedure each -
This follows your 'never use procs only packages' approach but seems a complete
waste of time as none of the dependency, etc arguments apply in this case.

4. Packages by object/table; e.g. a user package containing Save, Get, etc
procedures -
Nice from a logical encapsulation point of view, however, these procedures are
only ever called individually. When a proc within the package is called the
whole package is loaded into memory. What are the performance implications?

Thanks.
and we said...
4 is the correct approach.

Name the package after the table  "table_pkg"
Name the methods in the package consistently "get", "save", "etc"

The package will load into 4k chunks on the first call -- after that -- everyone
shares the code from the shared pool.  You need not worry about any overhead
whatsoever connected with packages and loading.  It happens so fast, you don't
even realize it.  And after that -- they are cached for all to use.
Review & Followup
Rating: 3
Package load November 05, 2002
Reviewer: Chris from UK
Thanks Tom for clearing that up. Because the pkg loads almost instantaneously,
and after which it will be in the shared pool, I don't have to worry about the
load performance.

However, just out of curiosity ...

You mentioned that the package loads in 4K chunks. I'm assuming that the whole
package still has to be loaded before you can call a method from it regardless
of the size of the loading packets?

... or ... are you trying to say that if the procedure I'm calling is only 2K
then I will only have to load a single chunk? Not sure I'm convinced.

If I have a MASSIVE package, and assuming that the whole package has to load
before I can call a method, then will I have to suffer a performance hit the
first time I call it (after which it will be resident in the shared pool) or is
this still negligible?

Followup:
It'll load the hole thing in 4k chunks (so as to NOT fragment the shared pool by
doing lots of different sized allocations.  PLSQL code sort of "chunks itself"
into bit sized pieces, all get loaded, but in tiny pieces here and there)

I would like to see the example of a massive package that impacts the first
caller more then a fraction of a second (meaning -- show me the massive package
that after a "flush shared pool" -- the next invocation takes a second longer
then the last or next invocation did/does)

November 05, 2002 Hussain -- Thanks for the question regarding "Reference relation between two tables in different databases using a DB link", version 8.1.7
You Asked
Hello,

Can we create a reference relation (foreign key relation) between two tables in
different Oracle databases (located in different machines) linked together using
a database link? If we can not, how can I implement a master-detail relation
between these two tables?
and we said...
No, and think about it -- if you could -- these two databases would be 100%
totally dependent on eachother.

No modifications to the parent unless the child db was up.
No modifications to the child unless parent db was up.

You lose all benefits of a distributed system.
You gain all of the performance implications.

the net net is -- you lose more then you could ever hope to gain.

These tables rightly belong in the same database, period.
Review & Followup
Rating: 4
benefits of a distributed system November 05, 2002
Reviewer: Jack Mulhollan from Little Rock, AR USA
Tom,

Would you elaborate on the benefits of a distributed system and the performance
implications?  Would you give a few scenarios that are ideal for a distributed
system?

Is RAC an alternative in any of these scenarios (I'm envisioning two instances
with dedicated processors and memory, but equal read performance on the shared
DASD)?

What is the theoretical performance difference between moving data with
transportable tablespaces versus a database link?  Seems to me that network
bandwidth is the bottleneck and that copying datafiles shouldn't be any faster
than selecting rows.  In my experience, however, database links are slow.

Is there any way to tune database links?

In my world, I occasionally (say weekly) need to join large (say 50 gig) tables
that reside on remote systems.  What are my best options?

I'd appreciate your insight.  Thanks.

Followup:
distributed system can sometimes mean "more available" -- eg: HR on system1.
Financials on system2.  HR crashes -- is Financials down?  no.....

Now, if financials needs to query the HR database for every transaction -- is
financials down? (yes, yes it is if HR isn't there).  If financials has to query
HR for every transaction is financials running as fast as it should/could?  No,
no it is not -- distributed = slower then not-distributed for queries.

RAC is what would be proposed, yes.

I cannot compare transportable tblspaces to database links.  That is like
comparing roller skates with race cars.  They are totally different (but both
have wheels).

Database links are *slower* then "no database link".  To say they are "slow" is
really wrong.  They are not as fast -- but of course they cannot be -- think
about it.  You are tunneling all of the data through this pipe -- it will not be
as fast as reading from disk directly.

It can be faster to move datafiles since you can:
o compress them
o use much less "chatty protocols" such as FTP

See the distributed systems guide and the network guides for tuning on dblinks.

50gig of data joined over a dblink, I wouldn't want to be doing that personally
-- think about it.  I cannot comment on your best options not knowing all of
your constraints, your environment, your system, your versions -- tons of stuff
(please -- don't post it here, It would take lots of space).  This is something
you need to sit down with people who have worked on similar problems in the past
and come to some best practice type of conclusion.

November 05, 2002 Jan -- Thanks for the question regarding "CREATE OPERATOR", version 8.1.7
You Asked
Can you please explain on an example how to use CREATE OPERATOR command and how
can I use the product of this command in my SQL and PL/SQL? I cannot imagine it
from the documentation. Thank you. Jan
and we said...
You would want to read:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76937/toc.htm
that command is useful in creating your own datatypes (like TEXT, Audio,
Spatial, etc).

It is not a "standalone" command that is easily demonstrated in 10 words or less
-- it is part of a much larger framework for creating new datatypes.
Review & Followup
Rating: 5
November 05, 2002
Reviewer: AP
Hi Tom,

I was looking into the create operator command for achieving something silly
like below: It occured to me that surely you will have some easy work around:

We use a query like:

select col1, col2 from table1 where col3 in(select col3 from table2);

col2 is a varchar2(10) field. No FK between these tables.

Now we want to expand the scope like:

select col1, col2 from table1 where col3 LIKE(select substr(col3, 1, 5)||'%'
from table2);

Just like the in operator equates the columns, we want to have a LIKE used in
the query. However the subquery will return more than one row. Is it possible to
do using a single SQL statement? Thank you verymuch.

AP

Followup:
select * from table1 where substr( col3, 1, 5 ) in (select substr(col3, 1,5 )
from table2);

November 05, 2002 Aparajita -- Thanks for the question regarding "partitioning", version 8.1.7
You Asked
Hello Tom,

I have a requirement which is as follows:

I have a table A which is truncated everyday before loading data into it which
contains approx 2 milion rows. The table has a column called UPDATE_DATE which
is a date field. Now I have to take the data of table A over db_link and then
populate the table B which is supposed to be containg historical data of table A
(with same column names etc.), the requirement is that I have to keep 30 months
of historical data of table A in Table B. I have to drop the oldest partition of
table B that is older than 30 months. I have thought of using range partitioning
on UDATE_DATE, i.e. creating every day one partition and then do a parallel
insert as I cann't use exchange partition over dblink. Is my method is correct?
Can you please explain with an example. With my idea there will be around 900
partitions?
Also can you please let me know what are the things I need to consider(creating
local index etc..)Thanks in advance for your help
and we said...
Well, here is a different -- faster -- less down time is us -- approach.

Consider that two million row table is called big_Table.  Do it like this:

REM drop table big_table;
REM
REM CREATE TABLE "BIG_TABLE"."BIG_TABLE" ("OWNER" VARCHAR2(30) NOT NULL
REM ENABLE, "OBJECT_NAME" VARCHAR2(30) NOT NULL ENABLE, "SUBOBJECT_NAME"
REM VARCHAR2(30), "OBJECT_ID" NUMBER NOT NULL ENABLE, "DATA_OBJECT_ID"
REM NUMBER, "OBJECT_TYPE" VARCHAR2(18), "CREATED" DATE NOT NULL ENABLE,
REM "LAST_DDL_TIME" DATE NOT NULL ENABLE, "TIMESTAMP" VARCHAR2(19),
REM "STATUS" VARCHAR2(7), "TEMPORARY" VARCHAR2(1), "GENERATED"
REM VARCHAR2(1), "SECONDARY" VARCHAR2(1))
REM partition by range (object_id)
REM ( partition the_only_one values less than (maxvalue) tablespace users )
REM /
REM
REM insert /*+ APPEND */ into big_table select * from all_objects;
REM commit;
REM insert /*+ APPEND */ into big_table select * from big_table;
REM commit;
REM insert /*+ APPEND */ into big_table select * from big_table;
REM commit;
REM insert /*+ APPEND */ into big_table select * from big_table;
REM commit;
REM insert /*+ APPEND */ into big_table select * from big_table;
REM commit;
REM insert /*+ APPEND */ into big_table select * from big_table;
REM commit;
REM insert /*+ APPEND */ into big_table select * from big_table;
REM commit;
big_table@ORA920.US.ORACLE.COM>
big_table@ORA920.US.ORACLE.COM>
REM create table t tablespace users
REM as
REM select OWNER, OBJECT_NAME, SUBOBJECT_NAME,
REM -OBJECT_ID object_id, DATA_OBJECT_ID, OBJECT_TYPE,
REM CREATED, LAST_DDL_TIME, TIMESTAMP,
REM STATUS, TEMPORARY, GENERATED, SECONDARY
REM from big_table;
REM alter table t modify (object_id not null);

Now, T is the table with the "new" 2 million rows in it.  Our goal:

a) load big_table with ASAP (minimize downtime, there hasn't been any downtime
yet -- T was loaded without impacting anybody)

b) make it so we can get the data from BIG_TABLE to another database (why why
why another database?  would make much greater sense to use a single database
but anyway)

So, we:

big_table@ORA920.US.ORACLE.COM> set timing on
big_table@ORA920.US.ORACLE.COM>
big_table@ORA920.US.ORACLE.COM> select object_id from big_table where rownum=1;

 OBJECT_ID
----------
        30

Elapsed: 00:00:00.16
big_table@ORA920.US.ORACLE.COM> select object_id from t where rownum=1;

 OBJECT_ID
----------
       -30

Elapsed: 00:00:00.09
big_table@ORA920.US.ORACLE.COM>
big_table@ORA920.US.ORACLE.COM> alter table big_table
  2  exchange partition the_only_one
  3  with table t
  4  without validation
  5  /

Table altered.

Elapsed: 00:00:00.10
big_table@ORA920.US.ORACLE.COM>
big_table@ORA920.US.ORACLE.COM> select object_id from big_table where rownum=1;

 OBJECT_ID
----------
       -30

Elapsed: 00:00:00.00
big_table@ORA920.US.ORACLE.COM> select object_id from t where rownum=1;

 OBJECT_ID
----------
        30

Elapsed: 00:00:00.00
big_table@ORA920.US.ORACLE.COM>

Now, big_table is reloaded with virtually no downtime!

Now, what we do is transport the tablespace with T (contains the old data from
BIG_TABLE) and attach it to the other database and just do the switcher-roo over
there.
Review & Followup
Rating: 4
partitioning October 20, 2002
Reviewer: Aparajita from UK
Tom,
I don't know whether I have made my question clear enough or not. The table A as
referred to in my question has a date field (update_date) and contains 2 million
rows for a single update_date. My history table B is supposed to contain data
from A, which will be populated from A on a daily basis and the data retention
need for the history table is 30 months. Now I am not sure from your answer
about whether I should create a partition using update_date as key or not on
table B. Are you suggesting soem other field for partitioning key? Also please
can you discuss a little on local indexes and how to handle that in this
situation. Due to tablespace constraint I have to create table B on another
database. Now can you please clear me the following lines from your answer. I
did n't understand what you meant by 'switcher-roo'

"Now, what we do is transport the tablespace with T (contains the old data from
BIG_TABLE) and attach it to the other database and just do the switcher-roo over
"
there.

Thanks very much for your help.

Followup:
What I understand:

tableA is filled with data every night (2 million rows)

there is a column updated_date in tableA which I assume is the same for all 2
million rows?  (well, that is what I assumed -- that it is the load date)

So, I gave you a fast way to

o reload table a without any down time (load into a staging table, swap stage
with table a)

o a way to get the 2 million rows there were in a to table b fast (just
transport the tablespace that contains the "swapped" data)

o now, to add this data to table B, just swap this newly transported table with
an empty partition in B and wah-lah -- switcher-roo is done.   You have loaded A
with new data and B contains the data from yesterdays copy of A.

You would use local indexes on everything and just copy the indexes along with
the table data as you moved it from place to place.

Rating: 4
some more questions October 31, 2002
Reviewer: AD from UK
This is follow up of my earlier mail on this subject. The requirement has
suddenly changed and now it is like this.

I have to keep 12 months of data in the table. The table has to be partitioned
on update_date( one partition every day). But for manageability purposes the
data should be in 4 different tablespaces which should not be hard coded. Same
is for the indexes that is they should be in different tablespaces and should
not be hardcoded. Index tablespaces are different from data tablespaces.

CREATE TABLE A
     ( COL1
       COL2
       ....
       UPDATE_DATE  )
      PARTITION BY RANGE ( UPDATE_DATE)
     ( PARTITION A_part_20021028 VALUES LESS THAN TO_DATE(' 2002-10-28',
'yyyy-mm-dd')
       TABLESPACE tsa ,
       PARTITION A_part_20021029 VALUES LESS THAN TO_DATE(' 2002-10-29',
'yyyy-mm-dd')
       TABLESPACE tsa ,
       PARTITION A_part_20021030 VALUES LESS THAN TO_DATE(' 2002-10-30',
'yyyy-mm-dd')
        TABLESPACE tsa ,
       ......
       PARTITION A_part_20030130 VALUES LESS THAN TO_DATE(' 2003-01-30',
'yyyy-mm-dd')
        TABLESPACE tsb,
       PARTITION A_part_20030430 VALUES LESS THAN TO_DATE(' 2003-04-30',
'yyyy-mm-dd')
        TABLESPACE tsc,
       PARTITION A_part_20030730 VALUES LESS THAN TO_DATE(' 2003-07-30',
'yyyy-mm-dd')                    TABLESPACE tsd)

Can you please give me a quick example on this.

Rating: 4
November 01, 2002
Reviewer: AD from UK
Tom,

Can you please reponse to my questions I requested earlier.  Below is the link.
Thanks in advance
http://asktom.oracle.com/pls/ask/f?p=4950:8:1535483::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:6050339907954,%7Baparajita%7D

          Followup:

just use dynamic sql in plsql or whatever to script it.  tablespace names *must*
be hardcoded (cannot be bind variables) in sql statements so you'll be using a
script of some sort.

      Rating:
      4
clarifications  November 01, 2002

Reviewer:  AD  from UK
Tom,

I refer to the link below.

http://asktom.oracle.com/pls/ask/f?p=4950:8:1535719::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:1238800184155,%7Bpartitions%7D
"A partition exchange is simply an exchange of segments -- no movement of data
takes place.  It is a data dictionary update". That means if I have a partition
P1 of a partitioned table A in tablespace ta, and I exchange this partition with
a table B in tablespace Tb, then Partition P1 of Table A will now reside in
tablespace Tb and table B will reside in tablespace ta. with refernce to local
index what is needed to be done so that index tablespaces are swapped like this.
Is there any other mechanism aparat from
"The ALTER INDEX...REBUILD PARTITION statement rebuilds one partition of an
index. It cannot be used on a composite-partitioned table. At the same time as
you recreate the index, you can move the partition to a new tablespace or change
attributes"
How otherwise you meet the criteria of creating a partition P1 of table A
residing in tablespace t1, index of partition P1 in tablespace ti1 and so on...
Pleases elaborate.

Once again many thanks for your time.

          Followup:

And I refer you to a new Oracle9i feature that did not exist when I wrote that
response (always look at the version in the answer, the answer is specific to a
release in many cases - as new features are added)

see

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/server.901/a90117/whatsnew.htm#969846

      Rating: 4
local index
        November 02, 2002
Reviewer:  AD  from
      UK
Tom,

Many Thanks for the prompt response.
This is specific to oracle 8.1.7 .

I just want to know how you can create local index so that each index partition
resides in different tablespaces. I am still not clear whether I have made my
point clear or not. Please reply me back if my question is not clear.

          Followup:

Here is a trick I use often to figure out the full syntax of something when I
don't feel like reading the SQL Reference guide.  I use exp/imp on a "minimum
example".  Exp/Imp is very "verbose"

Here you go:

ops$tkyte@ORA920.US.ORACLE.COM> CREATE TABLE t
  2  (
  3    x int ,
  4    collection_year int
  5  )
  6  PARTITION BY RANGE (COLLECTION_YEAR) (
  7    PARTITION PART_95 VALUES LESS THAN (1996) ,
  8    PARTITION PART_96 VALUES LESS THAN (1997) ,
  9    PARTITION PART_01 VALUES LESS THAN (MAXVALUE)
 10  )
 11  ;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create index t_idx on t( x ) local;

Index created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> host exp userid=/ tables=t

Export: Release 9.2.0.1.0 - Production on Sat Nov 2 12:25:20 2002

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

Connected to: Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production
Export done in WE8ISO8859P1 character set and AL16UTF16 NCHAR character set

About to export specified tables via Conventional Path ...
. . exporting table                              T
. . exporting partition                        PART_95          0 rows exported
. . exporting partition                        PART_96          0 rows exported
. . exporting partition                        PART_01          0 rows exported
Export terminated successfully without warnings.

ops$tkyte@ORA920.US.ORACLE.COM> host imp userid=/ full=y show=y

Import: Release 9.2.0.1.0 - Production on Sat Nov 2 12:25:20 2002

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

Connected to: Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production

Export file created by EXPORT:V09.02.00 via conventional path
import done in WE8ISO8859P1 character set and AL16UTF16 NCHAR character set
. importing OPS$TKYTE's objects into OPS$TKYTE
 "CREATE TABLE "T" ("X" NUMBER(*,0), "COLLECTION_YEAR" NUMBER(*,0))  PCTFREE "
 "10 PCTUSED 40 INITRANS 1 MAXTRANS 255 TABLESPACE "USERS" LOGGING PARTITION "
 "BY RANGE ("COLLECTION_YEAR" )  (PARTITION "PART_95" VALUES LESS THAN (1996)"
 "  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 STORAGE(INITIAL 65536 FREEL"
 "ISTS 1 FREELIST GROUPS 1) TABLESPACE "USERS" LOGGING NOCOMPRESS, PARTITION "
 ""PART_96" VALUES LESS THAN (1997)  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRAN"
 "S 255 STORAGE(INITIAL 65536 FREELISTS 1 FREELIST GROUPS 1) TABLESPACE "USER"
 "S" LOGGING NOCOMPRESS, PARTITION "PART_01" VALUES LESS THAN (MAXVALUE)  PCT"
 "FREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 STORAGE(INITIAL 65536 FREELISTS "
 "1 FREELIST GROUPS 1) TABLESPACE "USERS" LOGGING NOCOMPRESS )"
. . skipping partition "T":"PART_95"

. . skipping partition "T":"PART_96"

. . skipping partition "T":"PART_01"

 "CREATE INDEX "T_IDX" ON "T" ("X" )  PCTFREE 10 INITRANS 2 MAXTRANS 255 LOGG"
 "ING LOCAL(PARTITION "PART_95"  PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE(I"
 "NITIAL 65536 FREELISTS 1 FREELIST GROUPS 1) TABLESPACE "USERS" LOGGING, PAR"
 "TITION "PART_96"  PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE(INITIAL 65536 "
 "FREELISTS 1 FREELIST GROUPS 1) TABLESPACE "USERS" LOGGING, PARTITION "PART_"
 "01"  PCTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE(INITIAL 65536 FREELISTS 1 F"
 "REELIST GROUPS 1) TABLESPACE "USERS" LOGGING ) "
Import terminated successfully without warnings.

ops$tkyte@ORA920.US.ORACLE.COM>

that shows the full verbose syntax for doing this, with tablespaces and all.

      Rating: 5
some more on
      local index  November 03, 2002
Reviewer:  AD
       from UK
Tom,
Thanks for providing such a detailed example. I should not have continued this
discussion further but I really need this point clear. This is fine if we know
the tablespaces for indexes (for differnt partitions ) at the time of creating
the local index. But that is not the case with my development requirement. As
the DBA insists that he is not going to create all the future tablespaces for
storing the data/ indexes at this point of time. They will be created later as
and when they are required. He thinks that these tablespaces should be parameter
driven of number of months of data etc.
Could you please tell me what should be the way to create the local index in
that situation. Do you think there is no other alternative than using "The ALTER
INDEX...REBUILD PARTITION" for each time a partition is added so that the index
patrtition is residing in a specific tablespace. Is it at all a good idea or I
have to think something else.

          Followup:

ops$tkyte@ORA920.US.ORACLE.COM> CREATE TABLE "OPS$TKYTE"."T" ("X" NUMBER(*,0),
"COLLECTION_YEAR"
  2  NUMBER(*,0)) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 TABLESPACE
  3  "USERS" LOGGING PARTITION BY RANGE ("COLLECTION_YEAR" ) (PARTITION
  4  "PART_95" VALUES LESS THAN (1996) PCTFREE 10 PCTUSED 40 INITRANS 1
  5  MAXTRANS 255 STORAGE(INITIAL 65536 FREELISTS 1 FREELIST GROUPS 1)
  6  TABLESPACE "USERS" LOGGING NOCOMPRESS, PARTITION "PART_96" VALUES
  7  LESS THAN (1997) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
  8  STORAGE(INITIAL 65536 FREELISTS 1 FREELIST GROUPS 1) TABLESPACE
  9  "USERS" LOGGING NOCOMPRESS ) ;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> CREATE INDEX "OPS$TKYTE"."T_IDX" ON "T" ("X" )
PCTFREE 10 INITRANS 2
  2  MAXTRANS 255 LOGGING LOCAL(PARTITION "PART_95" PCTFREE 10 INITRANS 2
  3  MAXTRANS 255 STORAGE(INITIAL 65536 FREELISTS 1 FREELIST GROUPS 1)
  4  TABLESPACE "USERS" LOGGING, PARTITION "PART_96" PCTFREE 10 INITRANS 2
  5  MAXTRANS 255 STORAGE(INITIAL 65536 FREELISTS 1 FREELIST GROUPS 1)
  6  TABLESPACE "USERS" LOGGING ) ;

Index created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> alter table t add partition part_97 values less
than (1998) tablespace tools
  2  /

Table altered.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select 'TABLE', partition_name, tablespace_name
  2    from user_tab_partitions
  3   where table_name = 'T'
  4   union all
  5  select 'TABLE', partition_name, tablespace_name
  6    from user_ind_partitions
  7   where index_name = 'T_IDX'
  8  /

'TABL PARTITION_NAME                 TABLESPACE_NAME
----- ------------------------------ ------------------------------
TABLE PART_95                        USERS
TABLE PART_96                        USERS
TABLE PART_97                        TOOLS
TABLE PART_95                        USERS
TABLE PART_96                        USERS
TABLE PART_97                        TOOLS

6 rows selected.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> alter index t_idx rebuild partition part_97
tablespace xdb;

Index altered.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select 'TABLE', partition_name, tablespace_name
  2    from user_tab_partitions
  3   where table_name = 'T'
  4   union all
  5  select 'TABLE', partition_name, tablespace_name
  6    from user_ind_partitions
  7   where index_name = 'T_IDX'
  8  /

'TABL PARTITION_NAME                 TABLESPACE_NAME
----- ------------------------------ ------------------------------
TABLE PART_95                        USERS
TABLE PART_96                        USERS
TABLE PART_97                        TOOLS
TABLE PART_95                        USERS
TABLE PART_96                        USERS
TABLE PART_97                        XDB

6 rows selected.

would be the way to go if you really truly think you need, want or desire
the index to be in a separate tablespace.  Me, I would keep it in the same
tablespace probably -- makes the tablespace self contained (easier to transport
or unplug).  There isn't any real performance benefit to separating these things
or anything.  I would probably just let them be together.

Since the index partition is empty, the rebuild it instantaneous.

      Rating: 5
Indexes
        November 03, 2002
Reviewer:  AD  from
      UK
Tom,
Thanks so much for your help with this. I have read few discussions on this site
about the issue of keeping the index and data in separate tablespaces and I
definitely appreciate your suggestions of keeping them in same tablespaces but
some people dont think very much logically. That is the reason of keeping them
in separate tablespaces.

I am partitioning on update_date but most of the queries asked on this table
will be something like

select * from table A
where col1 =to_date('03-nov-2002', 'dd-mon-yyyy')
and   col2 = 'A'

or

Select * from table A
where col2='A'

Here col1 is date field and let's say with 400,000 distinct values and col2 with
three distinct values in 2 million rows table. What kind of indexes should be
preferred.
1)Local nonprefixed index on col1 and local nonprefixed bitmap index on col2
2) or local nonprefixed composite index on (col1, col2)
or something else.

Your suggestion will be very much appreciated.

          Followup:

yuck -- you partition on update_date but query on two unrelated columns.

if you have 10 partitions -- you'll need to do 10 index range scans on col1.  If
you have N partitions -- you'll need to do N index range scans!!!

That won't be *faster* that is for sure.

one would need to know the distribution of col2 in order to say -- is the value
you really query on 'A'?  or was 'A' just an example? how many 'A's

if you want the queries to go fast -- i'm thinking "global index"

I'm definitely knowing -- YOU will want to benchmark and test various
implementations.

      Rating: 5
Further points
      on indexing  November 05, 2002
Reviewer:  AD
       from UK
Tom,

First of all thanks for your help.
I have taken note of your suggestion for a global index. But I have two points
to understand
i) It may not be possible to keep each index partition in separate tablespace in
case of global index - and that is why my choice is restricted to use local
index
ii)It is a case of rolling window where I may need to rebuilt the global index -
which may be time consuming.

My question is as I don't know exactly what kind of queries will be running on
this partition table - is it advisable to the users for the inclusion of
update_date (partition_key) in all their queries so that partition elimination
takes palce. Can you please suggest.

          Followup:

i) false -- each index partition can be in an different partition

ii) 9i lets you use "update global indexes" to maintain a global index during
these operations

it only makes sense to have update date in the query IF it makes sense to have
it there.

November 05, 2002 Govind -- Thanks for the question regarding
      "Increase utl_file max _linesize parameter dymamically ", version 9.2

      You Asked
Tom ,
  Thanks for this wonderful site,Helpled be me more intimate with the Database.
I have a situation which needs to increase the max_linesize on the file after
the fopen operation.

  utl_file.fopen ( l_outdir , l_outfile, 'W') assigns default 1023 for the line
size.
Is there any way to increase it to 32K  via PL/SQL without reopening the file
again?. Or if this is not possible can we  override  Oracle default of 1023 to
32K at  init.ora level?

Thanks in advance.
Govind.

      and we said...
You have to re-open the file, which is 100% possible.

If you cannot change the original FOPEN call (you should be able to???) -- you
can simply

o close the file
o reopen the file in APPEND mode with the new linesize

no, you cannot change the default.
November 06, 2002 Vinod -- Thanks for the question regarding
      "Computing execution time for procedure, function or SQL statement",
      version 8.1.7

      You Asked
Hi Tom,

Is there are any pre-defined procedures or functions available which gives the
execution time for a user defined procedure or function or SQL statement in
Oracle.

Any help would be greatly appreciated.

Thanks.

Regards
Vinod

      and we said...
No, not "really".

You would either

a) time it yourself, eg:
   declare
      n number := dbms_utility.get_time;
   begin
      procedure (or insert or whatever );
      n := (dbms_utility.get_time-n)/100;
   end;

   (n has elapsed time in seconds now)

b) use dbms_profiler (search this site for that)

c) use timed_statistics+sql_trace and tkprof

d) in sqlplus you can

   SQL> set timing on
November
      06, 2002 Ashiq -- Thanks for the
      question regarding "Percentage of Exp or Imp got over", version 8.1.7

      You Asked
Hi Tom,

 1) I know ur dead against taking logical dumps.But i want to know ,when we take
logical dumps (either exp or imp) is it any way to know ,how much dump it got
over when we're taking it.just for a curiosity i want to know!

2) Is any way i can add a new column in between a existing columns in a table .

example:emp table

ename
sal
deptno
job

now i want to add a new column  btw sal and deptno in emp table.

ename
sal
empno <-- new column
deptno
job

Of course ,we can create  temp table with the same structure what we want ,drop
the emp table and re-create with the same structure of temp table.

 Other than this is any other way is there to do this ,'cos drop the emp table
causes me in lot of dependent objects to get invalidate and referencial
constraints problem i'm getting it.

thanx

      and we said...
1) I'm not against it -- I against people thinking "this is my backup and
recovery strategy".  I do exports 3 times a week myself - but not for backups.
I use it solely to

a) test the integrity of the tables/data dictionary
b) recover that "whoops, I lost a procedure" accident
c) recover that "whoops, I dropped the wrong table -- last nights copy is good
enough" accident

there is nothing to show you are "50%" done, you can use FEEDBACK on exp so exp
prints out a little marker every N rows exported (sort of like FTP with "hash
on").  That'll give you an idea.

2) well, I would seriously question WHY the order of the columns is even a tiny
bit relevant.  I would HOPE you never use select * in real code (other then in
PLSQL where it is very very safe to do that).  I would HOPE you never insert
into t values (....) without listing the columns!!!  If you never do that -- the
order of the columns is quite simply "not even a tiny bit relevant".

However, this is what you do:

alter table emp add empno;
rename table emp to emp_table;
create view emp as select ename, sal, empno, deptno, job from emp_table;
grant on emp to ..... <all of the grants you had on emp_table>;

done.

Review &
      Followup

      Rating: 5
Percentage of
      Exp or Imp got over  November 06, 2002
Reviewer:
      Ashiq Shamsudeen  from Trichy,TamilNadu,India
Thanx for ur timely reply...

November 06, 2002 partha -- Thanks for the question regarding
      "Convert Long Column to Varchar2", version 8.1.6

      You Asked
I have following select statement from which i'm generating a Create trigger
statement, can you please help me how do i concatenate complete text of
trigger_body(long) to other fields.

Eg:

select 'CREATE OR REPLACE TRIGGER'||' '||trigger_name||'
'||description,trigger_body
from all_triggers
where owner = 'XXX';

Thanks in advance.

Partha

      and we said...
You cannot (nor do you want to)

set echo off
set verify off
set feedback off
set termout off
set heading off
set pagesize 0
set long 5000
spool &1..sql

select
'create or replace trigger "' ||
         trigger_name || '"' || chr(10)||
 decode( substr( trigger_type, 1, 1 ),
         'A', 'AFTER', 'B', 'BEFORE', 'I', 'INSTEAD OF' ) ||
              chr(10) ||
 triggering_event || chr(10) ||
 'ON "' || table_owner || '"."' ||
       table_name || '"' || chr(10) ||
 decode( instr( trigger_type, 'EACH ROW' ), 0, null,
            'FOR EACH ROW' ) || chr(10) ,
 trigger_body
from user_triggers
where trigger_name = upper('&1')
/
prompt /

spool off
set verify on
set feedback on
set termout on
set heading on

followup to comment one

Add a set long 5000 (or more) to the script -- sorry, I have that in my
login.sql to show upto 5000 bytes of a long -- not the default of 80
Review &
      Followup

      Rating: 2
Generating
      create Trigger statement  June 14, 2001
Reviewer:
      helen clark  from USA
i can't see complete info of the trigger, when i ran the statement?.

Partha

      Rating: 5
Generate Create
      Statement for Triggers  June 14, 2001
Reviewer:
      Partha  from USA
This is very useful for DBA's.

Partha

      Rating: 4
Generate Create
      Statement for Triggers   February 27, 2002

Reviewer:  Marta  from Spain
thanks, just what i need.

      Rating:
      5
gettrigger  November 06, 2002
Reviewer:
      mo
Tom:

I tried your code to copy a trigger from one DEV to TEST but the trigger in TEST
had errors.

here is the trigger in DEV and it was copied.

CREATE OR REPLACE TRIGGER pending
  AFTER INSERT OR UPDATE of LOG_APP on intreq
  FOR EACH ROW
  WHEN (new.log_app='A')

in TEST:

create or replace trigger "PENDING"
AFTER
INSERT OR UPDATE
ON "IMSADMIN"."INTREQ"
FOR EACH ROW

It did not have the when condition and table.column format was different?

Thanks,

          Followup:

yup, i don't get whens, referencing and some other things (feel free to modify
it and post it back up !)

      Rating: 5
This worked
      well in Oracle8i, 9i  June 27, 2003
Reviewer:
      Winston
set echo off
set long 50000
set verify off
set feedback off
set termout off
set heading off
set pagesize 0
spool &1..sql

select
'create or replace trigger ' ||
substr(description,1,254)
|| DECODE(when_clause, null, null, 'when ( '||when_clause||')'||CHR(10)) ,
 trigger_body
from user_triggers
where trigger_name = upper('&1')
/
prompt /

spool off
set verify on
set feedback on
set termout on
set heading on

November 06, 2002 -- Thanks for the question regarding "different
      cost", version 9.0.1

      You Asked
Hi Tom,

We have Dev and Prod servers. They have same schema, the rows on both server are
almost same. I used explain plan to analyze same SQL on both server. However I
got different results back. SQL code is quite simple:

select col1, substr(col2..), rowtohex(col3)... from table1;

The cost I got from dev server is 1 while the cost from prod server is 9.

Could you please tell me why this happens.

Thanks.

      and we said...
Tons of things influence the cost.  Consider the effects of changing the
multi-block read count on the cost of a full table scan:

big_table@ORA920.US.ORACLE.COM> set autotrace traceonly explain
big_table@ORA920.US.ORACLE.COM> select * from big_table;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2508 Card=2128826
Bytes=272489728)
   1    0   TABLE ACCESS (FULL) OF 'BIG_TABLE' (Cost=2508 Card=2128826
Bytes=272489728)

big_table@ORA920.US.ORACLE.COM> alter session set db_file_multiblock_read_count
= 1;

Session altered.

big_table@ORA920.US.ORACLE.COM> select * from big_table;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=15548 Card=2128826
Bytes=272489728)
   1    0   TABLE ACCESS (FULL) OF 'BIG_TABLE' (Cost=15548 Card=2128826
Bytes=272489728)

big_table@ORA920.US.ORACLE.COM> alter session set db_file_multiblock_read_count
= 1000;

Session altered.

big_table@ORA920.US.ORACLE.COM> select * from big_table;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=639 Card=2128826
Bytes=272489728)
   1    0   TABLE ACCESS (FULL) OF 'BIG_TABLE' (Cost=639 Card=2128826
Bytes=272489728)

*_size, optimizer_*, block buffer size, etc -- they all can affect the cost of
steps in a query plan.

I will guess your init.oras are different in some fashion OR the stats are way
out of wack.

November 06, 2002 Pasko -- Thanks for the question regarding
      "Oracle Expert", version 9iR2

      You Asked
Hi Tom,

I have a simple Question..

Should we trust the Implementation scripts generated by Oracle Expert ?

Thanks in advance.

      and we said...
Suggestion:

understand them
evaluate them
test them
then -- trust them

Simple question:  do you trust all of the advice you get from your Sr technology
people?  Trust this as much as you would trust that.

The key is to understand what expert is suggesting, test it out.
November 06, 2002 jamie -- Thanks for the question regarding
      "table/index storage,commit,database code", version 9.2.0.1.0

      You Asked
Hi Tom,
Have a few quick questions hoping you could clarify a few things.
1) in 9i when setting index and table storage parameters
   is it just the initial,next and pctincrease that are not needed anymore or
are there others I can leave off when using lmt's. Also
can you show an sample breakdown of how you size a table based on block
size,estimate of rows/row length,number users inserting into the table are
etc... Is it just having an idea of largest possible row in bytes then how many
of theses can fit into my particular blocksize taking into account the types of
operations(insert,update,delete) and number of users.

2)Does a commit take the same amount of time regardless of the amount of work
that is being committed. I have a co-worker that says when doing large inserts
he would commit every 1000 rows and it sped up the process.He said when he let
the entire transaction run and then commit it would take much longer to finish
because of the amount of rollback being used.Does this sound correct to you.

3)I was wondering how you felt about applications with database code put into
the middle tier of an application logic server. Besides having lost control of
security and basically the entire database what about performance is there any
noticeable difference or is it a matter of who is doing the coding. Have you
ever had the opportuinty to compare an app where the majority of the code is in
the database against an application that has all database code moved to the
middle tier.

      and we said...
initial, next, pctincrease, minextents, maxextents -- all sort of "not relevant"

we'll use initial, next, minextents and pctincrease to decide how much to
initially allocate as demonstrated here:

ops$tkyte@ORA920.US.ORACLE.COM> create tablespace t
  2  datafile size 5m
  3  uniform size 64k
  4  /

Tablespace created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create table  t ( x int )
  2  storage ( initial 128k next 256k pctincrease 100 minextents 5 ) tablespace
t;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select (128+256+512+1024+2048)/1024 from dual;

(128+256+512+1024+2048)/1024
----------------------------
                       3.875

ops$tkyte@ORA920.US.ORACLE.COM> select sum(blocks)*8/1024 || 'm' from
user_extents where segment_name = 'T';

SUM(BLOCKS)*8/1024||'M'
-----------------------------------------
3.875m

So it would be easier just to use INITIAL to say "i want 3.875m" and be done
with it.

Maxextents is utterly ingored.  Unlimited is the setting (except for RBS --
they'll be 32k)

This

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:406873025890
is the only way I know to really size stuff...

2) Your coworker is describing how to SLOW DOWN THE ENTIRE PROCESS.  Not speed
it up.  Not speed it up.  Not speed it up.

they are describing how to

a) make it run slower
b) make it generate more redo
c) make it do more work
d) make it wait more often for lgwr

they have it 100% BACKWARDS.

If you have my book -- the proof is in there.  The amount of time taken to
commit is not a function of the amount of work you've done really -- however if
you commit 100 times and I commit 1 time -- you'll have spent 100 times longer
committing then i did!!!

If you want to prove it to them, get them a copy of my book -- they need it.

(pet peeve of mine, can you tell?)

3) boy oh boy -- pet peeve number 2.   You just want to get me going don't
you....

Hate it, don't like it, avoid it, don't do it myself.

there you go.

(yes I have, I'll take a well designed and implemented data BASED application
over a zillion lines of really cool middle tier code that needs 15 app servers
to "scale")

Review &
      Followup

      Rating:
      5
  November 06, 2002
Reviewer:  jamie

Thanks Tom as usual your answers are logical and to the point.Excellent!

November 07, 2002 Paul -- Thanks for the question regarding "Where
      has all the tablespace gone and how can I recover it.", version 8.1.7

      You Asked
Dear Tom

I wanted to run some stats pack reports prior to some major changes planned for
our database.
Database is v8.1.7  running on Solaris8.

At first I had an error due to my TOOLS tablespace being 100% full. My TOOLS
tablespace is 52Mbytes.
I ran rdbms/admin/sppurge and rdbms/admin/sptrunc thinking that they would clear
all the objects that were holding old snapshot data that I no longer need. But
this only freed up some 7.5 Mbytes of space. I have a tablespace 85% full, and I
cannot see why.

Following are some queries I ran to show what I mean:

NETDB: SYSTEM>  select * from dba_data_files
  2 where TABLESPACE_NAME = 'TOOLS'

FILE_NAME                    FILE_ID TABLESPACE_NAME                     BYTES
  BLOCKS STATUS
------------------------- ---------- ------------------------------ ----------
---------- ---------
RELATIVE_FNO AUT   MAXBYTES  MAXBLOCKS INCREMENT_BY USER_BYTES USER_BLOCKS
------------ --- ---------- ---------- ------------ ---------- -----------
/rep1/NETDB/tools01.dbf            2 TOOLS                            52428800
    6400 AVAILABLE
           2 NO           0          0            0   52420608        6399

NETDB: SYSTEM> select sum(bytes) from dba_free_space
  2  where TABLESPACE_NAME = 'TOOLS';

SUM(BYTES)
----------
   7593984

Only user perfstat has quota on TOOLS;

NETDB: SYSTEM> select username from dba_ts_quotas
  2  where TABLESPACE_NAME = 'TOOLS';

USERNAME
------------------------------
PERFSTAT

User perfstat has 59 objects

select object_name, object_type, status from user_objects;
..............
..............
59 rows selected.

All are either tables or indexes.
As the tables are empty I must assume that all the space is taken by the
indexes.
But shouldn't they have cleared when I emptied the tables??

I can't drop the indexes because of their constraints to the tables. Well I
could drop the constraints first I suppose but then I might as well drop the
whole schema and re-install it.

Can you enlighten me as to what's going on here and tell me how to truncate the
indexes, if possible.

regards

Paul

      and we said...
When you delete data from a table -- the space is in fact "freed" -- for reuse
BY THAT TABLE.

Once a table grows -- it'll be that big forever until you

a) drop it
b) truncate it

deleting rows frees space, but it cannot give it back to the "tablespace".

If you search this site for a script

show_space

you'll find a way to measure how empty a "table" is -- how much free space is
available in it.

The space is there -- there space is available -- statspack is ready to go
again.
November 07, 2002 vikrant -- Thanks for the question regarding
      "bulk loading", version 9.0.1

      You Asked
hi tom

I have a code that uses the utl_file package to read from a flat file that has
millions of records.
I read the file one line at a time and store the fetched values into
arrays(pl/sql tables of varchar2).
The program has to read a lot of files, depending upon the transactions.

The program works really well for a limited amount of loads. When the number of
files increases, the
rate of loading gets slower and slower.

eg.
 In case of files having uniform number of rows, for 650 files, the rate of
loading is 13 files
 in one min (combined number of records is around 50,000) into 57 different
tables.
 The whole loading process completes in about two minutes.
 When the number of files increases,the rate of loading gets slower gradually
and for 1300 files,
 the whole loading process completes in about twenty-two minutes.

i am unable to figure out why.
plzzzzz help me.

i am sending u a piece of my code that could perhaps explain something.
There are nearly 30 such loops as shown in the listing below.

the configuration of my server is:
physical ram 512 m.
disks : 1 (size 40 gb)

BEGIN
  forall v_itc_hdrindx in 1..v_itc_routekey.COUNT
  INSERT /*APPEND*/  INTO T_DOCUMENT_UHH nologging
  values
  (
   v_ctry_code,
   v_co_code,
   v_itc_docseqno(v_itc_hdrindx),
   v_itc_prefix(v_itc_hdrindx)||v_itc_documentnbr(v_itc_hdrindx),
   to_number(v_itc_routekey(v_itc_hdrindx)),
   v_cycle_nbr,
   v_route_nbr,
   v_itc_empid(v_itc_hdrindx),
   v_itc_docdate(v_itc_hdrindx),
   v_itc_doctime(v_itc_hdrindx),
   v_itc_transdate(v_itc_hdrindx),
   v_itc_transtime(v_itc_hdrindx),
   v_itc_transtype(v_itc_hdrindx),
   v_input_srce,
   0,
   v_itc_voidindic(v_itc_hdrindx),
   'F',
   v_dc_nbr,
   'F',
   v_input_srce
  );
  commit;

  EXCEPTION
   WHEN NO_DATA_FOUND then
    dbms_output.put_line('No data found');
   WHEN OTHERS then
    dbms_output.put_line('No data found' || sqlerrm);
 END;

THANKS IN ADVANCE
NIKHIL
INDIA

      and we said...
You do understand that the /*+ append */ hint you have is

a) useless
b) misleading to others reading the code
c) not doing what you might believe it is.

Read:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c21dlins.htm#365
to find out why.

Then, if you have my book "Expert one on one Oracle" -- read chapter 10 to learn
how to use TKPROF and STATSPACK and since you have lots of PLSQL code -- read
the appendix on dbms_profiler

These tools will be invaluable to you in order to pinpoint where your specific
bottleneck is.  If you do not have the book "Expert one on one Oracle" -- you
can see

TKPROF

http://asktom.oracle.com/~tkyte/tkprof.html
STATSPACK

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96533/tools.htm#29555
and search this site for dbms_profiler

I would hazzard a guess however -- you are loading everything into memory, you
have a tiny bit of memory -- you are most likely causing NT to swap and thrash
about like mad.  My laptop has more memory then that server does.

Oh -- another thing -- it is almost certainly a BUG to have:

   WHEN OTHERS then
    dbms_output.put_line('No data found' || sqlerrm);
   END;

you are in effect saying "hey, get an error -- just IGNORE it".  Same with that
no_data_found. The ONLY way thats going to happen is if

o your arrays are not filled in properly -- that would be a bug
o you have a trigger that raises that error -- that would be a bug

suggest you REMOVE exception blocks UNLESS You are actually expecting the
exception to happen and you deal with it.  Here, you just "ignore them" -- that
is a bug.

November 07, 2002 Chris -- Thanks for the question regarding "Using
      "PRAGMA automated_transactions " in forms 6i Poses problems", version
      forms 6i

      You Asked
Hi Tom

I am having this problem with pragma's and i think you
can be of help

We are trying to use this pragma
"PRAGMA automated_transactions"  in forms6i, it always
comes up with an error "undefined pragma" without any oracle database error
code, although we
are able to create a database procedure which contains
this PRAGMA and it works fine, calling this procedure
does not work in forms6i giving this same error,

Can you be of help please

we are currently using oracle 9i database

thanks a million

chris

      and we said...
there is no such pragma "automated_transaction".

You might mean "autonomous_transaction"

http://asktom.oracle.com/~tkyte/autonomous/index.html
and they only make sense INSIDE the database -- not in a client side procedure.
You would put that in a DATABASE stored procedure.

Review &
      Followup

      Rating: 3
Forms6i
      'equivalent' to autonomous transaction  November 07, 2002

Reviewer:  Gary  from Sydney, Aus
Forms6i has a built-in package, EXEC_SQL, which can be used with a similar
effect to an autonomous transaction.
Specifically it allows the form to create a separate session on the same (or
another) database and SQLs can be run in that separate session, inserting,
updating, deleting and commiting independently of the main session. You code
EXEC_SQL pretty much the same way as DBMS_SQL, and it's all in the Forms Builder
help files.

November 07, 2002 Rizwan -- Thanks for the question regarding "Join
      table (many to many) query", version 8.1.7

      You Asked
Tom,
  I have a join table named fa which has two columns ft_id and im_id both of
type numbers. ft_id is a foreign key to table ft and im_id is a foreign key to
the table im.

viz.
Table fa
Column ft_id  number
Column im_id  number
PrimaryKey ft_id, im_id

Now suppose there is an application which queries the table fa by selecting a
random number of ft_id(s) from a select box. I want a result which shows Which
im_id(s) correspond to those selected ft_id(s) and what percentage of the
selected ft_id(s) are available in each im_id.

To make it clear here is an example:
Suppose we select the following ft_id(S) from the select box: 2,45,76,89 (ie 4
ft_ids)
Now there are im_id(S) 34353,35667,36767 which consists of atleast one ft_id
from the above set 2,45,76,89.
If im_id 34353 consists of 2,45,76 then the relevance column will show 3/4*100 %
 ie 75%
(ie 75% of selected ft_id(s) were found in the im_id 34353)...and so on.

So the result should be something like this

im_id  num_matches  percentage
34353  3            75%
35667  3            75%
36767  1            25%

Can you show me a way where I can get the above result with a single query
or in a most efficient way?

      and we said...
ops$tkyte@ORA920.US.ORACLE.COM> select * from fa;

     FT_ID      IM_ID
---------- ----------
         2      34353
        45      34353
        76      34353
        76      35667
        89      35667
        45      35667
        45      36767

7 rows selected.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select im_id, count(*), count(*)/cnt
  2    from (
  3  select im_id, count(distinct ft_id) over () cnt
  4    from fa
  5   where ft_id in ( 2, 45, 76, 89 )
  6         )
  7   group by im_id, cnt
  8  /

     IM_ID   COUNT(*) COUNT(*)/CNT
---------- ---------- ------------
     34353          3          .75
     35667          3          .75
     36767          1          .25

ops$tkyte@ORA920.US.ORACLE.COM>

would do it
Review & Followup

      Rating: 5
Join table
      (many to many) query  November 06, 2002
Reviewer:
      Rizwan Qazi  from Hayward, CA USA
Great solution. It is exactly what I wanted...
Thanks very much!

      Rating: 5
Join table
      (many to many) query  November 07, 2002
Reviewer:
      Rizwan Qazi  from Hayward, CA USA
Tom,
  In continuation of the above question, can you show me an alternate way where
by I can pull only those im_id(s) which correspond to ALL the selected ft_id(s)
only.
egs. If im_id = 34353 corresponds to ALL of 2,45,76,89 the result should show
34353.
 Two methods I know:
a) One is to do a chain of intersects like:
select im_id from fa where ft_id = 2
intersect
select im_id from fa where ft_id = 45
intersect
.
.
.

b) To use your query as an inline view and filter it for the count of ft_id(s):
select a.im_id from (
select im_id, count(*) ftmatch, count(*)/cnt
      from (
    select im_id, count(distinct ft_id) over () cnt
      from fa
     where ft_id in ( 2, 45, 76, 89 )
           )
     group by im_id, cnt) a
where a.ftmatch = 4

But you might be knowing a better way to accomplish this.
Thanks.

          Followup:

just use the inline view with

where count(*) = cnt

      Rating: 5
Join table
      (many to many) query  November 10, 2002
Reviewer:
      Rizwan Qazi  from Hayward, CA USA
Thanks very much!

November 07, 2002 Richard -- Thanks for the question regarding
      "Determining which query caused deadlock.", version 8.1.7

      You Asked
Our application is causing deadlocks.  After doing research I'm almost certain
it is due to not having an index on a couple of foreign key columns.

*** 2002-11-04 09:17:47.998
*** SESSION ID:(12.346) 2002-11-04 09:17:47.960
DEADLOCK DETECTED
Current SQL statement for this session:

delete from X where (col1 is null and col2=:b0)
The following deadlock is not an ORACLE error. It is a
deadlock due to user error in the design of an application
or from issuing incorrect ad-hoc SQL. The following
information may aid in determining the deadlock:
Deadlock graph:
                       ---------Blocker(s)--------  ---------Waiter(s)---------
Resource Name          process session holds waits  process session holds waits
TM-000010da-00000000        11      12   SSX             33      31         SSX
TM-00000e72-00000000        33      31    SX             11      12           S
session 12: DID 0001-000B-00000002    session 31: DID 0001-0021-00000002
session 31: DID 0001-0021-00000002    session 12: DID 0001-000B-00000002
Rows waited on:
Session 31: no row
Session 12: no row
===================================================
PROCESS STATE
-------------
Process global information:
     process: cacf1354, call: cad90df0, xact: cb872134, curses: cad1f878,
usrses: cad1f878
  ----------------------------------------
  SO: cacf1354, type: 1, owner: 0, pt: 0, flag: INIT/-/-/0x00
  (process) Oracle pid=11, calls cur/top: cad90df0/cad90df0, flag: (0) -
            int error: 0, call error: 0, sess error: 0, txn error 0
  (post info) last post received: 85 0 4
              last post received-location: kslpsr
              last process to post me: cacefd90 1 2
              last post sent: 3414985080 0 13
              last post sent-location: ksasnd
              last process posted by me: cacefd90 1 2
    (latch info) wait_event=0 bits=10
      holding     cca39230 Parent+children enqueue hash chains level=4
        Location from where latch is held: ksqcmi: kslgpl:
        Context saved from call: 0
        state=busy
        recovery area:
Dump of memory from 0xCACEEDF0 to 0xCACEEDF8
<snip>

Notes:
1. X is the parent of both of the tables involved in the deadlock.  Other than
this, the two tables (call them A & B) have no relationship.
2. Transactions that involve A never involve B and vice versa.
3. X has approximately 1/2 million rows.
4. B has 0 rows, A has about 10,000 rows

I want to reproduce this deadlock so I can prove that creating an index will fix
it.

1. How do I determine the sequence of queries in each session that lead up to
the deadlock?

2. Should all foreign key columns have indexes on them?  If so, is it sufficient
if a foreign key column is the first column in a seperate composite index?

If you need more info I will be happy to post it.

Thanks for the great site!

      and we said...
The delete from X will cause a FULL TABLE lock on both A and B before it starts
processing.

If another session has a lock on a row in X that your delete will ultimately hit
-- that will block your delete.

If that other session then attempts to modify a row in either of A or B, it will
deadlock.

This script in 817 and before will demonstrate this:

-----------------------------------------------------------------
set echo on

drop table X cascade constraints;
drop table A cascade constraints;

create table X ( x int primary key );
create table A ( x references X );

insert into x values ( 1 );
insert into x values ( 2 );
commit;

/*
In another session, go and issue "update x set x=2 where x=2"
and come back here and hit enter
*/
pause

/*
this will block.  When it does, goto the other session
and issue "insert into a values (2)"
*/
delete from x where x > 0;
--------------------------------------------------------------------

In 9i the behavior is a little different -- this particular example won't
DEADLOCK as written -- but it will block and can still deadlock under many
conditions.  The duration of the child table lock is not for the length of the
transaction in 9i (as in 8i and before) rather it is for the length DELETE
statement itself.  It reduces the scope of the problem -- does not REMOVE it.

If you have my book "Expert one on one Oracle" -- I go into depth on when to
index fkeys -- basically if you

o update the parents primary key (some tools do this "blindly" so watch out for
that) OR delete from the parent

o AND you modify the child

then yes, you want to index the fkeys.  And yes, the fkey columns need only be
on the leading edge of some index - so the index can be "bigger" then the fkey
itself.

Review &
      Followup

      Rating: 5
So you can't do
      it?  November 07, 2002
Reviewer:  Richard

So I guess there is no definitive way to determine the sequence of events that
led up to the deadlock?

Creating an index on A and B has helped, but I don't KNOW that the user has
re-enacted the sequence of events that created the deadlock in the first place.

Oh, I forgot to mention that transactions involving updating/deleting the
primary key of X do not manipulate A or B directly.

          Followup:

The transactions that update/delete the parent table DO manipulate A and B
directly -- they LOCK them!

You can get a little more info by taking:

TM-000010da-00000000        11      12   SSX             33      31         SSX
TM-00000e72-00000000        33      31    SX             11      12           S

and then

select object_name, object_type from all_objects
where object_id in ( to_number( '10da', 'xxxx' ), to_number( '0e72', 'xxxx' ) );

One session was waiting on TM-10da, the other on TM-0e72.  That'll tell you the
two tables involved here.

This trace does not look like a simple deadlock as I have depicted, no - there
was something else here.  Maybe when you see the objects involved -- the pieces
will start to fall into place.

      Rating:
      4
Pieces  November 07, 2002
Reviewer:
      Richard
I looked up A and B in dba_objects.  That's how I was able to create indexes on
them and tell you their only relationship is that both have X as a parent.
Learning this information only made the puzzle harder because the two tables
have nothing to do with one another.  One is a history of customer activities
the other a list of purchase orders (made by the company).  Furthermore the site
experiencing the problem does not have the purchase order module for the app.

Given this scenario, I'm having a hard time figuring out what has to happen to
cause the deadlock.

>The transactions that update/delete the parent table DO >manipulate A and B --
they LOCK them!

So if I have two sessions going and both try to update the pk of X this could
cause a deadlock?  Doesn't Oracle lock A and B in the same order every time?  If
so, wouldn't this just cause a block?

          Followup:

No, they lock the child table in a shareable mode -- prevents x-locks but not
other share level ones.

Do A and B have fkeys pointing back to the SAME constraint or do their fkeys
point back to different columns of X?

      Rating: 5
Both point to
      the same column   November 07, 2002
Reviewer:
      Richard
It's a number(9) and the PK.  The only other column in X is a blob.

November 08, 2002 John -- Thanks for the question regarding "create
      like user", version 8.1.6

      You Asked
Tom,

Other than using OEM's create like command to copy an account from one user name
to another, is there a way to just re-name an existing account and maintain all
of their data like: alter user test rename test1 cascade or something like that
?

thx.

      and we said...
There is no way to rename a schema.

One method that works is:

o exp .... owner=old_name
o imp .... fromuser=old_name touser=new_name
o drop user old_name cascade

You will have to beware of things like:

create or replace trigger my_trigger
before insert ont OLD_NAME.T   <<<== qualified table name
....

create or replace view V
as
select * from OLD_NAME.T <<<=== qualified table name

If you use import/export as the fully qualified name will persist across the
from user/to user.  Those triggers and views will need to be manually fixed.

Review &
      Followup

      Rating:
      5
  March 26, 2001
Reviewer:
      A reader
Thanks Tom !!

      Rating: 4
Renaming a
      user  June 10, 2002
Reviewer:  D Marikar
       from Cambridge, UK
Having to deal with regular schema updates and usernames based on the schema
version, this tip will come in quite handy!

      Rating: 4
copy user from
      one environment to other  September 12, 2002

Reviewer:  Ravi Kumar  from Chicago, IL, USA

Hi Tom,

There are some users already got created in one environment, lets say "DEV". And
I want to copy them from this environment to other environment (DEV to PROD)
with the same configuration.

How can I do that? Can I generate a statement and run it in the new environment?
Please suggest.

Thanks,
Ravi.

          Followup:

If you mean "i want to copy their objects", read the answer above.  exp/imp does
that.

If you mean "i created some users, they have passwords and roles and I want to
copy the CREATE USER and GRANTS", then you would be using a tool or creating
"create" statements from the data dictionary using queries (by hand).  Eg:

select 'create user ' || username || ' identified by values ''' || password ||
        ''' temporary tablespace ' || .....
 from dba_users;

spool that and you have your create user statements.

      Rating: 3
A
      Reader  September 13, 2002
Reviewer:  Rahul
       from India
Hi Tom

I have face this problem many time 'qualified table name'
(schemaname.tablename). This is very irritating to manually remove this problem.
How can we avoid this problem at the time of exp/imp ??

Regards
Rahul

          Followup:

Umm, use relative references at the time of CREATION?

I mean -- if you have:

create table MYNAME.T ( .... );
create trigger MYNAME.TRIGGER_NAME on MYNAME.T
.....

You did it yourself.  why would you put the schema name there?  EXP/IMP cannot
auto-fix that -- it would CHANGE the meaning of many things.  I pointed it out
as something you need to be aware of.

I've NEVER used "create table OPS$TKYTE.T ( x int .... )".  Never have I seen
the point to doing so.  If you don't do that, you won't have a problem.

      Rating: 5
Just what I'm
      looking for  November 08, 2002
Reviewer:  Ralph
      V.  from Makati City, Philippines
I'm going to clone some databases tommorow and am searching your site for
information on how to do it. I already have the procedures thanks to your site.

I might also have to clone a database from 816 to 817. I am not sure whether
it's possible(?). If it's not, then I plan to use import/export to transfer THE
schema to 817.

The script you provided will take care of the other (non-owner) users.

By the way, how do I extract the roles and priveleges for these users?

Thanks very much

          Followup:

I write queries against dba_roles, dba_sys_privs and such to extract their
grants.

November 08, 2002 Antoine -- Thanks for the question regarding
      "undo tablespace", version 8.1.7

      You Asked
Hi Tom,

Is it possible to explain me how the undo tablespace works under the hood?

Is it true that sybases or sql servers just have a big rollback segment for all
the transaction? I ask you these question because I thought that all SGBD
ensures that readers do not block writer and writers do not block readers!

Antoine

      and we said...
Just like a rollback segment always worked.  An undo tablespace is simply a
tablespace that Oracle will create rollback segments in for you -- instead of
you creating rollback segments manually.

It frees the DBA from having to figure out "how many" and "how big".  Oracle
will dynamically add rollback segments as needed and extend them to satisfy your
undo_retention requirements.

SGDB?  No idea what that is.

SQLServer (which is the name of both Sybase and MS's database -- since sybase
invented and MS borrowed it) places before and after images in its logs and
doesn't have the concept of "undo spaces".  Hence -- they have no
multi-versioning and must employ a shared read lock metaphor to give you the
correct answer -- meaning that READS block WRITES and WRITES block READS.

In Oracle -- reads are not blocked by writes and a write is not blocked by
reads.  And we give the correct answer to boot.
Review &
      Followup

      Rating:
      5
thanks!  November 08, 2002
Reviewer:
      Antoine  from Montreal,Canada
Thanks Tom,

sorry, SGBD is the french for RDBMS.

Thanks also for the explaination on SQLServer undo spaces!

November 08, 2002 Sharon -- Thanks for the question regarding "want
      to find a way to shift column values up or down", version 8.1.7

      You Asked
Tom,

to illustrate what I want to accomplish, as an example, table test has two
columns, which looks like

  1000    12300
  1002     5690
  1003    10000
  1010     8765

I want to shift the vaules of the second column up so that
the table will look like
  1000    5690
  1002   10000
  1003    8765
  1010   (I don't care, or make it 12300 if possible)

shift the values down is fine too.

The reason behind this is we want to shield sensitive data in HR when refresh
test/dev database. I could scramble them but they might
not mean anything. By shifting the values no one knows what the original data is
at the meantime we could maintains the data fairly close to real world when
doing testings. This column might be salary or bonus or review rating. If you
could come up with some other solutions, keep the original data but change the
orders of the values of a specific column, that would be even better. Thanks Tom
for your time.

      and we said...
When you copy the data -- use analytic functions when you query it to "shift".
Here is an example that shows the original val and the shifted val:

ops$tkyte@ORA920.US.ORACLE.COM> select empno, val,
  2         nvl( lead(val) over (order by empno),
  3              first_value(val) over ( order by empno)) shifted
  4    from t
  5   order by empno;

     EMPNO        VAL    SHIFTED
---------- ---------- ----------
     14160        838        646
     36137        646        682
     39200        682        468
     39911        468        430
     84512        430        838

analytic functions *rock*

Review &
      Followup

      Rating: 5
It really is
      *rock* :;-))  November 08, 2002
Reviewer:
      A reader
I fixed a problem for which I was writing a complex query. Thanks Tom.

      Rating: 4
The Question in
      words  November 08, 2002
Reviewer:  Robert
       from PA
ops$tkyte@ORA920.US.ORACLE.COM> select empno, val,
  2         nvl( lead(val) over (order by empno),
  3         first_value(val) over ( order by empno)) shifted
  4    from t
  5   order by empno;

Tom, other than this "shifting" purpose, can you please put in words the
question this query answers ?

Thanks

          Followup:

well, it says:

for each row in T select the EMPNO column, the VAL column and the value of the
VAL column for the next row in T after sorting by EMPNO (reach forward a row and
get me their VAL as well).  If that next VAL is null -- then assign the
FIRST_VALUE of VAL found in T after sorting by EMPNO

      Rating: 5
This is really
      helpful  November 08, 2002
Reviewer:  Sharon
       from California, USA
Tom, as always,your answers are quick, useful and fairly easy to implement. I
appreciate your time and sure I'll use this approach to scramble my data,
ordering it in empid or ssn order and shift the column values.

      Rating: 5
Best in Oracle
      - Many Thanks Tom  November 08, 2002
Reviewer:
      Marc  from Switzerland

      Rating: 5
Oracle in
      Oracle : TOM  November 11, 2002
Reviewer:
      Riaz  from Pakistan

November 08, 2002 Steven -- Thanks for the question regarding "New
      TYPE to encapsulate java BigDecimal", version 9.0.1

      You Asked
Connected to:
Oracle9i Enterprise Edition Release 9.0.1.1.0 - 64bit Production
With the Partitioning option
JServer Release 9.0.1.1.0 - Production

Dear Tom,
I can't get this to work. I know the basics here, but ...

big_decimal_t.sql:
------------------
drop type Big_Decimal
/
create type Big_Decimal as object
(
    x    RAW(2000),

    member function abs( SELF IN Big_Decimal ) return Big_Decimal
        /* Returns a Big_Decimal whose value is the absolute value of SELF. */
)
/
show errors

****************************************************************
big_decimal_tb.sql:
-------------------
CREATE OR REPLACE
TYPE BODY Big_Decimal
AS
    member function abs( SELF IN Big_Decimal ) return Big_Decimal
        /* Returns a Big_Decimal whose value is the absolute value of SELF. */
    IS
    LANGUAGE JAVA
    NAME 'BigDecimal_Interface.abs( oracle.sql.STRUCT ) return
oracle.sql.STRUCT';
END;
/
show errors

****************************************************************
bigdecimal_interface6.sql:
--------------------------
CREATE OR REPLACE AND COMPILE
JAVA SOURCE NAMED "BigDecimal_Interface"
AS
import java.sql.*;
import java.io.*;
import oracle.sql.*;
import oracle.jdbc.*;
import oracle.oracore.*;
import oracle.jdbc2.*;
import java.math.*;

public class BigDecimal_Interface extends Object
{
    // Implement the attributes and operations for this type.
    //private byte[]    x;
    //private BigDecimal    x;

    private static Connection    conn0 = connect();
    private static StructDescriptor    structdesc0 = MyCreateDescriptor();
    //private static java.lang.Object[]    attributes0;    // ???
    private static java.lang.Object[]    attributes0 = { new BigDecimal( "0" )
};

    //private static Connection connect() throws SQLException
    private static Connection connect()
    {
        Connection    conn1 = null;
        try {
            // connect with the server-side internal driver
            OracleDriver    ora1 = new OracleDriver();
            conn1 = ora1.defaultConnection();
        } catch (SQLException e) {}

        return conn1;
    }
    private static StructDescriptor MyCreateDescriptor()
    {
        StructDescriptor    structdesc1 = null;
        try {
            structdesc1 =
                StructDescriptor.createDescriptor( "BIG_DECIMAL", conn0 );
        } catch (SQLException e) {}

        return structdesc1;
    }
    private static oracle.sql.STRUCT MyCreateStruct( BigDecimal n )
    {
        oracle.sql.STRUCT    struct1 = null;

        try {
            attributes0[ 0 ] = n;
            struct1 = new oracle.sql.STRUCT( structdesc0, conn0, attributes0 );
        } catch (SQLException e) {}

        return struct1;
    }

    public oracle.sql.STRUCT abs( oracle.sql.STRUCT s_in )
    {
        System.out.println( "abs: Main entry ..." );
        STRUCT    s_out = null;

        try{
            System.out.println( "abs: Getting attributes ..." );
            Object[]    in_attrs = s_in.getAttributes();
            System.out.println( "abs: Creating output structure ..." );
            s_out = MyCreateStruct( ((BigDecimal) in_attrs[ 0 ]).abs() );
        } catch (SQLException e) {}
        //} catch (java.sql.SQLException e) {}

        System.out.println( "abs: Exiting ..." );
        return s_out;
    }
}
/
show errors java source "BigDecimal_Interface"

****************************************************************
install.sql:
------------
set define off
SET ServerOutput ON
spool install
prompt install ...
--
-- 2002.11.07:    SRG -- Created
--
@@bigdecimal_interface6
-- Packages
-- @@bigdecimal_interface_p
--
-- Contexts
--@@create_contexts
--
-- Functions
--
-- Procedures
--@@approval_sp
--
-- Package Bodies
-- @@bigdecimal_interface_pb
--
-- Other Scripts
@@big_decimal_t
@@big_decimal_tb
spool off

****************************************************************
install.LST:
------------
install ...

Java created.

Elapsed: 00:00:02.64
No errors.

Type dropped.

Elapsed: 00:00:00.50

Type created.

Elapsed: 00:00:00.21
No errors.

Type body created.

Elapsed: 00:00:00.50
No errors.

****************************************************************
test.sql:
---------
spool test
exec dbms_java.set_output( 1000000 )
DECLARE
    /* n    Big_Decimal := Big_Decimal(); */
    n    Big_Decimal := Big_Decimal( UTL_RAW.CAST_TO_RAW( '0' ) );
    n2    Big_Decimal;
BEGIN
    -- Big_Decimal.set1( n, 1 );
    n2 := n.abs();
END;
/
spool off

****************************************************************
test.LST:
---------

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.80
DECLARE
*
ERROR at line 1:
ORA-00932: inconsistent datatypes
ORA-06512: at "SYSTEM.BIG_DECIMAL", line 0
ORA-06512: at line 7

Elapsed: 00:00:00.51

Please help.

Thanks,
Steven

      and we said...
Ok -- i see what you are trying to do now and I'm going to suggest a TOTALLY
different way.

It looks to me like you want to "hide" a java BigDecimal in a RAW -- thats not
going to work very well (if at all).  Also, trying to pass the structs back and
forth gets very ugly after a while.  My implementation uses a method of
implementing Object Type methods in Java using serialization of the attributes.
So, my PLSQL object type looks like this:

CREATE OR REPLACE TYPE BigDecimal
AS OBJECT
(
   string_representation  varchar2(255),

   MEMBER PROCEDURE abs
   AS LANGUAGE JAVA
   NAME 'MyBigDecimal.abs()',

   member function abs return BigDecimal,
   static function new return BigDecimal
)
/

the string representation is going to hold the BigDecimal value as a string in
the PLSQL layer.  So you can look at it and "see" it.

We'll implement abs as a PROCEDURE in java -- letting it work on the attributes
of the object type directly.  We'll implement abs as a FUNCTION in plsql --
makes it much easier to deal with the object types (tons less code -- trust me).

The static function is just a non-default constructor -- so you can have a
BigDecimal variable that is not "initialized" really....

The type body is:

create or replace type body BigDecimal
as

   member function abs return BigDecimal
   is
       l_newone BigDecimal := BigDecimal( self.string_representation );
   begin
       l_newone.abs;
       return l_newone;
   end;

   static function new return BigDecimal
   is
   begin
       return BigDecimal(NULL);
   end;
end;
/

The ABS function emulates the behavior of the Java ABS method in the BigDecimal
class -- it "news" a bigDecimal and absolute value's it.  Now, onto the Java
Class:

create or replace and compile
java source named "MyBigDecimal"
as
import java.sql.*;
import java.math.*;

public class MyBigDecimal implements SQLData
{
  private BigDecimal num;
  private String sql_type;

  public void abs()
  {
      num = num.abs();
  }

  // need to implement these three -- readSQL and writeSQL just
  // deserialize/serialize the attributes of the PLSQL object type
  // that this class is mapped to.  Much easier then dealing with
  // oracle.sql.STRUCT IMHO

  public String getSQLTypeName() throws SQLException
  {
      return sql_type;
  }
  public void readSQL( SQLInput stream, String typeName ) throws SQLException
  {
      sql_type = typeName;
      num  = new BigDecimal(stream.readString());
  }

  public void writeSQL( SQLOutput stream ) throws SQLException
  {
      stream.writeString(num.toString());
  }

}
/

And now we are ready to use it.  Note that our implementation allows for abs to
be called as a member PROCEDURE (Z below does that) or as a member function that
returns a "new" bigDecimal instance after abs'ing it:

ops$tkyte@ORA920.US.ORACLE.COM> declare
  2      x   bigDecimal := bigDecimal( -123.3221 );
  3      y   bigDecimal := bigDecimal.new;
  4          z   bigDecimal := bigDecimal( -12321.3234214234 );
  5  begin
  6      dbms_output.put_line( 'x = ' || x.string_representation );
  7      y := x.abs();
  8      dbms_output.put_line( 'y = ' || y.string_representation );
  9      dbms_output.put_line( 'z = ' || z.string_representation );
 10          z.abs();
 11      dbms_output.put_line( 'z = ' || z.string_representation );
 12  end;
 13  /
x = -123.3221
y = 123.3221
z = -12321.3234214234
z = 12321.3234214234

PL/SQL procedure successfully completed.

Hope that is what you were after -- it is a cleaner implementation this way.
November 08, 2002 Shalu -- Thanks for the question regarding
      "Calculating Weighted Percentile ", version 9.2.0.1.0

      You Asked
Hi,

I need to calculate Weighted Percentile as one of the columns in a SQL Query. I
am using this query to generate a report
using Reports Builder.

INSTR_ID      UPB          CUSIP       OAD1      OAD2         DIFF
      10      500          cusip1      .03       .01          .02
      20      100          cusip2      .09       .04          .05
      30      200          cusip3      .03       .00          .03
      40      200          cusip4      .05       .02          .03
      50      200          cusip5      .09       .07          .02
      60      400          cusip6      .05       .03          .02

where DIFF is the difference in OAD1 and OAD2.

I am using Oracle 9i Rel 2, I was going thru the Analytic Functions provided by
this release, I found two functions very
close but still I feel that they won't serve my purpose. They are
PERCENTILE_CONT( ) and PERCENTILE_DISC( ), because I need
to calculate the Weighted Percentile.

For the above data the Weighted Percentile should be:

INSTR_ID UPB  DIFF Weighted_Percentile
======================================
10       500  .02     0             ==>(0/1600), numerator is 0 because there is
no instr having diff <.02
20       400  .02     0             ==>(0/1600), numerator is 0 because there is
no instr having diff <.02
30       100  .02     0             ==>(0/1600), numerator is 0 because there is
no instr having diff <.02
40       200  .03    .625           ==>(500+400+100/1600), numerator is sum of
UPBS of instr havng diff < .03
50       300  .03    .625           ==>(500+400+100/1600), numerator is sum of
UPBS of instr havng diff < .03
60       100  .05    .9375          ==>(500+400+100+200+300/1600), numer is sum
of all UPBs having diff < .05

Where denominator is always sum of all the UPBs, which is 1600 in this case. And
Numerator is the sum of all the UPBs of
all those instruments which have DIFF less than the DIFF of the current
Instrument. So, to do this we should sort the records
on DIFF and use some function to calculate Weighted Percentile.

Thanks!!
Shalu

      and we said...
your exmaple is wrong -- it should have 500+400+200 when diff < .03.

Here is one way to get it (i assume that diff isn't really stored but derived so
I did it that way)

ps$tkyte@ORA920.US.ORACLE.COM> select instr_id, upb, (oad1-oad2) diff,
  2             (sum(upb) over ( order by oad1-oad2 )
  3              -sum(upb) over ( order by oad1-oad2 range between 0 preceding
and 0 following )) /
  4            sum(upb) over () weighted_pct
  5  from t;

  INSTR_ID        UPB       DIFF WEIGHTED_PCT
---------- ---------- ---------- ------------
        10        500        .02            0
        50        200        .02            0
        60        400        .02            0
        30        200        .03        .6875
        40        200        .03        .6875
        20        100        .05        .9375

6 rows selected.

Review &
      Followup

      Rating:
      1
  November 08, 2002
Reviewer:  Shalu
      Aggarwal  from Washington DC, USA
Hi, When I tried running my query after making the changes I am getting the
following error:

ORA-30483 Window functions are not allowed here.

I am using this query in Reports Builder.
In case you want to take a look at my query it is here, you can just check the
WEIGHTED_PERCENTILE column.

Select
c.name,
nvl(c.upb,0) upb,
nvl(abs(c.upb),0) abs_upb,
Power( nvl(c.upb,0) , 2) sqr_upb,
nvl(c.upb,0) * ( NVL(&p_col1,0) - NVL(&p_col2,0)) mean_diff,
ABS( nvl(c.upb,0) * ( NVL(&p_col1,0) - NVL(&p_col2,0) ) ) mean_abs_diff,
POWER ( nvl(c.upb,0) * ( NVL(&p_col1,0) - NVL(&p_col2,0) ), 2)
root_mean_sqr_diff,
nvl(c.upb,0) * CASE WHEN nvl(&p_col1,0) = 0 Then 0 ELSE round(((&p_col1 -
nvl(&p_col2,0) )/&p_col1),6) END mean_diff_per,
ABS( nvl(c.upb,0) * CASE WHEN nvl(&p_col1,0) = 0 Then 0 ELSE round(((&p_col1 -
nvl(&p_col2,0) )/&p_col1),6) END ) mean_abs_diff_per,
POWER ( nvl(c.upb,0) * ( CASE WHEN nvl(&p_col1,0) = 0 Then 0 ELSE
round(((&p_col1 - nvl(&p_col2,0) )/&p_col1),6) END ), 2) root_mean_sqr_diff_per,
c.product_type product_type,
c.ORIGINAL_TERM original_term,
c.wam wam,
c.wac wac ,
c.coupon coupon,
c.cusip,
( Sum(nvl(c.upb,0)) OVER ( ORDER BY  ABS( CASE WHEN nvl(&p_col1,0) = 0 Then 0
ELSE ((&p_col1 - nvl(&p_col2,0) )/&p_col1) END)  ) -
          Sum(nvl(c.upb,0) OVER ( ORDER BY ABS( CASE WHEN nvl(&p_col1,0) = 0
Then 0 ELSE ((&p_col1 - nvl(&p_col2,0) )/&p_col1) END) RANGE BETWEEN 0 PRECEDING
AND 0 FOLLOWING) ) /SUM(nvl(c.upb,0)) WEIGHTED_PERCENTILE,
NVL(&p_col1,0) - NVL(&p_col2,0) diff,
ABS( NVL(&p_col1,0) - NVL(&p_col2,0) ) abs_diff,
ABS( CASE WHEN nvl(&p_col1,0) = 0 Then 0 ELSE ((&p_col1 - nvl(&p_col2,0)
)/&p_col1) END ) abs_diff_per,
CASE WHEN nvl(&p_col1,0) = 0 Then 0 ELSE round(((&p_col1 - nvl(&p_col2,0)
)/&p_col1),6) END diff_per,
decode(&p_col1, 0, 'NaN', null, 'Missing', &p_col1) ref_col,
decode(&p_col2 ,0, 'NaN', null, 'Missing', &p_col2) test_col
From  VN_INSTRUMENT c, VN_RESULT a, VN_RESULT b
Where
c.instr_id = a.instr_id and
a.instr_id = b.instr_id and
a.group_id = 645 and
b.group_id = 655
Order By ABS( CASE WHEN nvl(&p_col1,0) = 0 Then 0 ELSE ((&p_col1 -
nvl(&p_col2,0) )/&p_col1) END )

          Followup:

did you try it in sqlplus -- you would need to give me a test case (create table
statement and as SMALL of a query as humanly possible).

be glad to try and help you -- however, you have to make it easy for me to do
so.

      Rating:
      5
  November 08, 2002
Reviewer:  Shalu
      Aggarwal  from Washington, DC - USA
Hi,

I figured it out. It was not working at my end because I had NVLs in my query.
When i removed all the NVLs it started working, otherwise on SQL Plus also I was
getting the same ORA error.

Thanks so much for your help, it is really great. But I would like to know few
things:
1. Does it take care of NULLs ?
2. What will it do if sum(upb) in the denominator for some reason is null or
zero ?
3. Can I use CASE statement inside it ?

Thanks,
Shalu

          Followup:

you can "order by whatever NULLS LAST" or "NULLS FIRST" to control where they
might pop up.  In general -- the functions ignore nulls -- sum() will for
example.

If the sum(upb) is NULL or ZERO -- you'll get an error -- so just decode or CASE
around that problem.

I don't know -- inside "what" exactly?

      Rating:
      5
  November 08, 2002
Reviewer:  Shalu
      Aggarwal  from Washington, DC - USA
Hi,

I am getting the same ORA error if I use CASE statement or NVL inside it. I need
to use CASE or NVL to make sure that  1.) denominator is never zero
2.) a.oad is never zero since I am dividing (a.oad-b.oad) by a.oad.

Look at the following query sample: WEIGHT column works fine here but not
WEIGHT1, actually I want to use it as I have used for WEIGHT1 column to handle
nulls and zeros.

 Select
 c.name,
 nvl(c.upb,0) upb,
        ( Sum(c.upb) OVER ( ORDER BY  ABS( (a.oad - b.oad)/a.oad) )  -
           Sum(c.upb) OVER ( ORDER BY  ABS( (a.oad - b.oad)/a.oad)
           RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING) ) /SUM(c.upb) OVER ( )
WEIGHT,
        ( Sum(c.upb) OVER ( ORDER BY  ABS( CASE WHEN a.oad = 0 Then 0 ELSE
((a.oad - b.oad )/a.oad) END )
           Sum(c.upb) OVER ( ORDER BY  ABS( CASE WHEN a.oad = 0 Then 0 ELSE
((a.oad - b.oad )/a.oad) END )
           RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING) ) /SUM(c.upb) OVER ( )
WEIGHT1,
         ABS( (a.oad - b.oad)/a.oad) abs_diff_per
 From  VN_INSTRUMENT c, VN_RESULT a, VN_RESULT b
 Where
 c.instr_id = a.instr_id and
 a.instr_id = b.instr_id and
 a.group_id = 654 and
 b.group_id = 655
 Order By ABS( CASE WHEN nvl(a.oad,0) = 0 Then 0 ELSE ((a.oad - nvl(b.oad,0)
)/a.oad) END )

Shall await your reply.

Thanks!
Shalu

          Followup:

you () are totally messed up -- but I have no problems using decode with
analytics:

ops$tkyte@ORA920.US.ORACLE.COM> select * from t;

  INSTR_ID        UPB CUSIP            OAD1       OAD2
---------- ---------- ---------- ---------- ----------
        10        500 cusip1            .03        .01
        20        100 cusip2            .09        .04
        30        200 cusip3            .03          0
        40        200 cusip4            .05        .02
        50        200 cusip5            .09        .07
        60        400 cusip6            .05        .03
        60        400 cusip6              0          0

7 rows selected.

ops$tkyte@ORA920.US.ORACLE.COM> Select
  2   instr_id,
  3   nvl(upb,0) upb,
  4   (Sum(upb) OVER ( ORDER BY  ABS( decode( oad1, 0, 0, (oad1 - oad2)/oad1)) )
 -
  5             Sum(upb) OVER ( ORDER BY  ABS( decode(oad1, 0, 0, (oad1 -
oad2)/oad1))
  6                             RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING) ) /
  7             decode(SUM(upb) OVER (), 0, 0, sum(upb) over()) WEIGHT1,
  8           ABS( decode(oad1, 0, 0, (oad1 - oad2)/oad1)) abs_diff_per
  9   From  t
 10   Order By ABS( CASE WHEN nvl(oad1,0) = 0 Then 0 ELSE ((oad1 - nvl(oad2,0)
)/oad1) END )
 11  /

  INSTR_ID        UPB    WEIGHT1 ABS_DIFF_PER
---------- ---------- ---------- ------------
        60        400          0            0
        50        200         .2   .222222222
        60        400         .3           .4
        20        100         .5   .555555556
        40        200        .55           .6
        10        500        .65   .666666667
        30        200         .9            1

7 rows selected.

      Rating:
      5
  November 08, 2002
Reviewer:
      A reader
I tried using decode, NVL and CASE statement, it gives same ORA error.

See if use decode as follows, I get the same error:

Select
c.name,
nvl(c.upb,0) upb,
( Sum(c.upb) OVER ( ORDER BY  ABS( decode(a.oad,0,0, (a.oad - b.oad )/a.oad )
-
  Sum(c.upb) OVER ( ORDER BY  ABS( decode(a.oad,0,0, (a.oad - b.oad )/a.oad )
          RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING) ) /SUM(c.upb) OVER ( )
WEIGHT
From  VN_INSTRUMENT c, VN_RESULT a, VN_RESULT b
Where
c.instr_id = a.instr_id and
a.instr_id = b.instr_id and
a.group_id = 654 and
b.group_id = 655
Order By ABS( CASE WHEN nvl(a.oad,0) = 0 Then 0 ELSE ((a.oad - nvl(b.oad,0)
)/a.oad) END )
/

It is not allowing any CASE/NVL/decode, is there any other alternative ??

      Rating:
      5
  November 08, 2002
Reviewer:
      A reader
Really. It's working fine now.

Thanks, so much!!
Shalu

November 08, 2002 Robert -- Thanks for the question regarding
      "LOADER: Data-conversion in the control-file ", version 8.1.7

      You Asked
Hallo Tom

I have to import data from our host-system that's provided in a flat-file. In
this flat file numeric values are represented like this:

1234567123-

In the host system this value is of type N 7,3
That means 7 pre-comma places and three fractional digits.
Minus at the end of the value means, that the value is negative.

In Oracle I would need type NUMBER(10,3).

Question:
How do I have to make the conversion in the control-file.

VALUE  NULLIF VALUE = BLANKS  "TO_NUMBER(:VALUE,'???')"

I don't know how to set the mask in this case.

Thanks in advance
Robert

      and we said...
use:

...
column_name   "to_number( :column_name, '9999999999s' )/1000",
.....

in the control file.
Review & Followup

      Rating:
      5
Thanks  November 07, 2002
Reviewer:
      Robert  from Luxemburg
Hallo Tom

That's it !!! Thank you !!!

This is a tiny cut-out from such a file:
(The number of rows is not fixed.)

>>>>>ÌEX_KU_DUÌ20021029Ì20021029
20021029ÌINÌ1Ì10Ì20021015Ì00000007669-
20021029ÌINÌ0Ì50Ì20021029Ì00000000000+
20021029ÌINÌ0Ì33Ì20021029Ì00000002556-
20021029ÌINÌ0Ì33Ì20021029Ì00000000306-
<<<<<Ì15

I wanted to skip the first and the last line.
Is this a good way to do it ?

LOAD DATA
APPEND
INTO TABLE TB_LKDUDEP
WHEN (01) != '<' AND (01) != '>'
FIELDS TERMINATED BY 'Ì'
(...

Greetings
Robert

      Rating: 4
Loading numeric
      data which contains negative signs on the end of the
      string.  November 07, 2002
Reviewer:  Mark
      Wade  from Houston, TX
The suggested SQL*Loader solution worked great for negative numbers but ended up
causing a problem with positive entries

Test Negative entry:
select decode(substr('   4654111.000-', 15), '-',to_number('   4654111.000-',
'9999999999.999s'),to_number(Trim('   4654111.000-'), '9999999999.999')) from
dual

Test Positive entry:
select decode(substr('   4654111.000 ', 15), '-',to_number('   4654111.000 ',
'9999999999.999s'),to_number(Trim('   4654111.000 '), '9999999999.999')) from
dual

To get SQL*Loader to handle both positive and negative entries the following
decode had to be added.

inv_qty       position(1:15) "nvl(decode(substr(:inv_qty, 15),
'-',to_number(:inv_qty, '9999999999.999s'),to_number(Trim(:inv_qty),
'9999999999.999')), 0)",

Alternative solution:
inv_qty       position(1:15) "to_number(substr(nvl(:inv_qty, 0), 1, 14)) *
decode(substr(:inv_qty, 15), '-', -1, 1)",

      Rating: 4
DECODE solution
      for "S" format  November 08, 2002
Reviewer:
      Sherry Parviz  from Houston, TX (USA)
Hi Tom,

After looking at Robert's data and reading yours and Mark's response, regarding
"S" format solution.

It works great as long as you have a "+" or a "-", it FAILS with a space. I
experimented in SQL*Plus:

1) Negative number:

select to_number('123456789.123-','999999999.999S') n from dual;

             N
--------------
-123456789.123

- It works.

2) Positive number without a sign:

select to_number('123456789.123 ','999999999.999S') n from dual;

select to_number('123456789.123 ','999999999.999S') n from dual
       *
ERROR at line 1:
ORA-01722: invalid number

- IT DOESN'T WORK

3) Positive number with a sign:

select to_number('123456789.123+','999999999.999S') n from dual

             N
--------------
 123456789.123

- It works

SO....to get SQL*Loader to handle a "+" or a "-" or a space entries the
following decode had to be done:

inv_qty       position(1:15) "nvl(decode(substr(:inv_qty, 15),
'-',to_number(:inv_qty, '9999999999.999s'),'+',to_number(:inv_qty,
'9999999999.999s'),to_number(trim(:inv_qty), '9999999999.999')), 0)",

Alternative solution:
inv_qty       position(1:15) "to_number(substr(nvl(:inv_qty, 0), 1, 14)) *
decode(substr(:inv_qty, 15), '-', -1, 1)",

Regards,

Sherry

          Followup:

decode is the answer then (or case)

      Rating: 4
Thanks
      again  November 11, 2002
Reviewer:  Robert
       from Luxemburg
Thank you Mark, thank you Sherry for this note.
Maybe I'll need it in cases when the delivery of the '+' or '-' is not ensured.
In the file I have to import now, the delivery of a blank instead of '+' or '-'
is not possible. It's ensured by the program that generates these files.

Greetings
   Robert

November 08, 2002 Yuemin -- Thanks for the question regarding "How
      to change a column name using SQL", version 9i

      You Asked
From time to time I need to change a table's column name. What I usually do is:
adding a new column, copy old column's data to the new column, then drop the old
column. The problem this way is that the order of columns on the new table will
be different from the old table.
Is any better way to do it? Thanks.

      and we said...
in 9i:

ops$tkyte@ORA920.US.ORACLE.COM> set linesize 70
ops$tkyte@ORA920.US.ORACLE.COM> desc t
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 INSTR_ID                                     NUMBER(38)
 UPB                                          NUMBER(38)
 CUSIP                                        VARCHAR2(10)
 OAD1                                         NUMBER
 OAD2                                         NUMBER

ops$tkyte@ORA920.US.ORACLE.COM> alter table t rename column upb to hello;

Table altered.

ops$tkyte@ORA920.US.ORACLE.COM> desc t;
 Name                                Null?    Type
 ----------------------------------- -------- ------------------------
 INSTR_ID                                     NUMBER(38)
 HELLO                                        NUMBER(38)
 CUSIP                                        VARCHAR2(10)
 OAD1                                         NUMBER
 OAD2                                         NUMBER

Review &
      Followup

      Rating: 5
What a
      difference a couple of versions make.  November 08, 2002

Reviewer:  John  from ma
http://asktom.oracle.com/pls/ask/f?p=4950:8:1568633::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:188212348071,%7Brename%7D%20and%20%7Bcolumn%7D

      Rating: 4
New in
      9i  November 08, 2002
Reviewer:
      A reader

      Rating: 5
change column
      name  November 11, 2002
Reviewer:  Yuemin
      Yang  from usa
It works.
Thanks a lot, Tom.

November 09, 2002 Nelson -- Thanks for the question regarding
      "Import with no data loaded.", version 9.0.1

      You Asked
Hi! Tom,

   I have a question to ask.
   About import and export.
   when i export a database to a dump file which only contain
   all the table structure and packages.

   But somehow when i import to a different place, it will expand destination
tablespace to the actual size of the source database.

   For example, when i export "A" database (tablespace size 8GB) to a dump file
(6MB). when i used the dump file to do importing, the 'B' tablespace will expand
to maximum size (1GB).

  How can be happen like this? any solution for this problem?

Thanks a lot.

regards,
Chai.

      and we said...
When you exported, you left the default "compress=y" which set the intial extent
on the create tables equal to the sum of all allocated extents in the existing
database.

Use compress=n on the export.
November 09, 2002 Ashiq -- Thanks for the question regarding
      "Regarding Index", version 8.1.7

      You Asked
Hi Tom,

   1)In many of ur answer what u've give in this site that index is not mainly
used for fast accessing of data ,then if this is the case then what is main use
of it.And u also said by swapping the tablespace ,rebuilding the index by doing
this it won't solve the performance issue.Can u explaing why i won't help us in
doing this.

 2) I've read in many books that if u select only 5% of data of table ,then
index will be used properly.if this is the case what is the use of it,becuase if
i query a table and it'll retrieve morethan 50% of data of a table.In this case
index won't be used.

3)And if the performance of the application is very slow ,used said to check out
with design how it is built.But ,my problem is it possible to change the design
,if the application in production.

thanx

      and we said...
Your keyboard is seriously broken.  It drops some of the most critical letters
in our language -- the vowels!!!  Y is missing frequently as it O.

1) I don't think I've ever said that.  I've said "indexes do not imply faster,
if you have a query that is not using an index that is OK".  I've never said
that an index is not mainly used for fast accessing of data -- that is EXACTLY
what they are used for.

Take the scott.emp table with a unqiue index on empno.  Put a million rows in
there.

select * from emp where empno = 55;

that'll benefit from an index.

select * from emp;

that will not.  One query should use an index, the other should not.

If you have my book "Expert one on one Oracle" -- I go into this topic in great
detail. (when and where to use indexes, what type of index to use, etc)

2) Exactly -- if you retrieve somewhere between 5 and 25% or less of the table
(depends on the table -- the percentages will change) and index will make it
"faster" to get to the last row of your result set.  If you go above that --
then an index will make it take LONGER to get to the last row of the result set.

That is just "physics" at work.  If you are getting lots of the table -- the
fastest method will be scanning the table and ignoring the index.

Look at it this way  -- supposed you had to find the first word of every "odd"
chapter in a 1,000,000 chapter book.  Do you want to goto table of contents,
find page number of first odd chapter, goto that page, goto table of contents,
find page number of second odd chapter, goto that page, etc etc etc

Or -- do you just want to flip through the book -- stopping everytime you hit a
chapter and seeing if it is odd or even?

3) Ahh -- great question.  And how pray tell did it get into production if it
didn't pass your performance tests that proved the application met the expected
performance requirements before going production?

Ahh -- I see, you didn't:

a) have any metrics when developing (you didn't know how fast it had to go)

b) scale the system up in test to see how it would perform against all of the
data

c) scale the system up in test to see how it would perform with N users -- where
N is the number you have to support.

My question to you would be -- how did this thing get into production in the
first place?  Sounds like you missed one of the crucial steps along the way --
benchmarking and testing to scale.  You don't roll out an application without
doing that first.

If you do it first, you won't have an app in production that performs poorly.

And if you do -- you need to FIX it, which 9999 times out of 1000 implies some
architectural change to the application (there is only so much magic
incantations over the databases init.ora file can do after all!)

You know, this question reminds me of a catastrophe I was made to work on once
about 2 years ago.  The system was "fielded" but it way dead in the water.
Under load it would just totally "shut down".  It would crash frequently.
Performance -- well, lets just say it was not "stellar".  We had patches that
needed to be applied (it was a 3rd party application here, we had performance
patches).  Well, since the system was "in production" this companies "rules for
protecting production systems" kicked in.  Meaning we had a 2 hour patch window,
in the middle of the night, IF and ONLY IF a committee "felt good about the
patch".

Needless to say -- not only did we miss multiple patch windows (committee didn't
feel "good" about a patch, needed to be convinced to "feel good") night after
night -- but the system failed every day like clockwork (did you ever see the
movie groundhog day with Bill Murray?  I lived it -- every morning was sonny and
cher singing "i got you babe"...)

For whatever reason -- this "production system" that did not work, people were
sitting on their butts looking at dead screens had to be protected.  If it
wasn't such a waste of my time (and sleep cycles) it would have been amusing.
In retrospect -- it is, at the time, not.

The system isn't a production system until it actually runs is my point here.
If you have serious performance issues -- it must be a brand new system that you
just fielded (or some new addition to an existing system -- in that case BACK
OUT the changes and go back to test).  If it is a brand new system, call it what
it is -- not production but rather "initial system acceptance testing".  In this
case, SAT failed -- roll back, you are in development again.

Review &
      Followup

      Rating: 5
Regarding
      Index  November 10, 2002
Reviewer:  Ashiq
      Shamsudeen  from Trichy,TamilNadu,India

November 09, 2002 Antoine -- Thanks for the question regarding
      "snmp", version 8.1.7

      You Asked
Hi Tom,

what is your point of view on the use of snmp with the database compare to
statspack or monitoring tool?

Which metric is good to collect with snmp?

Which metric is not good to collect with snmp because it's useless?

Thanks Tom!

      and we said...
SNMP is a framework -- it allows you to integrate the Oracle data in with OTHER
system data in order to give you a view of your entire system in a single tool.

You would use SNMP if you are using some framework, systems management software
that uses it.  Else, you probably would not.

I won't compare them -- since they do differnet things.  the SNMP traps are to
capture "up" "down", "good" "bad" -- not necessary fine grained tuning like
statspack and OEM tuning packs.  In fact the high level tools that use SNMP to
integrate the information from multiple pieces of your system often launch the
specifc tools in order to drill down.
Review &
      Followup

      Rating:
      3
snmp  November 08, 2002
Reviewer:
      antoine Bocti  from Montreal, Canada
Hi Tom, could you be more specific on which metric would be good to be collect
with snmp?

Thanks Tom.

          Followup:

Nope -- you need to tell ME what you would find relevant -- maybe just "up" or
"down" is good enough for you.  Maybe you want the number of active sessions.
Maybe you want the number of active transactions.  Maybe you want the amount of
memory being used.

You tell me what YOU want to have reported.

      Rating:
      5
snmp  November 09, 2002
Reviewer:
      Antoine Bocti  from Montreal, Canada
Hi Tom,

the idea of implenting snmp is from the sysadmin team at my job,
and I just want to know where to draw the line between snmp and tools like
statspack and runstats.
I know they want to put all the mib but I am not convinced about this!
With snmp like you told me, I would like to see "up or down", number of active
sessions, hot disk,amount of
memory being used, check the listener and check archive dest grownth.
I want to use snmp to detect potential problems quickly but I want to use
statspack and runstats to diagnose these problems in details.

what's your point of view?

thanks again!

          Followup:

There is no line to be drawn!

This is apples vs toaster ovens here -- they are totally different.

SNMP is a way to integrate into a single tool factoids from dozens or hundreds
of applications.  It is to give you the bird eye view of your system.

Statspack -- great for analying a performance issue once their birds eye view
has diagnosed it.

Runstats -- great for development, to test theories.

Neither statspack or my runstats help you monitor or manage lots of systems --
that is where SNMP comes into play.

      Rating: 5
thanks again
      !  November 09, 2002
Reviewer:  antoine
      bocti  from Montreal, canada
gotcha!

November 09, 2002 -- Thanks for the question regarding "D/W
      Database size estimation and configuration", version 8.1.7

      You Asked
Hi,
I want to configure the database for the DW environment.
Oracle 8.1.7 on Sun Solaris and Informatica is the platform.
How should I configure the database !!!!!

On what basis should one quote for the RAM required on the Server.
What are the InitOra parameters we need to consider for the configurationb of
the database and what are the storage parameters we need to consider for
tablespace and tables in these case?
Any basic thub rule for Database Capacity planning!!!

How should one estimate the size for tables and their indexes!!!!

Could you please explain with some examples !!!!!

Thanks and Regards,
Jitendra

      and we said...
well, don't take this personally but.... if you are getting ready to size for a
real data warehouse -- and you are asking fairly basic questions -- it might be
time for some outside but on-site help.  Just a thought.

As you are just building this -- I would ask you first -- why would you even
consider using 8iR3 when there have been 2 production releases of newer software
-- 9iR1 and 9iR2?  There are literally dozens, if not more then hundreds of new
features just for data warehousing.

Ram will be a function of the number of users, what they are doing, how often
they do it and how much money you have to spend.

If you use 9i, there are far fewer init.ora parameters to set.  In 8i, there are
too many to mention here.

You will use locally managed tablespaces -- so the ONLY storage type related
parameters will be PCTUSED and PCTFREE.  In a true data warehouse, they will be
90 and 0 (use the blocks until they are jammed full).

To size -- see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:406873025890

November 11, 2002 Dwayne -- Thanks for the question regarding
      "Object Views", version 8.1.7

      You Asked
Hi Tom,

I am developing an application on top of an Oracle database, and I want to put
my procedural logic in the database.  I want to use Oracles' object-oriented
constructs to encapsulate the data and the methods that act upon it.  By
searching your database, I understand that you do object-oriented programming in
the database as well, but I need more specific information.

I have learned that object views were created to this end.  What can and cannot
be done with object view methods?  If I wanted to do extensive procedural logic
on a table, are object view methods meant to accomplish this, or are there other
means?  What are the most important object-oriented features of Oracle that you
use, and could you provide some examples of how you use them?

Could you point me in the direction of other resources to help me accomplish
this goal?

Thanks so much for sharing your insight with us.

Dwayne Croteau

      and we said...
Well, in my book "Expert one on one Oracle" -- I spent a chapter on this topic
if you are interested in that.

There is also

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76976/toc.htm
which is a book on the topic.

you can do extensive procedural logic in object methods, yes.  PLSQL is a very
powerful programming language and you can use java or C as well if you need to.

I would question WHY in 2002 you would even consider using software that is
already 2 releases behind the curve (9iR2 is the only product I would consider
for new development).  There are literally hundreds of new features in 9iR2 over
8i (takes my over 12 hours just to list them all).  The new features in the
object RELATIONAL (not object oriented) stuff is simply amazing.  Inheritence,
polymorphism, TYPE EVOLUTION <<--- you *need* that -- all only available with 9i
and up.

Give that some serious thought before you dig too deep.

November 11, 2002 John -- Thanks for the question regarding
      "staging tables to main tables ", version 9.2

      You Asked
i want to load employee information to our data from staging tables

like i have a table

employee
employee_stag -- data loaded here thru sql loader fill and flush.

now i want to write a proc that updates the changes only to the employee table
from employee_stag

q1) is there any way to update only changes differences between these tables

i am planning to write something simular to this:

update employee a
set all the employee columns (select all columns from employee stag b where
a.emplid = b.emplid);

insert into employee select * from employee_stag stg where not exists
(select 1 from employee em where em.emplid = stg.emplid)

any suggestions or corrections?

we will have around 50,000 active employees

q2) how do i lock employee table as application developers may be accessing the
employee table(to select or update or delete)?

now i am loading employee_stag thru sql loader
i heard new concept of external tables.

q3)
can we schedule loading the data file to the external tables thru oracle once in
a day?

q4)the file name we mention for the data file to be loaded in access parameters
of external table,can be in any unix directory or is there any init.ora
parameter to be mentioned?

would really appreciate ur reply.

thanks

      and we said...
q1) I would

a) skip sqlldr
b) skip the staging table
c) merge

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1615330671789
you can MERGE the contents of that flat file with the EMP table in one
statement.

q2) lock table employee in exclusive mode;
    merge it with the external table

q3) dbms_job will do that -- it is used to run a procedure on a recurring basis.

q4) the filename needs to be a file in an Oracle "directory" object (see the
example above).  they can be created whilst the database is up and running (must
be up and running) and do not need init.ora parameter changes at all.
Review &
      Followup

      Rating: 5
looks wonderful
      to me -- an error with merge  October 20, 2002

Reviewer:  A reader
tom,

thanks so much for such a wonderful reply.
i need to have permission for "create or replace" directory
so, for the time being i tested the merge with a staging table it gives me an
error does it work with only external tables

SSQL*Plus: Release 8.1.6.0.0 - Production on Sun Oct 20 16:17:30 2002

(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Connected to:
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production

SQL> create or replace PROCEDURE em_personal_update AS
  2  BEGIN
  3     MERGE INTO emp em
  4     USING emp stg
  5     ON (stg.emplid = em.emplid)
  6     WHEN matched THEN
  7        UPDATE SET em.address1   = stg.address1,
  8                   em.address2   = stg.address2,
  9                   em.address3   = stg.address3
 10     WHEN NOT matched THEN
 11        INSERT (em.emplid,em.address1,em.address2,em.address3)
 12        VALUES (em.emplid,em.address1,em.address2,em.address3);
 13  END em_personal_update;
 14  /

Warning: Procedure created with compilation errors.

SQL> show err
Errors for PROCEDURE EM_PERSONAL_UPDATE:

LINE/COL ERROR
-------- -----------------------------------------------------------------
3/4      PL/SQL: ORA-00904: "EM"."EMPLID": invalid identifier
3/4      PL/SQL: SQL Statement ignored

          Followup:

it works with any table, view, subquery...

but I suspect it is your correlation names int he INSERT clause:

 10     WHEN NOT matched THEN
 11        INSERT ( emplid, address1, address2, address3)
 12        VALUES (em.emplid,em.address1,em.address2,em.address3);

they do not belong there.

      Rating: 5
thanks again..
      that is my mistake..  October 20, 2002
Reviewer:
      John
i have a 9i feature added to my code.
thanks to tom.

      Rating: 5
merge --
      delete  October 20, 2002
Reviewer:
      A reader
tom,

i want to delete the rows that are not present in external table and but are in
main table. how do i do this?

          Followup:

like this:

scott@ORA920.US.ORACLE.COM> create or replace directory data_dir as '/tmp/'
  2  /

Directory created.

scott@ORA920.US.ORACLE.COM>
scott@ORA920.US.ORACLE.COM> create table external_table
  2  (EMPNO NUMBER(4) ,
  3   ENAME VARCHAR2(10),
  4   JOB VARCHAR2(9),
  5   MGR NUMBER(4),
  6   HIREDATE DATE,
  7   SAL NUMBER(7, 2),
  8   COMM NUMBER(7, 2),
  9   DEPTNO NUMBER(2)
 10  )
 11  ORGANIZATION EXTERNAL
 12  ( type oracle_loader
 13    default directory data_dir
 14    access parameters
 15    ( fields terminated by ',' )
 16    location ('emp.dat')
 17  )
 18  /

Table created.

scott@ORA920.US.ORACLE.COM>
scott@ORA920.US.ORACLE.COM> host flat scott/tiger emp > /tmp/emp.dat

scott@ORA920.US.ORACLE.COM>
scott@ORA920.US.ORACLE.COM> set autotrace on

scott@ORA920.US.ORACLE.COM> delete from emp
  2   where empno not in ( select /*+ HASH_AJ */ empno from external_table where
empno is not null )
  3  /

0 rows deleted.

Execution Plan
----------------------------------------------------------
   0      DELETE STATEMENT Optimizer=CHOOSE (Cost=14 Card=1 Bytes=26)
   1    0   DELETE OF 'EMP'
   2    1     HASH JOIN (ANTI) (Cost=14 Card=1 Bytes=26)
   3    2       TABLE ACCESS (FULL) OF 'EMP' (Cost=2 Card=82 Bytes=1066)
   4    2       EXTERNAL TABLE ACCESS (FULL) OF 'EXTERNAL_TABLE' (Cost=11
Card=408 Bytes=5304)

You want the hash anti join to avoid full scanning the external table once for
each row in emp and since there won't be stats on it, we can help the SQL along
with the hint in this case....

see also

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:5081797376327
for "what is this hash anti join thing"

      Rating: 4
conditional
      merge  November 11, 2002
Reviewer:  John

can we do a conditional merge?

i want to leave the records that are changed

   MERGE INTO  ps_emergency_contact ec
   USING  ps_emergency_contact_stag stg
   ON (stg.emplid = ec.emplid AND
       stg.contact_name = ec.contact_name AND
       ec.ps_change_code != 'A')
   WHEN matched THEN
       -- update
   WHEN NOT matched THEN
       -- insert

the condition ec.ps_change_code != 'A' raises runtime error
saying invalid identifier.

i want to leave the records whose change_code = 'A'

          Followup:

No -- and it would be really bad if there was.  Consider, if you had a primary
key AND the pc_change_code != 'A' was "met" (eg there is an A record in both the
current table AND the table to merge with) -- you would end up with a
DUPLICATION key condition (they would NOT be matched, so it would be inserted --
but the primary key already exists of course so it would fail)

You can see this in your case by creating a view V as select * from
ps_emergency_contact where ps_change_code != 'A' and merging into the view --
you'll get the dup val on index.

Maybe you want to

when matched then
  update set ec.c1 = decode(ec.ps_change_code,'A',ec.c1, stg.c1 ),
             ........

      Rating: 5
thank
      you  November 11, 2002
Reviewer:  John

tom,

thanks for pointing out that duplicate condtion..
decode looks good.


Subject | Article | Last Updated (East coast time)
table | article only | Nov 12, 2002 10:15 am
XML Standard Schemas | article only | Nov 13, 2002 12:28 pm
optimizer hints | article only | Nov 13, 2002 12:36 pm
How to pass an array to a stored procedure | article only | Nov 13, 2002 12:51 pm
DBMS_JOB with interval | article only | Nov 13, 2002 12:58 pm
How to get max and sum from two different tables in the same select | article only | Nov 13, 2002 01:22 pm
Data Access from MS SQL SERVER 7.0 | article only | Nov 13, 2002 01:25 pm
Composite index | article only | Nov 13, 2002 02:04 pm
dormant users | article only | Nov 13, 2002 03:34 pm
The results of the database contest - smart dba challenge? | article only | Nov 13, 2002 05:54 pm
Cost /Rule based Optimizer behaviour | article only | Nov 13, 2002 05:59 pm
Optimizer mode = Rule and still have cost associated with it. | article only | Nov 13, 2002 06:02 pm
Basic XSQL | article only | Nov 13, 2002 06:03 pm
SQLLDR with no rollbacks | article only | Nov 13, 2002 06:07 pm
Checking Sysdba login | article only | Nov 13, 2002 07:31 pm
How to get rid of temp tables! | article only | Nov 14, 2002 07:16 pm
Strings of Zero Length Not Equivalent To NULL | article only | Nov 14, 2002 07:29 pm
Performnance | article only | Nov 14, 2002 07:45 pm
Query Idea | article only | Nov 14, 2002 07:47 pm
Update partial data in a varchar2(1000) field | article only | Nov 15, 2002 07:08 pm
Conversion/Extraction of Longs | article only | Nov 15, 2002 07:23 pm
Login.sql and partitions nad & ,&& | article only | Nov 15, 2002 08:15 pm
Collate | article only | Nov 16, 2002 01:05 pm
QUERY_STRING in the plsql cartridge | article only | Nov 16, 2002 01:13 pm
Statement audting vs. system privilege auditing, etc. | article only | Nov 16, 2002 01:18 pm
Dynamic Sql and Procedure | article only | Nov 16, 2002 04:47 pm
Displaying messages in procedure | article only | Nov 17, 2002 10:41 am
Using Parallel Query. | article only | Nov 17, 2002 10:47 am
Porting stand-alone application to 9i RAC with two nodes | article only | Nov 17, 2002 11:07 am
Oracle to other database communication | article only | Nov 17, 2002 12:41 pm
Materialized View Fast refresh containing UNION | article only | Nov 17, 2002 08:14 pm
Tuning Areas | article only | Nov 18, 2002 08:08 am
Clob Display and Insert | article only | Nov 18, 2002 08:29 am
SQL script to get all users on different oracle instances. | article only | Nov 18, 2002 10:24 am
row(s) 1 - 34 of 34
November 12, 2002 sivaram -- Thanks for the question regarding "table", version 7.2.2
You Asked
Dear Tom

I wanted to create a table with following specifications

columnname  datatype  size  constraint            default
---------------------------------------------------------
Id          varchar2   1     not null                A
                             check ('A','B','O')
Name        varchar2   10

i have tried the same as below:

Create Table Temp (
A    Varchar2 (1) Constraint nn_a Not Null,
B    Varchar2 (10));

Alter table temp add constraint chk_a check (a in ('A','B'));
but no default value.

can you help me out please?

sivaram
and we said...
You can either do this in one create table as such:

scott@ORA716.WORLD> drop table temp;

Table dropped.

scott@ORA716.WORLD>
scott@ORA716.WORLD> Create Table Temp (
  2  A    Varchar2 (1) default 'A'
  3                    Not Null
  4                    constraint chk_a check (A in ('A','B')),
  5  B    Varchar2 (10));

Table created.

Or you can alter each in like this:

scott@ORA716.WORLD> drop table temp;

Table dropped.

scott@ORA716.WORLD>
scott@ORA716.WORLD>
scott@ORA716.WORLD> Create Table Temp (
  2  A    Varchar2 (1),
  3  B    Varchar2 (10)
  4  );

Table created.

scott@ORA716.WORLD>
scott@ORA716.WORLD> alter table temp modify
  2  (a default 'A');

Table altered.

scott@ORA716.WORLD> alter table temp modify
  2  (a NOT NULL);

Table altered.

scott@ORA716.WORLD> alter table temp add constraint
  2  chk_a check ( A in ( 'A','B' ) );

Table altered.

scott@ORA716.WORLD>
Review & Followup
Rating: 3
Useful Stuff! November 12, 2002
Reviewer: Abhijit
Dear Tom,
It was useful to know how to create check constraints in Oracle on already
existing table. Could you please tell me how do I create a disabled check
constraint?

Cheers!
Abhijit

Followup:
ops$tkyte@ORA920.LOCALHOST> drop table t;

Table dropped.

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> create table t ( x int );

Table created.

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> insert into t values ( 0 );

1 row created.

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> alter table t add constraint t_check check(x>0)
disable;

Table altered.

Rating: 4
really useful! November 14, 2002
Reviewer: Abhijit from Mumbai
Thanks Tom. YOU ARE SIMPLY THE BEST WHEN IT COMES TO SOLVING ORACLE RELATED
DOUBTS.

-Abhijit

November 13, 2002 Bala -- Thanks for the question regarding "XML Standard Schemas ", version 9i
You Asked
Hi:

My question is Two parts, First one is about XML standards and the second one is
about loading XML data into Database

Where can we find standard XML Schemas for Orders, Purchase Orders to expand to
a specific industry? What is a good start to write our own schemas for specific
application use?

Other question is about validating the XML file against the schema and loading
it into relational tables. Let us say I have customer, order header, order lines
info in the XML file. I need to load Customer Information (One customer per
file) into Customer Tables, Order Header into Order Header (Multiple orders per
customer) table and multiple lines into Lines table (Multiple lines per order).

Our app is written based on Oracle DB, Oracle Forms and PL/SQL. What is a good
start and how to proceed further

Thanks
Bala
and we said...
I asked Sean Dillon, our local XML Technologist, to take a look at this and
here's what he had to say:
-----------------------------------

Hi Bala,

First of all, let me say thanks to you and your team for looking around for
pre-existing schemas rather than just setting out and building your own.  There
are a few places online you can check out for industry-specific XML Schemas:

-->
http://www.xml.org/xml/registry.jsp
- XML.ORG maintains a rather large alphabetized list of XML Schemas for
particular industries.
-->
http://xml.coverpages.org/
- Robin Cover's "XML Cover Pages" where you will see vocabularies and
markup languages appear before they ever make it into the standards bodies.
-->
http://www.ebxml.org/
- This is the last place I'll offer, not because of it's flurry of
available industry-specific XML Schemas but more for the premise of it's
existence.  ebXML is basically a complex Web Services standard that is based on
lines of business and industries getting together to decide how they will
conduct electronic business and what XML vocabularies they will use to do it.
You may find some useful information there.

If you are using Oracle9i Release 2 (often referred to as 9iR2), you can take
advantage of a variety of features that are bundled in a new component of the
database called XML DB.  Oracle XML DB provides XML Schema validation support
(via PL/SQL or Java) and also has what is known as the XML Repository.  The XML
Repository lets you load your XML data into the database via PLSQL, Java, FTP or
WebDAV.

Short of having 9iR2, you can also use what is known as the XML Developer's Kits
to accomplish what you are trying to do.  The Oracle "XDKs" are a suite of APIs
and utilities offering a large array of XML functionality in all the most
popular programming languages.  It definitely supports PLSQL, which you are
using in your app... but in addition to that the XDKs support Java, C, C++ and
there are also some JavaBeans for automating processing tasks.

So how would I do this?  I would most likely load the XML data into an XMLType
column or XMLType table (9iR2 only) and validate the XML against the XML Schema
directly from a table on-insert trigger.  Once the XML is in the table, I would
use Oracle's XML SQL Utility (XSU), which is a part of the XDK, to load the
CUSTOMER, ORDER and ORDER_LINES tables one at a time based on each of the
transformed XML documents.  You may choose to keep the XML file for auditing or
logging purposes, but after it is completely processed you could even remove it
from the system.

So how can you learn how to do all of this?  You need to read up on Oracle's XML
Developer's Kits in the Oracle documentation.  You can download (for free) and
read more about the XDKs here:
http://otn.oracle.com/tech/xml/content.html
  On OTN, you'll also find links to
Oracle's XML-related documentation, sample code, demonstrations of the
XML-related functionality Oracle offers, etc.

Good luck!

_____________________________________________________________________
Sean Dillon - Principal Technologist, Oracle Corporation
Author "Beginning Oracle Programming"
http://www.amazon.com/exec/obidos/ASIN/186100690X
Review & Followup
Rating: 4
xml db SCHEMA October 08, 2002
Reviewer: kumar from san diego
 Can we implement any trigger actions on this XMLDB schema? What I understand is
we can load objects into this schema through pl/sql, FTP, WEBDAV etc. But my
question is if I FTP some objects into this schema, can I have some trigger
event can pass on these when the Object come in. IF this is like RDBMS kind one
I should have, what is your opinion?

Thanks

Followup:
XMLType is just a datatype in a regular table if you like, all of the familar
tools you have are available to be used, yes.

Rating: 5
Very Helpful Information October 14, 2002
Reviewer: Bala from USA
Thanks for saving tons of time.

Rating: 4
XMLDB schema in Oracle 9i R2 October 30, 2002
Reviewer: Kumar from San Diego
Tom,

I prefer to bring this topic in this thread. Do you have any idea How to export
this new XDB schema. Oracle 9i Release 2 has this new XDB schema and we started
developing a lot in this. Unfortunately, when I try to export in several ways I
couldn't get any success of this schema. Did you hear anything about it? or any
key for this issue?

Followup:
wanna give us a clue as to what "couldn't get any success of this schema" means.
 error messages, codes, things like that.

Rating: 3
XML DB schema could not be exported November 12, 2002
Reviewer: Kumar from San Diegp
After all math I found this schema could not be exported like "sys" schema in
database. This was the main concern and we did develope a lot in this schema
directly. Well there is no way that I need to migrate the stuff from this schema
to another schema if I want to depend on export and import mechanisms.

Followup:
Well -- allow me to inject a meaningful comment here:

do not develop in Oracles schemas.  they are special, consider them magic, be
afraid to log in using them.  There are very special documented times you will
create objects in these schemas (eg: ctxsys -- to index a function -- you will
create the function in the ctxsys schema) but in general you never never never
use them (for just the above reasons!)

Rating: 5
Excellent Answer March 31, 2003
Reviewer: A reader
Can't expect any better/more information. Thank you

November 13, 2002 -- Thanks for the question regarding "optimizer hints", version 8.1.7
You Asked
From your earlier answers..
"set the optimizer_index_caching, optimizer_index_cost_adj init.ora
parameters.  I would avoid hints if at all possible. "

Why should we avoid optimizer hints.
and we said...
Hints are there as a last ditch, lets get this going.  By using a hint, you are
artificially constraining the system to some given plan.  You are not allowing
the system to change and adapt the query plan over time.  If the next release of
Oracle comes out with a new access method -- you will be avoiding using it
because you have these hints.

It is my philosophy that the optimizer should be left to do its job.  If and
only if for some reason it is coming up with a "bad plan" would I hint it.  I
would check with each relase of Oracle if I can remove the hints. They are
constraints on how the query should be executed -- that in general is not
desirable.  A cost based optimizer is supposed to change query plans over time
as it reacts to changing data.

I've seen many cases where people over hint, they go "index happy" in
development.  Works great in development with small data sets but really stinks
in production with the "real data".  The query plans didn't adapt to the change
in volume of the data and the hints that worked oh so well in test/development
don't make sense in production.
Review & Followup
Rating: 4
RBO... November 12, 2002
Reviewer: Kashif from Houston, TX
Hi Tom,

Your argument applies completely when using CBO, but I'm curious about the RBO
and how that argument applies. Frequently I come across applications/queries
which were developed using the RBO, and have since remained unchanged. Now, one
could argue that those queries should be converted to use CBO, but, for some
reason, companies/DBAs/developers are hesitant to convert to CBO completely
(maybe it's the FUD theory in action). In which case, overriding
optimizer-generated plans using hints is frequently the only option to generate
an optimal execution plan. A classic example is the use of the Ordered hint,
which allows devlopers to pick a join order which they know to be efficient, as
opposed to letting the RBO do it's thing and potentially pick a sub-optimal
plan, using unselective indexes and so forth. Any inconsistencies in my
argument? Thanks.

Kashif

Followup:
the RBO uses "ordered" by rule -- it is the way the RBO works (so, bad
example ;)

I would not use hints here, no.  I would explain that if you want performance --
we'll have to fix the problem which involves using the CBO.

I would avoid hints at all costs - they are my LAST choice (meaning I don't
outlaw them -- but I would do everything to avoid having to use them)

November 13, 2002 Prem -- Thanks for the question regarding "How to pass an array to a stored procedure", version
You Asked
I want to know if I can have one of the parameter in
stored procedure as an array.
Ex : I have a procedure
procedure employee_report
(emp_no      number
,emp_dept   varchar2
,emp_salary number
,emp_title  varchar2)

Instead can I define an object/array emp_property of structure
(emp_no      number
,emp_dept   varchar2
,emp_salary number
,emp_title  varchar2)
and use it a parameter in stored proc like
procedure employee_report(emp_prop emp_property ).
If this is possible than can I pass multiple records in this
procedure.
and we said...
Sure, you just need to declare new types.  You can have types
that are tables of a RECORD or types that are tables of SCALARS.
 I'll show both below.  See the plsql guide for how to declare
record types -- you can do them based on an existing table
structure, a cursor structure or by explicity defining your own
record type.

Note that if you are clling emp_report from a 3gl such as Pro*C,
OCI, VB, etc (eg: anything OTHER then plsql) you will need to
use tables of scalars -- NOT a table of records as tables of
records cannot be bound to by a 3gl.  Also, I suggest only use
charArray's if you are going to be calling this from a 3gl -- if
you made a table of NUMBER or a table of DATE -- we would expect
you to send the INTERNAL format of a number (22 byte field in an
obscure format) or a date (7 byte, equally obscure format).
Best to send strings and let the conversions take place.

ops$tkyte@8i> create or replace package demo_pkg
  2  as
  3      type empArray is table of emp%rowtype index by
binary_integer;
  4
  5      type charArray is table of varchar2(255) index by
binary_integer;
  6
  7
  8      procedure emp_report( p_inputs in empArray );
  9
 10      procedure emp_report( p_empno  in charArray,
 11                            p_deptno in charArray,
 12                            p_sal    in charArray,
 13                            p_job    in charArray );
 14
 15  end;
 16  /

Package created.

ops$tkyte@8i>
ops$tkyte@8i>
ops$tkyte@8i>
ops$tkyte@8i> create or replace package body demo_pkg
  2  as
  3      procedure emp_report( p_inputs in empArray )
  4      is
  5      begin
  6          for i in 1 .. p_inputs.count loop
  7              dbms_output.put_line( 'Empno = ' ||
p_inputs(i).empno ||
  8                                    ' Deptno = ' ||
p_inputs(i).deptno );
  9          end loop;
 10      end;
 11
 12
 13      procedure emp_report( p_empno  in charArray,
 14                            p_deptno in charArray,
 15                            p_sal    in charArray,
 16                            p_job    in charArray )
 17      is
 18      begin
 19          for i in 1 .. p_empno.count loop
 20              dbms_output.put_line( 'Empno = ' || p_empno(i)
||
 21                                    ' Deptno = ' ||
p_deptno(i) );
 22          end loop;
 23      end;
 24
 25
 26
 27  end;
 28  /

Package body created.

ops$tkyte@8i>
ops$tkyte@8i> set serveroutput on
ops$tkyte@8i> declare
  2      my_data   demo_pkg.empArray;
  3  begin
  4      my_data(1).empno := 1234;
  5      my_data(1).deptno := 10;
  6
  7      my_data(2).empno := 4567;
  8      my_data(2).deptno := 20;
  9
 10      demo_pkg.emp_report( my_data );
 11  end;
 12  /
Empno = 1234 Deptno = 10
Empno = 4567 Deptno = 20

PL/SQL procedure successfully completed.

ops$tkyte@8i>
ops$tkyte@8i> declare
  2      my_empnos   demo_pkg.charArray;
  3      my_deptno   demo_pkg.charArray;
  4      empty       demo_pkg.charArray;
  5  begin
  6      my_empnos(1) := 1234;
  7      my_deptno(1) := 10;
  8
  9      my_empnos(2) := 4567;
 10      my_deptno(2) := 20;
 11
 12      demo_pkg.emp_report( my_empnos, my_deptno, empty, empty
);
 13  end;
 14  /
Empno = 1234 Deptno = 10
Empno = 4567 Deptno = 20

PL/SQL procedure successfully completed.
Review & Followup
Rating: 5
March 23, 2001
Reviewer: Helena Markova from Bratislava, Slovakia

Rating: 5
October 17, 2001
Reviewer: A reader
3      type empArray is table of emp%rowtype index by
binary_integer;

Can pl/sql tables contain more than one column.

can I select all the rows in emp into emp array, and then insert emparray into
another table

Followup:
The example was of a PLSQL table type that is of more then one column.  empArray
is a table of records -- each record is the shape of a row in the emp table.

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1583402705463
for howto bulk collect into a SQL Table type (not a plsql type) and insert using
it.

Rating: 4
Pass array to stored proc from VB? February 06, 2002
Reviewer: Sasa Petkovic from Belgrade , Yugoslavia
Your examaple is very usefull and it works excellent in PL/SQL, but the problem
occurs when trying to call the sam procedure from VB. Parameter of a command
object is defined as adArray + adVarChar, and assigning a VB array of strings to
it goes fine, but .Execute crashes with "Type mismatch" error. Same thing
happens when using either ODBC or Oracle OLEDB drivers. Is this feature
currently supported, and if it is - then what is the lowest version of drivers
that support it?

Followup:
I don't know much about VB personally.  I know we can do this with OO4O (Oracle
Objects for OLE).  Sorry I cannot provide an example as I've never touched VB
personally.

Rating: 5
Great responses March 07, 2002
Reviewer: Teodora Jevtic from Livonia, MI USA
Tom,

Do you know if there are such limitations in ODBC that can prevent from calling
this kind of stored procedures - i.e in Crystal Reports. Some of the reports are
to complex to be written in one SQL statement.
I am thinking if the call to this kind of stored procedure is not possible(which
seems to be the case) to make a call to stored procedure (prior running the
report - from the GUI application) which will store the resultset into a Oracle
temporary table - and the report will just pull the data from it (or use
permanent table with a unqiue identifier for each calling session).
Do you have any other ideas? Thanks in advance.

Followup:
I've no idea what limits crystal reports might impose, sorry -- i've never used
that tool.

You can use global temporary tables for this purpose handily.  Very very little
redo log generated during insertion and automatic cleanup on commit or session
termination.

Rating: 5
RE: Great responses March 07, 2002
Reviewer: Kim from Boise, ID USA
As far as crystal reports go...
I suggest you look at the following (you have to set up a couple things in
Crystal Reports as well):
http://www.seagatesoftware.com/docs/
and search for
SCR_Oracle_Stored_Procedures.pdf
down where it says or search for the filename below.

Rating: 5
but how to delete the element June 28, 2002
Reviewer: Raj from Peoria, IL
Tom
Nice example.  Could you please tell me how to remove an element from the array.
 Once removed is the row empty or deleted completely?

Thanks for your help

Followup:
see
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a77069/04_colls.htm#14165
you'll use the .DELETE method.

The row is deleted from the collection.  It'll still "be there", it just won't
"exist" anymore.  Read the above link for all of the details

Rating: 5
June 28, 2002
Reviewer: Alex from Chicago, IL
I would like to comment on calling procedures from VB apps. If VB works the same
way as ASP, then you cannot use PL/SQL tables of rows - ASP (and possibly VB)
doesn't recognize it, so you must declare each field that will be used as a
separate PL/SQL table and pass it as a parameter.

Rating: 5
June 28, 2002
Reviewer: Robert from Memphis, TN USA

Rating: 5
Thanks June 28, 2002
Reviewer: Raj from Peoria, IL
Thanks Tom for your reply.

Rating: 4
How to pass arrya of number from oci to PL/sql November 12, 2002
Reviewer: sam
Dear Tom

This article is very useful to PL/SQL, but when i write a oci(C++) project, it
said "Type mismatch".

Can you give me a example in C++ of oci?

Followup:
Nope -- cuase only PLSQL can see PLSQL types-

You need to use OBJECT TYPES

create or replace type myObject as object .....
create or repalce type myArray as table of ....

See the OCI guides for that -- they have examples and PLSQL can use the object
types as freely as its own types.

Rating: 5
About OCI Object for PL/SQL Parameter November 12, 2002
Reviewer: sam
Tom

I have searched OCI Progammer Guide, But i cann't find and OCI number Array for
PL/SQL,

The following link is OCI Programmer Guide, page 154 is the example of number
array, is it for sql, not for pl/sql?
http://docs.oracle.com/cd_a87860/doc/appdev.817/a76975.pdf
How to pass number array to pl/sql?

thanks again

Followup:
it is an array bind
see
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76975/oci05bnd.htm#420907

Rating: 5
continued for above November 12, 2002
Reviewer: sam
The following is my package code:

package PKG_SCOTT2
    as
        type NumberArray is table of Number index by binary_integer;
        type CharArray is table of varchar2(255) index by binary_integer;

       procedure Dept_Insert2( p_DeptNo  in NumberArray,
                             p_DName in CharArray,
                             p_Loc   in CharArray );

   end;

package body PKG_SCOTT2
    as
        procedure Dept_Insert( p_DeptNo  in NumberArray,
                                p_DName in CharArray,
                                p_Loc   in CharArray )
           is
        begin
            for i in 1 .. p_DeptNo.count loop
                insert into dept
                 values(p_DeptNo(i), p_DName(i), p_Loc(i));
            end loop;
       end;
   end;

November 13, 2002 -- Thanks for the question regarding "DBMS_JOB with interval", version 8.1.7
You Asked
Hello Tom,

I want to tun some packages with it's parameter, let's call it :
package1('dba1')
package2('dba1')
package3('dba1')

My question is :
1. How to run these 3 packages starting at 6 pm everyday with interval between
package1 and package2 is about 5 minutes...and also the same interval between
package2 and package3
2. If I want to record the execution on tkprof report , should I alter session
auto trace first before I submitt the dbms_job ?

thx before, Tom :)
and we said...
1) schedule package1 at 6pm every day

dbms_job.submit( l_job, 'package1( ''dba1'' );',
                 trunc(sysdate)+18/24, 'trunc(sysdate)+1+18/24' );

have the last line of package1 be:

   dbms_job.submit( l_job, 'package2( ''dba1'' );', sysdate+5/24/60 );

and the last line of package2 be the same but for package 3

would be one way.

2) you would need to put the alter session into the job itself (they run in
another session when executed by the job queues).  You would want to turn it on
AND OFF explicitly.  Look for the trace files in the BACKGROUND dump dest.

You might consider just tracing without using the job -- eg: just set sql_trace
on and run package1 in the foreground.  should be the same results.
Review & Followup
Rating: 4
Mmmm OK ... but November 12, 2002
Reviewer: Chris
OK, I submit this job, and the tkprof didn't record the query...just like you
said :

declare v_jobno number;
begin
dbms_job.submit(v_jobno,'pkgProfileAnumber.SetProfileAnumber(''dba1'');' ,
trunc(sysdate)+21/24,null);
commit;
end;

Followup:
well, my advice is to just run the procedure in the foreground with tracing (no
need to run it in the background with tracing, it'll just COMPLICATE
everything).

But if you insist, you just:

v_jobno, 'execute immediate ''alter session set sql_trace=true'';
pkgprofileanumber.setprofileanumber(''dba1'');', .....

Rating: 4
continue November 12, 2002
Reviewer: A reader
Sorry I alwasy got trouble with truncated message.

my question is :
Where should I put "alter session set sql_trace = true; " ?
in "what" section ? or where ?

Rating: 4
continue November 12, 2002
Reviewer: Chris
Sorry I always got trouble with truncated message.

my question is :
Where should I put "alter session set sql_trace = true; " ?
in "what" section ? or where ?

November 13, 2002 Pablo -- Thanks for the question regarding "How to get max and sum from two different tables in the same select", version 8.1.7
You Asked
Hi Tom,
  I have tables with invoices, invoice payments and invoice accounting. I would
like to show the following:

 Invoice Number (from invoices)
 Last payment for the Invoice (from invoice payments, may be several per
invoice)
 Total Accounted Amount (from invoice accounting, may be several rows per
invoice)

I would like to do this in a single sql and if possible without using inline
views. I guess I can use analytic functions, but I cannot get it without inline
views. Is there any way?

Thanks for your time.
and we said...
Why the "fear" of inline views???

Anyway -- need a tad more detail:

invoice number -- easy enough

last payment for the invoice -- is the the last "payment_amount" for the
max(payment_date) or just the max(payment_date).  Eg: are you asking for the
salary of the last hired employee or asking for the date of the last hire

total accounted amount -- easy enough

Oh well -- I'll give you every possible answer then - starting with some test
data:

ops$tkyte@ORA920.US.ORACLE.COM> create table t1 ( id number primary key );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> create table t2 ( id references t1, payment_date
date, amt number );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> create table t3 ( id references t1, amt number
);

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into t1 select rownum from all_users
where rownum <= 5;

5 rows created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into t2 select mod(rownum,5)+1,
sysdate-dbms_random.value(1,100), round(dbms_random.value( 1, 100 ),2)
  2    from all_users;

38 rows created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into t3 select mod(rownum,5)+1,
round(dbms_random.value( 1, 100 ),2)
  2    from (select * from all_users union all select * from all_users );

76 rows created.

so, we have different numbers of rows in t2 and t3 for each ID like you do --
here are the singleton queries that show us what values we need to get:

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select t1.id, max(t2.payment_date) max_pay_date
  2    from t1, t2
  3   where t1.id = t2.id
  4   group by t1.id
  5  /

        ID MAX_PAY_D
---------- ---------
         1 31-OCT-02
         2 31-OCT-02
         3 02-NOV-02
         4 31-OCT-02
         5 16-OCT-02

ops$tkyte@ORA920.US.ORACLE.COM> select t1.id, substr(
max(to_char(t2.payment_date,'yyyymmddhh24miss') || amt), 15 ) max_t2_amt
  2    from t1, t2
  3   where t1.id = t2.id
  4   group by t1.id
  5  /

        ID MAX_T2_AMT
---------- ----------------------------------------
         1 20.22
         2 23.48
         3 71.17
         4 76.45
         5 81.02

(thats the amount for the max payment date)

ops$tkyte@ORA920.US.ORACLE.COM> select t1.id, sum(t3.amt) sum_t3_amt
  2    from t1, t3
  3   where t1.id = t3.id
  4   group by t1.id
  5  /

        ID SUM_T3_AMT
---------- ----------
         1     601.01
         2     821.66
         3     706.87
         4     857.05
         5     893.75

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select t1.id,
  2         (select max(payment_date) from t2 where t2.id = t1.id) max_pay_date,
  3         (select to_number(substr(
max(to_char(t2.payment_date,'yyyymmddhh24miss') || amt), 15 ))
  4           from t2
  5          where t2.id = t1.id) max_t2_amt,
  6         (select sum(amt) from t3 where t3.id = t1.id )  sum_t3_amt
  7    from t1
  8  /

        ID MAX_PAY_D MAX_T2_AMT SUM_T3_AMT
---------- --------- ---------- ----------
         1 31-OCT-02      20.22     601.01
         2 31-OCT-02      23.48     821.66
         3 02-NOV-02      71.17     706.87
         4 31-OCT-02      76.45     857.05
         5 16-OCT-02      81.02     893.75

Using a select of a select, we can get that information pretty easy.  Those
are not inline views -- technically -- so that is a "fair" answer....

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select t1.id,
  2         max(payment_date) max_pay_date,
  3         to_number(substr( max(to_char(t2.payment_date,'yyyymmddhh24miss') ||
t2.amt), 15 )) max_t2_amt,
  4         sum(t3.amt)/count(distinct t2.rowid) sum_t3_amt
  5    from t1, t2, t3
  6   where t1.id = t2.id and t2.id = t3.id
  7   group by t1.id
  8  /

        ID MAX_PAY_D MAX_T2_AMT SUM_T3_AMT
---------- --------- ---------- ----------
         1 31-OCT-02      20.22     601.01
         2 31-OCT-02      23.48     821.66
         3 02-NOV-02      71.17     706.87
         4 31-OCT-02      76.45     857.05
         5 16-OCT-02      81.02     893.75

I'll let you figure out why that one that uses no subqueries or inline views
works.... the count(distinct rowid) could be count(distinct t2.id) as well...

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select t1.id, max_pay_date, max_t2_amt,
sum_t3_amt
  2    from t1,
  3         ( select id, max(payment_date) max_pay_date,
  4                to_number(substr(
max(to_char(payment_date,'yyyymmddhh24miss') || amt), 15 )) max_t2_amt
  5           from t2
  6          group by id ) t2,
  7        ( select id, sum(amt) sum_t3_amt
  8            from t3
  9           group by id ) t3
 10   where t1.id = t2.id and t1.id = t3.id
 11  /

        ID MAX_PAY_D MAX_T2_AMT SUM_T3_AMT
---------- --------- ---------- ----------
         1 31-OCT-02      20.22     601.01
         2 31-OCT-02      23.48     821.66
         3 02-NOV-02      71.17     706.87
         4 31-OCT-02      76.45     857.05
         5 16-OCT-02      81.02     893.75

ops$tkyte@ORA920.US.ORACLE.COM>

and the inline view for completeness...
Review & Followup
Rating: 4
"How to get max and sum from two different tables in the same select", November 06, 2002
Reviewer: Prasanna from Miami, FL
Is there a different way to approach this problem possibly looking at the data
model aspect?

I do not know the full details of this database, but seems to me that between
invoice payments and invoice accounting the relationship is one to one.

In which case going back to Tom's example table t2 and t3 need to have
additional columns "id2".

Then the query becomes simpler

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select t1.id,
  2         max(payment_date) max_pay_date,
  3         to_number(substr( max(to_char(t2.payment_date,'yyyymmddhh24miss') ||

t2.amt), 15 )) max_t2_amt,
  4         sum(t3.amt) sum_t3_amt --/count(distinct t2.rowid) --No need for
Count
  5    from t1, t2, t3
  6   where t1.id = t2.id and --t2.id = t3.id
      t2.id2 = t3.id2
  7   group by t1.id
  8  /

Followup:
that would be true if and only if it was one to one -- but they said it wasn't
-- it was 1:n and 1:m -- hence you need to divide to get the sum back right.

Rating: 4
Not sure... November 06, 2002
Reviewer: Kashif from Houston, TX
Hi Tom,

I'm a little unclear on your second query, in which you use the count (distinct
t2.rowid). How does it work? I know the count (distinct t2.rowid) will give the
number of distinct rowids per id, but that's where I get lost. If we don't
divide by it, we'll get the sum of amts in t3 times the number of rows in t2.
But why? And why distinct? I guess my understanding of joins and groups is a
little lacking here... Any feedback would be useful. Thanks.

Kashif

Followup:
Ok, lets say there are

1 rows in t1 with id = 1
2 rows in t2 with id = 1
3 rows in t3 with id = 1

when we join:

from t1, t2, t3
where t1.id = t2.id and t1.id = t3.id

we'll get 6 rows -- (1*2 from joining t1 to t2) * 3 from joining that to t3

We apparently are counting each T3.amt 2 times -- once for each row in T2.  That
is the nature of the join.  Look:

ops$tkyte@ORA920.US.ORACLE.COM> create table t1 ( id int );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> create table t2 ( id int, msg varchar2(10) );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> create table t3 ( id int, amt int );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into t1 values ( 1 );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into t2 values ( 1, 'row1' );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into t2 values ( 1, 'row2' );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into t3 values ( 1, 2 );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into t3 values ( 1, 4 );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into t3 values ( 1, 6 );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select t1.id, t2.msg, t3.amt
  2    from t1, t2, t3
  3   where t1.id = t2.id
  4     and t1.id = t3.id
  5  /

        ID MSG               AMT
---------- ---------- ----------
         1 row1                2
         1 row2                2
         1 row1                4
         1 row2                4
         1 row1                6
         1 row2                6

6 rows selected.

So -- each row in T3 is "amplified" or "multiplied" by each row in T2.  two rows
in T2 -- then T3's rows will appear in the join twice.  Three rows in T2 -- then
T3's rows will appear in the join three times.  and so on.

So, when we sum them up:

ops$tkyte@ORA920.US.ORACLE.COM> select t1.id, sum(t3.amt), count(distinct
t2.rowid)
  2    from t1, t2, t3
  3   where t1.id = t2.id
  4     and t1.id = t3.id
  5   group by t1.id
  6  /

        ID SUM(T3.AMT) COUNT(DISTINCTT2.ROWID)
---------- ----------- -----------------------
         1          24                       2

ops$tkyte@ORA920.US.ORACLE.COM>

we doubled counted (in this example) everything.  Dividing the SUM by the number
of rows in T2 that contributed to this group will fix that -- 24/2 = 12 =
6+4+2...

Rating: 4
Does it not have to be count(distinct t2.rowid) always? November 07, 2002
Reviewer: Ramakrishna from India
Dear Tom,
    Thanks for your detailed explanation.  You have said in
your response that "count(distinct rowid) could be count(distinct t2.id) as
well".  But they return different results!  The count(distinct t2.id) for a
given t1.id always returns 1 unlike count(distinct t2.rowid) which will return
the factory to "de-amplify" by.  Is that right?

  Also, the Discoverer and Business Objects documentation
seem to call this 'Fan Trap' or 'Chasm Trap'.
It would be great if you could list the generic conditions
under which such connection traps can occur in SQL so that we will know what to
watch out for.

thanks,
Ramakrishna

Followup:
DOH -- sorry about that -- you are correct of course, needs to be count(distinct
t2.rowid) -- we need to know the number of rows.......

I've never heard of "Fan Trap" or "Chasm Trap" (I can see the "fan" fitting in
-- it "fans out" the result set -- chasm-- that one doesn't click with me)

Anyway -- here are the rules:

If

a) you are using aggregation
b) and you have more then 2 tables in the query (you have N tables resulting in
N-1 joins)
c) and there are NOT at least "N-2" one to one relationships involved

you have this issue

Eg: suppose you have:

select ..
from t1, t2, t3 where t1.x=t2.x and t1.x=t3.x   N=3, there are 2 joins

t1 1:m t2
t1 1:m t3   there are 0 1:1 conditions, 0 < 3-2 so this "fans" out

Suppose you have:

select ...
from t1, t2, t3 where t1.x=t2.x and t1.x=t3.x   N=3, there are 2 joins

t1 1:1 t2
t1 1:m t3  there are 1 1:1 conditions, 1 >= 3-2 so this does NOT fan out

Rating: 5
How about a new book: "Cool Little SQL Tricks" November 07, 2002
Reviewer: Chuck Jolley from Oklahoma City, OK USA
to_number(substr( max(to_char( t2.payment_date, 'yyyymmddhh24miss') ||
t2.amt), 15 )) max_t2_amt

Clever!
Thanks,
Chuck

Followup:
Well, actually, that is what my next book is mutating into -- more of a best
practices, rather then a formal "performance tuning text book".

Similar to:
http://www.amazon.com/exec/obidos/ASIN/0201310058
curious -- what do people think of that format for an Oracle best practices
book?  there are online samples of the content so you can see what it would look
like. (i have about 100 items in my list)

Rating: 5
Thanks. November 07, 2002
Reviewer: Kashif from Houston, TX
Thanks Tom, the explanation was fantastic. When do you think you might have a
revision for "Expert One on One" out for 9i, by the way? The best practices,
bullet-point style is perfect for recommending tips and tricks, especially for
intermediate to advance level programmers, which is probably the audience
interested in such a text. Prevents us readers from having to sift through tons
of text and having to dog-ear each page we're interested in, or even worse
highlighting lines in the text with fluoresecent markers. To get what we're
looking for, we can just zoom in to the topic we're interested in using the TOC.
I've read a best practices book by Feurstein, and while I don't agree with some
of his points, it was really hands-down the most effective tips book I have read
yet, primarily as a result of the concise, bullet-point style of the book.

Kashif

P.S. Is there a way to get in line to order a copy of the book now?

Followup:
I'm not planning on doing an Expert one on one for 9i - for a couple of reasons.

One is that 90% of the current one applies 100% to 9i (there is just more STUFF
in 9i, lots more).  So, the guts of the book is relevant.

Second is -- well, time is marching on and the next release would come out
before I could do it -- so, I'm thinking about targeting that one ;)

I don't even have an ISBN for the other book as yet.

Rating: 5
Interesting... November 07, 2002
Reviewer: jan van mourik from Houston, TX
That "Best Practices" books looks interesting, well, if I were a Java developer
:-) I'd definitely be interested in your Oracle version...

Rating: 4
Sufficient condition for fan trap? November 08, 2002
Reviewer: Ramakrishna from India
Dear Tom,
  Thanks for your answer.  Chasm trap and Fan trap seem to
be used to mean much the same thing.  I went through your
conditions when this occurs and wanted to know if it is
correct to restate them as follows:

The NECESSARY conditions for a 'fan trap' to occur in a
query are:
1.  Aggregation is being done
2.  Involves more than one 1:m relationship

However, I am unable to formulate the SUFFICIENT
conditions.  For example, there is one case where a query
meets both the above criteria but has no fan trap which is:
Queries on the classic 'star schema' used in data
warehouses involve many 1:m relationships and aggregations
are done on the central fact table and there is no problem.

For example, you could have a Day table, a Product table and a Region table all
linked via 1:m relationships to a
central Sales table but a query joining all these 4 tables
to find total sales by, say, Month, by Category and by
Geography does not face this problem.

I want to know if it will be possible to state the SUFFICIENT condition for this
in plain English so that I can
use it to check any multi-table queries easily.

thanks,
ramakrishna

Followup:
Queries on star schema's DO NOT involve more then one 1:m -- they are all 1:1 by
definition!!!

You have a series of dimension tables, you have a single FACT table.

The relationship from FACT to DIMENSION is always 1:1 (the relationship from
DIMENSION to FACT is generally 1:M but it is not relevant here -- you join FACT
to a SINGLE DIMENSION row -- you join from FACT to a dimensions primary key)

So, with a star schema -- you have a whole bunch of 1 to 1 relationships.

You were looking at the relationship the wrong way....

You stated the sufficient conditions.

Rating: 5
group functions November 12, 2002
Reviewer: Amit from India
Dear Tom,
  My requirement is I 've to find the sum of quantity and the date corresponding
to it but display only the max of the summed up value and its corresponding
date.I am using the following query :

select * from (select to_char(docdt,'DD-MON-YYYY'),sum(val/100000) from t
where trunc(dt) >= to_date(:fdt,'dd-mon-yyyy')
and trunc(dt) < to_date(:tdt,'dd-mon-yyyy')+1
group by to_char(dt,'DD-MON-YYYY') order by sum(val/100000) desc) where rownum=1

If my from and to dates are far apart, the query takes more time to execute.
Any improvement is greately appreciated.
TIA

Followup:
Hmm, the more data I have to scan and process the longer the query takes....
seems reasonable to me?

But anyway there is definite room for improvement.  for example:

select * from (select to_char(docdt,'DD-MON-YYYY'),sum(val/100000) from t
where trunc(docdt) >= to_date(:fdt,'dd-mon-yyyy')
and trunc(docdt) < to_date(:tdt,'dd-mon-yyyy')+1
group by to_char(docdt,'DD-MON-YYYY') order by sum(val/100000) desc) where
rownum=1

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.46       0.48         72         75          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.46       0.48         72         75          0           1
********************************************************************************
select *
  from (
select trunc(docdt), sum(val/100000)
  from t
 where docdt >= to_date(:fdt,'dd-mon-yyyy')
   and docdt < to_date(:tdt,'dd-mon-yyyy')+1
 group by trunc(docdt)
 order by 2 desc
       )
 where rownum = 1

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.22       0.24         72         75          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.22       0.24         72         75          0           1

those two queries are the same but by removing many of the unnecessary function
calls you are making -- we can cut the CPU needs in half (my table t was created
by select created, object_id from all_objects by the way).

I looked at:

where trunc(dt) >= to_date(:fdt,'dd-mon-yyyy')

Now, the trunc(dt) isn't needed since DT >= trunc(dt) always and if trunc(dt) >=
to_date(:fdt) then DT by definition is >= to_date(:fdt)

Looking at
trunc(dt) < to_date(:tdt,'dd-mon-yyyy')+1

we can also say that if DT = 01-jan-2002 23:59:59 and trunc(dt) = 01-jan-2002
00:00:00.  And if

    01-jan-2002 00:00:00 < to_date(:tdt)+1

then to_date(:tdt)+1 must be 02-jan-2002 OR GREATER (the minimum it could be is
02-jan-2002.

Hence, if trunc(dt) < to_date(:tdt)+1 in this case, dt is ALSO < to_date(:tdt)+1
(cause we are just whacking the time component here)

So, we can remove lots of trunc calls.  I also changed the TO_CHARS (lots of
overhead there to string-ize a date) to TRUNC -- achieving the same results but
at a lower CPU cost:

select to_char(docdt,'dd-mon-yyyy')
from
 t group by to_char(docdt,'dd-mon-yyyy')

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        3      0.16       0.19         72         75          0          17
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        5      0.16       0.19         72         75          0          17

select trunc(docdt)
from
 t group by trunc(docdt)

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        3      0.08       0.11         72         75          0          17
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        5      0.08       0.11         72         75          0          17

Now, the removal of the functions on docdt in the where clause will also permit
this to use an index on docdt when/if appropriate -- if you happen to have an
index on DOCDT,VAL -- you would get the biggest bang for the buck here as well.

Rating: 5
group functions November 12, 2002
Reviewer: Amit from India
Dear Tom,
Sorry.I just muffed up with the above query the correct one is :

select * from (select to_char(dt,'DD-MON-YYYY'),sum(val/100000) from t
where trunc(dt) >= to_date(:fdt,'dd-mon-yyyy')
and trunc(dt) < to_date(:tdt,'dd-mon-yyyy')+1
group by to_char(dt,'DD-MON-YYYY') order by sum(val/100000) desc) where rownum=1

TIA

Followup:
see above -- i figured dt and docdt were one in the same...

Rating: 5
what im waiting for November 13, 2002
Reviewer: Bill from SoCal
is a line of cool clothes called BIND VARIABLES
the tag on the clothes could be :TOM

Rating: 5
Explanation of the terms "fan trap" and "chasm trap" November 13, 2002
Reviewer: Dan Kefford from New Haven, CT
In case anyone was wondering, the following URL appears to explain the two terms
quite well:
http://www.businessobjects.com/services/InfoCenter/tips/unvdes/tip/ut001/ut001.htm

November 13, 2002 Souren -- Thanks for the question regarding "Data Access from MS SQL SERVER 7.0", version 8i
You Asked
Hi,

My company want to access data from both MS SQL Server 7.0 and
Oracle 8i. Please provide me the detailed steps of accessing
data from MS SQL Server 7.0 to Oracle 8i.

Looking for an early reply.

thankx

Souren
and we said...
There are at least two ways.

1) you could put the MS SQL server jdbc driver into the oracle
database.  then a java stored procedure can open a connection to
MS.  This connection would not have 2 phase commit abilities,
distributed query or anything but would let you query data from
there and update it as well..

2)
http://www.oracle.com/gateways
that will let you join data between oracle and ms, update data
in both and have a transparent two phase commit take place, and
so on.  It is a much more robust solution.
Review & Followup
Rating: 2
Porting issue November 12, 2002
Reviewer: Abhijit from Mumbai, IN
Hi Tom,
We are porting to Oracle 8.1.7 from SQL Server 7.0
I found that Oracle is very flexible, except for one particular area, namely
schedules.

This is one area where I feel that MSSQL scores.
I have a job which should run only on the days that the user chooses.

The user could choose any two days of the week or only a Sunday (in this case
its easy).
Or all the days of the week, twice a day. I mean MSSQL really allows you to do
this in an easy manner.

How do I implement the same in Oracle? Presently we have ported by cutting out
on this feature. We allowed the user
to have only two schedules, a weekly schedule and a daily schedule.

I have been asking this to a few people and they seem to think that what I have
done right now is enough.
I am not really satisfied with this however.

How do I implement the schedules?

Cheers!
Abhijit

Followup:
Umm, are you asking how to do DBMS_JOBs?

Not sure what you mean by "schedules".

But anyway -- all you need is a query that selects out what you want.  For
example "i want to run on WED and FRI" would be:

ops$tkyte@ORA920.LOCALHOST> alter session set nls_date_format = 'Dy Mon DD
yyyy';

Session altered.

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> select dt,  least( next_day( dt, 'WED' ),
next_day( dt, 'FRI' ) )
  2    from ( select sysdate+rownum-1 dt from all_objects where rownum <= 10 )
  3  /

DT              LEAST(NEXT_DAY(
--------------- ---------------
Tue Nov 12 2002 Wed Nov 13 2002
Wed Nov 13 2002 Fri Nov 15 2002
Thu Nov 14 2002 Fri Nov 15 2002
Fri Nov 15 2002 Wed Nov 20 2002
Sat Nov 16 2002 Wed Nov 20 2002
Sun Nov 17 2002 Wed Nov 20 2002
Mon Nov 18 2002 Wed Nov 20 2002
Tue Nov 19 2002 Wed Nov 20 2002
Wed Nov 20 2002 Fri Nov 22 2002
Thu Nov 21 2002 Fri Nov 22 2002

10 rows selected.

So your interval to dbms_job could be:

least( next_day( sysdate, 'WED' ), next_day( sysdate, 'FRI' ) )

for example

Rating: 4
Thanks November 12, 2002
Reviewer: Abhijit from Mumbai
Hi Tom,
Yes I did mean dbms_job.submit for schedules.
Now I have can schedule a task for anyday of the week. Thanks for your solution.

I am reading Sean's book co-authored by you and it has very little information
on
this supplied package as well.

Just one more question.
What if I wanted to schedule a task several times in a single day?

Thanks,
Abhijit

Followup:
My book "Expert one on one Oracle" has a ton of information on this -- it is a
more "advanced" topic if you will.

Just create a function that returns the values you want.  For example:

trunc(sysdate,'hh')+1/24

would run a job every hour for example.  We just take the current time, back it
off to the last hour (eg: at 1:24pm, trunc(sysdate,'hh') would return 1pm) and
then add an hour giving us 2pm - at 2pm, it'll return 3pm and so on...

Rating: 4
Thanks Tom! November 14, 2002
Reviewer: abhijit from Mumbai
Thanks Tom, I will purchase your book as well!

November 13, 2002 gaurang -- Thanks for the question regarding "Composite index", version 8.1.6
You Asked
hi tom

i have question regarding composite index.
how it is stroed in b*tree structure?
if i have composite index on(empno,deptno) and i am only using
deptno in where clause ,
how that index will be used?

Thanks
and we said...
If you query:

select count(distinct deptno) from t

and either of EMPNO or DEPTNO is defined as "not null" -- we may very well use
the INDEX via a FAST FULL INDEX SCAN over the table (the index being a "skinny
version" of the table in this case.

If you query "select empno, deptno from t where deptno = :x"  we MAY use the
index again (as a skinny table).

If you query "select ename, deptno from t where deptno = :x" we will NOT use the
index as we would have to full scan the entire index AND then access the table
anyway.  We would just full scan the table instead.

A composite index is stored sorted in key order (by empno and then deptno in
your case)
Review & Followup
Rating: 3
Response about "Composite index" August 31, 2001
Reviewer: Michael Leung from HK
What is "FAST FULL INDEX SCAN"? Are there many types of index scanning?

Also, what is the difference between the first query and second query of "select
empno, deptno from t where deptno = :x"?

Thank you for your kindly help to us!

Followup:
An INDEX_FFS (index fast full scan) is a process where by we read the index data
as if it were a table.  Normally, an index is processed a block at a time.  goto
the root block, use the branch blocks to navigate.  It is a data structure.

In a FAST FULL SCAN, we just read the entire structure as it exists on disk.  We
do not treat it as an index, but more like a table.  We read it out of order
(data does not come back sorted from an index ffs) and we read it using
multiblock IO.

This allows us to use an index like a "skinnier" version of the table.

We can:

range scan and index (ascending or descending)

combine indexes (what we do with bitmapped indexes to process complex AND/OR
conditions)

join indexes (use two or more indexes joined together)

full scan indexes as above.

I don't understand what you mean by "what is the difference between"

select count(distinct deptno) from t;
select empno,deptno from t where deptno = :x;

one returns a count of distinct deptno's in T, the other returns the
empno/deptno for a given department.  Both might use the index with a FULL scan
since the index can answer them (without having to hit the base table).

The last query "select ENAME, deptno from t where deptno = :x" most likely WON'T
use the index with a full scan for the simple reason that ENAME is not in the
index and we would have to full scan the index AND access the table to get ENAME
(so we would probably just full scan the table instead)

Rating: 3
November 13, 2002
Reviewer: A reader
In your first response, you have given example of 2 queries which are same..and
in one you say index will b used and in other us say index wont be used.That is
teh confusion. Here it is:
"If you query "select empno, deptno from t where deptno = :x"  we MAY use the
index again (as a skinny table).

If you query "select ename, deptno from t where deptno = :x" we will NOT use the

index as we would have to full scan the entire index AND then access the table
anyway.  We would just full scan the table instead.
"

Followup:
they are not the same

one selects EMPNO (in the index)
the other selects ENAME (not in the index)

November 13, 2002 meera -- Thanks for the question regarding "dormant users", version 8.1.7
You Asked
I am looking to get the dormant users in the database. Meaning the users who
have not logged on to the database for the past 3 mths. Is their any system
table from which I could get this information so that I can create  report of
all the userid's where the last log date is greater than 90 days or something
like that.
and we said...
You would enable auditing and in 90 days -- you'll know

See
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76956/audit.htm#1108
you would audit connect.
November 13, 2002 Doug -- Thanks for the question regarding "The results of the database contest - smart dba challenge?", version 8174
You Asked
Tom - I was wondering what the winning entry was for the contest you had posted
on your site last month.  Is there any way we can read it? I'm sure the winner
wouldn't mind the extra publicity.
and we said...
see
http://www.dbazine.com/smartdba.html
right now it says "Coming soon: the answer to our "essay" question- and the
winner announcement!"

check back every now and again to read the final answer (funny as it is -- they
gave us the entries "blind" -- no names.  The winner is someone I know!  Never
met him but I 'know' him -- regular visitor to this site actually)
Review & Followup
Rating: 5
A little irony. November 02, 2002
Reviewer: Jeremy Smith from Salt Lake City, UT
That page says the 25 winners for the SmartDBA challenge this month win...your
book!

My comment would be: should any Oracle DBA who doesn't already have your book,
by this time, really be considered a "Smart DBA?"

:)

Followup:
Hah, I like that ;)

Rating: 4
Can always use an extra copy though November 03, 2002
Reviewer: Doug from CT, USA
Yes.. but it's handy to have  copy of Tom's book in the office, one at home, one
in the car.

Rating: 3
Oracle Chip directly plugged to brain November 03, 2002
Reviewer: A reader
I'm in the process of developing a chip (still in the planning phase) which can
be directly plugged to the brain (eliminating other interfaces eyes etc.
(reducing I/O), getting enough batch window to down load the information
directly to brain while sleeping) and then Tom's book will be used for testing
the results, so more copies of Tom's book you have, better it is; as initial
version of the chip needs multiple copies of Tom's book.

Rating: 5
November 13, 2002
Reviewer: A reader
what was the overall rating of the responses
I can't believe we have that many Connors

Followup:
I don't know what you are trying to say by that.

Rating: 4
one in the what ??? November 13, 2002
Reviewer: Robert from PA
>> copy of Tom's book in the office, one at home,
>> one in the car.

??? one in the car ??? Pal...you're kidding right ???
Better stop it or you're gonna get one in the you-know-what....Geez, I'm glad I
moved out of CT....

Rating: 5
Congratulations, Connor !!!!!! November 13, 2002
Reviewer: SB from CA
Very informative.

November 13, 2002 Shivaswamy -- Thanks for the question regarding "Cost /Rule based Optimizer behaviour", version 8.1.7.0.0
You Asked
I am on NT 8.1.7.0.0. with server of 8 CPUs.

Yesterday I was trying with a query using dba_data_files and v$datafiles and
after running for say about 7 min query used to quit on ORA-4030. Process memory
exhausted. Going through your book on PGA/UGA gave a good understanding. When I
changed the query to run on Rule based optimizer, I go the result in say less
than 30 sec.

Today I was trying to find out the date of creation of partiotions with a join
on dba_data_files, v$datafile and dba_tab_partitions it took 12 minutes to
return the result. With yesterdays experience still green in my memory, I
changed the query to rule based and, yes in less than 30 sec I had the answer.

No, I have never analyzed SYS/SYSTEM. I was under the impression that, 4030
related bug was fixed in 816. Do you have any observations, comments,
suggestions, guidance on this?

TIA,
Shiva
and we said...
ORA-04030 is *not a bug*.

It is an error:

$ oerr ora 4030
04030, 00000, "out of process memory when trying to allocate %s bytes (%s,%s)"
// *Cause:  Operating system process private memory has been exhausted
// *Action:

There have been occurences in the past when the shared pool might have thrown an
ora-4031 due to fragmentation (but even then, if you used bind variables -- it
would NOT happen).

the CBO came up with a plan that exhausted memory, i don't know what your
sort_area_size and other paremters are but they can definitely contribute to it.
 Coupled with the fact that on NT -- there is this 2 gig limit on memory
typically by PROCESS and you are just a thread in a process.  So, you have to
subtract out your SGA, the size of everyone elses PGA and the memory used by the
server elsewhere to see how much you could possibly get.

You should not be using CBO on unanalyzed tables and as the dictionary is not
analyzed -- and should not be (and v$'s cannot be), you should not use it there.
 You should let the optimizer goal default to CHOOSE and this won't happen.
Review & Followup
Rating: 3
December 12, 2001
Reviewer: Shivaswamy from USA
Tom, thanks for your inputs.
I am sorry, I did not mean ORA-4030 is bug. My sort area size was 1 M. I tried
with reduced sort area and retained sizes to.. without any better results. I had
3 Gig of physical memory. My process was going beyond about 500M on
PGA/UGA(Dedicated server). Only handful of idle sessions were there.

** You should let the optimizer goal default to CHOOSE and this won't happen. **
 !!!

I let it(Optimizer) at CHOOSE I got ORA-4030. I used RULE, SAME query did not
return ORA-4030 under SAME conditions and answered the query. That was my
riddle.

Followup:
check to make sure someone hasn't accidently analyzed the SYS tables then!  If
you force it to rule and get different plans then when using choose -- its using
the CBO which would only happen if there were stats.

Rating: 4
analyze of sys tables December 13, 2001
Reviewer: A reader
Tom,

can you explain, what is wrong to analyze sys tables.
I know that it is wrong, but when I see it by the customers,
I can not explain it them.

Followup:
it is wrong because we say so.  It just is.  (cannot we just say "don't do that,
because its the rules").  You cannot analyze the full data dictionary (leads to
deadlocks -- sort of a chicken and egg problem there) which means not all tables
will have stats -- not having stats = bad queries = don't use cbo = don't
analyze any of the sys tables.

Rating: 3
Should all DBA Scripts use RBO? December 13, 2001
Reviewer: Shivaswamy from USA
Default is CBO:
16:14:44 SQL> SHOW PARAMETER OPTIMIZER_MODE
NAME                                 TYPE    VALUE
------------------------------------ ------- ------------------------------
optimizer_mode                       string  CHOOSE
SYS not analyzed, as seen below:
16:16:03 SQL> SELECT COUNT(*) FROM DBA_TABLES WHERE LAST_ANALYZED IS NOT NULL
AND OWNER LIKE 'SYS%';
  COUNT(*)
----------
         0
1 row selected.
How can I you justify this:
select  /*+ RULE */ dtp.partition_name, df.creation_time
from dba_tab_partitions dtp, v$datafile df, dba_data_files ddf
where dtp.tablespace_name = ddf.tablespace_name
and dtp.last_analyzed is null
and ddf.file_id = df.file#
/
=== I HAVE CUT THE OUT PUT OF 248 PARTITIONS HERE  ========
248 rows selected.
Elapsed: 00:00:01.00

select   dtp.partition_name, df.creation_time
from dba_tab_partitions dtp, v$datafile df, dba_data_files ddf
where dtp.tablespace_name = ddf.tablespace_name
and dtp.last_analyzed is null
and ddf.file_id = df.file#
/
=== I HAVE CUT THE OUT PUT OF 248 PARTITIONS HERE  ========
248 rows selected.
Elapsed: 00:11:698.00

While I am aware that, SYS uses RBO, does it mean that, our (DBA)scripts also
should use RBO..? I think not. Can you pl. clarify why is there so much of
marked difference in response? If it is ONLY optmizer, what about the so many
other DBA scripts we use, which return results fairly well? For SYS related
queries, is it our responsibility to go for RBO or CBO changes to RBO for such
queries intelligently?
TIA.

Followup:
can you do this with:

set autotrace traceonly explain

on?  I want to see if we get a COST/CARD with the first query (indicates CBO)
and not the second (indicates RBO).

I cannot observe any differences in execution plans with or without the hint and
on my system -- BOTH run with RBO.

Rating: 4
Why Different Plans December 13, 2001
Reviewer: An ardent fan from USA
Tom,

With reference to your earlier followup reply, I have a similar situation. I use
the init.ora optimizer_goal = CHOOSE but none of the tables have been analyzed.
In a particular query, it shows full table scans. Howeever if I alter the
session set optimizer_goal = rule, the plan becomes very different. As per you
advice I deleted the stats on sys and sttem tables even though there was none. I
ran dbms_stats.delete_table_stats as well as analyze table delete statistics.
But the query plan remains the same. Can you explain why this happens?

Thanks a lot in advance.

Query:

select TO_CHAR(COUNT(distinct
case_claim_line.ic_claim_version_id),'999,999,999'),acronym,
TO_CHAR(COUNT(case_claim_line.ic_id),'999,999,999'),
TO_CHAR(SUM(case_claim_line.saved_amount),'999,999,999,999,999.99'),
cases.reporting_category from rules_flat, case_claim_line,
case_claim, cases where rules_flat.rule_id=cases.rule_id+0 and
case_claim_line.claim_line_action = 'DELETE' and
case_claim_line.ic_claim_version_id = case_claim.ic_claim_version_id+0 and
case_claim_line.ic_case_id = case_claim.ic_case_id+0
and case_claim.ic_case_id = cases.ic_case_id+0
and cases.rule_id = 1397 and cases.ic_case_id in (select
distinct cp.ic_case_id from case_processing cp, case_claim_line ccl,
claim_batch clb where cp.state||'' = 'ACCEPTED'
and cp.workflow||'' = 'WF1' and
cp.ic_case_id = ccl.ic_case_id+0 and ccl.claim_line_action='DELETE' and
ccl.ic_claim_version_id = clb.ic_claim_version_id+0 and clb.ic_batch_id in
(140)) group by cases.reporting_category, rules_flat.acronym

AFTER

   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=26058940563 Card=518
          837573118 Bytes=81976336552644)

   1    0   SORT (GROUP BY) (Cost=26058940563 Card=518837573118 Bytes=
          81976336552644)

   2    1     HASH JOIN (Cost=509180 Card=518837573118 Bytes=819763365
          52644)

   3    2       VIEW OF 'VW_NSO_1' (Cost=101722 Card=2032557 Bytes=264
          23241)

   4    3         SORT (UNIQUE) (Cost=101722 Card=2032557 Bytes=670743
          810)

   5    4           HASH JOIN (Cost=1786 Card=2032557 Bytes=670743810)
   6    5             TABLE ACCESS (FULL) OF 'CASE_PROCESSING' (Cost=9
          48 Card=51 Bytes=13821)

   7    5             HASH JOIN (Cost=832 Card=3985405 Bytes=235138895
          )

   8    7               TABLE ACCESS (FULL) OF 'CASE_CLAIM_LINE' (Cost
          =826 Card=7114 Bytes=234762)

   9    7               TABLE ACCESS (BY INDEX ROWID) OF 'CLAIM_BATCH'
           (Cost=2 Card=56022 Bytes=1456572)

  10    9                 INDEX (RANGE SCAN) OF 'CLAIM_BATCH_BATCH_ID_
          IDX' (NON-UNIQUE) (Cost=1 Card=56022)

  11    2       HASH JOIN (Cost=1499 Card=25526348 Bytes=3701320460)
  12   11         TABLE ACCESS (FULL) OF 'RULES_FLAT' (Cost=2 Card=817
           Bytes=32680)

  13   11         HASH JOIN (Cost=1477 Card=3124400 Bytes=328062000)
  14   13           TABLE ACCESS (FULL) OF 'CASE_CLAIM_LINE' (Cost=132
          2 Card=7114 Bytes=327244)

  15   13           HASH JOIN (Cost=9 Card=4391903 Bytes=259122277)
  16   15             TABLE ACCESS (BY INDEX ROWID) OF 'CASES' (Cost=2
           Card=3827 Bytes=126291)

  17   16               INDEX (RANGE SCAN) OF 'FK_CASE_RULE_ID_IDX' (N
          ON-UNIQUE) (Cost=1 Card=3827)

  18   15             INDEX (FAST FULL SCAN) OF 'PK_CASE_CLAIM' (UNIQU
          E) (Cost=4 Card=114761 Bytes=2983786)

SQL/prd1> alter session set optimizer_goal = rule;

Session altered.

   1    0   SORT (GROUP BY) (Cost=111 Card=20 Bytes=3160)
   2    1     NESTED LOOPS (Cost=89 Card=20 Bytes=3160)
   3    2       NESTED LOOPS (Cost=88 Card=1 Bytes=118)
   4    3         HASH JOIN (Cost=80 Card=4 Bytes=288)
   5    4           TABLE ACCESS (BY INDEX ROWID) OF 'CASES' (Cost=2 C
          ard=20 Bytes=660)

   6    5             INDEX (RANGE SCAN) OF 'FK_CASE_RULE_ID_IDX' (NON
          -UNIQUE) (Cost=1 Card=20)

   7    4           MERGE JOIN (CARTESIAN) (Cost=77 Card=2000 Bytes=78
          000)

   8    7             VIEW OF 'VW_NSO_1' (Cost=73 Card=1 Bytes=13)
   9    8               SORT (UNIQUE) (Cost=73 Card=1 Bytes=330)
  10    9                 NESTED LOOPS (Cost=50 Card=1 Bytes=330)
  11   10                   NESTED LOOPS (Cost=42 Card=4 Bytes=236)
  12   11                     TABLE ACCESS (BY INDEX ROWID) OF 'CLAIM_
          BATCH' (Cost=2 Card=20 Bytes=520)

  13   12                       INDEX (RANGE SCAN) OF 'CLAIM_BATCH_BAT
          CH_ID_IDX' (NON-UNIQUE) (Cost=1 Card=20)

  14   11                     TABLE ACCESS (BY INDEX ROWID) OF 'CASE_C
          LAIM_LINE' (Cost=2 Card=20 Bytes=660)

  15   14                       INDEX (RANGE SCAN) OF 'CCLINE_IC_CLM_V
          SN_ID_IDX' (NON-UNIQUE) (Cost=1 Card=20)

  16   10                   TABLE ACCESS (BY INDEX ROWID) OF 'CASE_PRO
          CESSING' (Cost=2 Card=1 Bytes=271)

  17   16                     INDEX (RANGE SCAN) OF 'CASE_PROC_CASE_ID
          _IDX' (NON-UNIQUE) (Cost=1 Card=1)

  18    7             SORT (JOIN) (Cost=77 Card=2000 Bytes=52000)
  19   18               INDEX (FAST FULL SCAN) OF 'PK_CASE_CLAIM' (UNI
          QUE) (Cost=4 Card=2000 Bytes=52000)

  20    3         TABLE ACCESS (BY INDEX ROWID) OF 'CASE_CLAIM_LINE' (
          Cost=2 Card=20 Bytes=920)

  21   20           INDEX (RANGE SCAN) OF 'FK_CCLINE_CASE_CLAIM_ID_IDX
          ' (NON-UNIQUE) (Cost=1 Card=20)

  22    2       TABLE ACCESS (BY INDEX ROWID) OF 'RULES_FLAT' (Cost=1
          Card=2000 Bytes=80000)

  23   22         INDEX (UNIQUE SCAN) OF 'PK_RULES_FLAT' (UNIQUE)

Followup:
Hate to tell you but you were using CBO in both cases!

You see, the cost/card would not show up with RBO:

ops$tkyte@ORA8I.WORLD> analyze table emp compute statistics;

Table analyzed.

ops$tkyte@ORA8I.WORLD> set autotrace traceonly explain
ops$tkyte@ORA8I.WORLD> select * from emp;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=14 Bytes=560)
   1    0   TABLE ACCESS (FULL) OF 'EMP' (Cost=1 Card=14 Bytes=560)

ops$tkyte@ORA8I.WORLD> alter session set optimizer_goal = rule;

Session altered.

ops$tkyte@ORA8I.WORLD> select * from emp;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE
   1    0   TABLE ACCESS (FULL) OF 'EMP'

Is one of your tables partitioned? has a non-defaulit degree of paralellism set?
uses something that forces the CBO?

I believe you have a case where the CBO was used 100% on the first go around and
the RBO was used partially (subqueries and such can be optimized one way, the
rest of the query another)....

In any case -- you used CBO both times.

Rating: 5
December 14, 2001
Reviewer: Helena Markova from Bratislava, Slovakia

Rating: 4
CBO not using RULE for SYS Schema query December 14, 2001
Reviewer: Shivaswamy from Dallas, USA
Thanks, TOM. Here is the result.

09:58:06 SQL> set autotrace traceonly explain
09:58:22 SQL> select  /*+ RULE */ dtp.partition_name, df.creation_time
09:58:35   2  from dba_tab_partitions dtp, v$datafile df, dba_data_files ddf
09:58:35   3  where dtp.tablespace_name = ddf.tablespace_name
09:58:35   4  and dtp.last_analyzed is null
09:58:35   5  and ddf.file_id = df.file#
09:58:35   6  /

Execution Plan
----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=HINT: RULE

   1    0   MERGE JOIN

   2    1     SORT (JOIN)

   3    2       MERGE JOIN

   4    3         MERGE JOIN

   5    4           SORT (JOIN)

   6    5             MERGE JOIN

   7    6               SORT (JOIN)

   8    7                 VIEW OF 'DBA_DATA_FILES'

   9    8                   UNION-ALL

  10    9                     NESTED LOOPS

  11   10                       NESTED LOOPS

  12   11                         FIXED TABLE (FULL) OF 'X$KCCFN'

  13   11                         TABLE ACCESS (BY INDEX ROWID) OF 'FI

          LE$'

  14   13                           INDEX (UNIQUE SCAN) OF 'I_FILE1' (

          UNIQUE)

  15   10                       TABLE ACCESS (CLUSTER) OF 'TS$'

  16   15                         INDEX (UNIQUE SCAN) OF 'I_TS#' (NON-

          UNIQUE)

  17    9                     MERGE JOIN

  18   17                       SORT (JOIN)

  19   18                         NESTED LOOPS

  20   19                           NESTED LOOPS

  21   20                             FIXED TABLE (FULL) OF 'X$KTFBHC'

  22   20                             TABLE ACCESS (BY INDEX ROWID) OF

           'FILE$'

  23   22                               INDEX (UNIQUE SCAN) OF 'I_FILE

          1' (UNIQUE)

  24   19                           TABLE ACCESS (CLUSTER) OF 'TS$'

  25   24                             INDEX (UNIQUE SCAN) OF 'I_TS#' (

          NON-UNIQUE)

  26   17                       SORT (JOIN)

  27   26                         FIXED TABLE (FULL) OF 'X$KCCFN'

  28    6               SORT (JOIN)

  29   28                 FIXED TABLE (FULL) OF 'X$KCCFE'

  30    4           SORT (JOIN)

  31   30             FIXED TABLE (FULL) OF 'X$KCCFN'

  32    3         SORT (JOIN)

  33   32           FIXED TABLE (FULL) OF 'X$KCVFH'

  34    1     SORT (JOIN)

  35   34       VIEW OF 'DBA_TAB_PARTITIONS'

  36   35         UNION-ALL

  37   36           NESTED LOOPS

  38   37             NESTED LOOPS

  39   38               NESTED LOOPS

  40   39                 NESTED LOOPS

  41   40                   TABLE ACCESS (FULL) OF 'TABPART$'

  42   40                   TABLE ACCESS (BY INDEX ROWID) OF 'OBJ$'

  43   42                     INDEX (UNIQUE SCAN) OF 'I_OBJ1' (UNIQUE)

  44   39                 TABLE ACCESS (CLUSTER) OF 'USER$'

  45   44                   INDEX (UNIQUE SCAN) OF 'I_USER#' (NON-UNIQ

          UE)

  46   38               TABLE ACCESS (CLUSTER) OF 'SEG$'

  47   46                 INDEX (UNIQUE SCAN) OF 'I_FILE#_BLOCK#' (NON

          -UNIQUE)

  48   37             TABLE ACCESS (CLUSTER) OF 'TS$'

  49   48               INDEX (UNIQUE SCAN) OF 'I_TS#' (NON-UNIQUE)

  50   36           NESTED LOOPS

  51   50             NESTED LOOPS

  52   51               TABLE ACCESS (FULL) OF 'TABPART$'

  53   51               TABLE ACCESS (BY INDEX ROWID) OF 'OBJ$'

  54   53                 INDEX (UNIQUE SCAN) OF 'I_OBJ1' (UNIQUE)

  55   50             TABLE ACCESS (CLUSTER) OF 'USER$'

  56   55               INDEX (UNIQUE SCAN) OF 'I_USER#' (NON-UNIQUE)

  57   36           NESTED LOOPS

  58   57             NESTED LOOPS

  59   58               NESTED LOOPS

  60   59                 TABLE ACCESS (FULL) OF 'USER$'

  61   59                 TABLE ACCESS (BY INDEX ROWID) OF 'OBJ$'

  62   61                   INDEX (RANGE SCAN) OF 'I_OBJ2' (UNIQUE)

  63   58               TABLE ACCESS (BY INDEX ROWID) OF 'TABCOMPART$'

  64   63                 INDEX (UNIQUE SCAN) OF 'I_TABCOMPART$' (UNIQ

          UE)

  65   57             TABLE ACCESS (CLUSTER) OF 'TS$'

  66   65               INDEX (UNIQUE SCAN) OF 'I_TS#' (NON-UNIQUE)

09:58:35 SQL> select   dtp.partition_name, df.creation_time
09:58:44   2  from dba_tab_partitions dtp, v$datafile df, dba_data_files ddf
09:58:44   3  where dtp.tablespace_name = ddf.tablespace_name
09:58:44   4  and dtp.last_analyzed is null
09:58:44   5  and ddf.file_id = df.file#
09:58:44   6  /

Execution Plan
----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=108 Card=481769 Byte

          s=215350743)

   1    0   NESTED LOOPS (Cost=108 Card=481769 Bytes=215350743)

   2    1     NESTED LOOPS (Cost=94 Card=1 Bytes=404)

   3    2       HASH JOIN (Cost=87 Card=1 Bytes=391)

   4    3         NESTED LOOPS (Cost=79 Card=26 Bytes=2444)

   5    4           FIXED TABLE (FULL) OF 'X$KCCFE' (Cost=7 Card=1 Byt

          es=64)

   6    4           VIEW OF 'DBA_DATA_FILES' (Cost=72 Card=2618 Bytes=

          78540)

   7    6             UNION-ALL

   8    7               NESTED LOOPS (Cost=8 Card=1309 Bytes=479094)

   9    8                 NESTED LOOPS (Cost=8 Card=8 Bytes=2688)

  10    9                   FIXED TABLE (FULL) OF 'X$KCCFN' (Cost=7 Ca

          rd=1 Bytes=297)

  11    9                   TABLE ACCESS (BY INDEX ROWID) OF 'FILE$' (

          Cost=1 Card=818 Bytes=31902)

  12   11                     INDEX (UNIQUE SCAN) OF 'I_FILE1' (UNIQUE

          )

  13    8                 TABLE ACCESS (CLUSTER) OF 'TS$'

  14   13                   INDEX (UNIQUE SCAN) OF 'I_TS#' (NON-UNIQUE

          )

  15    7               NESTED LOOPS (Cost=64 Card=1309 Bytes=496111)

  16   15                 NESTED LOOPS (Cost=64 Card=8 Bytes=2792)

  17   16                   NESTED LOOPS (Cost=8 Card=8 Bytes=2584)

  18   17                     FIXED TABLE (FULL) OF 'X$KCCFN' (Cost=7

          Card=1 Bytes=297)

  19   17                     TABLE ACCESS (BY INDEX ROWID) OF 'FILE$'

           (Cost=1 Card=818 Bytes=21268)

  20   19                       INDEX (UNIQUE SCAN) OF 'I_FILE1' (UNIQ

          UE)

  21   16                   FIXED TABLE (FIXED INDEX #1) OF 'X$KTFBHC'

           (Cost=7 Card=100 Bytes=2600)

  22   15                 TABLE ACCESS (CLUSTER) OF 'TS$'

  23   22                   INDEX (UNIQUE SCAN) OF 'I_TS#' (NON-UNIQUE

          )

  24    3         FIXED TABLE (FULL) OF 'X$KCCFN' (Cost=7 Card=1 Bytes

          =297)

  25    2       FIXED TABLE (FIXED INDEX #1) OF 'X$KCVFH' (Cost=7 Card

          =100 Bytes=1300)

  26    1     VIEW OF 'DBA_TAB_PARTITIONS' (Cost=14 Card=48176927 Byte

          s=2071607861)

  27   26       UNION-ALL

  28   27         NESTED LOOPS (Cost=3 Card=8752109 Bytes=1741669691)

  29   28           NESTED LOOPS (Cost=3 Card=53497 Bytes=9950442)

  30   29             NESTED LOOPS (Cost=3 Card=327 Bytes=51012)

  31   30               NESTED LOOPS (Cost=1 Card=2 Bytes=226)

  32   31                 TABLE ACCESS (FULL) OF 'TABPART$' (Cost=1 Ca

          rd=99 Bytes=7326)

  33   31                 TABLE ACCESS (CLUSTER) OF 'SEG$'

  34   33                   INDEX (UNIQUE SCAN) OF 'I_FILE#_BLOCK#' (N

          ON-UNIQUE)

  35   30               TABLE ACCESS (BY INDEX ROWID) OF 'OBJ$' (Cost=

          1 Card=16360 Bytes=703480)

  36   35                 INDEX (UNIQUE SCAN) OF 'I_OBJ1' (UNIQUE)

  37   29             TABLE ACCESS (CLUSTER) OF 'TS$'

  38   37               INDEX (UNIQUE SCAN) OF 'I_TS#' (NON-UNIQUE)

  39   28           TABLE ACCESS (CLUSTER) OF 'USER$'

  40   39             INDEX (UNIQUE SCAN) OF 'I_USER#' (NON-UNIQUE)

  41   27         NESTED LOOPS (Cost=2 Card=26830 Bytes=2790320)

  42   41           NESTED LOOPS (Cost=2 Card=164 Bytes=14924)

  43   42             TABLE ACCESS (FULL) OF 'TABPART$' (Cost=1 Card=1

           Bytes=48)

  44   42             TABLE ACCESS (BY INDEX ROWID) OF 'OBJ$' (Cost=1

          Card=16360 Bytes=703480)

  45   44               INDEX (UNIQUE SCAN) OF 'I_OBJ1' (UNIQUE)

  46   41           TABLE ACCESS (CLUSTER) OF 'USER$'

  47   46             INDEX (UNIQUE SCAN) OF 'I_USER#' (NON-UNIQUE)

  48   27         NESTED LOOPS (Cost=9 Card=39397988 Bytes=5279330392)

  49   48           NESTED LOOPS (Cost=9 Card=240819 Bytes=29139099)

  50   49             HASH JOIN (Cost=9 Card=1472 Bytes=133952)

  51   50               TABLE ACCESS (FULL) OF 'TABCOMPART$' (Cost=1 C

          ard=9 Bytes=432)

  52   50               TABLE ACCESS (FULL) OF 'OBJ$' (Cost=7 Card=163

          60 Bytes=703480)

  53   49             TABLE ACCESS (CLUSTER) OF 'TS$'

  54   53               INDEX (UNIQUE SCAN) OF 'I_TS#' (NON-UNIQUE)

  55   48           TABLE ACCESS (CLUSTER) OF 'USER$'

  56   55             INDEX (UNIQUE SCAN) OF 'I_USER#' (NON-UNIQUE)

09:58:44 SQL> spool off

Also, about 4 weeks back I was floored. CBO did not use an index against a big
table for a query satisfying ALL the conditions to use an index. I must have
spent over 2 hours with all possible combinations - pl. trust me - to force it
to use index. And it was ultimately RULE hint, which used index and at the end,
unknowingly, I had exclaimed 'Useless Oracle..!' in front of a gleeful
developer, to her satisfaction! (I do not have those details, however on this
database now.)

TIA,
Shivaswamy

Followup:
Shivaswamy,

as i expected -- you have something analyzed somewhere -- you need to
investigate a little deeper.  Look at the query plans -- as I described -- the
one without RULE, its using the CBO.

run this for me and post the results (run as SYS or "as sysdba" or internal).
also -- whats your version/platform.
set autotrace traceonly explain
select count(*) from FILE$;
select count(*) from OBJ$;
select count(*) from SEG$;
select count(*) from TABCOMPART$;
select count(*) from TABPART$;
select count(*) from TS$;
select count(*) from USER$;
select count(*) from X$KCCFE;
select count(*) from X$KCCFN;
select count(*) from X$KCVFH;
select count(*) from X$KTFBHC;

As for the last part -- you are the starter of myths here.  In a court of law
this would be thrown out as hersay.  I believe you didn't have current stats on
the table OR the index should not have been used (the index would have been the
wrong plan) OR something else.  Without a test case, without an example -- I
have to reject comments like that out of hand.  There is a logical scientific
explanation for everything (its software after all).  Without info -- I can
mount no argument.  For everything I say you'll just say "no i checked that" --
well, we'll never know would we.d..

Rating: 5
Happens to me too... December 14, 2001
Reviewer: Jan van Mourik from Houston, Texas
Tried Shivaswamy's query on two of my databases, and the explain plans show it
too:
SELECT STATEMENT Optimizer=CHOOSE (Cost=157 Card=32758 Bytes
=15363502)

None of the sys tables have statistics, parallel degree = 1...

Followup:
show me:

set autotrace traceonly explain
select count(*) from FILE$;
select count(*) from OBJ$;
select count(*) from SEG$;
select count(*) from TABCOMPART$;
select count(*) from TABPART$;
select count(*) from TS$;
select count(*) from USER$;
select count(*) from X$KCCFE;
select count(*) from X$KCCFN;
select count(*) from X$KCVFH;
select count(*) from X$KTFBHC;

when connected as sys.

Rating: 5
No Surprises Here! December 14, 2001
Reviewer: Mark A. Williams from Indianapolis, IN USA
Well, since I haven't posted anything to AskTom for a week or so, I thought I
would just go ahead and jump right in here!

I ran the same query as Shivaswamy and Jan van Mourik against a 8.1.7 and a
9.0.1 database.  (I am certain I do NOT have stats on ANY sys objects in these
databases.)

Here are the first few lines of the "show plan":

8.1.7:
 ID Operation
--- ----------------------------------
  0   SELECT STATEMENT COST = N/A
  1     MERGE JOIN
  2       SORT
  3         MERGE JOIN

9.0.1:
 ID Operation
--- ----------------------------------
  0   SELECT STATEMENT COST = N/A
  1     CONCATENATION
  2       MERGE JOIN
  3         SORT

This is what I would expect to see with NO stats on my sys objects...

Now, just for fun, I ran dbms_stats.gather_database_stats against the same 8.1.7
database - I happen to know that there is a bug in the 8i gather_database_stats
that causes stats to be created for sys objects.  Here are the results of that:

8.1.7 (WITH sys stats):
 ID Operation
--- ----------------------------------
  0   SELECT STATEMENT COST = 112
  1     NESTED LOOPS
  2       NESTED LOOPS
  3         NESTED LOOPS

Surprise, surprise, surprise.  Notice anything different between the NO stats on
sys, and the WITH stats on sys?

Try running:

begin
 dbms_stats.delete_schema_stats('SYS');
end;
/

and then run Tom's suggestions again - bet you'll be surprised.

Rating: 5
Output traceonly December 15, 2001
Reviewer: jan van mourik from Houston, Texas
Tom,

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'FILE$'

<snip>

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (AGGREGATE)
   2    1     FIXED TABLE (FULL) OF 'X$KTFBHC'

The execution plan was exactly the same for each and every table you asked.
After that I ran the "dbms_stats.delete_schema_stats('SYS');". But still the
same, still showing cost, no difference!
Wonder how it will go for Shivaswamy...

Followup:
Mystery solved -- here is the answer

In 815 and 816, the definition of dba_data_files is:

select v.name, f.file#, ts.name,
       ts.blocksize * f.blocks, f.blocks,
       decode(f.status$, 1, 'INVALID', 2, 'AVAILABLE', 'UNDEFINED'),
       f.relfile#, decode(f.inc, 0, 'NO', 'YES'),
       ts.blocksize * f.maxextend, f.maxextend, f.inc,
       ts.blocksize * (f.blocks - 1), f.blocks - 1
from sys.file$ f, sys.ts$ ts, sys.v$dbfile v
where v.file# = f.file#
  and f.spare1 is NULL
  and f.ts# = ts.ts#
union all
select /*+ ordered use_nl(f) use_nl(hc) */
       v.name, hc.ktfbhcafno, ts.name,
       decode(hc.ktfbhccval, 0, ts.blocksize * hc.ktfbhcsz, NULL),
       decode(hc.ktfbhccval, 0, hc.ktfbhcsz, NULL),
       decode(f.status$, 1, 'INVALID', 2, 'AVAILABLE', 'UNDEFINED'),
       f.relfile#,
       decode(hc.ktfbhccval, 0, decode(hc.ktfbhcinc, 0, 'NO', 'YES'), NULL),
       decode(hc.ktfbhccval, 0, ts.blocksize * hc.ktfbhcmaxsz, NULL),
       decode(hc.ktfbhccval, 0, hc.ktfbhcmaxsz, NULL),
       decode(hc.ktfbhccval, 0, hc.ktfbhcinc, NULL),
       decode(hc.ktfbhccval, 0, hc.ktfbhcusz * ts.blocksize, NULL),
       decode(hc.ktfbhccval, 0, hc.ktfbhcusz, NULL)
from sys.v$dbfile v, sys.file$ f, sys.x$ktfbhc hc, sys.ts$ ts
where v.file# = f.file#
  and f.spare1 is NOT NULL
  and f.file# = hc.ktfbhcafno
  and hc.ktfbhctsn = ts.ts#

/

See that -- they stick a HINT in that view, causing any query that used it to
use the CBO unless that query used the rule hint/optimizer_goal!

This hint made dba_data_files run ok (they added this for locally managed
tablespaces) in isolation but when you started joining that to other objects --
it could get nasty.

Here is more info:

BugTag:             Support notes on <Bug:1614732> - DDR info <BugDesc:1614732>
Affects:            RDBMS (81-820)
NB:                    FIXED
Abstract:           Data dictionary selects using DBA_DATA_FILES may be slow in
8i
Fixed-Releases:     8172 8200
Tags:               PERF REGRESSION
Details:
  This problem is introduced in 8.1.5.
  The presence of hints in the dba_data_files definition can cause
  performance degradation over earlier releases.

In my 8172 database -- this is patched (hence why so many of us cannot reproduce
this - the hints are removed)....

So, there we go -- the scientific explaination I was looking for.  For every
effect there is an identifiable cause.  In this case, the presence of a hint in
a view caused the CBO to be used.

Rating: 5
It's that easy... December 15, 2001
Reviewer: jan van mourik from Houston, Texas
Thanks Tom!

Makes me think of the "good old days" when people always thought they had found
a bug in the compiler. Always turned out to be something in the source code
though...
Learn something every day from your site!

Rating: 5
Interesting MetaLink Info December 15, 2001
Reviewer: Mark A. Williams from Indianapolis, IN USA
Tom:

I did some research on MetaLink after reading your findings in regard to the
hint in the dba_data_files view.  Seems the same hint is in other views as well
(dba_free_space for example).  At least it is good to know why I couldn't
reproduce ("correctly") the issue with my 8.1.7.2.5 and 9.0.1.2.0 databases.

Whilst reading info on this topic, I came across this comment (apparently from
an Oracle developer):

"We do not guarantee the performance against our dba views with joins.    These
views are designed to have the optimal performance when queried directly not for
joins.   In most cases we tell customers that if they don't like our views that
are provided wiht [sic] the database, then they can go ahead and create their
own."

I frequently do joins that involve dba_* views, and don't really want to have to
re-create the views...  I rarely use OEM, but it seems like it generated
statement with similar joins.  I guess that comment just caught me a bit by
surprise.

Thanks,

Mark

Rating: 5
Remote Guru December 15, 2001
Reviewer: Shivaswamy from Dallas, TX
Tom,

*Thanks So much * for all the trouble you have taken to arrive at the
explanations and the conclusion. I have not posted the results for what you have
asked, as you have given satisfying answers, without them.

Your approach to the problems and the way you come out with the explanations
based on the basic principles are simply marvellous. You are (remote)"GURU" in
the Truest sense of the word.

Thanks, again.
Shivaswamy

Rating: 4
Error after processing of certain records November 12, 2002
Reviewer: hrishab from India
Hi Tom,

   I was executing one procedure which has query as

   SELECT  account_id
    FROM    monthly_table tds1
    MINUS
    SELECT  account_id
    FROM    other_table tds2
    WHERE BUSINESS_DATE = TO_DATE('31-OCT-2002','DD-MON-YYYY')

 This query get around 750000 records in the cursor.

 Then for each account_id gets the latest record and inserts into a table.

 After processing roughly around 169000 records, procedure is thrown out with
ORA-04030 error. I've gone through the explain plan and did not find any signs
of table being analyzed. Can you please explain or guide me to a link where I
can find a solution.

Followup:
Well, you must have some procedural code wrapped around this since there is not
any "insert into" (sounds like this should be a SINGLE SQL STATEMENT TO ME!!!!!!
I doubt you need to have the procedural code)

I would believe the culprit is your procedural code -- not this query.

Rating: 4
Join Problem November 12, 2002
Reviewer: A reader
Hi, Tom,

Thanks a lot for your precious comments.
We are now facing a big problem:
basically, we have two identical database(everything including data, index, and
other strunctures are the same).
we have a very simple join query:
select table1.*, table2.* from table1, table2
where table1.pk=table.pk
this query runs instantly on database1, and it
uses index scan for one table in the execution plan,
while the same query runs forever in another database2,
when we look at the execution plan, it doesn't use
the index. Bothe databases are default to COST not RULE
based. We test the same query by;
select table1.pk, table2.pk from table1, table2
where table1.pk=table.pk
and it runs very fast in both the databases and have the
same execution plan.
Now, would you please tell me what could be the problem and
which parts should we look for?(the two databases are setup
the same and located in the same machine).

Thanks a million,

Followup:
lets see the output of an "autotrace traceonly explain" run for both queries
(you know, an example)

Rating: 4
Please answer the question below November 12, 2002
Reviewer: Balaji CN from India Chennai
Hi Tom
I am a new reader of ur site.Sivaswamy had asked "
While I am aware that, SYS uses RBO, does it mean that, our (DBA)scripts also
   should use RBO..? I think not. Can you pl. clarify why is there so much of
   marked difference in response? If it is ONLY optmizer, what about the so many

   other DBA scripts we use, which return results fairly well? For SYS related
   queries, is it our responsibility to go for RBO or CBO changes to RBO for
such
   queries intelligently?
   TIA.
"

Should I use a RULE hint explicitly or should i let optimizer CHOOSE the plan
for DBA Scripts.

Followup:
just run the queries.  no hints.

Rating: 4
Here is the execution plan November 13, 2002
Reviewer: A reader
Hi, TOm,

Here are the execution plan that you requested--they are the same query on
different DBs in one machine, both DBs
setup same and data from the table is the same too. Both
DBs' optimizer_mode are default to "CHOOSE".
a) -- very fast, why still RULE based even DB default CHOOSE?
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'TBL1'
   2    1     INDEX (FULL SCAN) OF 'TBL1_PK3976135997099' (UNIQUE)

b) -- COST based but exetremely slow.
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=10824 Card=212156 By
          tes=15063076)

   1    0   SORT (ORDER BY) (Cost=10824 Card=212156 Bytes=15063076)
   2    1     TABLE ACCESS (FULL) OF 'TBL1' (Cost=419 Car
          d=212156 Bytes=15063076)

The query is:
SELECT * FROM tbl1 order by col1_pk, col2_pk;
Where col1_pk and col2_pk is the composite primary key
of the table.

And the table in both DBs have the same number of records,
same structure, index etc.

In the slower query one(b), when I add the hint--/*+RULE */
it becomes the same speed with the query(a), I also
change the parameter: optimizer_index_cost_adj = 35,
but got the same answer.

Do we really need the hint under the COST based system
if the query response time is really slow? Why RULE
based query has more faster response time?

Could you elaborate these issue please?

Thanks a million

Followup:
you wrote:
...
We are now facing a big problem:
basically, we have two identical database(everything including data, index, and
other strunctures are the same).
we have a very simple join query:
select table1.*, table2.* from table1, table2
where table1.pk=table.pk
....

Now -- that is not the case -- one database has statistics, the other does not.
The query isn't even close to what you said it was (and why didn't you just cut
and paste the query into the question with the autotrace -- makes me TOTALLY
suspicious that the query is not what you say but anyway....)

The first query will return the first row very fast but -- have you timed how
long it takes to get the LAST row???  the second query will get the last row
faster I would believe then the first query would.

So, lets use TKPROF and show me the END result -- the time to get the last row
you fetch.  I think you might be comparing the time to get the first row vs the
time for the entire query to run.

November 13, 2002 Luis -- Thanks for the question regarding "Optimizer mode = Rule and still have cost associated with it.", version 7.3.4.0.0
You Asked
The output is as follows:

TABLE_NAME                     DEGREE
------------------------------ ----------
BILLCYCLES                        DEFAULT
CONTRACT_ALL                      DEFAULT
CUSTOMER_ALL                      DEFAULT

Thanks again.

====================================================================

Good morning, Mr. Kyte.

Unfortunately, these tables are just regular tables, nothing special about them
nor about the indexes related to them. They are not IOTs, the degree of
parallelism is default and they are not partitioned (db version 7.3.4).
Thanks again. I really appreciate your help.

====================================================================
Hi, Mr. Kyte.

I have searched for 'Optimizer=RULE (Cost=' in your site and in google and got
nothing back. I have the following execution plan from a query and, as you can
see, the above 'string' appears in it. I've tried 'alter session set
optimizer_mode = rule' and deleting the statistics related to the tables with no
success though.
Could you please help me on this one?
Thanks in advance.
Best regards.
Luis C L Nobre.

SQL> set autotrace traceonly explain;
SQL> SELECT CU.CUSTOMER_ID, CU.BILLCYCLE, PAYMNTRESP, CO.CO_ACTIVATED
  2  FROM CUSTOMER_ALL CU, CONTRACT_ALL CO, BILLCYCLES BC
  3  WHERE CSLEVEL='40'
  4  AND   CSTYPE='a'
  5  AND   CU.CUSTOMER_ID=CO.CUSTOMER_ID
  6  AND   CU.BILLCYCLE=BC.BILLCYCLE
  7  /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=RULE (Cost=221 Card=400 Bytes=256
00)

   1    0   NESTED LOOPS* (Cost=221 Card=400 Bytes=25600)              :Q177160
                                                                       00

   2    1     NESTED LOOPS* (Cost=221 Card=20 Bytes=1220)              :Q177160
                                                                       00

   3    2       TABLE ACCESS* (FULL) OF 'CUSTOMER_ALL' (Cost=219 Card= :Q177160
          1 Bytes=39)                                                  00

   4    2       TABLE ACCESS* (BY ROWID) OF 'CONTRACT_ALL'             :Q177160
                                                                       00

   5    4         INDEX* (RANGE SCAN) OF 'FKICOCSID' (NON-UNIQUE)      :Q177160
                                                                       00

   6    1     INDEX* (UNIQUE SCAN) OF 'PKBILLCYCLES' (UNIQUE)          :Q177160
                                                                       00

   1 PARALLEL_TO_SERIAL            SELECT /*+ ORDERED NO_EXPAND USE_NL(A2) INDE
                                   X(A2) */ A1.C0,A1.C1,A1.C2,A1.C3,A1.

   2 PARALLEL_COMBINED_WITH_PARENT
   3 PARALLEL_COMBINED_WITH_PARENT
   4 PARALLEL_COMBINED_WITH_PARENT
   5 PARALLEL_COMBINED_WITH_PARENT
   6 PARALLEL_COMBINED_WITH_PARENT
and we said...
Yes, so it is the default degree of parallelism (DOP).

If you do nothing when you create a table -- it will have degree = 1.  Someone
went out of the way to set degree = default, which doesn't mean "one", it means,
use the default DOP as setup in the init.ora.  These tables have a DOP set on
them -- meaning all queries that access them will use the CBO.

Consider:

ops$tkyte@ORA734.WORLD> select table_name, degree from user_tables where
table_name = 'T';

TABLE_NAME                     DEGREE
------------------------------ ----------
T                                       1

ops$tkyte@ORA734.WORLD> set autotrace traceonly explain
ops$tkyte@ORA734.WORLD> select * from t;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'T'

that is RBO.. no cost, no card

ops$tkyte@ORA734.WORLD> alter table t parallel ( degree default );
Table altered.

ops$tkyte@ORA734.WORLD> set autotrace off
ops$tkyte@ORA734.WORLD> select table_name, degree from user_tables where
table_name = 'T';

TABLE_NAME                     DEGREE
------------------------------ ----------
T                                 DEFAULT

ops$tkyte@ORA734.WORLD> set autotrace traceonly explain
ops$tkyte@ORA734.WORLD> select * from t;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=82 Bytes=2296)
   1    0   TABLE ACCESS* (FULL) OF 'T' (Cost=1 Card=82 Bytes=2296)
                              :Q2000

   1 PARALLEL_TO_SERIAL            SELECT /*+ ROWID(A1) */ A1."DEPT",A1."DEPT_G
                                   ROUP",A1."AMOUNT" FROM "T" A1 WHERE ROWID BE
                                   TWEEN :B1 AND :B2

now, that is CBO -- has to be

ops$tkyte@ORA734.WORLD>

ops$tkyte@ORA734.WORLD> alter table t parallel ( degree 1 );

Table altered.

ops$tkyte@ORA734.WORLD> select * from t;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'T'

back to RBO.  Reset the DOP on those tables if you don't want CBO
Review & Followup
Rating: 5
Thanks again. July 03, 2002
Reviewer: Luis from Brasil
I had the wrong idea of what DEFAULT meant.
Thanks again.

Rating: 4
November 13, 2002
Reviewer: AP from Trenton, NJ
Hi Tom,

How do we decide on the degree of parallelism? Does the degree of parallelism
has anything to do with the number of partitions? In our warehouse, we have fact
tables that has 50 partitions. Would it be a good idea to set DOP to 50 on these
tables? (It's Sun 10K domain with 24 cpu). I know it is not (because we have
partitions meant for partition pruning in the first place), but I am not able to
convince people here. How can I prove that an excessive DOP will have a negative
effect on the query performance?

We have dev and qa systems, but they are less powerful than production and they
have different disk lay-outs. We can not change anything on the production
system without testing and proving it on dev and qa. But how can we use these
smaller systems to find out what is the best DOP under production conditions
(with concurrent users)? The statspack data does not seem to help to reach on an
optimal DOP.

I searched about DOP on your site, but couldn't find much. Hoep you will cover
this topic in detail. As always, thank you verymuch for your invaluable openion.

Followup:
I don't set it on tables anymore -- try using the automatic parallel tuning
instead.
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c22paral.htm#365
has a good write up on it (that pesky server concepts manual strikes again!)

Rating: 4
One More November 13, 2002
Reviewer: Bharath from USA
>>that is RBO.. no cost, no card
if there is only Cost no card what does it mean.

Followup:
do you see (Cost=221 Card=400 Bytes=25600)  in the query plan?  if not (numbers
will be different of course) you are using the RBO.

if you do, you are using the CBO.  Only the CBO knows "cost".

November 13, 2002 Sajid -- Thanks for the question regarding "Basic XSQL", version 9.0.1
You Asked
Hi Tom,

I am trying see how XSQL runs and all. For that i tried following but got back
the error as it below.
http://localhost/xsql/
It displays an HTML page with link to :
Hello World page
Employee page
Insurance claim page
Invalid classes page
Do you XML? Site
and many more....

Oracle XSQL Servlet Page Processor 9.0.1.1.0 (Prod)
XSQL-007: Cannot acquire a database connection to process page.
Io exception: Connection
refused(DESCRIPTION=(TMP=)(VSNNUM=150999297)(ERR=12505)(ERROR_STACK=(ERROR=(CODE=
12505)(EMFI=4))))

Could you please help me.

Thanks in advance.

Regards,

Sajid Anwar
London
and we said...
I asked Sean Dillon, our local XML technologist, to take a look at this and
here's what he had to say:
---------------------

Sajid,

The XSQL servlet is trying to connect to a database to query data for the
demonstrations you are trying to run.  There is a file named XSQLConfig.xml
which defines the database configuration for your servlet.  This file (1) needs
to be in your servlet engine's classpath and (2) needs to be provided with
database information to connect to your database.  Typically, this file is found
here:

$XSQL_HOME/xdk/admin/XSQLConfig.xml

For installation/configuration instructions for the XSQL servlet on whichever
servlet engine you are running, go to this URL:
http://otn.oracle.com/docs/tech/xml/xdk_java/doc_library/Production9i/index.html
...and look at the "Installation" section.

You also need to create a database user to own the sample schemas & data to run
those demonstrations.  To install the necessary database objects and data into
this schema, look at the following file:

$XDK_HOME/xdk/demo/java/xsql/install.sql

This basically runs the installation scripts for all the XSQL demos from the
page you are seeing.  (oh btw, once you define the user that owns all this data,
you will have to make sure the appropriate username & password are in the
XSQLConfig.xml file for the "demo" connection.

Hope that helps!

_smd_

_____________________________________________________________________
Sean Dillon - Senior Technologist, Oracle Corporation
Author "Beginning Oracle Programming"
http://www.amazon.com/exec/obidos/ASIN/186100690X
Review & Followup
Rating: 5
Extremely helpful August 14, 2002
Reviewer: Sajid Anwar from London
Thanks a million Sean and Tom. It was really very helpfull. I would definitely
try that and would let you know. Thanks once again.

Best Regards,
Sajid Anwar
London

Rating: 4
XSQL exception November 13, 2002
Reviewer: Saradha Bavanandam from NJ
Tom,
Can you please help me in resolving the following XSQL error.

oracle.xml.sql.OracleXMLSQLException: Internal Error: Unable to resolve name

After I drop and re-create an object types, which has dependents, XSQL Servlet
raises exception for a query that is working fine in the Oracle Server itself
(ie, in SQL*Plus) as in the following test case:

CREATE OR REPLACE TYPE o_object_inner
AS OBJECT ( char1 char(1) )
/
CREATE OR REPLACE TYPE n_nested_table
AS TABLE OF o_object_inner
/
CREATE OR REPLACE TYPE o_object_outer
AS OBJECT ( nNestedTab n_nested_table )
/

For above, the following query encounters no problems on Oracle Server or in
XSQL servlet:

SELECT o_object_outer(NULL) AS "theOuterObj" FROM dual

But if I want to make a change to o_object_inner object type then I drop this
object type using FORCE and re-create this with the modification.

DROP TYPE o_object_inner FORCE
/
CREATE OR REPLACE TYPE o_object_inner
AS OBJECT (char1 char(1) )
/

SELECT o_object_outer(NULL) AS "theOuterObj" FROM dual

The above query now encounters no problems in SQL*Plus but generates exception
in XSQL servlet as follows:

oracle.xml.sql.OracleXMLSQLException: Internal Error: Unable to resolve name

Any insight or help would be greatly appreciated!

But If I drop all of the dependents and recreate them in the
order then It works fine.

Other info:
Oracle Server 8.1.7.4 on HP-UX
XDK 9.2.0.1 (Production) for Java on NT
JDBC/OCI8 drivers for NT (latest for 8.1.7)

Thanks
Pushparaj

Followup:
can you try restarting the servlet engine, looks like it is caching some stuff
and when you drop and recreate -- the cached data is "not right" anymore.

Rating: 4
XSQL exception November 13, 2002
Reviewer: Saradha Bavanandam from NJ
Tom,

Thank you for your quick response.
Actually I get this error only if I stop and restart the engine.

Let me give you the sequence.

1) Start the Servlet engine (tomcat)
2) Run the query from XSQL
   SELECT o_object_outer(NULL) as "theOuterObj" FROM dual
3) No error raised.
4) Drop and re-create n_nested_table type.
5) Run the same query from XSQL
   SELECT o_object_outer(NULL) as "theOuterObj" FROM dual
6) No error raised and it works fine.
7) Stop and restart the servlet engine.
8) Run the same query from XSQL
   SELECT o_object_outer(NULL) as "theOuterObj" FROM dual
9) Now the error is raised as
oracle.xml.sql.OracleXMLSQLException: Internal Error: Unable to resolve name

Thanks again.

Followup:
i'll have to refer you to support -- never seen this myself.

November 13, 2002 R -- Thanks for the question regarding "SQLLDR with no rollbacks", version 8.1.7
You Asked
Tom:
I would like to load 10G worth of data using sqlldr but I don't want to create
any extra overhead like creating rollback log or any other extra overhead....I
would like to do the fastest way possible to load the data...please help me out
here how to do this?

R
and we said...
direct path load it.

make sure you read about direct path loads
make sure you understand direct path loads

they are documented fully in the utilities guide.
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76955/ch08.htm#4621
Review & Followup
Rating: 4
direct=true works wonders but... November 13, 2002
Reviewer: AC from Arlington, VA
On a table that is a master snapshot table with a trigger for an audit table.  I
found that direct load won't work.  I tried the rows parameter but for some
reason, it won't change the default commit to the number I specified on
conventional load.  So, loading about a million records with a control file that
looks like this

office filler,
seatid char "substr(:seatid,1,2) || 'H' || substr(:seatid,3,7)",

takes about 30-45 minutes.  Any suggestions on how to make this load faster?

Followup:
skip the audit.  load the audit like you load the table (same difference no?)

November 13, 2002 Saibabu -- Thanks for the question regarding "Checking Sysdba login", version 8.1.5
You Asked
Hi Tom,
       I have been wondering how to check whether a particular
user logged in as a sysdba or sysoper or normal id.
   After working at sqlprompt long time sometimes I forget
whether my login id is sys or sys as sysdba.Please guide me.
 Thanks
 Saibabu
and we said...
ops$tkyte@DEV816> show user
USER is "OPS$TKYTE"
ops$tkyte@DEV816> connect / as sysdba;
Connected.
ops$tkyte@DEV816> show user
USER is "SYS"
ops$tkyte@DEV816> connect /
Connected.
ops$tkyte@DEV816> show user
USER is "OPS$TKYTE"
ops$tkyte@DEV816>
Review & Followup
Rating: 3
Is sysdba a privilege or a role ? November 13, 2002
Reviewer: Ravi from Reading, UK
Tom,

Is SYSDBA a privilege or a role ?  How do I find which users have the
priivege/role to connect as SYSDBA ?

Thanks in advance.

Followup:
It is like a "magic" role.

sys@ORA920.LOCALHOST> select * from v$pwfile_users;

USERNAME                       SYSDB SYSOP
------------------------------ ----- -----
SYS                            TRUE  TRUE
OPS$TKYTE                      TRUE  FALSE

and in unix -- anyone in the DBA group has it (at the OS level, if they use OS
authentication)

Rating: 5
execution plan November 13, 2002
Reviewer: A reader
Hi, Tom,

What kind roles should i be granted in order to do:
set autotrace on explain;

I have some problem when i doing that right now, I think
i lack some role.
But when I conect / as sysdba, the same problem, what is
wrong?

Thanks

Followup:
http://asktom.oracle.com/~tkyte/article1/autotrace.html

      Rating: 4
as for set
      autotrace on  November 13, 2002
Reviewer:
      chao_ping  from china
run $ORACLE_HOME/sqlplus/admin/plustrce.sql as system.
run $ORACLE_HOME/rdbms/admin/utlxplan.sql as system.
Create public synonym plan_table for system.plan_table;
grant all on plan_table to public;
grant plustrace  to public;
I think this will help.

And for sysdba user login, autotrace won't display anything.

      Rating: 4
and for
      sysdba  November 13, 2002
Reviewer:
      chao_ping  from china
I think checking v$sesstat will give a better view of whether a user is login as
sys or sysdba.

      Rating: 4
Oops,
      sorry  November 13, 2002
Reviewer:
      chao_ping  from china
should be session_privs;
sorry.

          Followup:

that would be the answer to "how do i tell if I am logged in as sysdba"

if the question is "who CAN log in as sysdba...." it wouldn't work ;)

November 14, 2002 Justin -- Thanks for the question regarding "How
      to get rid of temp tables!", version 8.1.6

      You Asked
I do reports with procedures like this, the principle is always the same.
First a select statement gets the basic data into the temp table, then one to
many cursors add 1->n data to the data collected in the first query.Then a selct
* from temp passes the whole set to the report. This is done all from inside a
VB (third party) application.
We just write the procedures for the reports ourself.

so here is my example:

CREATE OR REPLACE PROCEDURE coll_emp3
IS
BEGIN
   DELETE FROM tora_coll_emp;

   INSERT INTO tora_coll_emp
               (job_id, members)
      SELECT job_id, 'N/A'
        FROM jobs;

   DECLARE
      CURSOR c1
      IS
         SELECT        e.last_name, t.job_id
                  FROM employees e, tora_coll_emp t
                 WHERE t.job_id = e.job_id
         FOR UPDATE OF members;
   BEGIN
      FOR m_rec IN c1
      LOOP
         UPDATE tora_coll_emp
            SET members = DECODE (
                             members,
                             'N/A',
                             m_rec.last_name,
                             members || ', ' || m_rec.last_name
                          )
          WHERE CURRENT OF c1;
      END LOOP;
   END;

   COMMIT;
END coll_emp3;
/

q1: How to get rid of tora_coll_emp, using some kind of virtual tables to make
the procedure faster. I have to be able to add columns, update values, delete
rows etc.
q2: Is it better to use a if-than-else clause than decode?

thanks for any help

justin

      and we said...
wow, how sqlserver like -- yuck.

Ok, looks like you are building a comma delimited list of last_names for each
job_id in jobs.

create or replace function get_emp_names( p_job_id in number ) return varchar2
is
   l_string varchar2(4000);
begin
   for x in ( select last_name from employees where job_id = p_job_id )
   loop
      l_string := l_string || ', ' || x.last_name;
   end loop;
   return substr( l_string, 3 );
end;

And now you simply:

select job_id, get_emp_names( job_id )
  from jobs;

QED.

In Oracle9i, we would use my stragg function instead (general purpose user
defined aggregate).  search this site for stragg to see an example

For a more complex example see:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3092495541617

As for #2, it is always better to do as much as you can in SQL alone.
Review &
      Followup

      Rating:
      5
Re:  August 22, 2002
Reviewer:
      Justin  from Europe
Thanks, this almost seems too simple.
And your sharp eye is right: It's an app developped on and for MSSQL and ported
to Oracle.
Now just for learning then (cause I tried with two books): how would I do this
with virtual tables or something like it in memory.Is it possible to create a
structure in memory where I could cycle through and change data etc. like with
temp tables? Or is there never a need for this concept?

I really liked this
"No need to
insert/update/delete/ and then finally query when all you really want to do is
-- QUERY."
from the other article. An eyeopener

thanks

j.

          Followup:

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:110612348061
for the virtual table method -- but i would not use it here,

stragg in 9i, this plsql function before then.

      Rating: 5
One more
      Q  November 14, 2002
Reviewer:  Justin
       from Europe
I used the function method with succes.

Now I need a procedure to collect some data which I could do fine with this
method.
But the first line of this returned table has to have some fixed values (Column
headers used by the grid control) followed by the selected ones.

Is there an elegant way to do it without writing the data somewhere (temp_table,
object type table..) before returning?

Thanks

justin

PS: How much faster is it (~) to write data to an object type table than to a
temp_table and read it again for output?

          Followup:

select 1, 'heading', 'heading', 'heading', ....
union all
select 2, c1, c2, c3, ....
 from ....
ORDER by 1, .....

will do that.  I've never timed it -- probably easiest to use temp table IF YOU
REALLY HAVE TO (cause most of the time you do not need to use either a temp
table or object table type)

November 14, 2002 Saradha -- Thanks for the question regarding
      "Strings of Zero Length Not Equivalent To NULL", version 9.2.0

      You Asked
Tom,

As we are planning to upgrade to 9i version, I am reviewing the Oracle 9i
migration documents and in one of the topic, I read
"Strings of Zero Length Not Equivalent To NULL".

"A string of zero length ('') is not equivalent to a NULL value. According to
the ANSI SQL 1992 Transitional standard, a zero-length or empty string is not
the same as NULL. Ensure that applications do not treat empty strings and NULL
values equivalently."

I tested the following PL/SQL blocks in Oracle8i version and the
behavior looks different for CHAR variable and VARCHAR2 variable.

SQL> set serveroutput on

SQL> DECLARE
  2  col1 VARCHAR2(1);
  3  BEGIN
  4  col1 := '';
  5  DBMS_OUTPUT.PUT_LINE('Length :'||TO_CHAR(LENGTH(col1)));
  6  IF (col1 IS NULL) THEN
  7     dbms_output.put_line('Treated as NULL');
  8  END IF;
  8  IF (col1 = '') THEN
 10     dbms_output.put_line('Treated as EMPTY STRING');
 11  END IF;
 12* END;
 13  /

Length:
Treated as NULL

PL/SQL procedure successfully completed.

In the above example, col1 variable is declared as VARCHAR2 and the
empty string is treated as NULL.

SQL> DECLARE
  2  col1 CHAR(1);
  3  BEGIN
  4  col1 := '';
  5  DBMS_OUTPUT.PUT_LINE('Length :'||TO_CHAR(LENGTH(col1)));
  6  IF (col1 IS NULL) THEN
  7     dbms_output.put_line('Treated as NULL');
  8  END IF;
  8  IF (col1 = '') THEN
 10     dbms_output.put_line('Treated as EMPTY STRING');
 11  END IF;
 12* END;
 13  /

Length :1

PL/SQL procedure successfully completed.

In this example, col1 variable is declared as CHAR variable and
the empty string is neither treated as NULL nor treated as empty string. The
length of the string is 1 in this case eventhough I have assigned an empty
string to col1.

I have tested the above examples in 9i version also and the results
are same.

Can you please verify this difference as we have to modify some our PL/SQL
programs according to this.

Thanks
Saradha

      and we said...
A ZERO length varchar is treated as NULL.

'' is not treated as NULL.

'' when assigned to a char(1) becomes ' ' (char types are blank padded strings).

'' when assigned to a varchar2(1) becomes '' which is a zero length string and a
zero length string is NULL in Oracle (it is no long '')

Now, I don't know what you mean by "we'll have to modify our code due to this"
since this behaviour has been consistent for quite a while.  Consider:

tkyte@ORA716.WORLD> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle7 Server Release 7.1.6.2.0 - Production Release
PL/SQL Release 2.1.6.2.0 - Production
CORE Version 2.3.7.1.0 - Production (LSF Alpha)
TNS for SVR4: Version 2.1.6.0.0 - Production
NLSRTL Version 2.3.6.0.0 - Production

tkyte@ORA716.WORLD> set echo on
tkyte@ORA716.WORLD> @test
tkyte@ORA716.WORLD> DECLARE
  2  col1 VARCHAR2(1);
  3  BEGIN
  4  col1 := '';
  5  DBMS_OUTPUT.PUT_LINE('Length :'||TO_CHAR(LENGTH(col1)));
  6  IF (col1 IS NULL) THEN
  7     dbms_output.put_line('Treated as NULL');
  8  END IF;
  9  IF (col1 = '') THEN
 10     dbms_output.put_line('Treated as EMPTY STRING');
 11  END IF;
 12  END;
 13  /
Length :
Treated as NULL

PL/SQL procedure successfully completed.

tkyte@ORA716.WORLD>
tkyte@ORA716.WORLD> DECLARE
  2  col1 CHAR(1);
  3  BEGIN
  4  col1 := '';
  5  DBMS_OUTPUT.PUT_LINE('Length :'||TO_CHAR(LENGTH(col1)));
  6  IF (col1 IS NULL) THEN
  7     dbms_output.put_line('Treated as NULL');
  8  END IF;
  9  IF (col1 = '') THEN
 10     dbms_output.put_line('Treated as EMPTY STRING');
 11  END IF;
 12  END;
 13  /
Length :1

PL/SQL procedure successfully completed.

Review &
      Followup

      Rating:
      5
  October 15, 2002
Reviewer:
      Saradha  from NJ, USA
Tom,

If Strings of Zero Length for VARCHAR2 variable is NULL
and Strings of Zero Length for CHAR variable is assigned with space values to
the size of the variable, then what does the following convey:

"Strings of Zero Length Not Equivalent To NULL".

How do I store an empty string or How do I check a
variable in the program if it contains an empty string.

Thanks
Saradha

          Followup:

well, as shown, a varchar2 will be NULL -- you would not be able to use a
varchar2 variable and once assigned to a char -- it'll not be an "empty" string
anymore.

      Rating: 5
Pls commnet on
      this wrt 9ir2  October 16, 2002
Reviewer:
      A reader
SQL> set null NULL

SQL> ed
Wrote file afiedt.buf

  1*  select 1 ,length('') LEN  from dual where '' is null
SQL> /

         1        LEN
---------- ----------
         1 NULL

  1*  select 1 ,length('') LEN  from dual where to_char('') is null
SQL> /

         1        LEN
---------- ----------
         1 NULL

Q)
This does mean that empty string is null and the default data type of empty
string is  varchar2 else if it where char then automatically its length would
have been one.

          Followup:

the '' is a being used as a varchar2, it is null.

      Rating:
      5
nulls  October 16, 2002
Reviewer:
      Guy  from Israel
Tom,

SQL> create table chr(x char(1));

Table created.

SQL> insert into chr values(null);

1 row created.

SQL> insert into chr values(null);

1 row created.

SQL> insert into chr values('G');

1 row created.

SQL> select * from chr where x<>'G';

no rows selected

SQL> select count(*) from chr where x is null;

  COUNT(*)
----------
         2

Why null is not different from 'G' ?

Thanks,
Guy

          Followup:

tri valued logic.

it is UNKNOWN whether null = 'G' or null <> 'G' is true.

      Rating: 4
Why different
      from SQL ?  November 06, 2002
Reviewer:
      robert  from PA
>> '' when assigned to a char(1) becomes ' ' (char types
>> are blank padded strings).

Tom why is this behavior true in PL/SQL but NOT in SQL ?
that is, why did the designers of PL/SQL take a diff , (non-ANSI ?)  approach
here ?

SQL> desc  rc_test
Name Type
---- -----------
A    CHAR(1)
B    CHAR(3)
C    VARCHAR2(3)

SQL> insert into rc_test values ('', 'A', 'A');

1 row inserted

SQL> select * from rc_test where a is null;

A B   C
- --- ---
  A   A

thanks

          Followup:

don't know

      Rating:
      3
Confused  November 06, 2002
Reviewer:
      Doug  from CT, USA
The original poster refers to some 9i migration documentation that says "Strings
of zero length are not equivalent to null", yet your explanation regarding
char's and varchars indicates the opposite behavior for varchars all the way
back to version 7.  So is the migration documentation not really referring to
anything new? or it is wrong, or what?

          Followup:

the docs are telling you want ansi says vs what we do.

      Rating: 3
zero length
      CHAR field  November 06, 2002
Reviewer:
      Gary  from Sydney, Aus
Regarding

>SQL> insert into rc_test values ('', 'A', 'A');
>1 row inserted
>SQL> select * from rc_test where a is null;
>A B   C
>- --- ---
>  A   A

In 8.1.7.4, if you do a DBMS_SQL.DESCRIBE of "select '', 'A', 'A' from dual",
you'll see the first column is a CHAR field of length 0, whereas the other two
are length 1.
I guess that it's being treated as a null CHAR(0) field at that stage, and then
gets converted to a null CHAR(1) field for the insert. Just because the user is
trying to insert it into a CHAR(1) field, doesn't mean it starts out being a
CHAR(1).

      Rating: 4
different
      effect in SQL Server  November 06, 2002
Reviewer:
      A reader
In SQL Server, null is absolutely null and empty string is absolutely emtry
string.  '' is nevel equal to null

Does it mean SQL Server and Oracle treats null and empty string differently?

          Followup:

among zillions of other differences - in a word "yes"

      Rating: 5
Can we make
      Oracle return zero length string instead of NULL  November
      14, 2002
Reviewer:  Arun Gupta  from Harrisburg, PA
      USA
Tom
Is it possible somehow to make Oracle return a zero length string instead of
NULL? We have lot of vb/asp code already written where programmers didn't check
for NULL. It seems that vb/asp doesn't handle NULL. It crashes if we assign NULL
values to variables and try to do some string operation. We have two options:
a) Modify vb/asp code (difficult, lot of code to change).
b) Make Oracle return zero length string instead of NULL (easy since only code
within packages need to be changed).

We are on Oracle 8.1.7.

Thanks...

          Followup:

there is no zero length string.  you'd have to return a 1 character length
string

      Rating: 5
there is no
      zero length string  September 11, 2003
Reviewer:
      A reader
Right. In a recent mysql to oracle intergration I found the NULL in mysql became
oracle NULL (no problme there), and the "empty string" in mysql became ASCII
code 0 in Oracle, which is a one char string (not null). ASCII code 0 represents
the "NUL" character, and can be entered using oracle's CHR() function [ chr(0)
]. The entered value can be tested with ascii() function (i.e. select * from tab
where ascii(col) = 0).

November 14, 2002 Sreenivasa -- Thanks for the question regarding
      "Performnance ", version 8.1.7

      You Asked
Hi Tom,

I have query like below

select 'x'
        from uda_item_ff
       where item = i_key_value
          or exists (select item
                       from item_master
                      where (item_parent = i_key_value
                         or item_grandparent = i_key_value)
                        and item_master.item = uda_item_ff.item)
         for update nowait;

uda_item_ff will have very few records may be in thousands

item_master will have records in millions

My question is  Am i better off to use EXISTS in this case or IN is good..client
was saying the above query is good when it was reformatted with IN clause,can
you suggest me why it was good with IN, but not with EXISTS

Thanks in Advance
Sreeni

      and we said...
It depends on the existence of indexes -- how many rows the IN would return vs
how many rows the outer query would have to execute the inner query.

If the exists had to be executed 1,000 times and it took say 0.05 seconds to
execute the exists -- it could take 50 seconds to run!

Now, say the query

select item from item_master where item_parent = i_key_vlaue or item_grandparent
= i_key_value

took 0.5 seconds to execute and find all of the rows -- the IN clause would
probably be MUCH faster.

It is pure math -- where exists is very good when the outer query will execute
the inner query few times AND the inner query would return a very large result
set if we used an IN.

in is good if the outer query would have executed the inner query LOTS or if the
inner query would return tons of data in the IN clause.

so maybe

where item in ( select i_key_value from dual
                union all
                select item from item_master where item_parent = i_key_value
                  or item_grandparent = i_key_value )

would be best.

Review &
      Followup

      Rating:
      5
deciding  November 13, 2002
Reviewer:
      A reader  from Austin, TX
In making a decision between IN and WHERE EXISTS, would you be sure to trace the
statement both ways or would you be inclined to rely on the "pure math"?

          Followup:

I would probably use IN and if I needed to tune, look at where exists.

IN is more intuitive for most people.

      Rating: 4
A quirky
      question...  November 14, 2002
Reviewer:
      Kashif  from Houston, TX
Hi Tom,

This might not be the most challenging (!) question you've come across, but
let's say that I have the choice of doing:

1)
...
where t1.id IN (select id
                  from t2
                 where t1.id = t2.id)

versus

2)
...
where EXISTS (select null
                from t2
               where t1.id = t2.id)

The quirk is that I'm using the IN operator with the correlated join (t1.id =
t2.id) in the first query. There's a composite index on t2.id which causes a
range scan, and t2 is around 50K rows. The outer query would produce around 1000
rows. I've run some tests and my results show that the IN (first) version of the
where clause runs a bit faster (24 secs vs 27 secs for my query). Could there be
an explanation for that, or am I splitting hairs? I would have thought logically
that the EXISTS would run faster (less code to evaluate, I guess). Also, the
subquery gets executed for each row in the outer query in both cases, and can at
most produce one row, so that confuses me a little more. Could it be that the IN
operator was designed to handle such subqueries better or should I just stick to
asking questions and stop philosophizing ;) ? Thanks.

Kashif

P.S. I got your book yesterday (finally), and now I can't wait to read it cover
to cover. Do you have a discussion on this IN vs EXISTS issue in it? Couldn't
find it, though maybe that's more appropriate for your next book...

          Followup:

I would put the diffs down to "dumb luck" -- without seeing a tkprof or knowing
what optimizer you used or anything else.  Here is an example:

big_table@ORA920.LOCALHOST> create table t as select id from big_table;

big_table@ORA920.LOCALHOST> analyze table t compute statistics;

big_table@ORA920.LOCALHOST> alter session set sql_trace=true;

big_table@ORA920.LOCALHOST> set autotrace traceonly

big_table@ORA920.LOCALHOST> select * from t where t.id in ( select big_table.id
from big_table where big_table.id = t.id );

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1711 Card=1 Bytes=8)
   1    0   HASH JOIN (Cost=1711 Card=1 Bytes=8)
   2    1     INDEX (FAST FULL SCAN) OF 'BIG_TABLE_PK' (UNIQUE) (Cost=202
Card=1000000 Byt
          es=4000000)

   3    1     TABLE ACCESS (FULL) OF 'T' (Cost=151 Card=1000000 Bytes=4000000)

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       3630  consistent gets
      10336  physical reads
          0  redo size
   14246869  bytes sent via SQL*Net to client
     733825  bytes received via SQL*Net from client
      66668  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
    1000000  rows processed

big_table@ORA920.LOCALHOST> select * from t where EXISTS ( select NULL from
big_table where big_table.id = t.id );

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1711 Card=1000000
Bytes=8000000)
   1    0   HASH JOIN (SEMI) (Cost=1711 Card=1000000 Bytes=8000000)
   2    1     TABLE ACCESS (FULL) OF 'T' (Cost=151 Card=1000000 Bytes=4000000)
   3    1     INDEX (FAST FULL SCAN) OF 'BIG_TABLE_PK' (UNIQUE) (Cost=202
Card=1000000 Byt
          es=4000000)

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       3630  consistent gets
       6751  physical reads
          0  redo size
   14246869  bytes sent via SQL*Net to client
     733825  bytes received via SQL*Net from client
      66668  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
    1000000  rows processed

big_table@ORA920.LOCALHOST> set autotrace off

Here -- the plans were very very similar (almost the same) but the consistent
gets were identical -- they ran the same.

I don't deal with this in Expert one on one -- but it may well be a topic in the
next one ;)

November 14, 2002 Ik -- Thanks for the question regarding "Query
      Idea", version 8.1.7

      You Asked
Tom,
   I have a requirement.

I need to get the SUM of num for all rows having the value of column b equal to
the value of column a.

For eg:- The first row has column a is 'A' and the requirement is to get
SUM(num) for all the rows which have column b = 'A'. In this case it is 5+7 =12.

And similarly for the second row, it should be 4+3=7

a    b   num
---------------
'A'  'B'  4
'B'  'V'  2
'C'  'N'  1
'D'  'A'  5
'E'  'A'  7
'F'  'B'  3
'A'  'C'  2

regards

      and we said...
select a, (select sum(num) from t t2 where t2.b = t.a ) sum_num
  from t;

is one way

select t1.a, t2.sum_num
   from t t1,
        (select b, sum(num) sum_num from t t2 group by b ) t2
  where t1.a = t2.b;

is another....
Review & Followup

      Rating: 3
Not exactly
        November 13, 2002
Reviewer:  Ik  from
      BG
Thanks Tom for the speedy response. Thought you were away for the seminar.

Iam sorry..should have put the question straight.

In the real scenario, the table t results from a table join of 5 tables and all
these 3 columns happen to be in different tables. And, the query results in
minimum 3K records. This is jsut a portion of the whole query and thus i was
looking at the most inexpensive way to deal with this. Assuming that t was the
table and a,b and num are its columns, can i
SELECT a,b,num, other_value from t?

other_value could be some sort of analytic function...

or even...SELECT a,b,num, other_value FROM (SELECT...FROM t)

I tried everything ... even doing a SUM(num) OVER and having RANGE
PRECEDING/FOLLOWING. I used dbms_utility.get_hash_value to get numeric value of
columns a and b. It didnt get me anywhere.

Taking the case of table t. I should query the table only once and from that
resultset i should be able to build up other_value. I want to avoid hitting the
table again. Its only because this query would get drastically slower.

I know its wierd...but before giving up i thought i should get an expert opinion
also.

          Followup:

test case -- give me create tables, some inserts and the query that needs to
have this result... I'll take a look at that.

      Rating:
      3
  November 14, 2002
Reviewer:  Ik
       from BG
Tom,
   Thanks again.

Providing actual test data is nearly impossible because of its volume. Would
this suffice?

This is what i was trying with. I tried with this as an inline view.

I need it to be SELECT a,b,num, (result) from test_tab;
test_tab should not be queries twice.

PS:- test_tab is the resultset from a join of multiple tables.

create table test_tab as
select a, b, num from
(select 'A' a, 'B' b, 5 num from dual
UNION ALL
select 'A' a, 'F' b, 9 num from dual
UNION ALL
select 'B' a, 'C' b, 1 num from dual
UNION ALL
select 'C' a, 'A' b, 7 num from dual
UNION ALL
select 'D' a, 'A' b, 8 num from dual
UNION ALL
select 'E' a, 'Z' b, 3 num from dual
UNION ALL
select 'F' a, 'Z' b, 2 num from dual
UNION ALL
select 'G' a, 'Z' b, 4 num from dual)

regards

          Followup:

In 9i -- with the subquery factoring -- yes.

prior to that -- no.

If I don't know how you actually join the tables and such -- I cannot say
anything better then that.

(and you don't need tons of data -- just a couple of rows sufficies -- i do most
of my examples on EMP after all)

November 15, 2002 lisa -- Thanks for the question regarding "Update
      partial data in a varchar2(1000) field", version 817

      You Asked
I have a table contains many columns, one of the column datatype is
varchar2(1000), I need to do a global change of data from "ASME BPE-97"
to "ASME BPE" in the varchar2(1000) field, could you show me how to write this
script?
Each record is unique with primary key on part no.
the column need to update from ASME BPE-97 to ASME BPE looks like this:

 LONG_DESCRIP
---------------------------------------------------------------------------------
--------------------------------------------------
"Tubing Smls SS ASTM A270 TP316L Manuf to  ASME BPE-97 Max 15 Ra Electropolish
ID, 30 Ra Mech Polish OD"
"90 Deg Bend Sanitary Tube Fitting Orbital Butt Weld Ends 316L SS Dims to ASME
BPE-97  Max 15 Ra Electropolish ID, 30 Ra Mech Polish OD"
"45 Deg Bend Sanitary Tube Fitting Orbital Butt Weld Ends 316L SS Dims to ASME
BPE-97 Max 15 Ra Electropolish ID, 30 Ra Mech Polish OD"
"Equal Tee Sanitary Tube Fitting Orbital Butt Weld Ends 316L SS Dims to ASME
BPE-97 Max 15 Ra Electropolish ID, 30 Ra Mech Polish OD"
"Equal Cross Sanitary Tube Fitting Orbital Butt Weld Ends 316L SS Dims to ASME
BPE-97 Max 15 Ra Electropolish ID, 30 Ra Mech Polish OD"
"Reducing Tee Sanitary Tube Fitting Orbital Butt Weld Ends 316L SS Dims to ASME
BPE-97 Max 15 Ra Electropolish ID, 30 Ra Mech Polish OD"
"Concentric Reducer Sanitary Tube Fitting Orbital Butt Weld Ends 316L SS Dims to
ASME BPE-97 Max 15 Ra Electropolish ID, 30 Ra Mech Polish OD"
"Reducer Eccentric Sanitary Tube Fitting Orbital BW Ends 316L SS Dims to ASME
BPE-97 Max 15 Ra Electropolish ID, 30 Ra Mech Polish OD"
"Elbow 90 Deg Sanitary Tube Fitting Orbital BW x Clamp Ends 316L SS Dims to ASME
BPE-97 Max 15 Ra Electropolish ID, 30 Ra Mech Polish OD"
"Elbow 45 Deg Sanitary Tube Fitting Orbital BW x Clamp Ends 316L SS Dims to ASME
BPE-97 Max 15 Ra Electropolish ID, 30 Ra Mech Polish OD"
"Tee Short Outlet Reducing Sanitary Tube Fitting Orbital BW x Orbital BW x Clamp
Outlet 316L SS Dims to ASME BPE-97 Max 15 Ra Electropolish ID, 30 Ra Mech Polish
OD"
"Tee Short Outlet Equal Sanitary Tube Fitting Orbital BW x Orbital BW x Clamp
Outlet 316L SS Dims to ASME BPE-97 Max 15 Ra Electropolish ID, 30 Ra Mech Polish
OD"

      and we said...
update t
   set col = replace( col, 'ASME BPE-97', 'ASME BPE' )
 where col like '%ASME BPE-97%';

ops$tkyte@ORA920.LOCALHOST> insert into t values ( 'Tubing Smls SS ASTM A270
TP316L Manuf to  ASME BPE-97 Max 15 Ra Electropolish ID, 30 Ra Mech Polish OD');

1 row created.

ops$tkyte@ORA920.LOCALHOST> insert into t values ( '90 Deg Bend Sanitary Tube
Fitting Orbital Butt Weld Ends 316L SS Dims to ASME BPE-97  Max 15 Ra
Electropolish ID, 30 Ra Mech Polish OD');

1 row created.

ops$tkyte@ORA920.LOCALHOST> insert into t values ( '45 Deg Bend Sanitary Tube
Fitting Orbital Butt Weld Ends 316L SS Dims to ASME BPE-97 Max 15 Ra
Electropolish ID, 30 Ra Mech Polish OD');

1 row created.

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> update t
  2     set col = replace( col, 'ASME BPE-97', 'ASME BPE' )
  3   where col like '%ASME BPE-97%';

3 rows updated.

ops$tkyte@ORA920.LOCALHOST>
ops$tkyte@ORA920.LOCALHOST> select * from t;

COL
---------------------------------------------------------------------------------
-------------------
Tubing Smls SS ASTM A270 TP316L Manuf to  ASME BPE Max 15 Ra Electropolish ID,
30 Ra Mech Polish OD
90 Deg Bend Sanitary Tube Fitting Orbital Butt Weld Ends 316L SS Dims to ASME
BPE  Max 15 Ra Electro
polish ID, 30 Ra Mech Polish OD

45 Deg Bend Sanitary Tube Fitting Orbital Butt Weld Ends 316L SS Dims to ASME
BPE Max 15 Ra Electrop
olish ID, 30 Ra Mech Polish OD

Review &
      Followup

      Rating: 5
Slight
      variation  November 14, 2002
Reviewer:  SB
       from CA
Hi Tom,

I have similar situation except that the word being replaced as in the above
case "ASME BPE-97" can be in mixed case or upper case. I tried to use upper
before replace but it is updating the entire column in upper case. Any
suggestions?

Thankx.

          Followup:

more details -- i doubt you really have ASME BPE-97 right?  give me the
specifics of your situation and i'll tell you what I think might be best.  It is
not as straight forward of course.

      Rating: 4
Assistance
      please  November 14, 2002
Reviewer:  Rory
       from Ph
Hi Tom,

     I like your solution. And your syntax is new to me. I tried to do it but I
had an error. Here it is. Thanks.

spawn@point@CRM_DEVT> create table t
  2  (name varchar2(100));

Table created.

spawn@point@CRM_DEVT> insert into t values ('I am Rory');

1 row created.

spawn@point@CRM_DEVT> insert into t values ('the am Rory');

1 row created.

spawn@point@CRM_DEVT> commit;

Commit complete.

spawn@point@CRM_DEVT> select * from t;

NAME
-------------------------------------------------------------------
I am Rory
the am Rory

spawn@point@CRM_DEVT> update t
  2  set col = replace(col,'REMIL','Rory')
  3  where col like '%Rory%';
where col like '%Rory%'
      *
ERROR at line 3:
ORA-00904: invalid column name

spawn@point@CRM_DEVT> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle8i Enterprise Edition Release 8.1.7.0.0 - 64bit Production
PL/SQL Release 8.1.7.0.0 - Production
CORE    8.1.7.0.0       Production
TNS for IBM/AIX RISC System/6000: Version 8.1.7.0.0 - Developmen
NLSRTL Version 3.4.1.0.0 - Production

          Followup:

umm, well -- you don't have a column named col -- yours is called NAME.  try
that instead.

      Rating: 5
Mixed
      case  November 14, 2002
Reviewer:  SB
       from CA
Hi Tom,

My requirement is as follows:
create table t (descr  varchar2(1000));

insert into t values ('how now brown cow');
insert into t values ('how now brown Cow');
insert into t values ('how now brown COW');

select * from t;

DESCR
-----------------------------------
how now brown cow
how now brown Cow
how now brown COW

Now I want to replace cow(any case) with dog.
I tried the following query:
update t set descr = replace(upper(descr), 'COW', 'dog');
3 rows updated.
select * from t;
DESCR
-------------------
HOW NOW BROWN dog
HOW NOW BROWN dog
HOW NOW BROWN dog

I want the output as :

how now brown dog
how now brown dog
how now brown dog

Thankx for your time, Tom.

          Followup:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace
  2    function iREPLACE(SRCSTR VARCHAR2 ,
  3                      OLDSUB VARCHAR2 ,
  4                      NEWSUB VARCHAR2 ) return VARCHAR2 is
  5       brk binary_integer;
  6    begin
  7      if srcstr is null then return srcstr; -- NULL
  8      else
  9          brk := instr(upper(srcstr), upper(oldsub));
 10          if brk > 0
 11          then
 12            return(substr(srcstr, 1, brk - 1) || newsub
 13                   || replace(substr(srcstr,
 14                                      brk + length(oldsub)),
 15                              oldsub, newsub));
 16          else return srcstr;
 17          end if;
 18      end if;
 19    end;
 20  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

DESCR
----------------------------------------
how now brown cow
How now brown Cow
How Now Brown COW

ops$tkyte@ORA817DEV.US.ORACLE.COM> update t set descr = ireplace( descr, 'COW',
'dog');

3 rows updated.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

DESCR
----------------------------------------
how now brown dog
How now brown dog
How Now Brown dog

ops$tkyte@ORA817DEV.US.ORACLE.COM>

      Rating: 4
To
      SB  November 15, 2002
Reviewer:  Michel
      Cadot  from France
Hello All,

create table t (descr  varchar2(1000));
insert into t values ('how now brown cow');
insert into t values ('how now brown Cow');
insert into t values ('how now brown COW');
select * from t;
DESCR
-----------------
how now brown cow
how now brown Cow
how now brown COW

3 ligne(s) sélectionnée(s).

update t set descr = lower(replace(upper(descr), 'COW', 'dog'));

3 ligne(s) mise(s) à jour.

select * from t;
DESCR
-----------------
how now brown dog
how now brown dog
how now brown dog

3 ligne(s) sélectionnée(s).

      Rating: 4
Write your own
      case insensitive replace function  November 15, 2002

Reviewer:  Mike  from Portland, OR
SQL> CREATE OR REPLACE FUNCTION Replacei(SourceStr VARCHAR2,
  2  Findstr VARCHAR2, Replstr VARCHAR2) return VARCHAR2 AS
  3  retval VARCHAR2(4000);
  4  nAt NUMBER;
  5  BEGIN
  6      retval := SourceStr;
  7      nAt := Instr(Upper(SourceStr),Upper(FindStr));
  8      IF nAt > 0 THEN
  9          retval := Substr(SourceStr,1,nAt-1)||
 10          Replstr||Substr(SourceStr,nAt+Length(Findstr));
 11      END IF;
 12      Return retval;
 13  END;
 14  /

Function created.

SQL>
SQL> drop table t;

Table dropped.

SQL> create table t (descr  varchar2(1000));

Table created.

SQL>
SQL> insert into t values ('How now brown cow');

1 row created.

SQL> insert into t values ('How now brown Cow');

1 row created.

SQL> insert into t values ('How now brown COW');

1 row created.

SQL>
SQL> select * from t;

DESCR
------------------------------------------------------------

How now brown cow
How now brown Cow
How now brown COW

SQL>
SQL> update t set descr = replacei(descr, 'cow', 'dog');

3 rows updated.

SQL>
SQL> select * from t;

DESCR
------------------------------------------------------------

How now brown dog
How now brown dog
How now brown dog

November 15, 2002 Joshua -- Thanks for the question regarding
      "Conversion/Extraction of Longs ", version Oracle 8.1.7

      You Asked
I have a long datatype that contains only Varchar2 characters. I need to extract
only sections of the long datatype rather than retrieving the whole attribute.
THe lentghs and content i am trying to extract are variable.

Is there a way of doing this short of writing the long to file and then
reloading the file using a 3gl ? ( There is a string pattern that can be used to
identify the start and end of the content required )

Thanks in advance
Josh

      and we said...
Well, in 8.0 and up you should not use LONGS and LONG RAWS, the data should be
converted to CLOBS and BLOBS.  Then you would have dbms_lob.instr,
dbms_lob.substr and so on to do this easily.

You can conver longs to clobs in 8i using the built in TO_LOB() sql function.
You would use this to permanently convert the longs to clobs.

If you must keep it as a long for whatever reason, the following might be of
some help.  With LONGS (but not LONG RAWS) we can get piecewise READ ONLY access
to the long via dbms_sql.  Here is an exmaple that will get the substr of any
long from any table.  What I am expecting here is a query that takes on bind
variable to identify the row.  In a normal table -- ROWID would work best.  For
this, I'm using a view so I have to use the "primary key" of the view instead:

sys@TKYTE816> create or replace function longsubstr( p_query   in varchar2,
  2                                          p_bind      in varchar2,
  3                                          p_from_byte in number,
  4                                          p_for_bytes in number )
  5  return varchar2
  6  as
  7      l_cursor    integer default dbms_sql.open_cursor;
  8      l_long_val  long;
  9      l_buflen    integer;
 10      l_ignore    number;
 11  begin
 12      dbms_sql.parse( l_cursor,
 13                      p_query,
 14                      dbms_sql.native );
 15
 16      dbms_sql.bind_variable( l_cursor, ':bv', p_bind );
 17
 18      dbms_sql.define_column_long(l_cursor, 1);
 19      l_ignore := dbms_sql.execute(l_cursor);
 20
 21      if (dbms_sql.fetch_rows(l_cursor)>0)
 22      then
 23           dbms_sql.column_value_long
 24           ( c => l_cursor,
 25             position => 1,
 26             length   => p_for_bytes,
 27             offset   => p_from_byte,
 28             value    => l_long_val,
 29             value_length=> l_buflen );
 30     end if;
 31
 32     return l_long_val;
 33  exception
 34     when others then
 35        if dbms_sql.is_open(l_cursor) then
 36           dbms_sql.close_cursor(l_cursor);
 37        end if;
 38        raise;
 39  end longsubstr;
 40  /

Function created.

sys@TKYTE816>
sys@TKYTE816>
sys@TKYTE816> begin
  2      for x in ( select view_name, text_length
  3                   from user_views
  4                  where text_length = ( select max(text_length)
  5                                          from all_views )
  6               )
  7      loop
  8          dbms_output.put_line( 'Last 100 bytes of view ' || x.view_name );
  9          dbms_output.put_line
 10          ( longsubstr
 11            ( 'select text from user_views where view_name = :bv',
 12              x.view_name,
 13              greatest( x.text_length-100, 0 ),
 14              100 )
 15          );
 16      end loop;
 17  end;
 18  /
Last 100 bytes of view STRADDLING_TS_OBJECTS
and  bitand(t.property,512)!=0
  and  t.bobj# = i.bo#
  and  ip.bo#= i.obj#
  and  ip.ts# != tp.ts#

PL/SQL procedure successfully completed.

That can return any 32k substr of the long.  Search this site for SHOW_LONG for
another example using this functionality.

Review &
      Followup

      Rating: 4
Works -- but
      why?  May 07, 2002
Reviewer:  Vladimir
      Andreev  from Germany
This is definitely a way around the LONG columns. But that does NOT alter the
fact that Oracle keeps LONG columns in the dictionary even while strongly
encouraging developers to migrate to LOBs.
I could understand keeping the datatype unchanged for the old columns (like
USER_VIEWS.Text), but why defining NEW dictionary columns (like
USER_IND_EXPRESSIONS.COLUMN_EXPRESSION) of the LONG datatype beats me. That
converts otherwise trivial tasks into nightmare -- try to get the equivalent of
user_ind_columns with real column names when some of them are descending, for
example. Or try to automate range partition maintenance operations based on the
high values and not names, and you'll se what I mean.

      Rating: 5
USER_VIEWS
      column TEXT is of datatype LONG --> VARCHAR2  November
      14, 2002
Reviewer:  Alex Hendriks  from Netherlands

Thanks! This really helped me a lot. I implemmented it in a meta-table system we
use here.

I wonder if it is also possible to use these longs to varchar2 conversion with
execute_emmediate instead of dbms_sql?

          Followup:

nope

      Rating:
      5
  November 15, 2002
Reviewer:
      A reader
Hi Tom,

 Since LONG data type is depricated in oracle why oracle has long datatype is
user_views  views(In oracle 9.2)???

SQL> desc user_views;
 Name                                      Null?    Type
 ----------------------------------------- -------- --------------------
 VIEW_NAME                                 NOT NULL VARCHAR2(30)
 TEXT_LENGTH                                        NUMBER
 TEXT                                               LONG <<------
 TYPE_TEXT_LENGTH                                   NUMBER
 TYPE_TEXT                                          VARCHAR2(4000)
 OID_TEXT_LENGTH                                    NUMBER
 OID_TEXT                                           VARCHAR2(4000)
 VIEW_TYPE_OWNER                                    VARCHAR2(30)
 VIEW_TYPE                                          VARCHAR2(30)
 SUPERVIEW_NAME                                     VARCHAR2(30)

          Followup:

backwards compatibility for now.

      Rating:
      3
  November 15, 2002
Reviewer:
      A reader
In oracle 9i R2

SQL> select count(*) from  all_tab_columns where DATA_TYPE      = 'LONG';

  COUNT(*)
----------
        99

Still oracle is using LONG. Strange !!!!!

November 15, 2002 Sam -- Thanks for the question regarding
      "Login.sql and partitions nad & ,&&", version 8.1.7

      You Asked
Tom

1.Can you give me the the link to your login.sql script. I want to display the
instance name and user name instead of sql>  in  my plus.
( the login.sql on your website prompts to enter a value when I login, that
doesnt work for me).
What is the difference between glogin and login.sql.

Can I change the color and font in sqlplusw?

2.A table t has partitions with partition bounds
values less than 16-nov-01  and
values less than max value

Now if I want to split max value and add partition 15-nov-01, Icannot , I need
to split partition with values less than 16-nov01.

Why is it so. What are the internal reasons for this kind of functinality.

Woudnt it be good, if I have the ability to split the max value partition and
add any date range I want, and oracle takes care of where it should put it.

3.Lastly tom, this is silly, but can you give a small example and explain the
difference between using & and && .

Thank you

      and we said...
1) you apparently have my login.sql -- if it is prompting you, change it to:

define gname=NO_ACCESS_TO_GLOBAL_NAME
column global_name new_value gname
...

(add the bold text).  That will set the prompt to NO_...NAME  when you cannot
get to the global_name table for whatever reason.

see

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/win.817/a82954/apa.htm#423093
2) Have you looked at your table and thought about this?

You have partition "a" with values LESS then 16-nov-2001.  That means data such
as:

01-nov-2001
02-nov-2001
03-nov-2001
...
15-nov-2001

are all in there (you might not have any data in there, but the fact remains --
that data CAN be in there, nothing preventing that data from being there).

Hence, if you want to add a partition that has the vales less than 15-nov-2001
-- there is exactly ONE partition that can be split to do that. You have to
split the partition "a" that might contain values that will be part of this new
partition.  It would make no sense whatsoever to split the maxvalue partition
here -- there may be data in partition "a" that would need to go into this newly
split one.

The "internal reason" is one of data consistency and integrity.  There may well
be data in partition "a" that needs to be in this newly split partition.  Hence
you must split that partition and rewrite the data it contains.

If you think about it -- this makes 100% perfect, logical sense and is in fact
the only way to do this.
3)

ops$tkyte@ORA9I.WORLD> set echo on
ops$tkyte@ORA9I.WORLD> @test
ops$tkyte@ORA9I.WORLD> prompt &x
Enter value for x: hello
hello
ops$tkyte@ORA9I.WORLD> prompt &x
Enter value for x: hello
hello
ops$tkyte@ORA9I.WORLD> prompt &&x
Enter value for x: hello
hello
ops$tkyte@ORA9I.WORLD> prompt &&x
hello
ops$tkyte@ORA9I.WORLD> prompt &x
hello

The && says -- just use this value for X without asking for it again.  Note how
the first three &x, &x, &&x all asked me to enter a value for X but after we hit
&&x -- it stopped doing that and just used whatever was there.

Review &
      Followup

      Rating: 4
CONDITIONAL
      LOGIN.SQL  March 11, 2002
Reviewer:  Ravi
      Kumar  from Chicago, IL
Hi Tom,

If I want the settings to be different for different servers I login, what
should I do.

For example, If I am connected to Dev and Test servers, I would like to set time
on and also serveroutput on. But if I am on Prod server I don't want these.

Thanks,
Ravi.

          Followup:

create a script called DEV.sql, one called TEST.sql

then:

column foo new_value x
select global_name FOO from global_name;  -- run some query that returns TEST
                                          -- when in test and DEV when in dev

@&foo

that'll run either dev.sql or test.sql

      Rating: 5
Thanks a
      lot!!  March 12, 2002
Reviewer:
      A reader

      Rating: 5
small
      modification ;)  March 12, 2002
Reviewer:  Ravi
      Kumar  from Chicago, IL
instead of

@&foo

we need to write

@&x

          Followup:

thats right -- thanks...  (should remember to run these things instead of just
typing sometimes ;)

      Rating:
      4
Glogin.sql  March 13, 2002
Reviewer:
      Alexandre Matthes  from Rio de Janeiro - Brasil
Hi tom,

I´ve added these commands bellow into glogin.sql file.

set termout off
define new_prompt='nolog'
column value new_value new_prompt
select username || '@' ||
substr(global_name,1,DECODE(instr(global_name,'.'),0,LENGTH(global_name),instr(gl
obal_name,'.')-1)) || ':' || host_name
value from  global_name, gv$instance,user_users;
set sqlprompt "&new_prompt> "
set termout on

Every time I log , sqlplus prompt shows me:

SYSTEM@DBTNET:DADOS1>

That´s Ok, but this only works for users who have access to those three views on
the select statement above, if I log in as a "merely mortal" user, the prompt
is:

nolog>

1) I´d like to know if there is a way to bypass this situation WITHOUT granting
select on those views to these simple users?

2) If I use the connect command on the same sqlplus screen to connect another
user into another database, the sqlplus prompt stays the same way... is there a
way to change the prompt "dinamically" when using this connect command on the
same sqlplus window?

Thanks in advance and congratulations for this outstanding site.

          Followup:

create or replave view system_name
as
select username || '@' ||
substr(global_name,1,DECODE(instr(global_name,'.'),0,LENGTH(global_name),instr(gl

obal_name,'.')-1)) || ':' || host_name
value from  global_name, gv$instance,user_users;

run that as some user who has DIRECT select on global_name, gv$instance,
user_users.  grant select on that to public.

i have a script connect.sql that contains

login &1
@login

and have trained myself to use

SQL> @connect scott/tiger

instead of connect scott/tiger.

      Rating: 5
why global_name
      , why not v$database or v$instance  June 25, 2002

Reviewer:  bole taro dolbaje
Tom

why should I select the instance name from global_name , why cant I select it
from v$database or v$instance.

Actually what is the purpose of global_name? is it there to just help us know
the instance we are connected to ...

Kindly explain

          Followup:

go for it.  I myself like to use the global_database name cause

a) i use global_names = true, my databases are uniquely named by their global
name
b) i don't use the sid, i use the global name to id what database I'm in.

global_name is supposed to be a globally unique name for a database in a given
set of related but distributed databases.  They could ALL have the same sid (and
some of mine do!) but since they are on different machines and I've named them
differently (with alter database rename global_name ...) -- that makes it better
for me.

      Rating: 5
Way to accept
      "hidden" password?  June 26, 2002
Reviewer:
      Dennis  from Missouri, USA
Tom,

This is good stuff.  I always had my glogin doing the time and elapsed time, and
feared putting the SID in there precisely for the connect to another instance
showing the same SID.  Now that you have a way to do it, I'm trying to duplicate
it.  If I could borrow your mind for a little while, then I'd be set :)

I tried the connect.sql with the login $1 and @login.sql, and it had errors.  It
seems like since I am using glogin that I would need to call glogin (in fact I
don't have a login.sql - do I need to make one?).  Now, I've changed connect.sql
to be:
connect &id/&pass@&sid
@D:\ora816\sqlplus\admin\glogin.sql

But when I run it, everything is of course visible:
Enter value for id: scott
Enter value for pass: tiger
Enter value for sid: hii2

Is there a way I can hide or "mask" the password so that it is either not
visible, or just a bunch of stars?  I tried turning termout off, but all that
did was hide the prompts, it was up to me to figure out that the blinking cursor
on the next line down wanted me to type something (ok so it didn't take a lot of
time for that), but what I typed was still visible.
07:03:47 SCOTT@HII2:sca02> @connect
scott
tiger
his2

Obviously the first approach is better as far as user interfaces go (but hey if
you know the program, you don't need bells and whistles!), but I would still
like to be able to hide the password.  I usually wind up with a developer in my
cube and me logging onto an instance with either my id or an id that they
shouldn't be using, and I think it may be disruptive if I keep yelling Fire or
something to make them look away so I could login and clear screen (ok, I would
add clear screen to the script to save time :) ).   Although I probably would
have more fun with that approach...

Thanks Tom, soon we'll all be Tom wannabes...wait a sec...I think we already
are!!!

          Followup:

ops$tkyte@ORA817DEV.US.ORACLE.COM> accept pass prompt Password hide
Password
ops$tkyte@ORA817DEV.US.ORACLE.COM> select '&pass' from dual;
old   1: select '&pass' from dual
new   1: select 'abc' from dual

'AB
---
abc

Instead of letting us just ask you for &pass -- use an ACCEPT before the CONNECT
to prompt you for the password  and use the HIDE option of the accept command.

      Rating: 5
The accept is
      killing me  June 26, 2002
Reviewer:  Dennis
       from Missouri, USA
I cannot make my accept work like yours:

07:56:12 SCOTT@HII2:sca02> accept pass prompt password hide pass
SP2-0003: Ill-formed ACCEPT command starting as pass

What am I doing wrong?

I am assuming I would have to get all three variables, and then use a connect
after gathering the details?

      Rating:
      5
gah  June 26, 2002
Reviewer:  Dennis
       from Missouri, USA
Ok, I figured out the accept thing (no pass after hide).

Should have spent more time on it...sorry.

I couldn't connect to the database at first.  I had changed my connect statement
to be id/pass@sid, so it wouldn't prompt (since I had the & there).  I should
have realized that this isn't AIX (I was thinking of $, but decided maybe name
would work).  Anyway, I had to use && to solve it.  It works great now.

accept id prompt userid:
accept pass prompt Password:   hide
accept sid prompt sid:
connect &&id/&&pass@&&sid
@D:\ora816\sqlplus\admin\glogin.sql

Thanks a lot for your help Tom.   Sorry a stupid syntax error is wasting bytes
on your server.

Dennis

      Rating:
      5
?  June 26, 2002
Reviewer:  bole taro
      dolbaje
"global_name is supposed to be a globally unique name for a database in a given
set of related but distributed databases.  They could ALL have the same sid (and

some of mine do!) but since they are on different machines and I've named them
differently (with alter database rename global_name ...) -- that makes it better

for me. "

1.Do you mean that global_name table will not get populated unless and untill
the global_names parameter is not set TO  yes in the init.ora.

2.Sid cannot be changed where as global_name can be changed using  'alter
database rename global_name' ?

3.Our dba says that we should not use global_name at all , but use only
V$database, and doesnt site any reasons. Is there any justification in this kind
of arguement.

4.'i don't use the sid, i use the global name to id what database I'm in.'

what difference does using sid over global_name or vice versa make..

      Rating: 3
remember to
      change GLOBAL_NAME when cloning a database  June 26, 2002

Reviewer:  andrew  from ca, usa
When you clone a database (by copying the datafiles) the GLOBAL_NAME retains the
name it had in the one it was cloned from. In my case, dynamically created shell
scripts use this value to specify where to connect to (sqlplus a/b@GLOBAL_NAME).
The production database almost got trashed by tests running on the cloned one.

      Rating: 3
Surprising
      problem  June 26, 2002
Reviewer:  Vishal
       from NY USA
When I add these lines of code in my glogin.sql
set termout off
define new_prompt='nolog'
column value new_value new_prompt
select username || '@' ||
substr(global_name,1,DECODE(instr(global_name,'.'),0,LENGTH(global_name),instr(gl

obal_name,'.')-1)) || ':' || host_name
value from  global_name, gv$instance,user_users;
set sqlprompt "&new_prompt> "
set termout on

My prompt comes like
&new_prompt>

Although the query runs fine.

          Followup:

use single quotes, not double.

sql sqlprompt '&new_prompt> '

      Rating: 1
Double quotes
      is not a problem something else is  June 27, 2002

Reviewer:  VISHAL  from NY
I am sorry for being less communicative on the first place. I had tried with
single quotes and even without quotes. It doesn't work. I tried on another
machine and "Double quotes" works fine. Let me ask this;; Can this be some
minor/major problem on SQLPLUS installation on that perticular machine where I
am not been able to set the sqlprompt with the help of a variable ????

          Followup:

then someone has issued

set define off

or set define on <someother character>

issue

set define on

and it'll be

o enabled
o set to &

      Rating: 5
Exactly That
      was the problem  June 27, 2002
Reviewer:
      VISHAL  from NY USA
It was the exact problem. Hats off to you.
Thanks

      Rating: 5
@connect sys as
      sysdba  November 15, 2002
Reviewer:  SB
       from OH
Hi Tom,
I got used to using @connect for quite a while now. But, when I use @connect
sys@test as sysdba, comes out with ORA-28009 error. Any workaround for this?

Thankx.

          Followup:

@connect "sys@test as sysdba"

      Rating:
      5
perfect  November 18, 2002
Reviewer:
      SB  from CA
Thankx,Tom

November 16, 2002 -- Thanks for the question regarding "Collate",
      version 8.1.7

      You Asked
Tom,

I have two questions:

I have a following table T with

SDATE    AMT

10/01/2002  10
10/01/2002  10
10/05/2002  30
10/08/2002  40
10/08/2002  10

The output which i require is

SDATE     AMT

10/01/2002   80  --> Total sum Amt where SDATE > CURRENT SDATE
10/05/2002   50
10/08/2002    -

I got the output using

select col1, col2 , (select sum(amt) where sdate > t1.sdate).....

but I was trying to use analytics for this kind. But could not get.
Can we solve this using analytical query? Further with Range between
and Rows between clause there is an option of "COLLATE" what is it and how is it
used?

I was trying to use COLLATE for SDATE but could not do it.

Q2)  I have a main table say T where pflag is one of the column.
Based on type of pflag I have to join to different tables to get different
result sets which have to unioned before i proceed with my main logic.

My main table T is a huge table, where i need to join with other
small tables to get other resultant columns. and then based on
pflag I have to join to 15 other big tables. How should i approach this kind of
queries?  I know in your book you have mentioned that
i cannot use temporary tables for this kind of operations...but
I was under the idea that initially I will get the result set of Table
"T" in to a temporary table which is indexed according to my query needs and
then I proceed with other big table joins. My idea is to have proper join order
to eliminate cartesian product between huge tables. What are the important
things I should consider before even i start coding?

Thanks and Regards,

      and we said...
q1) analytics rock:

ops$tkyte@ORA920.US.ORACLE.COM> select distinct
  2         sdate,
  3         sum(amt) over ( order by sdate DESC ) sum_before,
  4         sum(amt) over ( order by sdate DESC
  5                         range between 0 preceding
  6                                   and 0 following ) sum_same,
  7         sum(amt) over ( order by sdate DESC )
  8         - sum(amt) over ( order by sdate DESC
  9                           range between 0 preceding
 10                                     and 0 following ) amt
 11    from t
 12   order by sdate
 13  /

SDATE      SUM_BEFORE   SUM_SAME        AMT
---------- ---------- ---------- ----------
10/01/2002        100         20         80
10/05/2002         80         30         50
10/08/2002         50         50          0

ops$tkyte@ORA920.US.ORACLE.COM>

don't know what you meant by "trying to use collate"

q2) wow, sounds like a really bad design don't you think???

without a concrete example - i cannot help you.  For some queries of this ilk --
select (select) ... (what you did in q1) can help -- try that i suppose.

Review &
      Followup

      Rating:
      5
collate  November 16, 2002
Reviewer:
      A reader
{SUM|AVG|MAX|MIN|COUNT|STDDEV|VARIANCE|FIRST_VALUE|LAST_VALUE}
  ({<value expression1> | *}) OVER
    ([PARTITION BY <value expression2>[,...]]
      ORDER BY <value expression3> [collate clause>]
                [ASC| DESC] [NULLS FIRST | NULLS LAST] [,...]
    ROWS | RANGE
      {{UNBOUNDED PRECEDING | <value expression4> PRECEDING}
      | BETWEEN
          {UNBOUNDED PRECEDING | <value expression4> PRECEDING}
     AND{CURRENT ROW | <value expression4> FOLLOWING}}

I took the literal value of COLLATE and thought ORDER BY SDATE (collate the
sdate for duplicate dates and then apply my analytics... what is collate clause?

For my question - 2

Assignment Table (my Main Table)

Assignment_id number
Assignment_type varchar2(25)
dt_cr   date

... Other Columns

This is a huge table with over 5 million records per day and partitioned by
dt_cr no PK on Assignment_id

If Assignment_type='RJ' (Rejected samples)

select total_Charge, FEE, Percent_rate, {other columns from other small tables}
from Rejected_Assignments rej , Assignments Asig, {other small tables to get
Assigment descriptions }
where rej.Assignment_id = Asig.Assignment_id
{other conditions}

If Assigment type= 'PE' (partial Assignments)

select total_Charge, FEE, completed_rate, {other columns from other small
tables}
from partial_Assignments pas , Assignments Asig, {other small tables to get
Assigment descriptions }
where pas.Assignment_id = Asig.Assignment_id
{other complex conditions}

This is for different Assignment types....

Thanks and regards

          Followup:

don't know what they meant by collate in that context other then perhaps NLS
specific settings (nls_langs and such).  Anyway -- that question is answered
using straight analytics as shown.

As I said -- sounds like a bad design to me given the question you want to ask.
Either do a SELECT (*select*) using a DECODE( assignment_type, 'RJ', (select
...), 'PE', (select .... ) or a UNION all of two selects.  There is no such
thing as a conditional select.

November 16, 2002 Don -- Thanks for the question regarding
      "QUERY_STRING in the plsql cartridge", version

      You Asked
I'm using a web form and Oracle's PL/SQL
cartridge.  I'm passing a lot of data from
the from to a another stored procedure.
I want to get at the QUERY_STRING data
and extract what I have sent, but I don't
know the SQL syntax to get at this
system variable.  Can you help me out?
Thanx!

Don

      and we said...
The Query String is not ever sent to the PLSQL routine.
Parameters are passed to the PLSQL routine in one of two ways
only:

o As formal named parameters.  that is, you have a URL
http://host/plsql/foo?x=5&y=hello&z=10
 you
must have a procedure that looks like:

create procedure foo( x in <varchar2|number> <default
somevalue>,
                      y in varchar2          <default
somevalue>,
                      z in <varchar2|number> <default
somevalue>,
                      <other IN PARAMETERS, each with a default
value....> )

The variables in the URL, x, y, and z, will be passed as formal
named parameters as such:

   begin
      foo( x => 5, y => 'Hello', z => 10 );
   end;

o A correlated arrays of variable name, value pairs (called
Flexible Parameter Passing, see
http://www.olab.com/doc/books/plsql/pl_invk.htm#1014574
for documentation on this
feature).

In this case, instead of getting 3 formal parameters with names
x, y, z - your procedure can request to be sent

- the number of arguments (3 in this example)
- an array of the NAMES of the arguments, this array in this
example would have the strings ( 'x', 'y', 'z' )
- an array of the VALUES of the arguments, this array in this
exmaple would have the strings ( '5', 'Hello', '10' )
- an extra reserved variable that is currently not used.

You can use this in cases where there are many arguments to a
routine (your case) or where you might not know all of the names
of the variables before hand (a generic forms processing routine
for example).

I believe that you can use the Flexible Parameter Passing to
achive what you want.  Instead of having to pass lots of
arguments around, you can pass around these 2 arrays.

Review &
      Followup

      Rating: 5
flexible
      parameters passing  November 13, 2002
Reviewer:
      mo
Tom:

Thanks a lot about pointing me this neat web feature:

1.  Is this what you use always in asktom site?

2.  I was trying to see more examples of this but the above link you refer to
does not seem to work?

3.  Let us say I have the following html form and after user fills it out I want
to post values to a save procedure:

htp.formtext(p_lastname,30,30,null);
htp.formtext(p_firstname,30,30,null);
htp.formtext(p_city,30,30,null);
htp.formtext(p_state,10,10,null);
htp.formtext(p_zip,5,5,null);
FOR x in (1..5) LOOP
htp.formtext(p_itemno,2,2,null);
htp.formtext(p_description,30,30,null);
htp.formtext(p_qty,5,5,null);
htp.formtext(p_unit_price,5,5,null);
END LOOP;

---------*this is how I used to do it.

create or replace procedure save_form (
  p_lastname    VARCHAR2  DEFAULT NULL,
  p_firstname   VARCHAR2 DEFAULT NULL,
  p_city        VARCHAR2 DEFAULT NULL,
  p_state       VARCHAR2 DEFAULT NULL,
  p_zip         VARCHAR2 DEFAULT NULL,
  p_itemno      TYPES.ARRAY DEFAULT TYPES.EMPTY_ARRAY,
  p_description  TYPES.ARRAY DEFAULT TYPES.EMPTY_ARRAY,
  p_qty          TYPES.ARRAY DEFAULT TYPES.EMPTY_ARRAY,
  p_unit_price  TYPES.ARRAY DEFAULT TYPES.EMPTY_ARRAY,)

as

v_personid   number(5);

begin
    select person_id into v_personid from dual;

    insert into person (personid,lastname,
                        firstname,state,zip )
                    values
                      (v_personid,p_lastname,p_firstname,
                       p_city,p_state,p_zip);
   If (SQL%ROWCOUNT > 0) THEN
FOR i in 1..p_description.count
LOOP
       insert into items (personid,itemno,description,
                       qty,unit_price)
                      values
                      (v_personid,p_itemno(i),
                       p_description(i),p_qty(i),
                       p_unit_price(i) );
  END LOOP;
ELSE
  rollback;
END IF;

END;
-----Now to use flexible parameters is this how you do it?

create or replace procedure save_form (
  name_array        TYPES.ARRAY DEFAULT TYPES.EMPTY_ARRAY,
  values_array      TYPES.ARRAY DEFAULT TYPES.EMPTY_ARRAY)

as

v_personid   number(5);

begin
    select person_id into v_personid from dual;

    insert into person (personid,lastname,
                        firstname,city,state,zip )
                    values
                      (v_personid,
                       value_array(1),value_array(2),
                       value_array(3),value_array(4),
                       value_array(5) );
   If (SQL%ROWCOUNT > 0) THEN
FOR i in 1..name_array.count
LOOP
    If (name_array(i)='p_description') THEN
       insert into items (personid,itemno,description,
                       qty,unit_price)
                      values
                      (v_personid,value_array(i),
                       value_array(i+1),value_array(i+2),
                       value_array(i+3) );
  END LOOP;
ELSE
  rollback;
END IF;

END;

4.  when would you use a four parameter interface instead of two?

Thank you,

      Rating: 5
flexible
      parameter  November 16, 2002
Reviewer:  mo

Tom:

Just want to make sure you looked at the above Question on flexible parameters?

THanks

          Followup:

i did but I sort of took a pass on it - I do not respond to each followup --
just the ones I want to ;)

your followup is more like a "brand new big huge question" (you do that alot).
I'm not going to followup on lots of those anymore -- takes too much time away
from being able to accept new questions in the place where I want them to come
-- the new question queue.

You personally do ask quite a few question -- tons actually.  Trying to spread
around the answers more fairly is all.

      Rating:
      5
flexible  November 16, 2002
Reviewer:
      mo
Tom:

sorry I thought you want new questions under the same subjects as followups.  I
guess you want followups only to the original question.

THank you,

November 16, 2002 Frank -- Thanks for the question regarding
      "Statement audting vs. system privilege auditing, etc.", version 9ir2

      You Asked
Hi Tom,

These two types of auditing are quite similar and therefore quite confusing.
Could you show me a simple way to separate them?

I also have a couple of questions on user privileges.
1. How do I show a user's security/privilege domain? It seems session_privs
gives a pretty good result--including the directly owned, granted and indirectly
granted privileges currently activated. Am I right?
Is there a view to show all available privileges to a user including the ones
from roles that are not enabled at the current session?
2. As a DBA, how could you tell who has access to a particular object, not only
directly granted but also indirectly granted, through object privilege granting
as well as system privileges?
3. As a DBA, how could you tell who has a particular system privilege such as
select_any_dictionary, not only directly granted but also indirectly granted?

Many Thanks,

Frank

      and we said...
they are basically the same -- I don't see what is confusing about them?  There
are two types of grants -- object grants (eg: select on emp) and system grants
(eg: create any table).

We audit when someone uses something they've been granted.  Eg: you can audit
when someone uses create session (connects - a system privilege).  You can audit
when someone uses SELECT ON EMP (selects from the emp table, an object
privilege)

if you have a specific question or area of confusion -- feel free to followup
and I'll try to address it but not seeing any confusion myself, its hard to know
what to say to clear yours up....

1) yes, session_privs is a good start.  Search this site for

user_role_hierarchy

to see a more "comprehensive" one

2) see #1, you can use that more "comprehensive" view (you'd need to rewrite a
tad to show privs by user -- it currently shows privs for A user -- the
connected user)

3) see #2.
Review & Followup

      Rating: 4
The online doc
      could be clearer  November 16, 2002
Reviewer:
      Frank  from Canada
Thanks, Tom.

My confusion was on system privilege auditing vs. statement auditing. As you
said they are the same thing. But in Oracle document both names are used in a
way to make you think there are some differences between them.  I guess the
online doc could be a little clearer on this unless there are some minor
differences which I am not aware of.

I have no problem with the distinction between system priv auditing and object
priv auditing. Thanks.

November 16, 2002 Bill -- Thanks for the question regarding
      "Dynamic Sql and Procedure", version 8.1.7.4

      You Asked
Hi Tom
I would like to run this procedure by you to see what you, think - is there a
better way to do this - am i using binds etc.  My requirement is that it has be
be compatible back to Oracle 8.0.
What i am trying to do is "audit" a database schema and return a result set of
"orphaned" rows. I have tables that contain the definitions of what the Parent
and child relationships are.  These definitions are passed as parameters to the
below procedure, and the number of parameters may differ.  Our database does not
have RI, there are no primary keys no foreign keys, and the integrity is
"enforced" through application code ( although i highly doubt that ).  Anyway -
this is my assignment - to create this report of "orpaned" rows.
My concerns are: 1) am i using bind variables.  This procedure will not be run
very often - maybe once or twice a week.  I had a real problem coding the
dynamic sql to accept the table name( which must be pre-qualified ) and build at
run time.  This package/procedure is run as a different user.
2) Is this a solid approach.
The procedure runs very fast ( ~1 sec give or take ) and processes thousands of
rows.  I don't have a problem with the performance at this point.
My goal is to have this be as efficient as possible + scalable, but still
compatible back to Oracle 8.0.x, and for me - TO ALWAYS CONTINUE LEARNING. I
read your book ( especially chapter 1+2 ) on a daily basis + Oracle Concepts
guide. Thanks again for your hard work.

CREATE OR REPLACE PACKAGE audit_stat
AS
   -- Accept one key value as an argument
   PROCEDURE entity_aud (
      tablename   IN   VARCHAR2,
      entnum      IN   NUMBER,
      ckey1       IN   VARCHAR2,
      pkey1       IN   VARCHAR2,
      ptable      IN   VARCHAR2
   );
   -- Accept two key values as arguments
   PROCEDURE entity_aud (
      tablename   IN   VARCHAR2,
      entnum      IN   NUMBER,
      ckey1       IN   VARCHAR2,
      ckey2       IN   VARCHAR2,
      pkey1       IN   VARCHAR2,
      pkey2       IN   VARCHAR2,
      ptable      IN   VARCHAR2
   );
   -- Accept three key values as arguments
   PROCEDURE entity_aud (
      tablename   IN   VARCHAR2,
      entnum      IN   NUMBER,
      ckey1       IN   VARCHAR2,
      ckey2       IN   VARCHAR2,
      ckey3       IN   VARCHAR2,
      pkey1       IN   VARCHAR2,
      pkey2       IN   VARCHAR2,
      pkey3       IN   VARCHAR2,
      ptable      IN   VARCHAR2
   );

END audit_stat;
/
CREATE OR REPLACE PACKAGE BODY audit_stat
AS
   -- Accept one key value as an argument
   PROCEDURE entity_aud (
      tablename   IN   VARCHAR2,
      entnum      IN   NUMBER,
      ckey1       IN   VARCHAR2,
      pkey1       IN   VARCHAR2,
      ptable      IN   VARCHAR2
   )
   AS
      cursor1    PLS_INTEGER;
      v_key1     VARCHAR2 (128);
      sqltext1   VARCHAR2 (500);
      v_dummy    PLS_INTEGER;
   BEGIN
      cursor1 := DBMS_SQL.open_cursor;
      sqltext1 :=    'SELECT DISTINCT '
                  || ckey1
                  || ' FROM STAT.'
                  || tablename
                  || ' WHERE '
                  || ckey1
                  || ' NOT IN ( SELECT '
                  || pkey1
                  || ' FROM STAT.'
                  || ptable
                  || ')';
      DBMS_SQL.parse (cursor1, sqltext1, DBMS_SQL.v7);
      DBMS_SQL.define_column (cursor1, 1, v_key1, 128);
      v_dummy := DBMS_SQL.EXECUTE (cursor1);

      LOOP
         EXIT WHEN DBMS_SQL.fetch_rows (cursor1) = 0;
         DBMS_SQL.column_value (cursor1, 1, v_key1);

         INSERT INTO audit_report
                     (audit_id, entity_cd, childname, keycol1)
              VALUES (audit_report_seq.NEXTVAL, entnum, tablename, v_key1);
      END LOOP;

      DBMS_SQL.close_cursor (cursor1);
      COMMIT;
   EXCEPTION
      WHEN DUP_VAL_ON_INDEX
      THEN
         DBMS_SQL.close_cursor (cursor1);
      WHEN TOO_MANY_ROWS
      THEN
         DBMS_SQL.close_cursor (cursor1);
      WHEN OTHERS
      THEN
         DBMS_SQL.close_cursor (cursor1);
         RAISE;
   END entity_aud;
   -- Accept two key values as an argument
   PROCEDURE entity_aud (
      tablename   IN   VARCHAR2,
      entnum      IN   NUMBER,
      ckey1       IN   VARCHAR2,
      ckey2       IN   VARCHAR2,
      pkey1       IN   VARCHAR2,
      pkey2       IN   VARCHAR2,
      ptable      IN   VARCHAR2
   )
   AS
      cursor1    PLS_INTEGER;
      v_key1     VARCHAR2 (128);
      v_key2     VARCHAR2 (128);
      sqltext1   VARCHAR2 (500);
      v_dummy    PLS_INTEGER;
   BEGIN
      cursor1 := DBMS_SQL.open_cursor;
      sqltext1 :=    'SELECT DISTINCT '
                  || ckey1
                  || ', '
                  || ckey2
                  || ' FROM STAT.'
                  || tablename
                  || ' WHERE ('
                  || ckey1
                  || ', '
                  || ckey2
                  || ') NOT IN ( SELECT '
                  || pkey1
                  || ', '
                  || pkey2
                  || ' FROM STAT.'
                  || ptable
                  || ')';
      DBMS_SQL.parse (cursor1, sqltext1, DBMS_SQL.v7);
      DBMS_SQL.define_column (cursor1, 1, v_key1, 128);
      DBMS_SQL.define_column (cursor1, 2, v_key2, 128);
      v_dummy := DBMS_SQL.EXECUTE (cursor1);

      LOOP
         EXIT WHEN DBMS_SQL.fetch_rows (cursor1) = 0;
         DBMS_SQL.column_value (cursor1, 1, v_key1);
         DBMS_SQL.column_value (cursor1, 2, v_key2);

         INSERT INTO audit_report
                     (audit_id, entity_cd, childname, keycol1,
                      keycol2)
              VALUES (audit_report_seq.NEXTVAL, entnum, tablename, v_key1,
                      v_key2);
      END LOOP;

      DBMS_SQL.close_cursor (cursor1);
      COMMIT;
   EXCEPTION
      WHEN DUP_VAL_ON_INDEX
      THEN
         DBMS_SQL.close_cursor (cursor1);
      WHEN TOO_MANY_ROWS
      THEN
         DBMS_SQL.close_cursor (cursor1);
      WHEN OTHERS
      THEN
         DBMS_SQL.close_cursor (cursor1);
         RAISE;
   END entity_aud;
   -- Accept three key values as an argument
   PROCEDURE entity_aud (
      tablename   IN   VARCHAR2,
      entnum      IN   NUMBER,
      ckey1       IN   VARCHAR2,
      ckey2       IN   VARCHAR2,
      ckey3       IN   VARCHAR2,
      pkey1       IN   VARCHAR2,
      pkey2       IN   VARCHAR2,
      pkey3       IN   VARCHAR2,
      ptable      IN   VARCHAR2
   )
   AS
      cursor1    PLS_INTEGER;
      v_key1     VARCHAR2 (128);
      v_key2     VARCHAR2 (128);
      v_key3     VARCHAR2 (128);
      sqltext1   VARCHAR2 (500);
      v_dummy    PLS_INTEGER;
   BEGIN
      cursor1 := DBMS_SQL.open_cursor;
      sqltext1 :=    'SELECT DISTINCT '
                  || ckey1
                  || ', '
                  || ckey2
                  || ', '
                  || ckey3
                  || ' FROM STAT.'
                  || tablename
                  || ' WHERE ('
                  || ckey1
                  || ', '
                  || ckey2
                  || ', '
                  || ckey3
                  || ') NOT IN ( SELECT '
                  || pkey1
                  || ', '
                  || pkey2
                  || ', '
                  || pkey3
                  || ' FROM STAT.'
                  || ptable
                  || ')';
      DBMS_SQL.parse (cursor1, sqltext1, DBMS_SQL.v7);
      DBMS_SQL.define_column (cursor1, 1, v_key1, 128);
      DBMS_SQL.define_column (cursor1, 2, v_key2, 128);
      DBMS_SQL.define_column (cursor1, 3, v_key3, 128);
      v_dummy := DBMS_SQL.EXECUTE (cursor1);

      LOOP
         EXIT WHEN DBMS_SQL.fetch_rows (cursor1) = 0;
         DBMS_SQL.column_value (cursor1, 1, v_key1);
         DBMS_SQL.column_value (cursor1, 2, v_key2);
         DBMS_SQL.column_value (cursor1, 3, v_key3);

         INSERT INTO audit_report
                     (audit_id, entity_cd, childname, keycol1,
                      keycol2, keycol3)
              VALUES (audit_report_seq.NEXTVAL, entnum, tablename, v_key1,
                      v_key2, v_key3);
      END LOOP;

      DBMS_SQL.close_cursor (cursor1);
      COMMIT;
   EXCEPTION
      WHEN DUP_VAL_ON_INDEX
      THEN
         DBMS_SQL.close_cursor (cursor1);
      WHEN TOO_MANY_ROWS
      THEN
         DBMS_SQL.close_cursor (cursor1);
      WHEN OTHERS
      THEN
         DBMS_SQL.close_cursor (cursor1);
         RAISE;
   END entity_aud;

END audit_stat;
/

      and we said...
Well, I have this to say -- we can make this much much smaller (and easier to
add more and more columns as a side effect).

Also -- if you get a dup_val_on_index -- that would be from the insert into the
audit table which should be IMPOSSIBLE so rather then IGNORE it as you are now,
we should let the procedure fail.  ONLY catch exceptions you a) expect to have
and b) can handle.  In this case -- if you got a dup_val_on_index -- this
program should right bomb out.

Same with too_many_rows.

Your "others" is ok -- clean up the resource (although I have what I believe to
be a better way in my code -- we'll use a global cursor and just never ever
close it) and re-raise the error.  the other two, they ignore errors that should
rightly never happen.  If they do happen, we want to know!

Rather then do row by row processing, we can do this easily with a insert into
as select using MINUS.  In general, our query will be:

insert into audit_report
( audit_id, entity_cd, childname, keycol1, keycol2, keycol3 )
select audit_report_seq.nextval, :entnum, :childname, a.*
  from ( select cx keycol1,cy keycol2,cz keycol3
           from c3
          MINUS
         select x,y,z
           from p3
        ) a

and if we have less then three key colums -- we just substitute in the word NULL
for cz/z  or cy/y.  Hence, we can use a single procedure:

ops$tkyte@ORA806.WORLD> create or replace package audit_stat
  2  as
  3      procedure entity_aud( p_child in varchar2,
  4                            p_parent in varchar2,
  5                            p_entnum in number,
  6                            p_ckey1  in varchar2,
  7                            p_pkey1  in varchar2,
  8                            p_ckey2  in varchar2 default 'NULL',
  9                            p_pkey2  in varchar2 default 'NULL',
 10                            p_ckey3  in varchar2 default 'NULL',
 11                            p_pkey3  in varchar2 default 'NULL' );
 12  end;
 13  /

Package created.

by defaulting the p_ckey2 (child key -- #2) and p_pkey2 (parent key -- #2) to
the WORD 'null' (not a null value, the WORD null, my code is reduced to a single
externally exposed procedure.  It looks like this:

ops$tkyte@ORA806.WORLD> create or replace package body audit_stat
  2  as
  3
  4  g_cursor     pls_integer default dbms_sql.open_cursor;
  5
  6
  7  procedure execute_immediate( p_sql in varchar2,
  8                               p_entnum in number,
  9                               p_child  in varchar2 )
 10  is
 11      ignore          pls_integer;
 12  BEGIN
 13      dbms_sql.parse(g_cursor, p_sql, dbms_sql.native);
 14      dbms_sql.bind_variable( g_cursor, ':entnum', p_entnum );
 15      dbms_sql.bind_variable( g_cursor, ':childname', p_child );
 16      ignore := dbms_sql.execute(g_cursor);
 17  END;
 18
 19  procedure entity_aud( p_child in varchar2,
 20                        p_parent in varchar2,
 21                        p_entnum in number,
 22                        p_ckey1  in varchar2,
 23                        p_pkey1  in varchar2,
 24                        p_ckey2  in varchar2 default 'NULL',
 25                        p_pkey2  in varchar2 default 'NULL',
 26                        p_ckey3  in varchar2 default 'NULL',
 27                        p_pkey3  in varchar2 default 'NULL' )
 28  as
 29      l_stmt long;
 30  begin
 31      l_stmt :=
 32      'insert into audit_report
 33       ( audit_id, entity_cd, childname, keycol1, keycol2, keycol3 )
 34       select audit_report_seq.nextval, :entnum, :childname, a.*
 35         from ( select ' || p_ckey1 || ' keycol1,' || p_ckey2 || ' keycol2,'
||
 36                            p_ckey3 || ' keycol3
 37                  from ' || p_child || '
 38                 MINUS
 39                select ' || p_pkey1 || ',' || p_pkey2 || ',' || p_pkey3 || '
 40                  from ' || p_parent || '
 41              ) a';
 42
 43      execute_immediate( l_stmt, p_entnum, p_child );
 44  end;
 45
 46  end;
 47  /

Package body created.

Now we are ready to test:

ops$tkyte@ORA806.WORLD> create table audit_report
  2  ( audit_id int, entity_cd int, childname varchar2(30), keycol1
varchar2(255), keycol2 varchar2(255), keycol3 varchar2(255) );

Table created.

ops$tkyte@ORA806.WORLD> create sequence audit_report_seq;

Sequence created.

ops$tkyte@ORA806.WORLD> create table p ( x int, y int, z int );

Table created.

ops$tkyte@ORA806.WORLD> create table c ( cx int, cy int, cz int );

Table created.

ops$tkyte@ORA806.WORLD>
ops$tkyte@ORA806.WORLD> insert into p values ( 1,1,1 );

1 row created.

ops$tkyte@ORA806.WORLD> insert into c values ( 1,1,1 );

1 row created.

ops$tkyte@ORA806.WORLD> insert into c values ( 2,2,2 );

1 row created.

and now to see that it works:

ops$tkyte@ORA806.WORLD> exec audit_stat.entity_aud( 'c', 'p', 1, 'cx', 'x' );

PL/SQL procedure successfully completed.

ops$tkyte@ORA806.WORLD> exec audit_stat.entity_aud( 'c', 'p', 2, 'cx', 'x',
'cy', 'y' );

PL/SQL procedure successfully completed.

ops$tkyte@ORA806.WORLD> exec audit_stat.entity_aud( 'c', 'p', 3, 'cx', 'x',
'cy', 'y', 'cz', 'z' );

PL/SQL procedure successfully completed.

ops$tkyte@ORA806.WORLD>
ops$tkyte@ORA806.WORLD> select * from audit_report;

  AUDIT_ID  ENTITY_CD CHILDNAME                      KEYCOL1  KEYCOL2  KEYCOL3
---------- ---------- ------------------------------ -------- -------- --------
         1          1 c                              2
         2          2 c                              2        2
         3          3 c                              2        2        2

i tested in 806 on up -- works like a charm.  The trick here is that MINUS
works differently with NULL values then = does. So,

ops$tkyte@ORA817DEV.US.ORACLE.COM> select cx, null, null from c
  2  minus
  3  select x, null, null from p;

        CX N N
---------- - -
         2

"works" -- it returns the row (and even does the distinct for us!

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into c values ( 2, 2, 2 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>  select cx, null, null from c
  2  minus
  3  select x, null, null from p;

        CX N N
---------- - -
         2

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from c where cx = 2;

        CX         CY         CZ
---------- ---------- ----------
         2          2          2
         2          2          2

Review &
      Followup

      Rating: 5
wrapper
      proc  November 16, 2002
Reviewer:  Bill
       from SoCal
Just what i was looking for. Thank You.
I am also looking at idea of a 'wrapper' proc that would feed the entity_aud
proc.  this would loop through the pre-created audit tables (that define the
entries) and execute the entity_aud.
this proc is not pretty, and I would really like to re-write it:
Below are the tables that hold the relationship data:
(This app has a java swing front end that calls these via callable statements ).

CREATE TABLE AUDIT_ENT_CD (
  ENTITY_CD    NUMBER        NOT NULL,
  ENTITY_NAME  VARCHAR2 (128)  NOT NULL,
  PARENT_NAME  VARCHAR2 (30)  NOT NULL,
  PARENT_KEY1  VARCHAR2 (30),
  PARENT_KEY2  VARCHAR2 (30),
  PARENT_KEY3  VARCHAR2 (30),
  PRIMARY KEY ( ENTITY_CD )
    USING INDEX
     TABLESPACE STATUSR PCTFREE 10
     STORAGE ( INITIAL 163840 NEXT 131072 PCTINCREASE 0 ))
   TABLESPACE STATUSR
   PCTFREE 10
   PCTUSED 40
   INITRANS 1
   MAXTRANS 255
  STORAGE (
   INITIAL 163840
   NEXT 131072
   PCTINCREASE 0
   MINEXTENTS 1
   MAXEXTENTS 2147483645
   FREELISTS 1 FREELIST GROUPS 1 )
   NOCACHE ;

CREATE UNIQUE INDEX STAUDIT.AUDIT_ENT_CD_IDX1 ON
  AUDIT_ENT_CD(ENTITY_CD, ENTITY_NAME, PARENT_NAME)
  TABLESPACE STATUSR PCTFREE 10  STORAGE(INITIAL 163840 NEXT 131072 PCTINCREASE
0 )
;

DROP TABLE AUDIT_BUILD CASCADE CONSTRAINTS ;

CREATE TABLE AUDIT_BUILD (
  AUDIT_ID   NUMBER (10)   NOT NULL,
  ENTITY_CD  NUMBER        NOT NULL,
  CHILDNAME  VARCHAR2 (30)  NOT NULL,
  KEYCOL1    VARCHAR2 (128)  NOT NULL,
  KEYCOL2    VARCHAR2 (128),
  KEYCOL3    VARCHAR2 (128),
  KEYCOL4    VARCHAR2 (128),
  PRIMARY KEY ( AUDIT_ID )
    USING INDEX
     TABLESPACE STATUSR PCTFREE 10
     STORAGE ( INITIAL 81920 NEXT 65536 PCTINCREASE 0 ))
   TABLESPACE STATUSR
   PCTFREE 10
   PCTUSED 40
   INITRANS 1
   MAXTRANS 255
  STORAGE (
   INITIAL 81920
   NEXT 65536
   PCTINCREASE 0
   MINEXTENTS 1
   MAXEXTENTS 2147483645
   FREELISTS 1 FREELIST GROUPS 1 )
   NOCACHE ;

CREATE UNIQUE INDEX STAUDIT.AUDIT_BUILD_IDX1 ON
  AUDIT_BUILD(ENTITY_CD, CHILDNAME, KEYCOL1)
  TABLESPACE STATUSR PCTFREE 10  STORAGE(INITIAL 81920 NEXT 65536 PCTINCREASE 0
)
;

ALTER TABLE AUDIT_BUILD ADD  CONSTRAINT FK_AUDIT_BUILD_AUDIT_ENT_CD
 FOREIGN KEY (ENTITY_CD)
  REFERENCES STAUDIT.AUDIT_ENT_CD (ENTITY_CD) ;

PROCEDURE audwrapper
   IS
      v_ctable    VARCHAR2 (30);
      v_entnum    NUMBER (3);
      v_ckey1     VARCHAR2 (30);
      v_pkey1     VARCHAR2 (30);
      v_ptable    VARCHAR2 (30);
      v_ctable2   VARCHAR2 (30);
      v_entnum2   NUMBER (3);
      v_ckey12    VARCHAR2 (30);
      v_ckey22    VARCHAR2 (30);
      v_pkey12    VARCHAR2 (30);
      v_pkey22    VARCHAR2 (30);
      v_ptable2   VARCHAR2 (30);
      v_ctable3   VARCHAR2 (30);
      v_entnum3   NUMBER (3);
      v_ckey13    VARCHAR2 (30);
      v_ckey23    VARCHAR2 (30);
      v_ckey33    VARCHAR2 (30);
      v_pkey13    VARCHAR2 (30);
      v_pkey23    VARCHAR2 (30);
      v_pkey33    VARCHAR2 (30);
      v_ptable3   VARCHAR2 (30);
      cursor1     PLS_INTEGER;
      cursor2     PLS_INTEGER;
      cursor3     PLS_INTEGER;
      v_dummy     PLS_INTEGER;
      args1       VARCHAR2 (500);
      args2       VARCHAR2 (500);
      args3       VARCHAR2 (500);
   BEGIN
      DELETE FROM staudit.audit_report;

      DELETE FROM staudit.audit_system_tbl;

      DELETE FROM staudit.audit_syschild_tbl;

      DELETE FROM staudit.audit_chlastcsr;

      DELETE FROM staudit.audit_ptlastcsr;

      DELETE FROM staudit.audit_sys_report;

      COMMIT;
      cursor1 := DBMS_SQL.open_cursor;
      cursor2 := DBMS_SQL.open_cursor;
      cursor3 := DBMS_SQL.open_cursor;
      args1 :=
            'SELECT A.CHILDNAME, A.ENTITY_CD, A.KEYCOL1, B.PARENT_KEY1,
B.PARENT_NAME '
         || 'FROM AUDIT_BUILD A , AUDIT_ENT_CD B WHERE ( (A.ENTITY_CD =
B.ENTITY_CD) AND '
         || 'A.ENTITY_CD IN (2,3,4,5,6,14,15,16,17,19,20,22,28,29) )';
      args2 :=
            'SELECT A.CHILDNAME, A.ENTITY_CD, A.KEYCOL1, A.KEYCOL2,
B.PARENT_KEY1, '
         || 'B.PARENT_KEY2, B.PARENT_NAME FROM AUDIT_BUILD A , AUDIT_ENT_CD B '
         || 'WHERE ( (A.ENTITY_CD = B.ENTITY_CD) AND A.ENTITY_CD IN (
1,13,18,21,23,24,25,27 ))';
      args3 :=
            'SELECT A.CHILDNAME, A.ENTITY_CD, A.KEYCOL1, A.KEYCOL2, A.KEYCOL3,
B.PARENT_KEY1, '
         || 'B.PARENT_KEY2, B.PARENT_KEY3, B.PARENT_NAME  FROM AUDIT_BUILD A ,
AUDIT_ENT_CD B '
         || 'WHERE ( (A.ENTITY_CD = B.ENTITY_CD) AND A.ENTITY_CD IN (8, 9, 10,
30) )';
      DBMS_SQL.parse (cursor1, args1, DBMS_SQL.v7);
      DBMS_SQL.define_column (cursor1, 1, v_ctable, 30);
      DBMS_SQL.define_column (cursor1, 2, v_entnum);
      DBMS_SQL.define_column (cursor1, 3, v_ckey1, 30);
      DBMS_SQL.define_column (cursor1, 4, v_pkey1, 30);
      DBMS_SQL.define_column (cursor1, 5, v_ptable, 30);
      DBMS_SQL.parse (cursor2, args2, DBMS_SQL.v7);
      DBMS_SQL.define_column (cursor2, 1, v_ctable2, 30);
      DBMS_SQL.define_column (cursor2, 2, v_entnum2);
      DBMS_SQL.define_column (cursor2, 3, v_ckey12, 30);
      DBMS_SQL.define_column (cursor2, 4, v_ckey22, 30);
      DBMS_SQL.define_column (cursor2, 5, v_pkey12, 30);
      DBMS_SQL.define_column (cursor2, 6, v_pkey22, 30);
      DBMS_SQL.define_column (cursor2, 7, v_ptable2, 30);
      DBMS_SQL.parse (cursor3, args3, DBMS_SQL.v7);
      DBMS_SQL.define_column (cursor3, 1, v_ctable3, 30);
      DBMS_SQL.define_column (cursor3, 2, v_entnum3);
      DBMS_SQL.define_column (cursor3, 3, v_ckey13, 30);
      DBMS_SQL.define_column (cursor3, 4, v_ckey23, 30);
      DBMS_SQL.define_column (cursor3, 5, v_ckey33, 30);
      DBMS_SQL.define_column (cursor3, 6, v_pkey13, 30);
      DBMS_SQL.define_column (cursor3, 7, v_pkey23, 30);
      DBMS_SQL.define_column (cursor3, 8, v_pkey33, 30);
      DBMS_SQL.define_column (cursor3, 9, v_ptable3, 30);
      v_dummy := DBMS_SQL.EXECUTE (cursor1);

      LOOP
         EXIT WHEN DBMS_SQL.fetch_rows (cursor1) = 0;
         DBMS_SQL.column_value (cursor1, 1, v_ctable);
         DBMS_SQL.column_value (cursor1, 2, v_entnum);
         DBMS_SQL.column_value (cursor1, 3, v_ckey1);
         DBMS_SQL.column_value (cursor1, 4, v_pkey1);
         DBMS_SQL.column_value (cursor1, 5, v_ptable);
         staudit.audit_stat.entity_aud (
            v_ctable,
            v_entnum,
            v_ckey1,
            v_pkey1,
            v_ptable
         );
      END LOOP;

      DBMS_SQL.close_cursor (cursor1);
      COMMIT;
      -- Begin 2nd arg proc here
      v_dummy := DBMS_SQL.EXECUTE (cursor2);

      LOOP
         EXIT WHEN DBMS_SQL.fetch_rows (cursor2) = 0;
         DBMS_SQL.column_value (cursor2, 1, v_ctable2);
         DBMS_SQL.column_value (cursor2, 2, v_entnum2);
         DBMS_SQL.column_value (cursor2, 3, v_ckey12);
         DBMS_SQL.column_value (cursor2, 4, v_ckey22);
         DBMS_SQL.column_value (cursor2, 5, v_pkey12);
         DBMS_SQL.column_value (cursor2, 6, v_pkey22);
         DBMS_SQL.column_value (cursor2, 7, v_ptable2);
         staudit.audit_stat.entity_aud (
            v_ctable2,
            v_entnum2,
            v_ckey12,
            v_ckey22,
            v_pkey12,
            v_pkey22,
            v_ptable2
         );
      END LOOP;

      DBMS_SQL.close_cursor (cursor2);
      COMMIT;
      -- Begin 3rd arg proc here
      v_dummy := DBMS_SQL.EXECUTE (cursor3);

      LOOP
         EXIT WHEN DBMS_SQL.fetch_rows (cursor3) = 0;
         DBMS_SQL.column_value (cursor3, 1, v_ctable3);
         DBMS_SQL.column_value (cursor3, 2, v_entnum3);
         DBMS_SQL.column_value (cursor3, 3, v_ckey13);
         DBMS_SQL.column_value (cursor3, 4, v_ckey23);
         DBMS_SQL.column_value (cursor3, 5, v_ckey33);
         DBMS_SQL.column_value (cursor3, 6, v_pkey13);
         DBMS_SQL.column_value (cursor3, 7, v_pkey23);
         DBMS_SQL.column_value (cursor3, 8, v_pkey33);
         DBMS_SQL.column_value (cursor3, 9, v_ptable3);
         staudit.audit_stat.entity_aud (
            v_ctable3,
            v_entnum3,
            v_ckey13,
            v_ckey23,
            v_ckey33,
            v_pkey13,
            v_pkey23,
            v_pkey33,
            v_ptable3
         );
      END LOOP;

      DBMS_SQL.close_cursor (cursor3);

      DELETE FROM audit_report
            WHERE entity_cd = 24 AND keycol1 = 'D' AND keycol2 = '2000';

      DELETE FROM STAUDIT.AUDIT_REPORT
        WHERE CHILDNAME = 'STAT_FILE_LOCATE'
        AND ENTITY_CD = 28
        AND NOT EXISTS (
            SELECT A.PS_DATABASE FROM STAT.STAT_FILE_LOCATE A,
                      STAT.STAT_GAPPLDB_CONFIG B
            WHERE A.PS_DATABASE = B.DB_CD ) ;

      COMMIT;

   EXCEPTION
      WHEN DUP_VAL_ON_INDEX
      THEN
         DBMS_SQL.close_cursor (cursor1);
         DBMS_SQL.close_cursor (cursor2);
         DBMS_SQL.close_cursor (cursor3);
      WHEN TOO_MANY_ROWS
      THEN
         DBMS_SQL.close_cursor (cursor1);
         DBMS_SQL.close_cursor (cursor2);
         DBMS_SQL.close_cursor (cursor3);
      WHEN OTHERS
      THEN
         DBMS_SQL.close_cursor (cursor1);
         DBMS_SQL.close_cursor (cursor2);
         DBMS_SQL.close_cursor (cursor3);
         RAISE;
   END audwrapper;

          Followup:

I don't understand why you are using dbms_sql? seems like the queries are pretty
"static" to me?

anyway -- use native, not v7 on the parse

lose the exception block (well, the others could stay if you like but the other
two -- they must go, they are bugs)

      Rating: 5
dbms_sql on
      'wrapper'  November 16, 2002
Reviewer:  Bill
       from SoCal
you are right, the sql is static
i will reexamine that one
thanks for your help!

November 17, 2002 Francisco -- Thanks for the question regarding
      "Displaying messages in procedure", version 8.1.7.4

      You Asked
Hi Tom.

I am using a shell program which is invoking an anonymous procedure and in this
procedure I use the DBMS_OUTPUT package to display some messages.

Something like this:

#!/bin/ksh

Some variables definition
......
sqlplus -s user/password <
my anonymous block of PL/SQL
.....
--Then putting some messages
DBMS_OUTPUT.PUT_LINE('Splitting partition....');
--Here is the process used to split the partition.
DBMS_OUTPUT.PUT_LINE('Analyzing partition');
--Here is the process used to analyze my partitions.

EOF

So, I want to display this kind of messages when the process is really
executing(Split and Analyze, something like prompt in SQL*Plus).

The problem is they are displayed, but until when my shell script has completed.

I don't know if it can be done using PL/SQL inside of a shell script program.

Any advice will be really appreciated.

Thanks in advance.

      and we said...
do you have my book?  if so, read the section on dbms_output in the appendix.  I
explain in huge detail how it works.

basically -- it works by buffering the output into an array.  At the end of the
procedure call -- SQLPlus itself pulls the array back and dumps it on screen.
There is NO way for PLSQL to do IO to the screen at all (think about it -- plsql
is running generally on a totally different machine elsewhere in the network --
no way it can print on your screen)..

You would have to break up the anonymous block into N blocks -- letting sqlplus
spit out the dbms_output buffer as you went along....

Review &
      Followup

      Rating:
      5
Thanks!!!!!  November 16, 2002
Reviewer:
      Francisco Martinez  from Monterrey, N.L., Mexico
Yes Mr. Tom, I have your book(it is excellent), I will read the appendix
section.

Is there any other way to do my request using any other package?, I have seen
the DBMS_APPLICATION_INFO package but I don't know if it can be used to do this?

Thanks for your quick response and I really appreciate your help.

Best regards.

          Followup:

not in the same session -- in order to accomplish your stated goal -- in version
817 -- you will need to break the anonymous block into smaller sub-blocks.
You'll see the output between "blocks of code"

      Rating:
      4
  November 16, 2002
Reviewer:
      A reader
"in order to accomplish your stated goal -- in version
817 -- "

And what would be the solution in later versions (9i)? Are the pipelined
functions useful here? Can you please demonstrate. Thanks

          Followup:

pipelines functions.  In the following  output -- I got to see the messages as
they were produced.  Note -- plus appears to have a minimum array fetch size
of 2 regardless -- hence the double "pipes".  I monitored the session from
another session to make sure the messages were in sync with what was happening
-- and they were.  So, I goto to see the progress of the long running function
as it happened (NO, i would not gather stats like this -- this is just a demo, i
wanted a couple of long running commands.  I would use a single dbms_stats call
in real life):

big_table@ORA920.US.ORACLE.COM> create or replace type vcArray as table of
varchar2(255)
  2  /

Type created.

big_table@ORA920.US.ORACLE.COM>
big_table@ORA920.US.ORACLE.COM> create or replace function long_running return
vcArray
  2  PIPELINED
  3  as
  4      pragma autonomous_transaction;
  5  begin
  6      pipe row ( 'Beginning at ' || systimestamp || ' going to analyze big
table...' );
  7      pipe row ( ' ' );
  8
  9      execute immediate 'analyze table big_table compute statistics for
table';
 10      pipe row ( 'Analyzed big table, time is ' || systimestamp || ' going to
do histograms' );
 11      pipe row ( ' ' );
 12
 13      execute immediate 'analyze table big_table compute statistics for
columns object_id, object_name';
 14      pipe row ( 'Histograms done, time is ' || systimestamp || ' going to do
indexes' );
 15      pipe row ( ' ' );
 16
 17      execute immediate 'analyze table big_table compute statistics for all
indexes';
 18      pipe row ( 'Indexes done, time is ' || systimestamp );
 19      pipe row ( ' ' );
 20
 21      return;
 22  end;
 23  /

Function created.

big_table@ORA920.US.ORACLE.COM> set arraysize 1
big_table@ORA920.US.ORACLE.COM> select * from TABLE( long_running() );

COLUMN_VALUE
---------------------------------------------------------------------------------
--------------------------------------------------
Beginning at 17-NOV-02 10.43.03.948031000 AM -05:00 going to analyze big
table...

Analyzed big table, time is 17-NOV-02 10.43.24.056726000 AM -05:00 going to do
histograms

Histograms done, time is 17-NOV-02 10.44.15.340227000 AM -05:00 going to do
indexes

Indexes done, time is 17-NOV-02 10.44.36.587224000 AM -05:00

8 rows selected.

November 17, 2002 Anthony -- Thanks for the question regarding
      "Using Parallel Query.", version 8.1.6.0

      You Asked
Hi Tom,

Nice to type in questions here again.
I'm trying to test the parallel query option. we have compaq 2 cpu and enough
disk controllers to support the disk. test scripts goes like:

SQL> CREATE TABLE TB_PART_PAR
(OWNER                                              VARCHAR2(30),
 OBJECT_NAME                                        VARCHAR2(128),
 SUBOBJECT_NAME                                     VARCHAR2(30),
 OBJECT_ID                                          NUMBER,
 DATA_OBJECT_ID                                     NUMBER,
 OBJECT_TYPE                                        VARCHAR2(18),
 CREATED                                            DATE,
 LAST_DDL_TIME                                      DATE,
 TIMESTAMP                                          VARCHAR2(19),
 STATUS                                             VARCHAR2(7),
 TEMPORARY                                          VARCHAR2(1),
 GENERATED                                          VARCHAR2(1),
 SECONDARY                                          VARCHAR2(1)
 ) PARTITION BY RANGE (CREATED) (
 PARTITION P1 VALUES LESS THAN (TO_DATE('01/02/2002','DD/MM/YYYY')),
 PARTITION P2 VALUES LESS THAN (TO_DATE('01/03/2002','DD/MM/YYYY')),
 PARTITION P3 VALUES LESS THAN (TO_DATE('01/04/2002','DD/MM/YYYY')),
 PARTITION P4 VALUES LESS THAN (TO_DATE('01/05/2002','DD/MM/YYYY')),
 PARTITION P5 VALUES LESS THAN (TO_DATE('01/06/2002','DD/MM/YYYY')),
 PARTITION P6 VALUES LESS THAN (TO_DATE('01/07/2002','DD/MM/YYYY')),
 PARTITION P7 VALUES LESS THAN (TO_DATE('01/08/2002','DD/MM/YYYY')),
 PARTITION P8 VALUES LESS THAN (TO_DATE('01/09/2002','DD/MM/YYYY')),
 PARTITION P9 VALUES LESS THAN (TO_DATE('01/10/2002','DD/MM/YYYY')),
 PARTITION P10 VALUES LESS THAN (MAXVALUE)
 ) PARALLEL 4
/

Table created.

SQL> INSERT /*+ append */ INTO TB_PART_PAR SELECT * FROM DBA_OBJECTS;
...
just populate tables till i reach 1.4m
...
SQL> SQL> SELECT COUNT(1) FROM TB_PART_PAR;

  COUNT(1)
----------
   1471488

SQL> CREATE TABLE TBL_NO_PAR NOLOGGING AS SELECT * FROM DBA_OBJECTS
WHERE 1=0;

Table created.
...
.Populate this table to get the same size as tb_part_par.
.. and create an index.
....
SQL> CREATE INDEX TX_PART_PAR ON
  TB_PART_PAR(CREATED)....
SQL> CREATE INDEX TX_NO_PART ON
  TBL_NO_PAR(CREATED)
.....
....
SQL> set timing on
SQL> set autotrace on
SQL> select count(1) from tb_part_par;

  COUNT(1)
----------
   1471488

Elapsed: 00:00:07.43

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1064 Card=1)
   1    0   SORT (AGGREGATE)
   2    1     SORT* (AGGREGATE)                                        :Q115000
   3    2       PARTITION RANGE* (ALL)                                 :Q115000
   4    3         TABLE ACCESS* (FULL) OF 'TB_PART_PAR' (Cost=1064 Car :Q115000
          d=1471488)

   2 PARALLEL_TO_SERIAL            SELECT /*+ PIV_SSF */ SYS_OP_MSR(COUNT(*)) F
                                   ROM (SELECT /*+ NO_EXPAND ROWID(A2)

   3 PARALLEL_COMBINED_WITH_PARENT
   4 PARALLEL_COMBINED_WITH_PARENT

Statistics
----------------------------------------------------------
         19  recursive calls
        338  db block gets
      17513  consistent gets
      17208  physical reads
        776  redo size
        478  bytes sent via SQL*Net to client
        548  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          3  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> select count(1) from tbl_no_par;

  COUNT(1)
----------
   1471488

Elapsed: 00:00:06.18

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'TBL_NO_PAR'

Statistics
----------------------------------------------------------
         36  recursive calls
        229  db block gets
      17498  consistent gets
      17437  physical reads
          0  redo size
        478  bytes sent via SQL*Net to client
        548  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> show parameter parallel

NAME                                 TYPE    VALUE
------------------------------------ ------- ------------------------------
fast_start_parallel_rollback         string  LOW
optimizer_percent_parallel           integer 0
parallel_adaptive_multi_user         boolean FALSE
parallel_automatic_tuning            boolean FALSE
parallel_broadcast_enabled           boolean FALSE
parallel_execution_message_size      integer 2176
parallel_instance_group              string
parallel_max_servers                 integer 5
parallel_min_percent                 integer 0
parallel_min_servers                 integer 4
parallel_server                      boolean FALSE

NAME                                 TYPE    VALUE
------------------------------------ ------- ------------------------------
parallel_server_instances            integer 1
parallel_threads_per_cpu             integer 2
recovery_parallelism                 integer 4

1. How come the time is almost the same? the table that has parallel should be
faster right (we have 4 disk controllers and 2 cpu)?
2. We have a trigger that assign a user on a variable. When I trace this session
this actually shows me "SELECT USER FROM DUAL". Does ORACLE needs to select from
dual. So if this trigger fired million times we actually have million "SELECT
USER FROM DUAL" to. You once said "selecting from dual many times is a bad thing
, unless you have to.". Same as assigning sysdate to variable.
3. How can I get the current time without selecting from dual? Is there anyway I
can achive this?

Regards,
NOTNA.

      and we said...
1) not necessarily -- for things that take single digit seconds or less to
complete -- parallel query isn't going to do very much.

There is overhead with parallel query that makes itself very evident in things
that run fast.  That and all of your IO is going against a single tablespace
that probably has a single file (you didn't really spread anything out).  Making
the problem even "worse" is you will find MOST of your objects were created on
exactly the same day!

ops$tkyte@ORA920.US.ORACLE.COM> select trunc(created,'mm'), count(*) from
dba_objects
  2  group by trunc(created,'mm');

TRUNC(CRE   COUNT(*)
--------- ----------
01-MAY-02      29680
01-AUG-02          9
01-SEP-02          7
01-OCT-02         29
01-NOV-02         91

(the day you installed or the day the sample datafiles used to seed your
database was stamped out)

For the overhead -- think of it like this (using an analogy).

You are a manager, you have a dozen people that work for you.  Your boss asks
you to write a one page summary of a meeting.  You have a dozen people so you
can either:

a) write it yourself right now.
b) call a meeting
   get the 12 people together
   assign someone to write paragraph 1 (takes time, you have to explain what
      you want, you have to divvy up the work)
   (and so on)
   collect all of their subresults
   put them together into a single paper
   deliver the results

for this one page summary -- it would be infinitely more efficient to do it in
serial mode, by yourself.

Now, your boss asks you to write a book.  12 chapters long.  Each chapter is
relatively independent of the other chapters -- but there will be some
coordination necessary.

You can either

a) write the book yourself (at about 1 hour per finished page)
b) divvy up the 12 chapters amongst your 12 employees
   as they finish, you review and edit them
   you assemble the book
   (this won't take 1/12th the time -- a little more, overhead and all to
    coordinate 12 people -- but faster than writing it yourself)

PQ is for taking massively hard, long things and making them faster.  You have a
short problem (seconds) and you have all of the stuff on one disk.  It is only
about 200meg or less of data (tiny -- i know cause my "big_table" is built on
1,000,000 rows from all_objects and its about 114meg).

2) yes.  You could obviate this by:

create package globals
as
   g_username  varchar2(30) := USER;
   .... others ....
end;
/

create package body globas
as

begin
    ... any complex initialization code you need, runs ONCE per session
end;
/

3) nope, for that -- you need to "goto the kernel".

Review &
      Followup

      Rating: 3
Number 2
      concern  November 17, 2002
Reviewer:  Anthony
      Velarde  from Phil.
actually, this executed on a trigger.

create table o (c1 varchar2(20));

create table o_aud (c1 varchar2(200));

 create or replace trigger o_Trg
 after insert or delete or update on o
 for each row
 declare
 x varchar2(20) := user;
 begin
    insert into o_aud values (:new.c1);
 end;
 /

 begin
   for row in 1..10
   loop
      insert into o values ('test row '||row);
   end loop;
 end;
/

alter session set events '10046 trace name context forever, level 4'

here's the trace file.

SELECT USER
FROM
 SYS.DUAL

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.03       0.03          0          0          0           0
Execute     10      0.13       0.11          0          0          0           0
Fetch       10      0.00       0.00          0         10         40          10
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       21      0.16       0.14          0         10         40          10

Misses in library cache during parse: 0
Optimizer goal: CHOOSE
Parsing user id: 19     (recursive depth: 2)
********************************************************************************

it executed also 10 times... I want it to execute only once since i know it is
the same user that fired this trigger per session. How can I achive this?

Thanks a lot.

Regards,
NOTNA

          Followup:

see the answer to #2.

then change the code to be

x varchar2(20) := globals.g_username;

that was my point in that answer.  use the global instead of the psuedo column
USER.

November 17, 2002 -- Thanks for the question regarding "Porting
      stand-alone application to 9i RAC with two nodes", version 9.0.1.x

      You Asked
Hi Tom , I was looking for information on 9i RAC and its new features. Is it
really true that ANY stand-alone Oracle-based application developed with
Java,Pro*C,OCI,Forms,Reports,VB..etc . can be ported on multi-node 9i Real
application Clusters , "WITH ANY APPLICATION CODE CHANGE " ?? . I mean prior to
9iRAC , developers build code to make application "Cluster Awareness" , So is
this all taken care by you guys ?.

I did seen many white papers and books and did some searched on Metalink on
9iRAC . I understand that "Any stand-alone application which is OCI aware can
ONLY be ported to multi-node 9i RAC" , without application change ...Is this
correct ?

1. Stand-alone Apps which is OCI Aware  = "Cluster Aware"
2. Stand-alone Apps which is Not OCI Aware != "Cluster Aware"

- Great service....Tom...I love this site...

      and we said...
Yes, in general, yes.

There are some exceptional cases that might need looking at -- eg: one that pops
into my head immediately is the development team that refused to use sequences.
They decided to use a table with

ID_NAME     ID_VALUE
--------    ------------
X           0
Y           0

and their method of getting a sequence is:

  update t set id_value = id_value + 1 where id_name = 'X';
  select id_value into l_value from t where id_name = 'X';

that creates a super white hot spot in the database (won't scale in a single
node and scales even less in a N node).

If your application was designed to scale (was correctly coded, scales nicely on
a single node), it'll scale in general on a N node.  If your application has
scalability issues right now and you are thinking "oh, well, I'll just through
more hardware onto it" you'll be disappointed.  RAC won't fix design flaws, in
fact it can magnify them.  If you have a well designed application -- you won't
be disappointed.

The word "port" is a bit strong.  "Deployed" is what I would say.  Sort of like
moving your database from windows to Unix -- you don't need to "port" your
application -- you just move the database and everything works as before.

OCI has nothing to do with it.  anything that can connect to Oracle can connect
to Oracle/RAC.
Review & Followup

      Rating:
      5
  November 17, 2002
Reviewer:
      A reader
Thanks Tom for clearing my miss conception about 9iRAC.

November 17, 2002 Ajeet -- Thanks for the question regarding
      "Oracle to other database communication", version 8.1.7

      You Asked
Hi Tom,
I have 2 questiones  -
1)Can we use Oracle Advance Queuing to get data from databases which are not
oracle -- in my case Informix and SQL server.
If not is there any oracle supplied package which I can use for this.
2)What are the 10(or few most important) parameters I should look  in a trace
file for a sql and pl/sql code to Judge a code as good or bad...I am doing a six
sigma project on SQL and my scope is to setup a process to judge a code good or
bad.When I say parameters then I mean - Logical reads,physical reads etc.I have
your Book Expert one on one but I was not able to make a conclusion , I have
read the chapter on performance tunning.

Thanks and Regards,
Ajeet

      and we said...
1) AQ is a implementation of message queuing software.  The informix of
SQLServer database applications would have to enqueue messages that your Oracle
application would dequeue and process.  So, could it -- yes.  Would it do it
automagically -- no.

There are generic connectivity facilities (using ODBC)

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4406709207206
as well as gateways (part of 9iAS)

http://www.oracle.com/gateways/
the gateways offer much more functionality such as 2 phase commit over the
generic connectivity services but the generic connectivity stuff might be all
you need.

2) Hate questions like this -- there are ALWAYS exceptions to the rules you
know!

But in general pages 459-460 have what you are looking for.  They are not
individual parameters but there is a list there to inspect.

Review &
      Followup

      Rating:
      5
Excellent  November 17, 2002
Reviewer:
      Ajeet  from Cincinnati,OH,USA
Thanks Tom as always it was a great help from you.

November 17, 2002 Lucy -- Thanks for the question regarding
      "Materialized View Fast refresh containing UNION", version 9.2.0

      You Asked
We would like to be able to use fast refresh on a materialised view which
contains a union.
This has worked when the union uses the same table. However it does not seem to
work with a different table name even though the primary key, and columns
selected are identical. We are using Oracle9i Enterprise Edition Release
9.2.0.1.0.

The example that worked:

Table name QH_PAT.
Name                            Null?    Type
 ------------------------------- -------- ----
 FCLTY_ID                        NOT NULL     VARCHAR2(6)
 PAT_ID                          NOT NULL     VARCHAR2(8)
 SEX                             NOT NULL     VARCHAR2(6)
 BIRTH_DATE                      NOT NULL     DATE
 BIRTH_DATE_EST_CODE                           VARCHAR2(6)
 VALID_FROM                      NOT NULL     DATE
 VALID_TO                        NOT NULL     DATE
 FCLTY_UNIQUE_ID                 NOT NULL     VARCHAR2(12)

Primary Key:
alter table qh_pat
add constraint qh_pat_pk
primary key
(
    fclty_id,
    fclty_unique_id
);

Snapshot log  script:
create snapshot log on QH_PAT
with primary key

Materialized View script:
create snapshot qh_pat_mv as
select   fclty_id,
        fclty_unique_id,
        pat_id,
        sex,
        birth_date_est_code
from        qh_pat
where   sex = ‘1’
union
select  fclty_id,
        fclty_unique_id,
        pat_id,
        sex,
        birth_date_est_code
from    qh_ pat
where    sex = ‘2’
/

The example that creates a complex materialized view that will not allow fast
refresh:

Table Name: QH_WORK_PAT
FCLTY_ID                   NOT NULL     VARCHAR2(6)
 PAT_ID                 NOT NULL     VARCHAR2(8)
 SEX                                   VARCHAR2(6)
 BIRTH_DATE                            VARCHAR2(8)
 BIRTH_DATE_EST_CODE                       VARCHAR2(6)
 VALID_FROM                NOT NULL     VARCHAR2(12)
 VALID_TO                             VARCHAR2(12)
 FCLTY_UNIQUE_ID        NOT NULL     VARCHAR2(12)

Primary Key:
alter table qh_work_pat
add constraint qh_work_pat_pk
primary key
(
    fclty_id,
    fclty_unique_id
);

Snapshot log script:
create snapshot log on QH_WORK_PAT
with primary key

Materialized View Script:
create snapshot qh_pat_mv as
select   fclty_id,
        fclty_unique_id,
        pat_id,
        sex,
        birth_date_est_code
from    qh_pat
union
select  fclty_id,
        fclty_unique_id,
        pat_id,
        sex,
        birth_date_est_code
from    qh_work_pat

In the first example using QH_PAT only, I can include ‘refresh fast with primary
key’ in the script. This also is ok if QH_WORK_PAT is used for both queries.
In the second example using QH_PAT and QH_WORK_PAT. If I put ‘refresh fast with
primary key’ in the create script I get the following error.
Creating snapshot QH_PAT_MV
    qh_work_pat
    *
ERROR at line 18:
ORA-12015: cannot create a fast refresh materialized view from a complex query

According to the 9i documentation a union does not make a materialized view
complex unless one of the defining queries is complex. I can not see any mention
of having different table names as a cause of ’complex’.

Have you any ideas.

      and we said...
Can you use a UNION ALL instead of a UNION?

UNION ALLS are supported.

A union B is like:

  distinct (sort ( A + B ))

A union all B is like:

A + B

if you do not need the sort/distinct, UNION ALL is the way to go.  As you can
imagine, trying to do a fast refresh from a UNION view of two tables would be
very very hard.  Suppose you just have two tables with one column each and one
row each.  Each has a row with X=1.  So,

a union b = { 1 }

just one tuple.  Now, you delete from B.  How do you apply that ?  That delete
should have no net effect on the materialized view -- but you don't know that
unless you reprocess the entire A union B.

with a UNION ALL -- it would be possible but you'll need to add some maintenance
columns:

ops$tkyte@ORA920.US.ORACLE.COM> create table t1 ( x int primary key, y int );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> create table t2 ( x int primary key, y int );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create snapshot log on t1 with primary key,
rowid;

Materialized view log created.

ops$tkyte@ORA920.US.ORACLE.COM> create snapshot log on t2 with primary key,
rowid;

Materialized view log created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create materialized view mv2
  2  refresh fast
  3  as
  4  select x, y from t1
  5  union
  6  select x, y from t2
  7  /
select x, y from t2
                 *
ERROR at line 6:
ORA-12015: cannot create a fast refresh materialized view from a complex query

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create materialized view mv2
  2  refresh fast
  3  as
  4  select x, y, 1 umarker , rowid rid from t1
  5  union  ALL
  6  select x, y, 2 umarker , rowid rid from t2
  7  /

Materialized view created.

See

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96520/mv.htm#574889
for the details on this new 9iR2 feature -- you cannot do UNION ALL mv's
before that release

Review &
      Followup

      Rating:
      5
  November 18, 2002
Reviewer:
      A reader

November 18, 2002 A -- Thanks for the question regarding "Tuning
      Areas", version 8.1.6

      You Asked
Hi Tom,

We are doing testing to an Application. Regarding the Database Tuning what areas
should we have to look and what statistics should we need to collect & Analyze
them. Can you give in detail. Please forward me some scripts and relevant
documents on this. If you can suggest some tools it would also be helpful to us.

      and we said...
Nope -- you see -- in order to test your application, what you need to do is
provide

o the expected runtimes
o the maximum allowed execution time
o the transaction load
o the number of concurrent users
o etc

you supply ALL of the parameters to this test.  Then, generally you use a tool
such as statspack and pay attent to the WAITS (ignore top sql, ignore lots of
stuff -- WAITS are the way to tune, remove the bottlenecks -- thats the goal.
Just remember, it is a never ending story so if you do not have YOUR list of
inputs you can never be done!)

When you run your benchmark, if you meet or exceed your stated goals -- STOP.
you are done.

If not, when you discover the waits, you determine the cause, fix the cause and
try again.  Iterate over and over.

Statspack, sql_trace, timed_statistics, tkprof (chapter 10 from my book "Expert
one on one Oracle" are the only tools I use personally.
Review &
      Followup

      Rating: 5
How to find
      Waits  November 17, 2002
Reviewer:  Balaji.M
       from Singapore
Hi Tom,
   In oracle how many types of WAITS are there. Can u refere to me a  place
where i can read about this, and how to find out whether there are any WAITS and
how to resolve these.
   If i give a search for mystat, i get many responses from your site. Where can
i get the code for @mystat.I think the script gives all the statistics of the
server.
    Can u let me know where can i find the documentation of the memory process
of Oracle and v$ tables associated with it.
Thanks and Regards
M.Balaji

          Followup:

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/server.901/a90190/apa.htm#264187
is about waits.

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/server.901/a87503/tools.htm#29125
is about statspack which is the easiest way to find the waits...

@mystat doesn't do that - it simply shows the stats matching a particular string
for the current session.

select a.name, b.value
from v$statname a, v$mystat b
where a.statistic# = b.statistic#
and lower(a.name) like '%' || lower('&1')||'%'
/

is the entire script (but it is NOT what you are looking for)

You want to read this CONCEPTS guide from cover to cover (if you do, and you
retain 10% of the information contained therein, I would put forth that you
would know 90% more then most about Oracle)

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/server.901/a88856/toc.htm
This is the reference manual:

http://technet.oracle.com/docs/products/oracle9i/doc_library/901_doc/server.901/a90190/toc.htm
that in addition to documenting the waits (above) documents the data dictionary
and init.ora parameters...

(amazingly enough ALL of these questions are answered in the documentation --
the CONCEPTS GUIDE -- the single most important Oracle doc, the REFERENCE guide
and the PERFORMANCE guide.)

      Rating: 5
How to find
      Waits  November 17, 2002
Reviewer:  Balaji.M
       from Singapore
Hi Tom,
   In oracle how many types of WAITS are there. Can u refere to me a  place
where i can read about this, and how to find out whether there are any WAITS and
how to resolve these.
   If i give a search for mystat, i get many responses from your site. Where can
i get the code for @mystat.I think the script gives all the statistics of the
server.
    Can u let me know where can i find the documentation of the memory process
of Oracle and v$ tables associated with it.
Thanks and Regards
M.Balaji

      Rating: 5
For
      Balaji.M  November 18, 2002
Reviewer:
      Connor  from UK
Try www.oraperf.com and download the YAPP paper.  Its a little old, but the
mechanics are the still the same

November 18, 2002 -- Thanks for the question regarding "Clob
      Display and Insert", version 817

      You Asked
Hi,
   Environment :j2ee,JSP,Weblogic,Oracle 817 ,3 Tier
   Q-Display)I am using a Refcursor to send record sets to a DAO in Java.
Some of the fields are CLOB type.Can I just simply select from the
Clob field and return it as Refcursor or have to convert it to varchar2 for
display.
AND in General how to handle the Display of clob IF
    Qa)If Length is <= 4000
    Qb)If Length <= 32K
    Qc)If Length >= 32K

Q-Insert) How to Insert values of Clob type field from front-end.
What must be the input parameter for the procedure, should it be varchar2.
     Case : Qa)If Length is <= 4000
            Qb)If Length <= 32K
            Qc)If Length >= 32K

How should java and oracle interace for such cases, I know refcursor will return
record set. Will it have to be looped thru for chunks of 32K, If yes then how?
Any Example will be helpful

      and we said...
q-display) clobs are handled as STREAMS in java.  you'll get a clob, fetch a
clob and open it as a stream.  It works for things of any size.

see

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/java.817/a83724/oralob.htm#1000888
q-insert) same as display -- read the chapter on working with lobs and bfiles.

November 18, 2002 Ajeet -- Thanks for the question regarding "SQL
      script to get all users on different oracle instances.", version 8.1.7

      You Asked
Hi Tom,
In my company there are 200+ oracle instances on 200+ different Unix serves.I am
suppose to produce a report daily ( using a single sql script ) which can give
list of users on each oracle instance - does not matter active or not.
Is there a way to write  this kind of SQL.Do you have any sample script.One of
my Sr DBA told that I can read ONS file for this - but I don't know how to write
this sql.If you can point me to a document or can give me sample script , it
would really help me.

Regards,
Ajeet

      and we said...
Sorry -- I've no such script and I do not know what an ONS file is.

My approach to this would be a little different.  I would set up an account in a
SINGLE database:

create user X identified by X default tablespace USERS temporary tablespace temp
quota unlimited on users;
grant create session, create table to X;

and then as X I would:

create table global_all_users as select rpad( '*', 255, '*' ) global_name,
sysdate last_date, all_users.* from all_users where 1=0;

Now, I would ask that each of the 200 databases out there install a script such
as:

create database link all_users_link
connect to X
identified by X
using YOUR_TNS_NAMES_ENTRY_FOR_YOUR_DATABASE_WITH_USER_X;

create or replace procedure update_global_all_users( NEXT_DATE in out DATE )
as
   l_gname varchar2(255);
begin
   select global_name into l_gname from global_name;

   /* SOME time between midnight and 5am tomorrow -- use the global name
      to spread the 200 refreshes out over that period so we don't slam the
      recieving server */
   next_date := trunc(sysdate)+1+
        dbms_utility.get_hash_value( l_gname, 1, 5*60 )/24/60;

   delete from global_all_users@all_users_link where global_name = l_gname;
   insert into global_all_users@all_users_link
   select l_gname, sysdate, all_users.* from all_users;
   commit;

end;
/

declare
   l_job    number;
begin
   dbms_job.submit( l_job,
                    'update_global_all_users;',
                    trunc(sysdate)+1 );
end;
/

That will run that procedure every night at sometime between minight and 5am.
When you come in in the morning, just query that one table and you can inspect
last_date to make sure all sites are reporting in.

otherwise each of the 200 some odd databases would have to provide you
o their tns names entry
o a username and password you can use

and then you would have to write a script that iterated over this.  I would
rather have ONE account X in my system with those two tiny privs of create
session and create table -- rather then 200 users spread out with passwords that
people might go and change on me and so on.  much easier to manage.

Review &
      Followup

      Rating: 5
It worked for
      me.  November 17, 2002
Reviewer:  Ajeet
       from Cincinnati,OH,USA
Tom - Thanks so much for this script.

          Followup:

just bear in mind to test it -- i just wrote it "by hand" here -- the concept is
sound, the syntax -- well I didn't actually compile it.

      Rating: 4
very
      useful  November 18, 2002
Reviewer:  Manish
      Upadhyay  from WI, USA
tom,
It is useful , but I have a question that we need to install the script on 200
database right? and who (which user) will own that database link, procedure and
then who will submit the job.

          Followup:

You will contact the adminstrators of the 200 databases and advise them to
install this script, make sure job queues are running (so it runs
automagically).

They will "own" the dblink -- it will be in the database they install this into.
 They can install it into any user defined schema (eg: not sys, not system - not
an Oracle schema) they want to that has  sufficient privs to create these
objects.

      Rating: 4
Useful for all
      sorts of things  November 18, 2002
Reviewer:
      A reader


Subject | Article | Last Updated (East coast time)
ORA-01502 error after ALTER TABLE t_name MOVE | article only | Nov 19, 2002 07:29 am
Job scheduling in 9i AS and 9i database | article only | Nov 19, 2002 10:16 pm
Ref Cursor Exception: ORA-1403 : No Data Found | article only | Nov 21, 2002 12:36 pm
Suggestion for books | article only | Nov 21, 2002 12:43 pm
Adjusting text field size | article only | Nov 21, 2002 01:04 pm
Newbie: SQL*Loader, Direct Path, and Sequences. | article only | Nov 21, 2002 01:32 pm
TCL scripts and DML through compiled Java scripts | article only | Nov 21, 2002 02:06 pm
in sql, how to combine 2 rows without reading the table twice | article only | Nov 21, 2002 02:25 pm
Will CBO execution plan change in function of the values entered in the where clause ? | article only | Nov 21, 2002 02:26 pm
How old is "Session Wait" methodolgy ?. Is this method Not Marketed properly :) . | article only | Nov 21, 2002 06:38 pm
FGAC | article only | Nov 22, 2002 07:31 am
Performance overhead of NOT NULL constraints | article only | Nov 22, 2002 07:36 am
Indexes and tables in same tablespace created by Oracle when using default option for database creation | article only | Nov 22, 2002 08:48 am
packege refresh.. i used compile option | article only | Nov 22, 2002 10:18 am
Java procedure in type | article only | Nov 22, 2002 10:22 am
Query to return a Substring | article only | Nov 22, 2002 07:23 pm
dbverify - rbs | article only | Nov 23, 2002 09:22 am
architecture | article only | Nov 23, 2002 09:41 am
Sql statement execution. | article only | Nov 23, 2002 10:16 am
Analytic functions | article only | Nov 23, 2002 11:50 am
Locking problem with counter functionality | article only | Nov 23, 2002 03:18 pm
Java Stored Proc | article only | Nov 24, 2002 01:39 pm
share lock on unindexed foreign keys in 9i | article only | Nov 24, 2002 02:31 pm
Dimensions | article only | Nov 25, 2002 07:32 am
Importing existing tables | article only | Nov 25, 2002 07:41 am
SQLLOADER error ORA-01722. | article only | Nov 25, 2002 08:46 am
Database parameters | article only | Nov 25, 2002 09:05 am
Reversing Alter Table x Set Unused (y); | article only | Nov 25, 2002 12:44 pm
Top 5 Keywords per each job role in a table | article only | Nov 25, 2002 02:44 pm
Error messages in a table. | article only | Nov 25, 2002 07:19 pm
row(s) 1 - 30 of 30
November 19, 2002 Ravi -- Thanks for the question regarding " ORA-01502 error after ALTER TABLE t_name MOVE", version 817
You Asked
Dear Tom,

 I had a table with one index on it. Then I used the cmd "Alter table t_name
move" to move to a
 different tablespace. Then the index which was created on it became unusable
and my select statemtn
 came out with the ORA-01502 error whenever the query tried to use the index.

 Is this the expected behaviour. I could not see this point documented anywhere.
Could you please
 clarify this. Is rebuilding indexes on the tables the only option.

 Example:

 create table test tablespace cdms_index as select * from dba_tables;

 Table created.

 create index test_i on test(owner) tablespace cdms_index;

 Index created.

 select * from test where owner='OPS$ORACLE';

 OWNER TABLE_NAME
 ------------------------------ ------------------------------
 TABLESPACE_NAME CLUSTER_NAME
 ------------------------ ------------------------------
 PCT
 IOT_NAME FREE PCT_USED INI_TRANS MAX_TRANS
 ------------------------------ ----- ---------- ---------- ----------
 INITIAL_EXTENT NEXT_EXTENT MIN_EXTENTS MAX_EXTENTS PCT_INCREASE
 FREELISTS
 -------------- ----------- ----------- ----------- ------------ ----------
 FREELIST_GROUPS LOG B NUM_ROWS BLOCKS EMPTY_BLOCKS AVG_SPACE
 CHAIN_CNT
 --------------- --- - ---------- ---------- ------------ ---------- ----------
 AVG_ROW_LEN AVG_SPACE_FREELIST_BLOCKS NUM_FREELIST_BLOCKS DEGREE
 INSTANCES
 ----------- ------------------------- ------------------- ---------- ----------

 CACHE TABLE_LO SAMPLE_SIZE LAST_ANALYZ PAR IOT_TYPE T S NES BUFFER_
 ROW_MOVE
 ----- -------- ----------- ----------- --- ------------ - - --- -------
--------
 GLO USE DURATION SKIP_COR MON
 --- --- --------------- -------- ---
 OPS$ORACLE EXCEPTIONS
 USER_TBS
 10 40 1 255
 81920 16384 1 505 0 1
 1 YES N 0 1 8 8098 0
 10 8098 1 1 1
 N ENABLED 0 28-MAR-2001 NO N N NO DEFAULT DISABLED
 YES NO DISABLED NO

 OPS$ORACLE TEMP_DOC
 NOVAMANAGE_TBS
 10 40 1 255
 7495680 1024000 1 505 0 1
 1 YES N 6 905 9 117 3
 30 8106 3 1 1
 N ENABLED 6 28-MAR-2001 NO N N NO DEFAULT DISABLED
 YES NO DISABLED NO

 alter table test move tablespace cdms_index;

 Table altered.
 select * from test where owner='OPS$ORACLE';

 select * from test where owner='OPS$ORACLE'
 *
 ERROR at line 1:
 ORA-01502: index 'OPS$ORACLE.TEST_I' or partition of such index is in unusable
state

Thanks in advance.

Regares
Ravi
and we said...
It is absolutely the correct behavior.  An index contains the index entry (the
indexed columns) and a rowid that points to the row.  when you move the table --
ALL indexes built on it now have rowids that are meaningless, they must be
rebuilt.

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:47812348053
for an example.
Review & Followup
Rating: 5
Index March 29, 2001
Reviewer: Ravi Ammamuthu from San Jose, CA US
I got the reply from you in 5 minutes. I wondered and am happy with the accuracy
of the info

Thanks
Ravi

Rating: 4
March 30, 2001
Reviewer: Eric Grancher from CERN, Switzerland

Rating: 4
And in the meantime November 18, 2002
Reviewer: Randy from Austin, TX
When you move a table, how do you avoid this error between the time the related
indexes become unusable and the time they get rebuilt?

I use the '...skip_unusable_indexes=true' for session level stuff, but wondered
about system wide for these table rebuilds to avoid affecting users

Followup:
In Oracle9i you can use the dbms_redefinition package in order to accomplish
this online.

In Oracle8i and before, there was no online redefinition of heap organized
tables.

If the table is READ ONLY during this time in 8i you can:

create table NEW as select...
index new table
drop old
rename new

only during the drop/rename will people be "offlined" -- just like with an
online redef in v9i and up.

Rating: 4
Changing rowid November 19, 2002
Reviewer: Parvez from India
1)In partion table there is an option where rows can migrate from one partition
to another in this case also rowid changes, will the problem of rebuliding an
index come in this case? If not that means index get rebuild automatically? Then
why not same feature in table move command
2)What are other side effects when a rows rowid changes.

Followup:
1) no, it is done for you. the index is NOT rebuilt -- the move partition is
done like a DELETE+INSERT.

why not in the alter table move?  cause you are not moving a SINGLE row  -- you
are moving perhaps millions or billions and you probably want to recreate the
indexes using parallel + nologging or other options.

It is the difference between DML and DDL

2) the rowid changes -- what more can be said?  things that copied the rowid
elsewhere will break.

November 19, 2002 Arun -- Thanks for the question regarding "Job scheduling in 9i AS and 9i database", version 9i
You Asked
Tom,

I need to run Oracle reports and other jobs (via command line interface) and I
am looking for a good technical solution for it.  So far I am considering Oracle
9i Advanced Queuing, 9i AS portal services and reports services.

Please advise me what I should expect for functionality, and issues, with either
of these technologies.  FYI, we use Oracle Apps.' concurrent manager today and
we need something close to it for features, but in a standalone environment.

Thanks,
Arun
and we said...
Well -- the reports server itself has job scheduling but the tool we would
really use for general purpose job scheduling would be OEM (enterprise manager).

see
http://technet.oracle.com/products/reports/htdocs/getstart/docs/A92102_01/pbr_run.htm#1008446
http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/em.920/a96674/overview.htm#1006833

Review &
      Followup

      Rating:
      4
Follow-up  November 18, 2002
Reviewer:
      A reader
Excellent suggestion, I didn't think about it until you mentioned it.  I'll
certainly consider one of the options.

For now, I am leaning more towards the 9i AS reports services because the
command line jobs we'd run are really reports developed with another reporting
tool.  Plus, I like some of the features this offers (like parameter sets).

However, I could not find any documentation on OTN to help me develop my own
reports engine (to process command line jobs).  Any suggestions?

Also, any heads-up in terms of what this might throw at me down the line?  I
have started working on a POC.

I cannot thank you enough for all your help and great tips...

Regards,
Arun

          Followup:

well, if it is a 3rd party reporting tool and not Oracle reports -- the 9iAS
reports stuff (which is Oracle Reports) won't be very useful.

OEM would.

      Rating: 4
Couple of
      follow-up questions on OEM  November 19, 2002

Reviewer:  Arun Shastry  from MA
Thanks for the OEM clarification.  I have a few follow-up questions regarding
OEM and I'll leave you alone :-)

1.  Does OEM provide with any API or even SQL interfaces to schedule jobs
without having to use the console?  Because my application front-end is a web
application, I cannot use OEM console.

2.  What about multiple queues? priority based queues supported?

3.  Any preference between OEM bundled with 8i vs 9i?

4.  How do I pass parameters from OEM to the command line job?

5.  I am looking at documentation from OTN - any suggestions which of the
manuals will address the above questions?

6.  Any suggestions to get a hold on OEM job subsystem data model docs?

Once again, thanks for all your time.

Regards,
Arun

          Followup:

1) you know, i don't know (it would be documented -- I'm not an OEM expert
personally).

but -- it seems anytime I put out an "i don't know" someone follows up ;)

2) yes, you can prioritize within a queue.  You can use the resource manager to
prioritize across users.

3) 9ir2 OEM is coolest

4) see #1 ;)

5) see #1 ;)

6) don't think that is possible (or desirable).

      Rating:
      5
...  November 20, 2002
Reviewer:  Chris
      Slattery  from Dublin
OEMUTIL .... for cmd line submissions...

here's the link....

http://otn.oracle.com/docs/products/oracle9i/doc_library/release2/em.920/a96670/oemutil.htm#631295

November 21, 2002
      Antonella -- Thanks for the question
      regarding "Ref Cursor Exception: ORA-1403 : No Data Found", version 8.1.7

      You Asked
Hi Tom,

the example that I send you it works: it's a SELECT INTO Problem.
I have no Scott Schema anymore, sorry.

CREATE OR REPLACE PROCEDURE Test2 (
    status OUT NUMBER,
    c_value OUT NUMBER
)
IS

BEGIN
    Select firm_oid_fk
    Into c_value
    From T_Firm
    where firm_oid_fk = 1 ;

    status := 0;

END Test2;

Best Regards
Antonella

Hi Tom
I'm sorry I didn't explain the Problem exactly: I apologize for the confusion.
You are right, it's not a RefCursor Problem anymore.
I give you a complete example:

PROCEDURE Test (
    status OUT NUMBER,
    c_values OUT NUMBER
)
IS

BEGIN
    Open c_values For
    Select *
    From T_Firm
    where firm_oid_fk = 1 ;

    status := 0;
END Test;

And the Java Code that calls it, looks like this:

[...]

DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
    Connection conn =
    DriverManager.getConnection ("jdbc:oracle:thin:@wvk-
server.gft.ch:1521:devzent",
                                      "wvk_dbdev", "wvk_dbdev");

      CallableStatement statement =
         conn.prepareCall(conn.nativeSQL("{call Test2(?,?)}"));

    statement.registerOutParameter(1, Types.NUMERIC);
    statement.registerOutParameter(2, Types.NUMERIC);

    statement.executeQuery();

      System.out.println();
        System.out.println("STATUS : " + statement.getLong(1));
        System.out.println("VALUE  : " + statement.getLong(2));
    System.out.println();

    statement.close();
      conn.close();

Well the problem is that If no Data is read a get an Exception and it takes upto
4 times the time it normally takes to execute successfully the Stored Procedure.
Why?? How can I debug what happens in the JDBC Driver?? How can I trace to a
File what happens in the Stored Procedure when I call it from the Java Jdbc
Client??

Best Regards
Antonella

Hi Tom,
here are the Additional Informations:
It's easier to get the error if you declare a stored procedure that has only an
OUT Parameter, e.g. a VARCHAR2:
Procedure NoDataTest(pippo OUT VARCHAR2) as
Select name into pippo from emp;
[...]

Hi Tom,

I'm experiencing a major Performance Problem with Stored Procedures that bring
back Data in a Ref Cursor: every time that No data is selected , Oracle bringt
an Exeption back to my JDBC Client--> "ORA 1403: No Data Found".
The Exception Handling takes a lot of Time: it's too expensive.

How can I avoid that the an invalid Ref Cursor is back to the Client?? Can I
initialize ref cursors??

Best Regards
Antonella Balduzzi

      and we said...
Ok, so this is an issue with regards to catching an exception in a java client
that was raised by the database (nothing at all to do with ref cursors...)

Thanks to the Piermarini brothers -  Mark and Matt - for their help on this one.
 They figured it out.  We don't have a good reason WHY, just that it is this way
(as follows, there is a solution)

So, here are the PLSQL snippets we used:

CREATE OR REPLACE
PROCEDURE Test1 ( status OUT NUMBER, c_value OUT NUMBER)
IS
BEGIN
    Select 1
    Into c_value
    From dual
    where 1 = 1 ;

    status := 0;

END Test1;
/

Test1 Never throws an exception

CREATE OR REPLACE
PROCEDURE Test2 ( status OUT NUMBER, c_value OUT NUMBER)
IS
BEGIN
    Select 1
    Into c_value
    From dual
    where 0 = 1 ;

    status := 0;

END Test2;
/

Test2 always throws an exception

CREATE OR REPLACE
PROCEDURE Test3 ( status OUT NUMBER, c_value OUT NUMBER)
IS
BEGIN
    Select 1
    Into c_value
    From dual
    where 0 = 1 ;

    status := 0;

exception
    when NO_DATA_FOUND then
        status := 1;
END Test3;
/
test3 always throws but also catches and handles the exception and returns a
return code to the caller via status

Then the java we test drove this with was:

import java.sql.*;
import oracle.jdbc.driver.*;
import oracle.sql.*;

class test
{

public static void showTimeStamp( String msg )
{
    System.out.println( (new
       Timestamp(System.currentTimeMillis())).toString() +
                  ":" + msg );
}

static CallableStatement cstmt_persistent;

public static void set_up( Connection conn, String p ) throws Exception
{
    cstmt_persistent =
            conn.prepareCall("{ call "+p+"( ?, ? ) }" );
    cstmt_persistent.registerOutParameter
              ( 1, java.sql.Types.NUMERIC );
    cstmt_persistent.registerOutParameter
              ( 2, java.sql.Types.NUMERIC );
}

public static void no_exception(Connection conn ) throws Exception
{
    if ( cstmt_persistent == null )  set_up(conn,"test1");
    cstmt_persistent.execute();
    cstmt_persistent.getLong(1);
    cstmt_persistent.getLong(2);
}

public static void with_exception(Connection conn ) throws Exception
{
    if ( cstmt_persistent == null ) set_up(conn,"test2");
    try
    {
        cstmt_persistent.execute();
    }
    catch (SQLException e)
    {
        //e.printStackTrace();
    }
}

public static void with_exception2(Connection conn ) throws Exception
{
    if ( cstmt_persistent == null ) set_up(conn,"test2");
    try
    {
        cstmt_persistent.execute();
    }
    catch (SQLException e)
    {
        cstmt_persistent.clearParameters();
    }
}

public static void no_exception_no_data_either(Connection conn )
throws Exception
{
    if ( cstmt_persistent == null ) set_up(conn,"test3");
    cstmt_persistent.execute();
    cstmt_persistent.getLong(1);
    cstmt_persistent.getLong(2);
}

public static void main(String args[])throws Exception
{
    DriverManager.registerDriver
     (new oracle.jdbc.driver.OracleDriver());

    Connection conn=DriverManager.getConnection
      ("jdbc:oracle:thin:@aria-dev:1521:ora816dev"
          ,"scott","tiger");

    DatabaseMetaData meta=conn.getMetaData();
    System.out.println
    ("JDBC driver version is "+meta.getDriverVersion());

    conn.setAutoCommit(false);

    // stmt.execute( "alter session set sql_trace=true" );

    showTimeStamp( "Going to no_exception" );
    for( int i = 0; i < 100; i++ ) no_exception(conn);

    cstmt_persistent = null;
    showTimeStamp( "Going to with_exception" );
    for( int i = 0; i < 100; i++ ) with_exception(conn);

    cstmt_persistent = null;
    showTimeStamp( "Going to with_exception2" );
    for( int i = 0; i < 100; i++ ) with_exception2(conn);

    cstmt_persistent = null;
    showTimeStamp( "Going to with_exception_no_data_either" );
    for( int i = 0; i < 100; i++ ) no_exception_no_data_either(conn);

    showTimeStamp( "done" );
}
}

Now, it'll run a procedure that gets no exceptions just to baseline.

Then, it runs with exceptions every time.  this is noticably slower.

Then it runs with exceptions every time but used "clearParameters" in the
exception handler.  That Clears the current parameter values immediately. In
general, parameter values remain in force for repeated use of a statement.
Setting a parameter value automatically clears its previous value. However, in
some cases it is useful to immediately release the resources used by the current
parameter values; this can be done by calling the method clearParameters. this
runs very fast.

Then, it calls a procedure that gets an exception but handles it and returns a
return code instead.

The output for example was:

$  java test
JDBC driver version is 8.1.6.2.0
2001-03-06 16:38:53.362:Going to no_exception
2001-03-06 16:38:53.569:Going to with_exception
2001-03-06 16:38:58.598:Going to with_exception2
2001-03-06 16:38:58.684:Going to with_exception_no_data_either
2001-03-06 16:38:58.818:done

so you see the big time delay:

2001-03-06 16:38:53.569:Going to with_exception
2001-03-06 16:38:58.598:Going to with_exception2

5 seconds to call that.  Same routine with clearParameters -- less then 1/10 of
a second.

The other option is to catch and handle the exception in PLSQL.

Review &
      Followup

      Rating:
      5
Hey!!!.......Keep it up  November 19, 2002

Reviewer:  sundar nambuvel  from usa
Hey dude...your answer is great... Do you know ...by any chance when a exception
is raised in the stored procedure..will it hold the transactions (which are
dealt in the procedure)in rollback segments to be alive(status=Active) for say 1
hour or something..?!

          Followup:

transactions stay alive UNTIL you commit or rollback -- period.

      Rating: 4
Printing
      RefCursor in SQL*Plus  November 20, 2002
Reviewer:
      Robert  from PA
Tom, re printing refcursor result in SQLPlus...

LMS1@dns > variable rc RefCursor
LMS1@dns > exec tdi_report2.RefCurProcedure2 (:rc, 'SBLD')

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.01
LMS1@dns > print :rc

SCRI
----
WO7
WP1

2 rows selected.

Elapsed: 00:00:00.01
LMS1@dns > print :rc
SP2-0625: Error printing variable "rc"

I'd expect to be able to print it again...
Why is it gone ?
Doesn't Sqlplus cache the refcursor ?
Thanks

          Followup:

A ref cursor is really no different then any other cursor.

What if

LMS1@dns > print :rc

SCRI
----
WO7
...... <snipped> ....
WP1

2,000,000 rows selected.

would you *really* want or even desire sqlplus to cache that?

Once fetched -- the rows are "gone"

November 21, 2002 Arumumgam -- Thanks for the question regarding
      "Suggestion for books", version 9.0.1

      You Asked
Hello TOM,
      Please suggest me some good books which covers the architectural ,
fundamentals including 9i. It should be used to attend the technical interview.
If you know some common interview questions please tell me.

Thank you in advance.

with regards,
sivababu

      and we said...
You only need one book and fortunately -- it is 100% free:

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/nav/docindex.htm#index-CON
The Oracle concepts guide.

If you read that and retain say 20% of it, you'll probably know 50% more then
the interviewer does.  (i'm making up numbers here -- most people have never
bothered to read that one extremely important key piece of literature)

I always ask people to goto the whiteboard and draw me a picture of Oracle --
the processes, the memory, the files -- label them -- and then explain what they
do.  But that is my question (and since only maybe 1 in 10 can do it -- I doubt
many other people actually ask it).

I also ask three very easy (well, two easy and one not so easy) SQL questions.
Goes like this:

you have a table T.

In this table is a column M representing the month.  It has values 1..12 in it
(12 distinct values)

You are in sqlplus -- please tell me the queries you would use to answer:

a) how many rows are in this table?
b) by month, how many rows do we have for each month?
c) and for the bonus question (to see whether 1) they can handle pressure cause
this is a pretty tricky question or 2) they know this stuff cold) -- write me a
single SQL query that returns the most frequently occuring month, and for bonus
points tell me why my question begs more questions from you (eg: my question is
ambigous and in order to get the right answer you need more information from me,
what is that information and why)

Review &
      Followup

      Rating: 4
Valuable
      info... but why ambiguous?  September 12, 2002

Reviewer:  Robert  from Memphis, USA
1) select count(*) from t;
2) select m, count(*) from t group by m;
3) select mon from
     (select m mon, count(*) c
        from t
       group by m
       order by c desc)
    where rownum < 2;

What's so ambiguous about the last question?

Thanks,

Robert

          Followup:

I've read the other reviews upto Connor's

You and Connor both give a CORRECT answer.

You both get DIFFERENT answers however.  Therein lies the ambiguity (Chris Gates
pointed that out)

ops$tkyte@ORA920.US.ORACLE.COM> create table t as select rownum m from
all_objects where rownum <= 12;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select mon from
  2       (select m mon, count(*) c
  3          from t
  4         group by m
  5         order by c desc)
  6      where rownum < 2;

       MON
----------
         1

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> SELECT * FROM
  2    (SELECT m,
  3       RANK() OVER (ORDER BY count(*) DESC ) AS mth_rank
  4     FROM t
  5     group by m )
  6  WHERE mth_rank = 1;

         M   MTH_RANK
---------- ----------
         1          1
         2          1
         3          1
         4          1
         5          1
         6          1
         7          1
         8          1
         9          1
        10          1
        11          1
        12          1

12 rows selected.

ops$tkyte@ORA920.US.ORACLE.COM>

btw, here is my favorite answer to #3 for the SQL part.  Favorite cause it uses
obscure stuff and works in any/all releases of Oracle and is ANSI (neither of
the above are that)

ops$tkyte@ORA920.US.ORACLE.COM> select m
  2  from t
  3  group by m
  4  having count(*) >= all ( select count(*) from t group by m );

         M
----------
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12

12 rows selected.

      Rating: 4
Ambiguity
      through ties?  September 12, 2002
Reviewer:
      Chris Gates  from Ann Arbor, MI USA
Robert -
Your solution for number 3 reveals an ambiguity regarding ties.  For example if
month 1 and 2 had an equal number of rows, your query would only return one (1
OR 2), and given your query, your result is not deterministic (as it depends on
optimization goals, indexes, etc.) so for the same data set there's no guarantee
it will return the same result from one execution to the next.

      Rating: 5
I'm going to
      guess...  September 12, 2002
Reviewer:  DBA
       from Columbus, OH
The ambigous part of the question is that we don't know what months the numbers
represent.  Is it the start of a calendar year or fiscal year? For example, if
it is a fiscal year, month 1 could represent October. If you are looking for
just the most frequently occuring numerical representation of the month, that is
straight-forward.  If you are looking for an actual month, we'll need to know
some more information.

Just a guess.

      Rating: 4
Ambiguous
      Question  September 12, 2002
Reviewer:  Dwayne
      Croteau  from Calgary, Alberta
Tom,

The question is ambiguous because you are asking for the frequency of the month,
but you are not telling us what the period is.  What's your period?  How many
rows?

      Rating: 5
Or just to be
      fancy...  September 12, 2002
Reviewer:
      Connor  from UK
SELECT * FROM
  (SELECT m,
     RANK() OVER (ORDER BY count(*) DESC ) AS mth_rank
   FROM t
   group by m )
WHERE mth_rank = 1;

Cheers
Connor

      Rating:
      5
  September 12, 2002
Reviewer:  Dave
       from Ohio
With regard to your favorite method ...

select m
from t
group by m
having count(*) >= all ( select count(*) from t group by m );

... how would the use of the subquery factoring method ...

with month_rows as
   (select m,
    count(0) row_count
    from t
    group by m)
select *
from   month_rows
where  row_count >= all (select row_count from month_rows)

... affect the performance/ANSI compatability/obsurity?

I favour it for the increase in obscurity, myself, but I'm unsure whether it
improves performance. 9i Documentation says "Oracle optimizes the query by
treating the query name as either an inline view or as a temporary table." Does
this mean that the factoring query might get "materialized" as a temp table for
the duration of the query? If so, I'm thinking that it might be enough to "close
the gap" with performance of an analytical function methodology for a large
table query

          Followup:

Yeah -- but mine works with sql89 (the original), yours is sql92 or better -- or
is the with sql99.  getting too hard to remember.

It would be ansi
But there would only be about 2 or 3 databases that could do it
Yes, mine is not performant
Yours should be better
rownum probably is the fastest
rank is the easiest to understand...

      Rating: 4
You've given
      the game away...  September 13, 2002
Reviewer:
      Adrian Billington  from UK
Tom

How about a job? I reckon I should be OK in the technical interview at least...
;)

Adrian

      Rating: 4
Why the SQL-101
      questions ?  September 13, 2002
Reviewer:
      Robert C  from PA
>>I also ask three very easy (well, two easy and one not so >>easy) SQL
questions.  Goes like this:

Uh, Tom...what's point of asking the SQL-101 questions when you ask the (poor)
guy to give a lecture on architecture ?
(It's like asking a physicist to talk about nuclear fission , then ask him to
write out chem. formula for water... :) )
Did I miss something ?

          Followup:

I ask the SQL ones first to see if we should even continue.

You might be AMAZED at how many people with SQL on their resume cannot do the
first query (how many rows)....

The architecture one is more DBA'ish (although I firmly believe it should be
well known by developers) actually -- I might let a developer slide on that one.

      Rating: 3
For the heck of
      it...  November 20, 2002
Reviewer:  Kashif
       from Houston, TX
Hi Tom,

I thought I'd apply a trick I had picked up from your website to question #3,
though I came across some interesting results which I thought I'd share with
you.

select substr (data, instr (data, '/')+1)
  from (select max (c||'/'||m) data
          from (select m, count (*) c
                  from t
                 group by m))
/

returns 9, which is correct from the perspective that it returns a month which
has the highest frequency (any month would be correct for that matter...), but
kind of surprising, since I thought it would have returned 12 (1/12 being sorted
to the top/maximum compared to 1/9). Am I missing something here? I've noticed
you use this trick only on dates though... Thanks in advance.

Kashif

          Followup:

Your approach wont work

You need to use something SORTABLE in STRINGS.  just gluing c || '/' || m won't
do it.

you would need to fix length the C field:

  max( to_char( c, '00000000000000000009' ) || '/' || m )

will work if C is ALWAYS positive (and fits in the format!)

when you use this "trick" on numbers -- you need to fix width the field so the
strings sort AND you need to know that it is a POSITIVE NUMER (there are tricks
you can play with decode to deal with negative numbers).

Same with STRINGS -- you need to rpad them to a fixed length

and with DATES you need to make them sortable as a string using yyyymmddhh24miss
which is a fixed length number that sorts "dates" correctly.

November 21, 2002 Munzer -- Thanks for the question regarding
      "Adjusting text field size", version 8.1.7

      You Asked
Tom:

I have an HTML form.  One of the fields is item description.

 htp.p('<TD><TEXTAREA name="i_item_description" cols=40 rows=2 readonly>');
    htp.p(y.item_description);
    htp.p('</TEXTAREA></TD>');

I am creating another form that prints what the user enter.

Problem is that one item may have 1 line of description, and another may have 20
lines of descritpion.

What I am trying to do in the second print version form is build the <textarea
cols=40  rows='||i||'>'

dynamically. so that it will only show the data and not a lot of white space.

Do you know how to do this.

I am thinking of counting how mnay lines in the field and based on that set i to
equal that.  Is there  function in oracle that counts number of lines in a
column.

Is there other ideas.

Thank you,

      and we said...
number_of_lines := length(string) - length(translate(string,chr(10),''))+1;

should do that.  Just remove the chr(10)'s (newlines) and see how much shorter
the string is.

Review &
      Followup

      Rating: 5
textarea
      tag  March 29, 2002
Reviewer:  munz
       from Reston, USA
Tom:

what is chr(10) do?

When I enter data into <textarea> field and I hit hard returns, it seems that it
goes to database as one long line because when i print it later it does not
format it as it was entered.

How can you print the text with hard returns and spaces entered into the
textarea tag.

          Followup:

chr is a function.

10 is the ascii code for a newline.

The data has a newline in it when save -- it is most likely that you are
printing the text in straight HTML.  HTML ignores whitespace unless you use a
PRE tag.

when you htp.p( the_text ), you can either:

htp.p( '<pre>' || the_text || '</pre>' );

or

htp.p ( replace( the_text, chr(10), '<br>' ) );

that is, replace the newlines with the <br> tag telling HTML to print out the
text with breaks.

      Rating:
      5
textare  September 20, 2002
Reviewer:
      mo
Tom:

1.  Do you know why the text does not wrap in a textarea column in NEtscape 4.7
while it does in IE 6.0.

2. Do you recommend any javascript site for questions/answers like yours?

Thank you

          Followup:

what tags are you using, this should wrap in all

<textarea NAME="p_t01" ROWS=16 COLS=80 WRAP="VIRTUAL"></textarea>

I know of no javascript site like this, no.

      Rating: 3
wrapping single
      line text  November 21, 2002
Reviewer:  Vikas
      Sharma  from Delhi India
Hi Tom,

I have a case in which user enter the text in the textarea field of a form but
does not press enter, so all the text get stores  in the database in one line. I
have a another form which display the data form this field. When i prints it it
gets print into a single long line which increases my page width and horizontal
scroll. In my reqiurment i need to print it in side <code> and <pre> tags.
somthing like <code><pre>my_long_multiline_text</pre><code> There is any way i
can avoid it by wrapping the text automatically. Or i have to write my
procedure/function

          Followup:

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2308223998065
...

      Rating: 5
wrapping single
      line text  November 22, 2002
Reviewer:  Vikas
      Sharma  from Delhi India
Hi Tom,

Thanks lot. I was looking for this only.

Regards,
Vikas Sharma

November 21, 2002 Jeff -- Thanks for the question regarding
      "Newbie: SQL*Loader, Direct Path, and Sequences.", version SQL*Loader:
      Release 8.1.6.1.0

      You Asked
I'm trying to use SQL*Loader to rapidly load our database with
data from an outside source. I read about Direct Path loading
with SQL*Loader, and attempted to perform this action, but
encountered a problem which maybe you can help me figure a way
around it.

Because this data is from an outside source, I produce my own
internal IDs as the primary key to guarantee uniqueness in my
system (I can't "trust" the outside source, and I need to load
all data no mater what).

Here's an abbreviated example of the of the problem I'm
encountering.

My table looks like this:

    CREATE TABLE THE_NAMES
     (NAMEID NUMBER(38) NOT NULL,
      NAME VARCHAR2(4)
     )
     INITRANS 1
     MAXTRANS 255
     PCTUSED 40
     PCTFREE 20
     STORAGE
     (
       INITIAL 3145728
       NEXT 155648
       PCTINCREASE 30
       MINEXTENTS 1
       MAXEXTENTS 255
       FREELISTS 1
       FREELIST GROUPS 1
     ) TABLESPACE NAME_DATA
     NOCACHE

    ALTER TABLE THE_NAMES
     ADD (CONSTRAINT TNA_PK PRIMARY KEY
      (NAMEID)
     USING INDEX TABLESPACE NAME_IDX)
    /

The sequence I'm using looks like this:
    CREATE SEQUENCE NAMEID_SEQ
     NOMAXVALUE
     NOMINVALUE
     NOCYCLE
     NOCACHE
    /

I have a control file which looks like this:

    LOAD DATA
      INFILE 'THE_NAMES.DAT'
      TRUNCATE
      INTO TABLE THE_NAMES
      (NAME                POSITION(01:04)   CHAR,
       NAMEID              "NAMEID_SEQ.NEXTVAL"
       )

I then try the following statement with SQL*loader with the
following
error message:

   $ sqlldr user/password@server the_names.ctl direct=true
errors=1000 data=./THE_NAMES.DAT

   SQL*Loader: Release 8.1.6.1.0 - Production on Wed Feb 14
14:51:10 2001

   (c) Copyright 1999 Oracle Corporation.  All rights reserved.

   SQL*Loader-417: SQL string (on column NAMEID) not allowed in
direct path.

I realize you can't make function calls during a direct load,
but I do need to insert a unique id for every row. If there is
some way to do this after the fact, that would be great.

Do you have any suggestions on what I could do? I want to do
direct load for speed, and so most everything else is secondary.

Thanks.

Jeff Braun
jeffb@halcyon.com

      and we said...
What you can do is before the load,

select nameid_seq.nextval from dual;

So, now you know the sequence is 102,323

In the control file you would code:

nameid  sequence(102350,1)

see

http://technet.oracle.com/doc/oracle8i_816/server.816/a76955/ch05.htm#5012
for details.

After the load, you would:

alter sequence nameid_seq increment by <records loaded+1000>;
select nameid_seq.nextval from dual;
alter sequence nameid_seq increment by 1;

and you are back in business (as long as no one uses the
sequence whilst you are loading the data.  if you believe they
will -- alter the sequence by some number BIGGER then the number
of records you will be loading BEFORE you start the load and let
sqlldr have the "gap" you just created)

Review &
      Followup

      Rating: 4
Can you pass
      the starting number as an argument?  November 21, 2002

Reviewer:  Christie  from St. Louis, MO USA

I realize this is an old question, but it fits our needs pretty well.  I do have
one question that I'm struggling with.

Since we need to do multiple loads using this technique every month, it would be
ideal for us not to have to go into each control file and manually edit them
each month.  Is there a way to pass the number to begin counting with into the
control file, instead of hardcoding it?

I'd like to be able to do this
     nameid  sequence($start_seq,1)
instead of
     nameid  sequence(102350,1)

Any suggestions?

          Followup:

see

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76955/ch05.htm#5023
it outlines using sequence, max, count and others.

You could use MAX(nameid) instead of sequence.

November 21, 2002 A -- Thanks for the question regarding "TCL
      scripts and DML through compiled Java scripts", version 8.1.7

      You Asked
Hi Tom,
One of the applications' DBs that I support here has web enabled interface
written in HTML, client and server side Java scripts,
iPlanet web server and 817 on HP-UX box.

Application's main objective is reporting but has data-load interface which is
web enabled too.
User can trigger a (moderate) data loading activity from the web interface. The
same loading could be started from the server side using cron or manually.

This particular loading activity is written in Java scripts which is compiled in
the html files. Most of data validations, pre-load data lookups, some
preliminary business logic is written in tcl scripts.

I can trigger the dataload by calling a particular url with required parameters
through the tcl scripts after data validations as such.

My questions now are -
1. Are there any implications towards scalability and performance if we use the
tcl and javascript approch?
2. I searched for tcl and oratcl documentation and got a few links on the web
but, I am curious to know if Oracle has some stock of documents on oratcl?
(apart from some references in OEM manuals)
3. What are advantages of using oratcl and not perl?
4. Do you see any obvious flaw in this design (not that I have given you
sufficient details but still) ?

Thanks much,
AB

      and we said...
1) as long as they use bind variables to do the loads and they run fast enough
-- not that I'm aware of.  There major limiter is

a) they cannot do direct paths
b) they are not generally "fast"

I don't know about tcl but java/jdbc supports batching and it should use it (if
you use the oracle jdbc driver, its a setting on the statement or connect, very
very easy)

2) I've never used tcl myself, I'm not aware of any but -- you might try the
internet usenet newsgroups -- tons of people out there and they might have some
feedback (free access via groups.google.com)

3) oratcl is just tcl really.  perl is perl.  they are just languages.  If you
know one and not the other -- the choice would be clear.  If you know both, you
would pick the one best suited for the job.  If you know neither - well, I won't
be of much help as I've never used either of them myself ;)

4) I would have used SQLLDR myself.  I hate writing code when I don't have to.
A loader is a very boring piece of software.  I would have used plsql for
validation, done it in the database.
Review &
      Followup

      Rating: 4
Thanks for the
      feedback  November 22, 2002
Reviewer:
      A reader
I shall look for bind variable use and java/jdbc batching.

Thanks,
AB

November 21, 2002 Stephane -- Thanks for the question regarding "in
      sql, how to combine 2 rows without reading the table twice", version 8.1.7

      You Asked
I'm building a tool to extract data from statspack tables.
All data is stored using the snap_id. For example, I want to find the CPU used
by this session, so I need to subtract the value field from two rows to get the
cpu used by this session between 2 snapshots.
I want to avoid to read the value for snap_id 32 then reread the table to get
the value for snap_id 33. I've looked at the new analytical SQL possibilities
but did not find what I need.

      and we said...
in general:

select .... <all other columns> ....,
       MAX( decode( snap_id, 32, COLUMN, NULL ) ) old_value,
       MAX( decode( snap_id, 33, COLUMN, NULL ) ) new_value
  from t
 where snap_id in ( 32, 33 ) and .....
 group by .... <all other columns> ....

does that very nicely....

November 21, 2002 -- Thanks for the question regarding "Will CBO
      execution plan change in function of the values entered in the where
      clause ?", version 9.2.0

      You Asked
When I finish all optimizations for my query and happy with an execution plan
which show me all desired indexes used, will this plan remain unchanged for any
values which may be entered for columns in the where clause ?

      and we said...
If you use bind variables
If you never change any init.ora parameters
If you never re-analyze anything
If nothing ever changes

Yes.

Otherwise -- no, all bets off.  That is *by design*.  As the data changes a CBO
is supposed to change plans with it.  That is the entire goal of a CBO.

If you want to "freeze" the plan -- read about query plan stability (stored
outlines).
Review & Followup

      Rating:
      4
  November 21, 2002
Reviewer:
      A reader

November 21, 2002 -- Thanks for the question regarding "How old is
      "Session Wait" methodolgy ?. Is this method Not Marketed properly :) .",
      version 8.1.7.x

      You Asked
Tom , Thanks for your great service. Sorry for long text ...

I am facing real problem convincing people in interviews especially in areas of
DATABASE Performance tuning and query optimization , that most efficient method
to tune any Oracle-based system is "Session Wait" method rather then old
traditional "Ratio-Based" method ( which we all used for nearly a decade).
I did practiced  "session waits" methodology for past 15 months after I attended
 below seminar / read books .I had very good trace record on performance tuning
and practiced exactly what these below Gurus said about performance
management...

1. Advanced Performance Management for Oracle Based Systems by Craig Shallahamer
 - Great Holistic Approach   www.orapub.com
2. Performance Tuning 101 By Gaja Krishna  -  Great Gaja Method - The book which
speaks about all Myth & Folklore about performance management.
3. Oracle SQL High-Performance Tuning - Guy Harrison  -  This is my favorite
book , its totally changes the way one tune the sql
4. I am going to read your book "Expert one-on-one"  .  I just purchased it  :)

In Most of the interviews ( 6 out of 8 ) ,  the interviewers are totally unaware
of this new approach or never practiced even they know that this method exists ,
but again they all believe that database tuning can be done very easily by
tweaking some parameters depending on "GUT" feeling of an experienced person.

And one guy asked  "Can't you just change some parameters to set "More buffer
cache","More SGA" or Can't you use your experience to directly tune the database
instead of  looking into "wait events" various statpacks and wasting time...some
Folks asked  "Don't you have any thumb rules" to set the Oracle memory
structures.

To all these people my answer  is same "There are no magic numbers " to set
memory structures (SGA)..or tune the database ...you need to start with some
reasonable numbers and start collecting stats using ( statspack, or
v$system_events,V$session_events,V$session_waits...) and try to eliminate Top
waits events , untill you get desired performance.  They never asked about wait
events questions ( which I expected for example  ( If you see large "Enqueue"
waits ,what this means to you and how you solve ..) they never asked such
questions ...I hope only reason is ,because they don't have knowledge about
these "wait event" or don't trust.

Most of them were looking answers which for more like  quick / easy / handy /
Magic kind / GUT feeling kind /Thumb rule kind of solutions to solves
performance issues.

I have seem many questions on your site ,which fall into same above category of
issues..example "my cache-hit ratio is low ? how do I increase" .."Thumbs rules
to size sag" ..."Indexing  strategies on row selectivity rather then using # of
I/O blocks " ...etc...

MY Question is :

How old is "Session Wait" methodology ? Why there are some many folks still uses
"Ratio-Based" method in today's world. Is this method is Not marketed properly
:) or Is this method is in initial phases of getting popular ? .

      and we said...
Because people read books and believe them 100%.  They are not creative enough
to test the theories, to see if they hold water.  They need to be told what to
do.

With ratios, it is easy to develop ROT (rules of thumb -- but the acronym is so
appropriate here -- ROT).  Cache hit below 95% -- increase it.  there you go.

But, if you start looking at wait events, well, now you have a problem.  Is
there a pat answer to wait event "x" -- not typically.  You sort of have to
understand how the system works and if it is even reasonable to try to reduce
that wait.  It requires thinking, it requires knowledge.  It is not trivial.

That and there are thousands of tools out there that rely on ratios.  They
blink, they flash red, they look really important.  But unfortunately the
information they provide is generally meaningless.

Don't know what to tell you here.  I have never -- i mean as in never -- used a
cache hit ratio or any ratio by itself in fact to "tune".  Never.  I look for
low hanging fruit (tkprof reports), use statspack (for WAITS <<====) and thats
about it.

I guess you could lie -- during the interview praise the all mighty cache hit
ratio -- just to get in and then corrupt them all.  It would be doing a good
thing in the end.

What say others out there?

Review &
      Followup

      Rating:
      5
  November 21, 2002
Reviewer:  Jim

Tom hit the nail on the head
when he said too many people buy
books and believe everything it says.

During an interview I try to gauge the
level of knowledge of the interviewers

If the are uptodate then I feel comfortable
answering with updated methods.

However if the interviewers hint at hoping to hear
"magic" ratios and parameter values I
tell them what they want to hear
then once I have the job prove better
ways to tune.

Sometimes during the interview
after providing the answers they want to hear I will
say something like " although according to Tom Kyte/ Guy Harrison / Jonathon
Lewis' book  and proven correct by my
own investigations a more efficient method is to etc etc"

      Rating: 5
RE: What say
      others out there?  November 21, 2002
Reviewer:
      Mark A. Williams  from Indianapolis, IN USA
Boy, an open invitation to express a rant on AskTom!

I went shopping for a new car the other day.  The ratio of Highway MPG to City
MPG was 1.15%.  Is that good?  Maybe.  Maybe not.

The problem with ratios (from my little perspective) is that they really don't
tell you a heck of a lot.  Plus, people typically derive them from a sample
period that is way too large.  Easy to hide the details in a large sample
period.  And we all know what is in the details, right?

Ratios seem to have been engendered with the same kind of voodoo that the x$
tables, "_" init parameters, etc... have been cloaked in for sometime now.  (I
did read somewhere that if you have the chicken's feet in just the right
position, and your buffer cache hit ratio is just the right amount, it is like
setting fast=true in your spfile.)  This is also reminiscent of the "single
extent" myth.  How come most "myths" seem to be based on inaccuracies?

The "cult of the ratio" makes things too simple, I think.  It is almost like you
don't need to know anything about a particular system.  Just call up the "ratio
hotline", tell them your ratios, and they can send the magic spell right on over
to fix that.  Wouldn't it be nice to know what is actually driving the numbers?

At OracleWorld I had someone say to me how much they liked the DBA tools from a
particular vendor, and why weren't we using those tools where we work...  The
reason why the tool was such a big hit?  It has pretty pictures.

I guess "the" paper that says it all is at www.hotsos.com...  Although Chapter
10 of Expert is a pretty darned good resource too...

Another $0.02 into the bucket...

- Mark

      Rating: 5
I sounded like
      a SALE guy , when I start comparing ratio vs waits methods
      :)..  November 21, 2002
Reviewer:
      A reader
Thanks guys for your time .... The statement made by Jim
"During an interview I try to gauge the level of knowledge of the interviewers"
...seems very good tip for me..

I guess I did ignored to gauge the interviewer knowledge , before answering
about any performance methodology questions and I may be sounded like a SALE guy
"trying to sell the waits method" , when I started comparing ratio-based method
and with its deadly sins of performance management.

      Rating: 5
Early
      waits...  November 22, 2002
Reviewer:  Connor
      McDonald  from UK
The earliest reference to using wait stats I've seen is Anjo Kolk's YAPP paper
written in June '99.  So its been out there for long enough - but all it took
was a quick walk about some of the presentations and booths around OracleWorld
to see a lot of people still in the dark...

Case in point: Next to the OakTable booth was a product that claimed a 10000%
percent performance boost by adding an extra proprietary cache on the database
server.  So you end up with:

- Disk level cache (track buffers etc)
- File system cache (on most sites I've seen)
- Oracle buffer cache

and now another cache on top of that...

Gimme a break :-)

      Rating:
      5
  November 22, 2002
Reviewer:  Chris
      Slattery  from Dublin, Ireland
Excellent stuff.

Not everything about ratios is bad, though, as long as you are experienced
enough to know what you are looking at.

The newbies must start somewhere. Not everyone can design their system from
scratch, and therefore must do what they can... and a lot of people go for the
(what seems to be)low-hanging fruit -e.g buffercache hit without looking to see
what will actually be cached and what will be directly read.

As Tom repeatedly says, read the Concepts manual [ and the SQL Reference ! heh
heh ]  BEFORE Designing and Tuning for Performance (or any of the oracle books)
Tom's book is the best one i've seen for the 'in-your-face' "PROVE IT"  You seem
to have taken a leaf from Dick Feynman - ' if a theory diagrees with experiment
(even once) it is WRONG'

November 22, 2002 Krishna -- Thanks for the question regarding
      "FGAC", version 8.1.6

      You Asked
Tom,
Finally it's my turn. I don't know where my list of questions is.
I remember 1 question from the list. FGAC- Can we use this feature to stop
end_user from accessing data during the normal business hours. The end
user/developer  wants to create a snapshot of Our oracle data and dump it into
SQL Server. We have an agreement with the user that he should do it only during
the night time after 8 PM but as any other DBA I don't believe the developers
and I want to implement FGAC so he will not see any rows during the normal
working hours. How can I do it?. By doing so I will be learning a lot too.
Thanks again

      and we said...
yes, definitely.  Just take the simple example I have at:

http://asktom.oracle.com/~tkyte/article2/index.html#Ex1
and change the policy to:

SQL> create function my_security_function( p_schema in varchar2,
  2                                        p_object in varchar2 ) return
varchar2
  3  as
  4  begin
  5      if ( user = 'THEIR_ACCOUNT' ) and
                to_number(to_char(sysdate,'hh24')) between 9 and 20 the
  6          return ' 1=0 ';
  7      else
  8          return '';
  9      end if;
 10  end;
 11  /

from 0900 to 2000 hours -- no data in the table for them
Review &
      Followup

      Rating: 5
Can't remember
      the specifics but...  November 22, 2002
Reviewer:
      Connor McDonald  from UK
...isn't there a potential problem with things like time in the security
function, like if a query got parsed at 19:00 but continues to be executed after
20:00 ?

If memory also serves, there were some changes in more recent versions to
address this...but once again, I can't recall the specifics

Signed
Too-lazy-to-look-it-up

Cheers
Connor

          Followup:

Yes, if they parse the query at 9pm but don't execute it until 10am the next
morning they could "get around this" HOWEVER -- they would also tend to hit
ora-1555's ;)

If this is a concern, a job (dbms_job) can be scheduled at 9am and 8pm to
execute dbms_rls.refresh_policy.  that'll flush the cursors, causing them to
reparse.

November 22, 2002 Tony -- Thanks for the question regarding
      "Performance overhead of NOT NULL constraints", version 8.1.6

      You Asked
Tom,
Is there any difference in terms of oracle processing for DML operations on the
following two options for creating the column definition?
Option1:
create table t ( a number default 1);
Option2:
create table t (a number default 1 not null);
Thanks

      and we said...
You cannot compare them.

The statement:

create table t ( a number default 1);

creates a table whereby A may be null:

   insert into t (a) values ( NULL );

whereas:

create table t (a number default 1 not null);

creates a table whereby A may NOT be null -- that insert would fail.

They are not remotely equivalent.  It matters not if the second one takes 2x or
200x as long -- the fact is "if a is NOT allowed to be null -- you had better
define it that way else a NULL value will at some point be put in there"

(it is neither 2x nor 200x -- that is for literary exageration)...  My runstats

http://asktom.oracle.com/~tkyte/runstats.html
says:

ops$tkyte@ORA920.US.ORACLE.COM> create table t1 ( x int default 1 not null );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> create table t2 ( x int default 1 );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5
  6      cursor c is select dummy from dual this_is_explicit;
  7      l_dummy  dual.dummy%type;
  8  begin
  9      insert into run_stats select 'before', stats.* from stats;
 10
 11      l_start := dbms_utility.get_time;
 12      insert into t1 select object_id from all_objects;
 13      for i in 1 .. 10000
 14      loop
 15          insert into t1 (x) values (i);
 16      end loop;
 17      l_run1 := (dbms_utility.get_time-l_start);
 18      dbms_output.put_line( l_run1 || ' hsecs' );
 19
 20      insert into run_stats select 'after 1', stats.* from stats;
 21      l_start := dbms_utility.get_time;
 22      insert into t2 select object_id from all_objects;
 23      for i in 1 .. 10000
 24      loop
 25          insert into t2 (x) values (i);
 26      end loop;
 27      l_run2 := (dbms_utility.get_time-l_start);
 28      dbms_output.put_line( l_run2 || ' hsecs' );
 29      dbms_output.put_line
 30      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 31
 32      insert into run_stats select 'after 2', stats.* from stats;
 33  end;
 34  /
380 hsecs
384 hsecs
run 1 ran in 98.96% of the time

PL/SQL procedure successfully completed.

they are pretty much the same.

Review &
      Followup

      Rating: 5
And whats
      more...  November 22, 2002
Reviewer:  Connor
       from UK
...NOT NULL does a lot more benefit for you than just ensuring something cannot
be null...

eg

SQL> create table x1 ( r number, p char(50));

Table created.

SQL> create table x2 ( r number, p char(50) NOT NULL);

Table created.

SQL> insert into x1 select rownum,rownum
  2  from sys.source$
  3  where rownum < 10000;

9999 rows created.

SQL> insert into x2 select rownum,rownum
  2  from sys.source$
  3  where rownum < 10000;

9999 rows created.

SQL> alter table x1 add constraint x1_pk primary key (r);

Table altered.

SQL> alter table x2 add constraint x2_pk primary key (r);

Table altered.

SQL> analyze table x1 compute statistics;

Table analyzed.

SQL> analyze table x2 compute statistics;

Table analyzed.

SQL> set autotrace on statistics
SQL> select count(p) from x1;

  COUNT(P)
----------
      9999

Statistics
----------------------------------------------------------
          0  recursive calls
          4  db block gets
    ==>  86  consistent gets
          0  physical reads
          0  redo size
        368  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> select count(p) from x2;

  COUNT(P)
----------
      9999

Statistics
----------------------------------------------------------
          0  recursive calls
          4  db block gets
    ==>  21  consistent gets
          0  physical reads
          0  redo size
        368  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          1  rows processed

=============
On an unrelated note...Since you've mastered the "complexities" of presenting
questions/answers in a monospaced font, maybe you could pass on this on to the
Metalink group for their TAR's and forum postings. Surely a "<pre>" can't be
that hard a change to the software?

Cheers
Connor

          Followup:

It is an art form to do everything in Courier ;)

      Rating:
      4
Thanks  November 22, 2002
Reviewer:
      A reader
Great explanation!
I am in the process of designing a Data Warehouse application. There are three
fact tables, and couple of dimension tables. The data in fact tables is loaded
with 5-10 Million rows in each month per table. There are about 50% of the
columns in fact tables, which can be null. But there is a debate that null
columns should be made as NOT NULL with some default value (-), as with OLAP
tools, end users using adhoc queries need to know the where clause semantic 'IS
NULL' etc. But at the same time, load performance is critical. Moreover, with
some columns the data will be either null or of low cardinality. So, with some
value for null can be useful for creating bitmap index, and can result in
improvement in query results. Could you please give your expert opinion on this?

          Followup:

well, in a bitmap index -- NULLS are indexed.

ops$tkyte@ORA920.US.ORACLE.COM> create table t ( x int );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create bitmap index t_idx on t(x);

Index created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> exec dbms_stats.set_table_stats( user, 'T',
numrows => 1000000, numblks => 100000 );

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA920.US.ORACLE.COM> select count(*) from t where x is null;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=13)
   1    0   SORT (AGGREGATE)
   2    1     BITMAP CONVERSION (COUNT)
   3    2       BITMAP INDEX (SINGLE VALUE) OF 'T_IDX'

ops$tkyte@ORA920.US.ORACLE.COM> set autotrace off

so, thats not the concern.

I would say you can either train your end users to use

o X is null

or train them that, when X is null, X will really be the value -99999999999 (for
numbers) or if X is a date, it will be 01-jan-1000 or if X is a string it will
be "-"

I would much rather go with "X is NULL".  Seems easier then making them remember
3 different values and consider what happens with aggregates like MIN, MAX, SUM,
AVG, etc -- they IGNORE nulls but would have to CONSIDER your "fake" values.

I would definitely discourage stuffing something in there in general.  There are
times and places where it makes sense, but not as a general RULE.

      Rating:
      5
  November 22, 2002
Reviewer:  Dave
       from Ohio
I generally prefer to substitute a value for nulls in DW fact tables for the
following reasons ...

i) You probably want to provide a description for the null value, such as "Not
Known" or "Not Applicable" which would have to be stored in the dimension table.

ii) If so, you will want to have an FK defined to the dimension table holding
this value, which would not allow the value to be null.

iii) third-party tools do not often respond well to having to join a nullable
fact column to a dimension table. Instead of fact.col1 = dim.col1, you need
(fact.col1=dim.col1 or (fact.col1 is null and dim.col1 is null)), which is
messier and would probably have a negative impact on your ability to use
materialized views and query rewrite.

November 22, 2002 Vijay -- Thanks for the question regarding
      "Indexes and tables in same tablespace created by Oracle when using
      default option for database creation", version 8.1.7

      You Asked
Hi Tom,
      Once again a small doubt, it is said (read in documents and some books)
not to create indexes and tables in same tablespace but when a database is
created by Oracle using default options you'll find that around 227 of indexes
of Sys and System user are created in System tablespace (which is the same for
tables on which indexes are created), any specific reasons for the same.

I understand that Sys is the owner of DataDictionary schema and System is the
basic tablespace when the database is created but other tablespaces such as one
for index is created, when one uses default options to create a database so why
not the indexes are placed on that tablespace.

Thanks in advance.

Regards,
Vijay.

      and we said...
Books are many (thousands of) times WRONG.

Unless they have an empirical proof, unless they have evidence, unless they tell
you WHY -- ignore what they say.  If a statement begins with

o it is my opinion that....
o I think that ....
o I feel it is best to ....
o and so on

without factual reasoning behind it, question the validity of that statement.

Read

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:5071430793941
for my thoughts on this (and do not be concerned about the fact that the indexes
and tables are in the same tablespace for SYS and SYSTEM)

Review &
      Followup

      Rating:
      4
  December 06, 2002
Reviewer:  Vijay
      Sehgal  from India
Thanks as always Tom, I'll keep your suggestion in mind always.

November 22, 2002 obul -- Thanks for the question regarding
      "packege refresh.. i used compile option", version 8.1.6.0

      You Asked
 hi Tom,

this  is  reddy trying to  compile  the list of packages
 in a user
1.alter package <p_name> compile body ;

then  i received the following error

LINE/COL ERROR
--------
--------------------------------------------------------
0/0      PL/SQL: Compilation unit analysis terminated
71/1     PLS-00452: Subprogram 'GET_ORD_ITEM_COUNT' violates its
         associated pragma

 then i checked list   of    oracle supported library in that
the following points are covered..

1. check and place exception with pragma with (WNDS) option
2. Replace the function with procedure..
3.Re-write the packages..

don't know what to do..

thanks regards..

      and we said...
That error is saying that you have coded in the package
specifiction:

   procedure get_ord_item_count( .... );
   PRAGMA RESTRICT_REFERENCES
            (get_ord_item_count, <purity levels here> );

And you specified a purity level that get_ord_item_count
violates.  For example, you might have put "RNDS" in the pragma
-- asserting that get_ord_item_count READS NO DATABASE STATE
(does no selects) but -- it does, hence it violates the pragma
OR something it CALLS violates that pragma.

In order to diagnose, one needs to look at the spec to see the
asserted purity levels and verify the code does not violate any
of them.

Review &
      Followup

      Rating: 5
Explain with
      Examples  November 21, 2002
Reviewer:  Dad

Tom

can you please expain with examples as to how WNDS, WNPS
can be violated.

What do they actually mean, and how does the violation occur. Can you please
create an example function and violate its pragma to exaplin the two.

Thanks

          Followup:

WNDS = you do an update of some sort (seems fairly obvious doesnt it?  WNDS=
WRITE no database state).

if your procedure WRITES to the database -- it'll violate that pragma.

WNPS = you do an update to a PACKAGE global variable.  You write to the package
state.  You modify a variable defined outside of a procedure.

Since these are really obsolete as of 8i, it matters not really.  You never need
specify them anymore.

      Rating: 5
Since these are
      really obsolete as of 8i, it matters not really  November
      21, 2002
Reviewer:  Dad
"Since these are really obsolete as of 8i, it matters not really"

? ... can you expand on the above... how does 8i handle this situation..

          Followup:

you don't need to put the purities in there, they are figured out automagically
for you.  they were a requirement in packaged functions/procedures before that.

      Rating:
      5
again  November 22, 2002
Reviewer:
      Dad
So if I create a function within a package and

1.try to update a table
2.change the state of a package global variable

then even without declaring WNDS, and WNPS  as the purity levels, I should get
an error..

I'm I right...

Can you prove the above

Thanks

          Followup:

can't you? (seems pretty darn straight forward doesn't it????)

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t (x int);

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package demo_pkg
  2  as
  3          function writes_database return number;
  4          function writes_package return number;
  5  end;
  6  /

Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package body demo_pkg
  2  as
  3          x int;
  4
  5          function writes_database return number
  6          is
  7          begin
  8                  insert into t values ( 1 );
  9                  return 0;
 10          end;
 11
 12          function writes_package return number
 13          is
 14          begin
 15                  x := nvl(x,0)+1;
 16                  return 0;
 17          end;
 18  end;
 19  /

Package body created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select demo_pkg.writes_database from dual;
select demo_pkg.writes_database from dual
       *
ERROR at line 1:
ORA-14551: cannot perform a DML operation inside a query
ORA-06512: at "OPS$TKYTE.DEMO_PKG", line 8
ORA-06512: at line 1

ops$tkyte@ORA817DEV.US.ORACLE.COM> select demo_pkg.writes_package from dual;

WRITES_PACKAGE
--------------
             0

you can update the package state -- it just prohibits that function from being
executed IN PARALLEL.

      Rating:
      5
link  November 22, 2002
Reviewer:
      Dad
Can you gimme the link to oracle documentation about the purity levels in 8i, or
the changes to purity levels in 8i.

thanks

          Followup:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a76939/adg10pck.htm#6048

November 22, 2002 Michel -- Thanks for the question regarding "Java
      procedure in type", version 9.0.1

      You Asked
Hi Tom,

When you create a type, is it possible to give as a member procedure
a java procedure that is not owned by the owner of the type?

For instance, USER1 creates a java source:

create or replace java source named "Hello" as
   public class Hello {
      static public String Msg(String tail) {
         return "Hello " + tail;
         }
      }
/

USER2 creates a type:

Create or replace type mytype as object (
   mydata varchar2(10),
   static procedure myproc
      is language java name
         '<how to name USER1's Hello.Msg?> (java.lang.String)';
   );
/

Thanks

      and we said...
http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/java.920/a96659/03_pub.htm#36009
...

Defining Call Specs: Basic Requirements

A call spec and the Java method it publishes must reside in the same schema
(unless the Java method has a PUBLIC synonym).
.....

So, it could look like this HOWEVER I would discourage this approach.  I
would put the call spec in USER1 and have USER2 call the call spec using the
schema name.  You don't want a proliferation of public synonyms out there -- it
gets nasty real quick.

ops$tkyte@ORA920.US.ORACLE.COM> create user user1 identified by user1;

User created.

ops$tkyte@ORA920.US.ORACLE.COM> create user user2 identified by user2;

User created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> grant create session, create procedure, create
public synonym to user1;

Grant succeeded.

ops$tkyte@ORA920.US.ORACLE.COM> grant create session, create type to user2;

Grant succeeded.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> @connect user1/user1
ops$tkyte@ORA920.US.ORACLE.COM> set termout off
user1@ORA920.US.ORACLE.COM> REM GET afiedt.buf NOLIST
user1@ORA920.US.ORACLE.COM> set termout on
user1@ORA920.US.ORACLE.COM> create or replace AND COMPILE java source named
"Hello" as
  2     public class Hello {
  3        static public String Msg(String tail) {
  4           return "Hello " + tail;
  5           }
  6        }
  7  /

Java created.

user1@ORA920.US.ORACLE.COM>
user1@ORA920.US.ORACLE.COM> grant execute on "Hello" to user2;

Grant succeeded.

user1@ORA920.US.ORACLE.COM> create public synonym "Hello" for "Hello";

Synonym created.

user1@ORA920.US.ORACLE.COM>
user1@ORA920.US.ORACLE.COM> @connect user2/user2
user1@ORA920.US.ORACLE.COM> set termout off
user2@ORA920.US.ORACLE.COM> REM GET afiedt.buf NOLIST
user2@ORA920.US.ORACLE.COM> set termout on
user2@ORA920.US.ORACLE.COM>
user2@ORA920.US.ORACLE.COM> Create or replace type mytype as object (
  2     mydata varchar2(10),
  3     static FUNCTION myproc( p_str in varchar2 ) return varchar2
  4        is language java name
  5           'Hello.Msg(java.lang.String) return java.lang.String'
  6     );
  7  /

Type created.

user2@ORA920.US.ORACLE.COM>
user2@ORA920.US.ORACLE.COM> begin
  2          dbms_output.put_line( myType.myproc( 'tom' ) );
  3  end;
  4  /
Hello tom

PL/SQL procedure successfully completed.

Review &
      Followup

      Rating: 5
Public Synonyms
      in General  November 22, 2002
Reviewer:  Tom
      Best  from PA
So, Tom, about public synonyms.  I've been in shops where EVERYTHING is
PUBLIC-SYNONYMed.  Tables, stored procs.  What guideline do you use to determine
when this is going to get out of hand?  Is it just name domain collisions that's
the issue?

          Followup:

there are performance issues to consider as well.

logistically -- it is a NIGHTMARE.  Totally heinous.  Would never permit it.

For a couple of TOP LEVEL objects (eg: on asktom we have 1 -- it is called "F"
-- see the URL) maybe.  For anything else -- never.  Use qualified schema's --
make your install scripts prompt you for schema names (so you can "parameterize
it" upon install but have it hard coded for runtime)

      Rating:
      5
  November 22, 2002
Reviewer:  Michel
      Cadot  from France
Thanks Tom for your reply.
I miss that point about publishing a Java method.

In addition, i totaly agree with you on the public synonym.

Regards

November 22, 2002 Dick -- Thanks for the question regarding "Query
      to return a Substring", version 8.1.6

      You Asked
Tom,
   I have a VARCHAR2 column that contains a value like this:
'C12345 64TXNU6574NY d567484'.
   We'd like to write a query that returns just the portion of the string that
contains 'TXNU'.  That is, given the above example, we'd like to get
'64TXNU6574NY'.  The original column value may be one or more 'parts' with each
of the parts separated by a space.  The TXNU portion could be in any of the
parts, not necessarily in the second part as it is in this example.
   Thanks in advance for your help.

      and we said...
ops$tkyte@ORA920.US.ORACLE.COM> variable x varchar2(80)
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> exec :x := 'C12345 64TXNU6574NY d567484'

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select
      substr( :x, instr( :x, ' ', -1*instr(:x,'TXNU') )+1,
                 instr( :x, ' ', instr(:x,'TXNU') )
                       -instr( :x, ' ', -1*instr(:x,'TXNU') )-1 )
  3    from dual
  4  /

SUBSTR(:X,INSTR(:X,'',-1*INSTR(:X,'TXNU'))+1,INSTR(:X,'',INSTR(:X,'TXNU'))-INSTR(
:X,'',-1*INSTR(:X,'TXNU'))-1)
---------------------------------------------------------------------------------
-----------------------------------------------
64TXNU6574NY

ops$tkyte@ORA920.US.ORACLE.COM>

is one way.
Review & Followup

      Rating:
      5
  November 22, 2002
Reviewer:
      A reader
Thanks, Tom.

      Rating: 3
One thing
      missed  November 22, 2002
Reviewer:  Jim
      Stuver  from Rochester, NY
What if the string you are searching for is at the beginning of the field that
does not have a space in front.  This question was to include that part as well.

          Followup:

I messed that up didn't I.  Here you go:

ops$tkyte@ORA920.US.ORACLE.COM> variable x varchar2(55)
ops$tkyte@ORA920.US.ORACLE.COM> variable y varchar2(25)
ops$tkyte@ORA920.US.ORACLE.COM> exec :x := 'C12345 64TXNU6574NY d567484'

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> exec :y := 'TXN'

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select
  2        substr( ' '||:x||' ',
  3                    instr( ' '||:x||' ', ' ', instr( ' '||:x||' ', :y
)-length(' '||:x||' ') )+1,
  4                instr( ' '||:x||' ', ' ', instr( ' '||:x||' ', ' ', instr( '
'||:x||' ', :y )-length(' '||:x||' ') ),2)
  5                         -instr( ' '||:x||' ', ' ', instr( ' '||:x||' ', :y
)-length(' '||:x||' ')-1 ) ) data
  6  from dual
  7  /

DATA
----------
64TXNU6574
NY

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> exec :y := '123'

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> /

DATA
----------
C12345

ops$tkyte@ORA920.US.ORACLE.COM> exec :y := 567

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> /

DATA
----------
d567484

(might be time to hide that in some plsql)

      Rating:
      3
  November 22, 2002
Reviewer:  Mita
       from NJ, US
You can use Case for that...

select
      substr( :x,
           case when instr( :x, ' ', -1*instr(:x,'TXNU') )+1 > instr(:x,'TXNU')
then 1
                else instr( :x, ' ', -1*instr(:x,'TXNU') )+1
           end,
                 instr( :x, ' ', instr(:x,'TXNU') )
                 -
             case when instr( :x, ' ', -1*instr(:x,'TXNU') ) > instr(:x,'TXNU')
then 0
             else instr( :x, ' ', -1*instr(:x,'TXNU') ) -1
           end) from dual

      Rating: 5
U DA
      MAN  November 25, 2002
Reviewer:  JIm Stuver
       from Rochester, NY
Looks great.  Thanks for the help.

      Rating:
      5
Thanks  November 25, 2002
Reviewer:
      Dick Babiarz  from Rochester, NY
This is great, Tom, thanks.

November 23, 2002 -- Thanks for the question regarding "dbverify -
      rbs", version 8.1.7

      You Asked
Should dbverify be used on datafiles of rollback segment tablespaces ?

      and we said...
it can be -- sure.

if you use dbv as part of your process, use it on your rollback.  Rollback is as
important (if not more so during recovery) as your real data.
Review &
      Followup

      Rating:
      5
dbv  November 22, 2002
Reviewer:
      atul  from india
Sir,

Can we use dbv for online datafiles..or should we have to make datafile offline
and then dbv??

Thanks.
atul

          Followup:

You can and will get spurious "errors" (that are not errors) checking an online
datafile as they are undergoing change.  dbv was really designed for offlined
files.

November 23, 2002 oralearner -- Thanks for the question regarding
      "architecture", version 8.15

      You Asked
1--In real world when a database is designed how do dbaS calculate its
components' size(datafiles,logfiles,SGA). I want  to calculate and design a
database as per 100 entries a day. can you give me guidance over architecture &
calculations and some web links for guidance.

2--- in your book you say --"My approach is to do everything I can in the
database. If my requirements go beyond what the database environment can offer,
I do it in Java outside of the database. ---
sir I don't really uderstand these lines you mean while developing a program you
try to do every thing in database rather in forms.
I hope my question is clear to you.
thankyou.

      and we said...
1) 100 entries per day doesn't mean anything to me.

datafiles are a function of the data you have to put in there.
If you have 500 gig of data -- that will drive the size of your datafiles.  If
you have 50meg -- that will drive the size of your datafile.

Logfiles are a function of the amount of redo you generate per minute.  Very
little redo generated -- small logs, lots -- bigger.

SGA is a function of the size of the machine, the number of users, the size of
the database.

There are no rules of thumb here, experience is the best teacher.

2) Here is what I mean (by example).

I need to schedule a recurring job to move some data from table1 to table2.
That is, I need to "archive" some data.  I could use the UNIX cron facility and
a shell script with perl to do this OR I could use PLSQL + DBMS_JOB *inside* the
database to do this.    I will choose PLSQL + DBMS_JOB to do this.

I need to have a an email sent out whenever a particular row is updated with a
value that exceeds a threshold (eg: when the INVENTORY on hand of something
falls below 5 units -- i want an email).  I could write a program in some
language that periodically polls a table looking for this condition and sends an
email when it finds it.  I could also write a trigger that used a java stored
procedure to send an email straight away.  I will use a java stored proceudre +
trigger (utl_smtp in 816 and up)

I need to make sure idle connections are disconnected from the database after 20
minutes of inactivity.  I could write a monitor program that wakes up every 20
minutes using the OS scheduling facility, looks for idle sessions and terminates
them.  Or, I could use a resource profile that limits idle time to 20 minutes
and assign that to users in the database.  I will use the resource profiles.

My point is -- there are perhaps an infinite number of ways to do things.  I
choose to look for how to implement something within the database, using the
database facilities.  In that fashion I am 100% portable (sending email from a
trigger on NT is the same as Unix).  I am certain my jobs only run when the
database is up and running (not so with cron or "at").  And so on.  I use the
database as my operating system -- only looking outside of it when it is not
possible (or practical) to do it inside the database.

Review &
      Followup

      Rating:
      3
architecture  April 10, 2002
Reviewer:
      oralearner  from us
1.I wanted to write 1000 by mistake I wrote 100.
but still I am confused how to start my calculations

2. 5 star answer.

          Followup:

100, 1000, 1000000 -- doesn't matter.

N entries a day -- that doesn't compute.

My answer however stands:

There are no rules of thumb here, experience is the best teacher.

I might start with:

1meg of ram * number of concurrent users
50meg shared pool
50meg buffer cache

and work up from there.  It is 100% impossible to say -- the above is sort of a
minimal configuration, something to start with.

sizing files - wholly 100% totally impossible, you have to do that based on what
you put in there.

      Rating:
      5
architecture  April 10, 2002
Reviewer:
      oralearner  from us
thankyou tom
atleast i get a start clue.
sometimes the  first step seems to be difficult.
Is there any manual I can read about this.

          Followup:

The admin guide for each platform has rudimentary sizing -- but it won't tell
you about the SGA sizing -- thats experience, knowledge of how its used (read
the concepts guide from cover to cover) and testing.

      Rating:
      5
  November 22, 2002
Reviewer:
      A reader
Regarding #2 of the original question:

I like your approach. Can I have your openion on this?

We have a data warehouse build which loads up the tables with data and then
recreate indexes, collect statistics etc. Due to issues with our data feeds, we
have to truncate and reload the tables everytime.

We are trying to make things faster by running jobs parallely. We are using
parallel Direct loads for Sqlloader. For creating indexes, we have functions
which will read the index ddl from ut_fil_dir. We are running all these jobs at
o/s level. Will there be any advantage/disadvantage if we schedule the
jobs(except sqlldr jobs) inside the d/b? I assume that, may be we can monitor
the resource usage in a better way inside the d/b and use parallel processing
more effectively. Some DBAs are of the openion that dbms_jobs will be slower
compared to o/s level schedulig. I can't believe that. What would be your
approach to this. Can there be any benefit at all to move these jobs inside the
d/b? Thank you for your time.

          Followup:

In 9i -- i would do it all in the DB with external tables and parallel automatic
tuning.

You get all of the advantages of parallel direct path loads.
Without having to mess with the OS.

In 8i -- since some of it has to be done "outside" -- from a coordination
perspective -- it may be easier to do it all outside.

      Rating:
      5
  November 23, 2002
Reviewer:
      A reader
Thanks for answering.

I understand about external tables. But it will take some more time for us to
get there. What we really wanted to know was if dbms_jobs has any
advantage/disadvantage as against o/s scheduling from a performance standpoint.
I am aware of the convenience of doing it all from either inside the db or
outside. I am looking at the performance gain only. I know that if it is done
inside the d/b, I can monitor the resource usage in a better way (without
depending on the Unix SAs). But there will be a host of other parameters like
job_que_processors/buffers which might affect the performance. I wanted to get
your openion specifically on that. Hope I am clear about what I am asking.

Thanks again for your time.

          Followup:

dbms_jobs and using the os will have no performance differences cause at the end
of the day -- the create statements are all about SQL and that is where the time
will be spent.

dbms_jobs has a limit as to the number of jobs it'll run at a time (so does cron
by the way, it'll stop doing things as the load explodes).

dbms_job has a period (interval), it'll only peek for jobs every N seconds.

there are no performance gains EITHER WAY (could not be) or performance hits.

November 23, 2002 Saibabu -- Thanks for the question regarding "Sql
      statement execution.", version 8.1.7.4

      You Asked
Hi Tom,

I have this simple question unanswered since long long time. I know Oracle does
syntax, semantic checks, execution path, optimize  (parsing either soft or hard)
before executing any sql statement.

Where does Oracle keep all the data being fetched or retrieved. If I say select
* from big_table, does it keep additional image of data buffers if the same is
being modified by some other session.

Does it keep all the resultset in cursor? If so, we never see big pga or uga
size.

I understand if the query is being sorted it goes to temporary segment after
exhausting sort_area.

I tried getting this information from 'pmap -x pid", more or less same memory as
I see in pga memory.

What is the difference between persistent area and runtime area. I am little
confused with Oracle documentation on these areas. They say persistent area is
same for subsequent runs if the cursor is shared. does this mean runtime area
contains resultset. But I see from v$sql persistent_mem is almost constant and
runtime_mem keeps changing but is at very low value. Please explain.

Thanks.

      and we said...
The answer is -- it all depends.

If you submit a query like "select * from one_billion_row_table" to the database
-- the results will not be "stored anywhere".  As you begin fetching and ask us
to retrieve the first row -- we will go to that big table and get the first row
for you and return it.  The cursor simply remembers where in the result set it
was (points into the big table).  The next request for a row picks up where the
other began.  You start getting data immediately, we don't copy it anywhere.

Now, suppose you issue "select a, count(*) from one_billion_row_table group by
a".  A couple of things could happen here:

o "A" could be indexed and NOT NULL (or A has a bitmap index).  The index might
be scanned in order to give you the answer

o "A" is not indexed -- now we decide to full scan the table and group by A
(generally implies a sort)

Looking at each.  Suppose we do the index trick.  Here, when you ask for row 1
-- Oracle will start scanning the index -- from the front to the back.  When it
hits the index, it'll get the first "A" value and start counting.  When it hits
an index entry whereby "A"'s value changes -- it'll return that count and the
cursor will "remember" where it left off.  Next row just picks up where we left
off.

Suppose we full scan.  Oracle will start scanning the table building a temporary
result set in your sort area that has "A" and a count.  If you overflow your
sort area, it'll go to TEMP and be stored there.  When the result set is built
(or even partially built, we may be able to return some rows before finishing
the entire query) -- Oracle will shrink the sort area down to the retained size
(putting out to temp whatever doesn't fit) and return the answers from your
retained sort area/temp.

The persistent area is stuff that can be shared across all sessions -- the query
itself for example.

The runtime or session specific area is the state variables YOUR session has
maintained regarding that cursor -- eg: the SCN in place at the time your query
began.  The current location a result set you might be.  Things specific to your
session.

The result set is not part of the cursor -- it is pointed to by a cursor perhaps
but not part of.

(this discussion should also point out why we cannot tell you how many rows a
query will return when you open it - we ourselves most likely have no idea --
we do not prebuild result sets in general)

November 23, 2002 Adolph -- Thanks for the question regarding
      "Analytic functions", version 8.1.7

      You Asked
Tom,
How can I modify this script using Analytic function so that my Sum(sal) column
will display just once for each DeptNo group as show below? (that is instead of
450,450,450 for each detail line of Deptno 10, I want just a single instance of
450. )

Thanks,
  Adolph.

select a.*, sum(sal) over ()
from (
select deptno, sum(sal) sal
from emp
where deptno in (10, 20)
group by deptno, sal) a

DEPTNO        SAL     Sum(sal)
------ -----------------------
    10        150
    10        150
    10        150      450

    20        100
    20        100
    20        200      400

    30        200
    30        200
    30        200      600

      and we said...
how about giving me a real query that matches your sample.

sum(sal) over ()

returns a constant for every row -- there is no partition by deptno hence what
you have and what you ask for are not even nearly close.

Sooo, I think your query is really:

scott@ORA920.US.ORACLE.COM> select deptno, sal, sum(sal) over ( partition by
deptno ) sum_sal
  2    from emp
  3  /

    DEPTNO        SAL    SUM_SAL
---------- ---------- ----------
        10       2450       8750
        10       5000       8750
        10       1300       8750
        20        800      10875
        20       1100      10875
        20       3000      10875
        20       3000      10875
        20       2975      10875
        30       1600       9400
        30       2850       9400
        30       1250       9400
        30        950       9400
        30       1500       9400
        30       1250       9400

14 rows selected.

that matches your output above -- a list of salaries in the department vs the
sum of sal in that deptno

We could use sqlplus formatting to get it once per:

scott@ORA920.US.ORACLE.COM>
scott@ORA920.US.ORACLE.COM> break on sum_sal skip 1
scott@ORA920.US.ORACLE.COM> select deptno, sal, sum(sal) over ( partition by
deptno ) sum_sal
  2    from emp
  3  /

    DEPTNO        SAL    SUM_SAL
---------- ---------- ----------
        10       2450       8750
        10       5000
        10       1300

        20        800      10875
        20       1100
        20       3000
        20       3000
        20       2975

        30       1600       9400
        30       2850
        30       1250
        30        950
        30       1500
        30       1250

14 rows selected.

or in a less efficient fashion trick the analytics into giving it to us:

scott@ORA920.US.ORACLE.COM>
scott@ORA920.US.ORACLE.COM> select deptno, sal,
  2         decode(last_value(rowid) over (partition by deptno ),
  3                    rowid, sum(sal) over (partition by deptno), null ) x
  4   from emp
  5  /

    DEPTNO        SAL          X
---------- ---------- ----------
        10       2450
        10       5000
        10       1300       8750
        20        800
        20       1100
        20       3000
        20       3000
        20       2975      10875
        30       1600
        30       2850
        30       1250
        30        950
        30       1500
        30       1250       9400

14 rows selected.

Review &
      Followup

      Rating:
      5
  November 23, 2002
Reviewer:
      A reader  from Atlanta
If Analytic Rocks, SO DO YOU !

A thousand thanks, Tom.
Adolph

November 23, 2002 Geetha -- Thanks for the question regarding
      "Locking problem with counter functionality", version 8.1.6

      You Asked
In our application we would like to implement queueing for a shopping cart kind
of functionality.

We have the values for "maximum available" and the "actual taken" in a table for
the item.
When the user adds an item to his cart we want to check if actual_taken <
maximum_available
If yes then we want to update the actual_taken field to actual + 1 and
reserve an item for the user.

To do this we are doing a select for update to lock the actual_taken
field.
Then we compare the actual_taken with the maximum_available
if actual_taken < maximum_available then we update actual_taken =
actual_taken + 1.
Commit;

However during this brief period the record is locked and any other
person trying to do this will be given an error message as the select
for update is with nowait.
If the select for update is with Wait then the others will be queued up until
the lock is freed.

We would like to have the users queued up but if there are a large
number of users queued up will they be waiting forever. Is there a
possibility for the webpage to timeout ?
Is there any better way to implement this functionality with queueing ?

      and we said...
why would they wait forever?  they will be processed very rapidly -- I mean --
look at the transaction, what does it take, maybe 0.01 seconds?  it would take
thousands of users all wanting the same exact item to get any sort of
appreciable wait.

You can speed it up even more by doing it in a SINGLE statement like this:

ops$tkyte@ORA920.US.ORACLE.COM> create table items ( id number primary key,
act_avail int, max_avail int );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into items values ( 1, 4, 6 );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create or replace function get_item( p_id in
number ) return number
  2  as
  3      l_id  number;
  4  begin
  5      update items
  6         set act_avail = act_avail+1
  7       where id = p_id
  8         and act_avail < max_avail
  9      RETURNING id into l_id;
 10
 11      return l_id;
 12  end;
 13  /

Function created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> exec dbms_output.put_line( 'returned ' ||
nvl(to_char(get_item(1)), 'YOU LOSE') );
returned 1

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> exec dbms_output.put_line( 'returned ' ||
nvl(to_char(get_item(1)), 'YOU LOSE') );
returned 1

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM> exec dbms_output.put_line( 'returned ' ||
nvl(to_char(get_item(1)), 'YOU LOSE') );
returned YOU LOSE

PL/SQL procedure successfully completed.

when you get NULL back from this function -- you lose, no more in the inventory.
 When you get the item id back, you win, you updated the row if and only if the
act_avail met your criteria.

No need to double hit the database -- this'll be so fast at the row level that
you would need thousands of users hitting the same item at the same time.

Now all you have to do is solve the problem of what happens when people put
something in their cart and then just exit the browser.  How are you going to
put that item BACK?  I wouldn't bother with this as people put things INTO the
cart.  I would only do it when they actually check out.  Thats how I see most
pepole doing similar things.
Review &
      Followup

      Rating:
      5
  December 01, 2002
Reviewer:
      A reader
Thanks for the great response. It was very helpful

      Rating:
      5
  December 01, 2002
Reviewer:
      Geetha
Thanks for a really helpful and timely response. The solution was really
described very well.

November 24, 2002 Graham -- Thanks for the question regarding "Java
      Stored Proc", version 9.2.0

      You Asked
I am trying to write a stored proc in Java and have come up against a situation
where I would (in PL/SQL) use the following:

DECLARE
   CURSOR crACursor AS SELECT column1, column2 FROM mytable;
BEGIN
  FOR data IN crACursor LOOP
    ....
    /* Use the data returned */
    ....
  END LOOP;

END;

but the nearest I can find is SelRowIter. What how can I get at the data in it?
Short of using a PL/SQL Construct to call the Java code I can not see any way
round the problem.

Thanks in advance.

Graham Harris

      and we said...
typically you use jdbc (you can use sqlj if you like but jdbc is more common)

here is an example of processing a result set:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/java.817/a83724/basic1.htm#1006487
but this is really more of a "how do I write programs in Java" question then a
real database question....

You'll want to read up on Java/JDBC...  For most things, you'll find PLSQL to be
much easier and faster when it comes to "doing things SQL"
November 24, 2002 Dmitry -- Thanks for the question regarding
      "share lock on unindexed foreign keys in 9i", version 9i

      You Asked
Hello Tom,

I have a question for you about a new feature in Oracle 9i. It is regarding
Oracle behaviour when we try to update/delete the primary key in a table and we
have unindexed foreign key(s) referenced to that PK.
What I read in the Oracle doc about it is:

"Oracle no longer requires a share lock on unindexed foreign keys when doing an
update or delete on the primary key. It still obtains the table-level share
lock, but then releases it immediately after obtaining it."

Could you please clarify how it works?
I did not found any clue on that in the documentation.

Thank you in advance,

      and we said...
In the past (7.2..8i, the behaviour was subtly different in 70/71) -- if you did
a delete on P or an update of X on P -- the table C would be locked for the
entire DURATION of the transaction that did the update/delete.  In 9i -- the
table C is still locked, but for a much shorter duration.  Just during the
delete/update itself.

So, the lock is still there -- still an EXCELLENT idea to index the fkey if you
update the primary key OR delete from the parent table and you have
modifications being made to the child.  The lock is just of a much shorter
duration.

So, if you insert into the child table in one session and goto another session
and try to delete from the parent table an unrelated row -- the delete of the
parent will block. It will block until the child table is able to be locked and
then it will lock it -- do the delete -- and unlock it right after.

Better then before but still undesirable, still can and will lead to deadlocks.
Advice to index fkeys when you update/delete parent and modify child tables is
still 100% in place

Review &
      Followup

      Rating: 2
You forgot
      examples....  November 24, 2002
Reviewer:
      Dmitry  from NH, USA
Hi Tom,

Thank you for the fast responce, but judging from the text, you forgot to
copy/paste examples of codes you were going to use in the answer.
If you could add them...

Thank you,

          Followup:

the examples are trivial:

ops$tkyte@ORA920.US.ORACLE.COM> create table p ( x int primary key );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> create table c ( x references p );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into p values ( 1 );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into p values ( 2 );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into c values ( 1 );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> update p set x = 3 where x = 2;

1 row updated.

ops$tkyte@ORA920.US.ORACLE.COM> /*
DOC>in another session update insert into c values ( 1 );  in 8i -- that would
block
DOC>in 9i, it won't
DOC>*/

and sure enough, the other session:

ops$tkyte@ORA920.US.ORACLE.COM> insert into c values ( 1 );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM>

did not block.  But, if we go back to the first session:

ops$tkyte@ORA920.US.ORACLE.COM> update p set x = 4 where x = 3;

and that'll block -- it's waiting for the lock on C which it cannot get until
you commit the insert into c. As soon as you do -- the update goes.

      Rating: 5
Yes, I
      understand now  November 24, 2002
Reviewer:
      Dmitry  from NH, USA
Thank you

November 25, 2002 kajool -- Thanks for the question regarding
      "Dimensions", version 8.1.5

      You Asked
hi tom
my ever first question for u;
tell me am I right or wrong;
1.when we create dimensions with different levels the aggreate
data is stored in this levels.
e.g
we create a dimension time with four levels;
year,quarter,month,day.
now when data is inserted into tables they are also inserted and
aggregated into dimensions and when a query
e,g ; a query wanna see data of 2nd quarter so it is read from
the quarter dimension instead on the table,causing a
rewrite?right or not?
2.what are temporary lobs and why we use them
thanks in advance
kajool

      and we said...
1) a dimension stores meta data about how columns/tables relate
to each other.  A dimension creates no physical data itself.

A dimension can be used to tell the system that a "date" rolls
up to a "month" rolls up to a "quarter" rolls up to a "year".
If I create a materialized view that aggregates data by MONTH
and I perform a query by QUARTER, the optimizer understands that
MONTHS can be aggregated to QUARTERS (because of the dimension)
and will therefore use the materialize view I created -- because
the dimension said it could.  Without the dimension, the
optimizer would have no idea that months can be rolled up to
quarters and would not be able to use this materialized view but
rather would have to go after the base tables themselves.

2) temporary lobs are lobs that do not exist in a table.  You
would use them whenever you needed to have a variable that is
more then 32k in size.  For example, the existing
UTL_HTTP.REQUEST_PIECES api will return a web page as a series
of 2000 byte rows in a plsql table.  You could take that table
and write all of the lines into a temporary lob.  Now you could
easily DBMS_LOB.INSTR() for example on the temporary lob --
something you cannot do on a plsql table.
Review &
      Followup

      Rating:
      5
  September 01, 2002
Reviewer:
      A reader
Hi Tom,

 Could you please,please explian DIMENSION with simple words by giving Simple
example.

  When even I see Fine grain topic(FGAC) OR dimension, I get confued.

  I know dimension is used with materialized view.

  I will appreciate your response.

  Thanks,

          Followup:

well, I was about to answer with

"search for create dimension on this site" but -- that brought me RIGHT BACK TO
THIS ANSWER!!!!

So, the simple words are right above.  A simple example is:

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76994/logical.htm#97703
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76994/dimensio.htm#11221
also, my book "Expert one on one Oracle" has what I believe to be a pretty
readable/understandle example case study in the chapter on MV's involving
dimensions.

      Rating: 5
querying wuth
      time intervals  November 25, 2002
Reviewer:
      A reader
hi

I was wondering if we could dividde a resultset into time intervals without
using dimensions

For example my emp table with hiredate and I want to find out how many employees
I hired per week and month in last year how could we do it without using
dimensions in a SQL query (not using PL/SQL)?

normally I would do
select count(*) from emp where hiredate between x and y

in my case x and y would be dynamic  (from day 1 to day 365 in a year)

cheers

          Followup:

sure. first, you could run two queries

select trunc(hiredate,'mm'), count(*) from emp group by trunc(hiredate,'mm');

would give you counts by month.  change 'mm' to 'w' and you have by week.

In 8iR2 and up, you can use group by cube to get the same in a single result:

scott@ORA920.US.ORACLE.COM> select trunc(hiredate,'mm') month,
trunc(hiredate,'w') week, count(*)
  2    from emp
  3   group by cube(trunc(hiredate,'mm'), trunc(hiredate,'w'))
  4  having (grouping( trunc(hiredate,'mm') ) = 1 and grouping(
trunc(hiredate,'w') ) = 0) OR
  5         (grouping( trunc(hiredate,'mm') ) = 0 and grouping(
trunc(hiredate,'w') ) = 1)
  6  /

MONTH     WEEK        COUNT(*)
--------- --------- ----------
          15-DEC-80          1
          15-FEB-81          1
          22-FEB-81          1
          01-APR-81          1
          01-MAY-81          1
          08-JUN-81          1
          08-SEP-81          1
          22-SEP-81          1
          15-NOV-81          1
          01-DEC-81          2
          22-JAN-82          1
          15-APR-87          1
          22-MAY-87          1
01-DEC-80                    1
01-FEB-81                    2
01-APR-81                    1
01-MAY-81                    1
01-JUN-81                    1
01-SEP-81                    2
01-NOV-81                    1
01-DEC-81                    2
01-JAN-82                    1
01-APR-87                    1
01-MAY-87                    1

24 rows selected.

And in 9iR1 and up, you can use grouping sets to get the same but more
efficiently (it doesn't build the entire CUBE and then just select the rows you
want as above -- rather it only builds the two combinations we are interested
in)

scott@ORA920.US.ORACLE.COM>
scott@ORA920.US.ORACLE.COM> select trunc(hiredate,'mm') month,
trunc(hiredate,'w') week, count(*)
  2    from emp
  3   group by grouping sets(trunc(hiredate,'mm'), trunc(hiredate,'w'))
  4  /

MONTH     WEEK        COUNT(*)
--------- --------- ----------
01-DEC-80                    1
01-FEB-81                    2
01-APR-81                    1
01-MAY-81                    1
01-JUN-81                    1
01-SEP-81                    2
01-NOV-81                    1
01-DEC-81                    2
01-JAN-82                    1
01-APR-87                    1
01-MAY-87                    1
          15-DEC-80          1
          15-FEB-81          1
          22-FEB-81          1
          01-APR-81          1
          01-MAY-81          1
          08-JUN-81          1
          08-SEP-81          1
          22-SEP-81          1
          15-NOV-81          1
          01-DEC-81          2
          22-JAN-82          1
          15-APR-87          1
          22-MAY-87          1

24 rows selected.

      Rating: 4
Used
      this  November 25, 2002
Reviewer:
      A reader
Hi

I ended up using following query (groupping by to_char(f_apertura,
'IYYY')||to_char(f_apertura, 'IW')), thank you very much for the reply

select 'Semana entre '||primer_dia_semana||' y '||ultimo_dia_semana semana,
       cola_ultima, num_dias_revisada,
       count(dias_revisada) numero_casos
from (select f_apertura, COLA_ULTIMA, trunc(f_apertura, 'iw') primer_dia_semana,

             trunc(f_apertura, 'iw')+6 ultimo_dia_semana,
             case
             when F_ULTIMA - F_APERTURA > 20 then 'Mas de 20 dias'
             when F_ULTIMA - F_APERTURA > 10  then 'Entre 10 dias y 20 dias'
             when F_ULTIMA - F_APERTURA > 5 then 'Entre 5 dias y 10 dias'
             when F_ULTIMA - F_APERTURA > 1 then 'Entre 1 dia y 5 dias'
             when F_ULTIMA - F_APERTURA > 1/12 then 'Entre 2 horas y 24 horas'
             when F_ULTIMA - F_APERTURA > 1/24 then 'Entre 1 hora y 2 horas'
             when F_ULTIMA - F_APERTURA > 0 then 'Menos de 1 hora'
             when F_ULTIMA - F_APERTURA = 0 then 'En el momento'
             else to_char(F_ULTIMA - F_APERTURA)
              end num_dias_revisada,
             F_ULTIMA - F_APERTURA dias_revisada
        from clarify.CASOS_COLA_TIEMPO_CAUP)
group by cola_ultima, num_dias_revisada, to_char(f_apertura,
'IYYY')||to_char(f_apertura, 'IW'),
         primer_dia_semana, ultimo_dia_semana
order by primer_dia_semana;

      Rating: 5
dimensions vs
      dimension tables  November 19, 2003
Reviewer:
      A reader
Hi

We can do

create dimension in oracle to add a time dimension

we can also create a plain table for time dimension

Which is more flexible? Performance wise?

Also is Oracle dimensions same concept as Datawarehouse dimensions? (Star Model)

November 25, 2002 Sanjaya -- Thanks for the question regarding
      "Importing existing tables", version 8.1.7

      You Asked
Hi Tom,

Problem on importing.
We can import a existing table on the database.
If the table columns are changed it give the error ; invalid column.

But what happens if only the length of a column is changed ? I have experinced
no error messages ? How can we track this ?

Thanks.

-San

------------------------------------------------------------------
Say I have a table emp on the database db1. It has 100 rows.
And I have another database db2 which also has the table emp. The second on has
200 rows. Initially both tables had the same structure.

Now I want to export the table from db2 and import it to db1.

I wanted to check 2 situations :
1.) I added a column to the table on db2 ; 'test' . Then I exported   the table
and tried to import it to db1. Here I get the error message
"invalid column.............". That's OK. I understand that.

2. ) I removed the addtional column from the table on db2. And changed the
leangth of a existing column. Say I changed the name from varchar(100) to
varchar(150).
Again I exported the table from db2 and tried to import it to db1.
Here I don't get any error messages.
So I have no way to track such changes. I there any way to identified changes ?

Another problem :

Say both table structures on db1 and db2 are same.
I import some addtional data from the table on db2 to the table on db1. Here are
the triggers on the table on db1 are fired ?
I ask this this when we import a new table to a database these triggers a not
fired.

Thanks .

-San

      and we said...
As long as the data all fit -- it won't care that it was varchar2(100) on one
database and varchar2(150) on another.  If all of the data fit in a
varchar2(100) there would be no issues moving back and forth.

EXP/IMP is not a change management tool

Oracle Enterprise Manager has such a tool if that is what you need.

If a table already exists AND it has a trigger on it and you import with
IGNORE=Y, then yes, the trigger will fire.

If a table does NOT exist then import will

a) create table
b) insert data
c) add trigger

in that order -- it would not make sense for the trigger to fire during the
import here as the trigger already FIRED in the source database and already did
its thing.  If that is not what you desire, then you would import the table with
rows=n and then import that table again with rows=y
Review &
      Followup

      Rating:
      5
  November 25, 2002
Reviewer:  Sanjaya
      Balasuriya  from Sri Lanka
Thanks Tom. I got the exact information I wanted.

-San

November 25, 2002 Azim -- Thanks for the question regarding
      "SQLLOADER error ORA-01722.", version 8.17

      You Asked
Can you help me with this.

Data File.
00000001   00000008    00000-900    00010000
00000002   00000012    00000000     00000300
00000003   00001008    00-56.02      00030300
00000004   00010009    0000-0.7      00330300
00000005   00000006    000-7.2        00506060
00000006   00003408    00-167.06    00670450

Trying to load into a table using sqlloader.
Table has all above columns defined as number.

I used the position to define the sqlldr.

column1     position(001:008),
column2     position(010:018),
column3    position(020:027),
column4    position(029:036)

The data file is in fixed positions.

I am having problems with the column that has negative numbers.

Azim

      and we said...
those aren't negative numbers as I recognize them -- they are expressions.  But
anyway:

column1     position(001:008) "nvl(ltrim( :column1, '0' ),'0')",
column2     position(010:018) "nvl(ltrim( :column2, '0' ),'0')",
column3    position(020:027)  "nvl(ltrim( :column3, '0' ),'0')",
column4    position(029:036)  "nvl(ltrim( :column4, '0' ),'0')"

will fix that right up for you.
Review &
      Followup

      Rating: 4
Smart
      Solution  November 25, 2002
Reviewer:  Abdul
      Samed  from Chennai, India
very smart solution.

      Rating:
      4
  November 25, 2002
Reviewer:  Sagi
       from India
Hi Tom,

When we are using the column position are both these same:

column1     position(001:008)
or
column1     position(1:8)

If not can you tell what is the difference and when to use which method

Thanx in Advance.
Sagi

          Followup:

001 is the same as 1.0 is the same as 01.000 is the same as 00000001

when dealing with numbers leading and trail zeroes don't count. (true you cannot
use decimal numbers like 1.0 in sqlldr but that was just for example)

they are the same.

I frequently use 0001:0008 (or 001:008) myself to help things line up

( c1     position(0001:0008),
  c2     position(0020:0200),
  c3 ....

      Rating:
      5
  December 17, 2002
Reviewer:
      A reader

November 25, 2002 Anupam -- Thanks for the question regarding
      "Database parameters", version 8.1.7.3

      You Asked
Tom,
What is the difference between the parameters NLS_CHARACTERSET and
NLS_NCHAR_CHARACTERSET in the view nls_database_parameters. Why do we have two
separate parameters when invariably the value is same in both?

      and we said...
They are not invariably the same - it depends on your needs.

This allows you to store data from two differing charactersets in your database.
 One character set in varchar2/clobs and a different one in nvarchar2 and
nclobs.

See

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76966/toc.htm
and specifically

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76966/ch3.htm#41673

November 25, 2002 Keith -- Thanks for the question regarding
      "Reversing Alter Table x Set Unused (y);", version 9.0.1.3.1

      You Asked
Tom,

Is there any way to reverse( or set used) an Alter Table x Set Unused (y);
statement?

Thanks.

Keith

      and we said...
sure, it is called a point in time recovery ;)

Seriously -- the only way (as with ALL DDL) to reverse the operation is to
recover the object to the point in time immediately prior to the DDL command.
DDL is otherwise permanent.
Review &
      Followup

      Rating: 5
Figured that's
      what I'd have to do  November 24, 2002
Reviewer:
      Keith  from Raleigh, NC, USA
I actually found the answer minutes after submitting the question but was so
excited at actually having the opportunity to submit a question thought I would
go ahead and do so. Like a good DBA, I have a backup so all is good. Thanks.

      Rating: 4
Why would we
      want to do this?  November 25, 2002
Reviewer:  A
      P Clarke  from London, England
To all intents and purposes ALTER TABLE x SET USED (y) is the same as ALTER
TABLE x DROP (y).  Had I not read this article I would have assumed there was a
corresponding SET USED clause (Why wouldn't there be?  After all, it's just
setting a flag in the data dictionary).  I am now on my guard but I wonder why
anybody would bother using this clause.

I would guess that Oracle have introduced this clause because it takes a lot
less time to mark a column 'UNUSED' than it does to physically DROP that column,
particularly with big tables.  So we can defer some work to a less busy time.
But if we still have to take a backup before doing this operation then I don't
see that we have gained much.  If we have the resources to do a backup why not
just drop the column and have done with it?

In short, I think this is going to trip up a whole lot more people than it's
going to help.

Cheers, APC

          Followup:

set unused (not set used -- doesn't exist) is very very different from drop.

drop -- physically rewrites the data, reclaims the space, can take a long time
on a big table.

usused -- flag delete, doesn't touch the existing data but all NEW data will
not have this column

It is that last part in bold that tells why unused it un-reversable.  There will
be rows which don't have this column, the data would appear corrupt, we cannot
un-use that which doesn't exist.

I fail to see how this would "trip up" anyone?  unused is a way to quickly and
permanently get rid of a column.  drop is a way to permanently get rid of a
column and reclaim the space.  Thats the difference between them -- they
conceptually have the same effect -- COLUMN GONE.

      Rating: 5
(For AP Clarke)
      On the converse...  November 25, 2002
Reviewer:
      Connor McDonald  from UK
You can make a pretty reasonable argument for never using 'drop column' and only
using 'set unused'.  Because even if you drop the column you end up with 2
possible scenarios:

a) you reclaim no space because the blocks still don't become free enough to get
put on the free list. Conclusion: you did all that work for nothing - 'set
unused' would have been just as good

b) you DO reclaim enough to have the blocks on the free list - but now you've a
performance nightmare because probably every block is on the free list.
Conclusion# 1: You need a do an 'alter table move' to solve the problem.
Conclusion #2: It would have been easier to do 'set unused' and just done the
alter table move

hth
connor

          Followup:

I agree with your conclusions pretty much, yup.

      Rating: 4
A bit rusty at
      the ol' geeky internals stuff  November 26, 2002

Reviewer:  A P Clarke  from London, England

Tom and Connor

Thanks for explaining the difference between DROP and SET UNUSED.  Now it makes
sense.  I have been doing pure PL/SQL apps development for some time now but I
hope to get back into DBA-ing quite soon and I am trying to get back into that
way of thinking.

There have been so many changes since I was last a DBA (7.3.4).  It is
relatively easy to discover what has changed, but it is harder to find out the
implications of those changes, for instance the different behaviour of SET
UNUSED and DROP with regards to free space.  This site is a good source for
gaining understanding of Oracle.

So, thanks once again.

Cheers, APC

November 25, 2002 Anila -- Thanks for the question regarding "Top 5
      Keywords per each job role in a table", version 9.0.2

      You Asked
Hi,

I'm trying to build a query which can do this:
I have a table called Emp_Roles which collects all the keywords searched by each
employee, i would like to get
top 5 keywords for all jobroles from this table, i.e top 5 keywords for CEO,top
5 keywords for Developer and so on.
Can i get some help me with this?

I searched the archives but couldn't find anything similar to this:

Let me describe my data here:

Table Log:

Role        Keyword
CEO        java
CEO         jdbc
CEO         Java
CEO        oracle
CEO        9i
CEO        java
CEO        database
CEO        test
CEO        jdbc
DEveloper    otn
DEveloper    t1
Developer    otn
Developer    larry

Now my Result table should bring up top 5 keywords or the most searched top5
keywords per CEO,Developer
so result would be
CEO    java
CEO    jdbc
CEO    SOME KEYWORD
CEO     SOME KEYWORD
CEO     SOME KEYWORD
Developer otn
and so on, though i can get this for a single role i need this information for
all the roles in the log table

Thanks a bunch.

      and we said...
first we need to get the counts:

select role, keyword, count(*) cnt from t group by role, keyword

then, we need to assign a number within each ROLE ordered by cnt:

select role, keyword, cnt,
       ROW_NUMBER() over ( partition by role order by cnt DESC ) rn
  from ( that_query )

Then, we need to keep just the top 5:

select role, keyword, cnt
  from (that_second_query)
 where rn <= 5
 order by 1, 3;

For example, pretend DT is role and SEARCH_CRITERIA is KEYWORD.  Here are the
top 5 search terms on asktom for the last week:

ask_tom@OSI> select *
  2    from ( select dt,
  3                  search_criteria,
  4                  cnt,
  5                  row_number() over ( partition by dt
  6                                      order by cnt DESC ) rn
  7             from ( select dt,
  8                           search_criteria,
  9                           count(*) cnt
 10                      from search_log
 11                     where dt >= sysdate-7
 12                     group by dt, search_criteria )
 13         )
 14   where rn <= 5
 15   order by dt, cnt desc
 16  /

DT        SEARCH_CRITERIA                                 CNT         RN
--------- ---------------------------------------- ---------- ----------
19-NOV-02 1555                                             13          1
          ref cursor                                       11          2
          outer join                                       10          3
          statspack                                        10          4
          dbms_job                                         10          5

20-NOV-02 dbms_job                                         20          1
          inner join                                       11          2
          memory                                           10          3
          Replication                                      10          4
          partition                                         9          5

21-NOV-02 dbms_job                                         10          1
          utl_file                                          9          2
          varray                                            8          3
          instr                                             8          4
          explain plan                                      7          5

22-NOV-02 sql                                              14          1
          autotrace                                        12          2
          dynamic sql                                      11          3
          replication                                       8          4
          performance                                       7          5

23-NOV-02 ocx                                               7          1
          complete refresh of fast snapshot                 6          2
          external file table                               5          3
          trace files                                       5          4
          driving table                                     4          5

24-NOV-02 bind variables in list dynamic                    6          1
          partition index status n/a                        5          2
          cast                                              4          3
          database link                                     4          4
          trigger                                           4          5

25-NOV-02 dbms_job                                          9          1
          xml                                               9          2
          decode                                            8          3
          ref cursor                                        7          4
          sequence                                          6          5

35 rows selected.

Looks like I need to write about DMBS_JOB alot more ;)

Analytics rock....
Review & Followup

      Rating: 5
Top 5 Keywords
      per each job role in a table  November 25, 2002

Reviewer:  Anila Varma  from San Francisco,CA USA

Thanks a lot, this is exactly what i need.
Best Always,
Anila

November 25, 2002 Ashiq -- Thanks for the question regarding "Error
      messages in a table.", version 8.1.7

      You Asked
Hi Tom,

  Is there any way to store the ALL the oracle error code and error message in a
table.?? Because instead of refering the document for every error ,it'll be
handy for me if its in a table.

thanx

      and we said...
already done:

scott@ORA920.US.ORACLE.COM> exec dbms_output.put_line( sqlerrm(-1) );
ORA-00001: unique constraint (.) violated

PL/SQL procedure successfully completed.

or, if you have the oraus.msg file:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:558822941373
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:2009886546021

Review &
      Followup

      Rating: 5
Never thought
      it would be so easy  November 25, 2002
Reviewer:
      Holger  from Germany
Way cool!

      Rating: 2
Storing SQLCode
      in a table  November 25, 2002
Reviewer:  Ben
      VanOsch  from Winnipeg, Mb, Canada
Hi,

I can find a oraus.msb file but cannot locate a oraus.msg. Is there something I
can do to translate the msb file into msg?

          Followup:

see the SECOND link, I've got one there.  windoze doesn't have the msg, just the
binary msb file.


Subject | Article | Last Updated (East coast time)
Disabling primary key for loading | article only | Nov 26, 2002 07:48 am
Query taking more time | article only | Nov 26, 2002 07:59 am
please tell me or refer me to some other | article only | Nov 26, 2002 08:36 am
Counts and Group By | article only | Nov 26, 2002 08:37 am
Archive log across network | article only | Nov 26, 2002 08:39 am
different queries in sql*plus and coldfusion | article only | Nov 26, 2002 10:46 am
How to work with Anydata type over JDBC | article only | Nov 26, 2002 01:06 pm
Modifying sequence numbers | article only | Nov 26, 2002 06:50 pm
pctincrease having 0 | article only | Nov 26, 2002 09:41 pm
Dynamic Sql,Bind Variables,TK PROF | article only | Nov 27, 2002 07:30 am
Migration from MS SQL to Oracle | article only | Nov 27, 2002 07:36 am
Killing a session | article only | Nov 27, 2002 08:06 am
select within select | article only | Nov 27, 2002 08:14 am
Restrict to All_source Table | article only | Nov 27, 2002 11:35 am
How to identify connection/server errors when sending email using utl_smtp? | article only | Nov 27, 2002 01:19 pm
Efficient & secure way of Deleting rows from a Table | article only | Nov 27, 2002 03:40 pm
ref cursor | article only | Nov 27, 2002 05:16 pm
Locally Managed System Tablespace | article only | Nov 27, 2002 06:43 pm
default users | article only | Nov 29, 2002 12:12 pm
Datafile error and Log Archive | article only | Nov 30, 2002 08:16 am
Sequences in Logical Standby database | article only | Nov 30, 2002 09:04 am
Long raw statistics | article only | Nov 30, 2002 09:09 am
Job_queue_process | article only | Dec 01, 2002 11:10 am
temp table,cursors,arrays | article only | Dec 01, 2002 01:46 pm
Natuer of Jobs when taking Hot Back Ups | article only | Dec 01, 2002 06:10 pm
Overhead of the MONITORING option in db objects | article only | Dec 01, 2002 07:09 pm
Shared Pool | article only | Dec 01, 2002 07:42 pm
Kill session and SID in oracle | article only | Dec 02, 2002 07:08 am
ETL -- warehousing | article only | Dec 02, 2002 07:10 am
difference between parse count (total) and parse count (hard) | article only | Dec 02, 2002 07:20 am
Dynamic query containing Blob, which contains different field. | article only | Dec 02, 2002 07:41 am
Pause in SQL*Plus | article only | Dec 02, 2002 07:44 am
converting user specified date into oracle date | article only | Dec 02, 2002 07:53 am
Control File Too Old | article only | Dec 02, 2002 09:10 am
Query/Insert | article only | Dec 02, 2002 10:37 am
How to get the record count of an OS file in the database? | article only | Dec 02, 2002 12:30 pm
Date function | article only | Dec 02, 2002 07:15 pm
row(s) 1 - 37 of 37
November 26, 2002 Lakshmi -- Thanks for the question regarding "Disabling primary key for loading", version 8.1.7
You Asked
Hi Tom
        I want to load 100,000  records to a table using sqlloader.The table has
a primary key and it has child tables.
Can I disable primary key during load operation and enable it after all records
are loaded.How do I  disable/enable  primary key constraint.

regards
Lakshmi
and we said...
for 100,000 records (very very small in my opinion) -- I would leave the key on.
 It is not worth the effort of:

a) disable all child keys
b) disable primary key
c) insert data
d) enable primary key
e) enable all child keys

100k records is very small by todays standards.  Just leave it on, it would most
likely be faster then doing steps d and e.

Consider this setup.  parent table with 100k rows and 3 child tables.  We want
to add another 100k rows to parent (t is parent, c1,c2,c3 are children):

ops$tkyte@ORA920.US.ORACLE.COM> create table t as select 999999 id,
all_objects.* from all_objects;
Table created.

ops$tkyte@ORA920.US.ORACLE.COM> insert /*+ append */ into t select * from t;
29303 rows created.

ops$tkyte@ORA920.US.ORACLE.COM> commit;
Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM> insert /*+ append */ into t select * from t
where rownum <= 42000;
42000 rows created.

ops$tkyte@ORA920.US.ORACLE.COM> commit;
Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM> select count(*) from t;

  COUNT(*)
----------
    100606

ops$tkyte@ORA920.US.ORACLE.COM> update t set id = rownum;
100606 rows updated.

ops$tkyte@ORA920.US.ORACLE.COM> commit;
Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM> host exp userid=/ tables=t

Export: Release 9.2.0.1.0 - Production on Mon Nov 25 07:55:09 2002

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

Connected to: Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production
Export done in WE8ISO8859P1 character set and AL16UTF16 NCHAR character set

About to export specified tables via Conventional Path ...
. . exporting table                              T     100606 rows exported
Export terminated successfully without warnings.

ops$tkyte@ORA920.US.ORACLE.COM> update t set id = -id;
100606 rows updated.

ops$tkyte@ORA920.US.ORACLE.COM> alter table t add constraint t_pk primary
key(id);
Table altered.

ops$tkyte@ORA920.US.ORACLE.COM> create table c1 ( x number, y char(255),
constraint c1_fkey foreign key(x) references t(id) );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> insert /*+ append */ into c1 select id, 'x' from
t;

100606 rows created.

ops$tkyte@ORA920.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create table c2 ( x number, y char(255),
constraint c2_fkey foreign key(x) references t(id) );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> insert /*+ append */ into c2 select id, 'x' from
t;

100606 rows created.

ops$tkyte@ORA920.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create table c3 ( x number, y char(255),
constraint c3_fkey foreign key(x) references t(id) );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> insert /*+ append */ into c3 select id, 'x' from
t;

100606 rows created.

ops$tkyte@ORA920.US.ORACLE.COM> commit;

Commit complete.

Ok, now, lets time "loading as is", this is on my desktop machine -- teeny tiny
machine as compared to a server

[tkyte@tkyte-pc tkyte]$ time imp userid=/ full=y ignore=y

Import: Release 9.2.0.1.0 - Production on Mon Nov 25 07:57:55 2002

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

Connected to: Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production

Export file created by EXPORT:V09.02.00 via conventional path
import done in WE8ISO8859P1 character set and AL16UTF16 NCHAR character set
. importing OPS$TKYTE's objects into OPS$TKYTE
. . importing table                            "T"     100606 rows imported
Import terminated successfully without warnings.

real    0m5.723s
user    0m1.037s
sys     0m0.094s
[tkyte@tkyte-pc tkyte]$

Ok, so it took like way less then 10 seconds.  And it was really easy to do...
Now, lets try the load differently.  I rebuilt the objects and then loaded via:

ops$tkyte@ORA920.US.ORACLE.COM> select systimestamp from dual;

SYSTIMESTAMP
---------------------------------------------------------------------------
25-NOV-02 08.02.21.882849 AM -05:00

ops$tkyte@ORA920.US.ORACLE.COM> alter table c1 disable constraint c1_fkey;

Table altered.

ops$tkyte@ORA920.US.ORACLE.COM> alter table c2 disable constraint c2_fkey;

Table altered.

ops$tkyte@ORA920.US.ORACLE.COM> alter table c3 disable constraint c3_fkey;

Table altered.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> alter table t disable constraint t_pk;

Table altered.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> host imp userid=/ full=y ignore=y

Import: Release 9.2.0.1.0 - Production on Mon Nov 25 08:02:22 2002

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

Connected to: Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production

Export file created by EXPORT:V09.02.00 via conventional path
import done in WE8ISO8859P1 character set and AL16UTF16 NCHAR character set
. importing OPS$TKYTE's objects into OPS$TKYTE
. . importing table                            "T"     100606 rows imported
Import terminated successfully without warnings.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> alter table t enable constraint t_pk;

Table altered.

ops$tkyte@ORA920.US.ORACLE.COM> alter table c1 enable constraint c1_fkey;

Table altered.

ops$tkyte@ORA920.US.ORACLE.COM> alter table c2 enable constraint c2_fkey;

Table altered.

ops$tkyte@ORA920.US.ORACLE.COM> alter table c3 enable constraint c3_fkey;

Table altered.

ops$tkyte@ORA920.US.ORACLE.COM> select systimestamp from dual;

SYSTIMESTAMP
---------------------------------------------------------------------------
25-NOV-02 08.02.48.038323 AM -05:00

ops$tkyte@ORA920.US.ORACLE.COM>

it took longer for this tiny bit of data.

So -- recommend -- just leave it be and load away
Review & Followup
Rating: 4
What if disable PK/UI but found dupes after load November 25, 2002
Reviewer: Robert from PA
Tom,
Actually I was just going to ask the exact same question...except I am loading
like 30 mil records first thing in the morning.(single column - takes about an
hour +-)
What if one does disable the PK or Unique Index of a table
that has no child, load the records and upon re-enabling the PK/Index find out
there are dupes in the column(s) ? What to do then ?
Thanks

Followup:
I would just use direct=true and let it fly (shouldn't take an hour).

that'll defer the index maintenance till the end of the load at which point the
new data will be merged with the old.

no dups = success
dups = primary key left in an unusable state

You might consider using deferable primary keys as they use a NON-UNIQUE index,
hence the index won't "disappear" if there are dups (and you can use the index
to more rapidly find the dups)

Rating: 5
impact of the primary key November 25, 2002
Reviewer: Prakash from singapore
Tom
That was a nice explanation , Thankyou very much.
First small question is
If i have a primary key on a table and if i BULK INSERT THE DATA into it. Now my
question is
when will the validation of primary key happens while inserting every record or
after completing the BULK INSERT
AND THEN validates the NEW CHUNK of Data .
If the validation happens while inserting every record then it is just the same
as like a normal insert.

second question is recently i have encountered an mysterious error in oracle
(816)
(we are in process of upgrading to 817).
ora-3232 . unable to allocate an extent of 127 blocks in tablespace 3 while
executing a procedure.
there is enough free space for the temp tablespace and related files. When i
have found in the metalink (id 125271.1)
it was explained as " Sometimes ora-3232 may be encountered when a query uses
HASH JOIN ".
This particular error im encountering at one sql statement which is a plain and
just usess one subquery(not correlated).
Just for testing, i have removed the subquery and ran the program and it went
well. then immediately i have added back
the same subquery and then it went fine . im wondering how come this MYSTERIOUS
behaviour possible.
We are not using the LOCALLY MANAGED TS. and also not MTS.

Appreciated your valuable answer

Thanks & Regards

Prakash

Followup:
each row is validated in a case like that.  You are just doing conventional
inserts in a more network/context switching efficient manner by employing array
processing.

$ oerr ora 3232
03232, 00000, "unable to allocate an extent of %s blocks from tablespace %s"
// *Cause:  An attempt was made to specify a HASH_MULTIBLOCK_IO_COUNT value
//          that is greater than the tablespace's NEXT value
// *Action: Increase the value of NEXT for the tablespace using
//          ALTER TABLESPACE DEFAULT STORAGE or decrease the value of
//          HASH_MULTIBLOCK_IO_COUNT.

It is not mysterious -- the error message actually explains it.

Look at your hash multiblock io count.  MAKE SURE your next extent size in TEMP
can hold AT LEAST that much.  If it cannot -- ora-03232.

Rating: 5
disable primary key November 25, 2002
Reviewer: lakshmi from Bangalore India
Hi tom
       Thanks a lot for your quick response.

November 26, 2002 Bhagat Singh -- Thanks for the question regarding "Query taking more time", version 816
You Asked
Hello Tom,

We are grappling with these two one have people have broken lot of head on it,
please suggest below I am mentioning the tkprof file output these queries have
just detroited the performance of the pro*c code.We tried with  EXEC SQL ALTER
SESSION SET EVENTS '10046 TRACE NAME CONTEXT FOREVER, LEVEL 8' but could not
surface wait reason we have only got is this please help!!!!!!

Regards
Bhagat Singh

PARSING IN CURSOR #11 len=321 dep=0 uid=18 oct=3 lid=18 tim=110876444
hv=1163802948 ad='8b5544c0'
 SELECT /*+ FIRST_ROWS */ nvl(CUST_COD , 0), nvl(CUST_STATUS,' '),
nvl(CONTRACT_TYP,' ') , nvl(UNIQ_NO, 0), nvl
(MOBILE_NO ,' '), nvl(PACK_COD ,' '), nvl(to_char(SUBSCRIBED_DT,'YYYYMMDD'),'
'), nvl(to_char(ACTIVATED_DT,'YYY
YMMDD'),' '), nvl(to_char(SUSPENDED_DT,'YYYYMMDD'),' ') FROM MAIN_MAST WHERE
CUST_COD= :bp_cust_cod
END OF STMT
PARSE #11:c=7,e=9,p=4,cr=253,cu=0,mis=1,r=0,dep=0,og=0,tim=110876444
WAIT #11: nam='SQL*Net message to client' ela= 0 p1=1413697536 p2=1 p3=0
WAIT #11: nam='SQL*Net message from client' ela= 0 p1=1413697536 p2=1 p3=0
EXEC #11:c=0,e=0,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=2,tim=110876444
WAIT #11: nam='SQL*Net message to client' ela= 0 p1=1413697536 p2=1 p3=0
WAIT #11: nam='SQL*Net message from client' ela= 0 p1=1413697536 p2=1 p3=0
WAIT #11: nam='file open' ela= 0 p1=0 p2=0 p3=0
WAIT #11: nam='db file sequential read' ela= 1 p1=7 p2=28116 p3=1
WAIT #11: nam='SQL*Net message to client' ela= 0 p1=1413697536 p2=1 p3=0
FETCH #11:c=1,e=1,p=1,cr=4,cu=0,mis=0,r=1,dep=0,og=2,tim=110876445
WAIT #11: nam='SQL*Net message from client' ela= 0 p1=1413697536 p2=1 p3=0

SELECT /*+ FIRST_ROWS */ nvl(CUST_COD , 0), nvl(CUST_STATUS,' '),
  nvl(CONTRACT_TYP,' ') , nvl(UNIQ_NO, 0), nvl(MOBILE_NO ,' '), nvl(PACK_COD ,
  ' '), nvl(to_char(SUBSCRIBED_DT,'YYYYMMDD'),' '), nvl(to_char(ACTIVATED_DT,
  'YYYYMMDD'),' '), nvl(to_char(SUSPENDED_DT,'YYYYMMDD'),' ')
FROM
 MAIN_MAST WHERE CUST_COD= :bp_cust_cod

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.04       0.04          0          0          0           0
Execute  11596      1.28       1.25          0          0          0           0
Fetch    11596      5.33      32.84       6166      46232          0       11444
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    23193      6.65      34.13       6166      46232          0       11444

Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer goal: FIRST_ROWS
Parsing user id: 18

Rows     Row Source Operation
-------  ---------------------------------------------------
  11444  TABLE ACCESS BY INDEX ROWID T_SUBSCRIBER
  11596   INDEX UNIQUE SCAN (object id 8246)

********************************************************************************

SELECT /*+ FIRST_ROWS */ nvl(CUST_COD, 0), nvl(TARF_PLAN,' '),
  nvl(to_char(tarf_st_dt,'YYYYMMDD'),' '), nvl(tarf_flags,' '),
  nvl(wll_grp_cod,' '), nvl(to_char(wll_st_dt,'YYYYMMDD'),' '),
  nvl(to_char(wll_ed_dt,'YYYYMMDD'),' '), nvl(cug_grp_cod,' '),
  nvl(to_char(rating_st_dt,'YYYYMMDD'),' '), nvl(tax_applicable,' '),
  nvl(airtime_tax,' '), nvl(tax_othr_serv_prov,' '), nvl(spl_tarf_cod,' '),
  nvl(to_char(spl_tarf_dt,'YYYYMMDD'),' '), nvl(rating_typ,' '),
  nvl(to_bill_seq_no, 0), nvl(epbax#normal,' '),
  nvl(to_char(last_dt_prort_chrg,'YYYYMMDD'),' '), nvl(chrg_exception_phone,'
  '), nvl(to_char(rejected_st_dt,'YYYYMMDD'),' '), nvl(bill_cycle,' '),
  nvl(ovr_unbil_amt, 0), nvl(credit_lmt_bar,' '), nvl(credit_lmt, 0),
  nvl(trig_slab_lookup_cod,' '), nvl(trig_threshold, 0), nvl(last_trig_actn,'
  '), nvl(to_char(last_trig_dt,'YYYYMMDD'),' '), nvl(to_char(sumry_ed_dt,
  'YYYYMMDD'),' '), nvl(to_char(mid_cycl_disc_dt,'YYYYMMDD'),' '),
  nvl(critical_serv,' '), nvl(to_char(serv_chng_dt,'YYYYMMDD'),' '),
  nvl(tarf_chng,' '), nvl(sumry_disc,' '), nvl(bill_tap,' '), nvl(CPV_SCORES,
  0)
FROM
 SUB_MAST WHERE  CUST_COD= :bp_cust_code

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.02       0.03          0          0          1           0
Execute  11444      1.59       1.61          0          0          0           0
Fetch    11444      7.19      32.44       6879      47797          0       11444
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    22889      8.80      34.08       6879      47797          1       11444

Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer goal: FIRST_ROWS
Parsing user id: 18

Rows     Row Source Operation
-------  ---------------------------------------------------
  11444  TABLE ACCESS BY INDEX ROWID T_SUBS_INFO
  11444   INDEX UNIQUE SCAN (object id 3239)

********************************************************************************
and we said...
seems obvious without even seeing the wait events that this is IO related.
Looks like you are doing 50-60meg of RANDOM (single block) IO if you have an 8k
block.  If it takes 0.003 (3 one thousands) of a second to do this random IO on
average -- that would totally account for the (34.08-8.80) second different
between CPU and ELAPSED times.

scott@ORA920.US.ORACLE.COM> select (34.08-8.80)/6879 from dual;

(34.08-8.80)/6879
-----------------
       .003674953

Consider we measure waits in 1/100ths of a second in 8i in the trace and you'll
find that you might not be able to find the waits (they are too small but you
have thousands of them)

I see you execute this query 11,444 times -- why?  isn't there some way to do it
more set oriented -- look at your logic, perhaps you are doing this query in a
loop and should not be?

Look at:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6601251003901
for example (ignore the CBO discussion -- you are mostly interested in the
bottom).  There I show the gains you can achieve by removing procedural code and
letting SQL do what SQL does best.

I think what you'll be looking for is an ALGORITHM change here, to something
more set oriented instead of something totally procedural.
Review & Followup
Rating: 5
Why RANDOM (single block) IO ? November 25, 2002
Reviewer: Doug from CT, USA
Tom - what led you to say it was random single block IO?

Followup:
because they executed a query that seemed to be reading by primary key:

 FROM MAIN_MAST WHERE
CUST_COD= :bp_cust_cod

further evidence:

Execute  11596      1.28       1.25          0          0          0           0
Fetch    11596      5.33      32.84       6166      46232          0       11444
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    23193      6.65      34.13       6166      46232          0       11444

11,596 executes/fetches -- 11,444 rows.  46k LIO's (look likes 4 LIO's per
execute).  No current mode gets (you would have some for a FULL SCAN).  Query
plan tells me:

Rows     Row Source Operation
-------  ---------------------------------------------------
  11444  TABLE ACCESS BY INDEX ROWID T_SUBSCRIBER
  11596   INDEX UNIQUE SCAN (object id 8246)

that is all true.

Hence -- lots of single block IO to read the index to read a block from the
table.

Also,

WAIT #11: nam='db file sequential read' ela= 1 p1=7 p2=28116 p3=1

is the wait for single block IO generally -- p3 is the number of blocks
requested...

Rating: 5
And, uh, you learned this from the concepts guide? November 25, 2002
Reviewer: A reader

Followup:
well, looking at the predicate was "common sense".  I made a judgement call on
that.

designing and tuning for performance guide (performance guide in 9i) tells me
all about explain plan and query plans and tkprof.

and the reference guide tells me about wait events and what they mean.

So, concepts guide, not this time, but the other books are on my roadmap of
stuff to read ;)

Rating: 5
And, uh, you learned this from the concepts guide? November 25, 2002
Reviewer: J from MA
sorry, forgot to sign me,(dazed)
(btw. us 7bit char set, think I'm toast?)

Rating: 5
November 26, 2002
Reviewer: Bhagat Singh from India, Delhi
Tom,

As you said it is procedural code yes you are right actually this is pro*c code
and the most of the information which does not change frequently we have stored
in structure arrays but this customers information can change any moment so we
have to pick the customers information in the loop itself in the records file
(flat file) we get the call records and rate them so this happen generally in
loop the funda is like.

1. read the record from the file
2. do the necessary validations.
3. get the current customers informatoin.
4. process it.

so we have to do in this regards

Bhagat Singh

Followup:
no you don't, you never HAVE to do what you are currently doing.

For example, bulk load the entire file into a temp table.
process it using SQL statements using SET ORIENTED logic.

There, you can erase the pro*c code now ;)

Seriously, you can (and in this case, if you want faster you will) change your
algorithms.  It can always be done.

November 26, 2002 muhammad -- Thanks for the question regarding "please tell me or refer me to some other", version 0.0.3
You Asked
I am going to create Middle-Tier Cache Module that will controle only dynamic
web contents to decrease the over loading for web server.So after seting up the
Dbserver,Application server and Web server using oracle 9i database,9i AS and 9i
cache module,is it possible to create another cache module that will work only
for dynamic contents under the set up of web server,application server and db
server.Is it possible only to add another parameter for web cache in the
parameter file ?
           or how is it possible?
and we said...
well, fortunately for you, it is already written, you just need to turn it on.

See
http://technet.oracle.com/docs/products/ias/doc_library/90200doc_otn/mix.902/q20102/toc.htm
and look for:
Web Cache Administration and Deployment Guide Contents
Web Cache Invalidation API Reference (Javadoc)

It uses the industry standard ESI spec. for cache management.
November 26, 2002 Andrew -- Thanks for the question regarding "Counts and Group By", version 9.0.1.3.1
You Asked
Hi Tom,

I am trying to write some SQL that will count certain rows dependent on an
indicator. I want to count the number of C's and T's for a certain field (ind)
and group by f1_pk. This is the SQL.

SELECT
  f1.f1_pk,
  DECODE (f12.ind, 'C', COUNT(f12.ind), 0) CONFIRMED_CNT,
  DECODE (f12.ind, 'T', COUNT(f12.ind), 0) TAKEN_CNT
FROM f1, f12
WHERE f1.f1_pk = f12.f1_fk
GROUP BY f1.f001_san, f12.dna_ind;

You get the following results :

f1_pk    CONFIRMED_CNT     TAKEN_CNT
263         1                   0
263         0                   1
299         0                   0
318         2                   0
etc.

The problem here is that duplicate f1_pk values come back i.e. where f1_pk =
263. I know it is a problem with the Group By clause. How would you change the
query so that you would get the following type of result set?

f1_pk     CONFIRMED_CNT     TAKEN_CNT
263         1                  1
299         0                  0
318         2                  0

i.e. Where the counts are contained in the same row.

Kind Regards

Andrew
and we said...
select f1.f1_pk,
       sum( decode( f12.ind, 'C', 1, 0 )) confirmed_cnt,
       sum( decode( f12.ind, 'T', 1, 0 )) taken_cnt
  from f1, f12
 where f1.f1_pk = f12.f1_fk
 group by f1.f1_pk;

will do that.
Review & Followup
Rating: 5
November 26, 2002
Reviewer: Andrew Ford from Glasgow, Scotland
Many Thanks.

You have certainly solved the problem.

November 26, 2002 Jim -- Thanks for the question regarding "Archive log across network", version 8.1.7
You Asked
Under windows 2000, is it safe to map a drive from another server to multiplex
archived redo logs.  Say I have drives D-H available on server A and then map
drive N from server B, modify my init.ora to include an optional destination to
N: (yes I have local mandatory destination also) and then go about my day.  What
I am worried about is the documentation always frowns about storing datafiles
and such on networked drives, but this is obviously how standby databases work
and I just want to be sure this is an approved solution.

Thanks!
and we said...
this is NOT how standby's work at all!!!

The supported configuration would be to use sqlnet to transfer the logs over the
network.  There we have software on both endpoints (something sending and
something recieving).  In that fashion, in the event of an error -- we are in
control.

Using mounted disks for any database files is not supported.  There could be
unanticipated side effects from doing so.

"Safe" - i would say no,  not safe.
Review & Followup
Rating: 4
Archive log across network November 25, 2002
Reviewer: Shankar from CA, USA
hi tom,

how secure its going to be if archive logs gets sent via net8 on a private
network across different locations for remote standby's. Though the network
between locations are vpn'd, is 'oracle advanced security' still an option to
it.

thanks for your reply,
Shankar

Followup:
Not in 9iR2 and before (read into that what you may...)

November 26, 2002 Luda -- Thanks for the question regarding "different queries in sql*plus and coldfusion", version 8.1.5
You Asked
Can you kindly explain me, if there is  the difference between these two pieces
of code;
When I need 5 last hired employees
In sql*plus I use following select statement:

SELECT   ename
FROM (SELECT   *  FROM emp
ORDER BY hiredate DESC)
WHERE ROWNUM <= 5

In Coldfusion  :

<cfquery name="query_name" datasource="source_name" maxrows=5 dbtype="ODBC">
Select ename
    From emp
            order by hiredate desc
</cfquery>

The result is the same. What select statement goes to Oracle? On what level is
it changed?
Thank you in advance.
and we said...
I cannot answer for cold fusion but -- they are PROBABLY not using the
infinitely more efficient ROWNUM trick.  They are probably running the query and
fetching the first five rows.

You can verify that via SQL_TRACE and TKPROF.  See
http://asktom.oracle.com/~tkyte/tkprof.html

Here is a quick and dirty example.  big_table is about 1,000,000 rows.  I query:

big_table@ORA920.US.ORACLE.COM> alter session set sql_trace=true;
big_table@ORA920.US.ORACLE.COM> begin
  2      for x in ( select *
  3                   from (select * from big_table order by object_name)
  4                  where rownum <= 5 )
  5      loop
  6          null;
  7      end loop;
  8  end;
  9  /
big_table@ORA920.US.ORACLE.COM>
big_table@ORA920.US.ORACLE.COM> declare
  2      l_cnt number default 0;
  3  begin
  4      for x in ( select * from big_table order by object_name)
  5      loop
  6          l_cnt := l_cnt+1;
  7          exit when (l_cnt = 5);
  8      end loop;
  9  end;
 10  /

Now, tkprof shows me:

select *
                 from (select * from big_table order by object_name)
                where rownum <= 5

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.08          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        6      2.32       3.60      13002      13021          0           5
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        8      2.33       3.68      13002      13021          0           5

Rows     Row Source Operation
-------  ---------------------------------------------------
      5  COUNT STOPKEY
      5   VIEW
      5    SORT ORDER BY STOPKEY
1000000     TABLE ACCESS FULL BIG_TABLE

********************************************************************************

select *
from
 big_table order by object_name

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.06          0          0          0           0
Execute      2      0.00       0.00          0          0          0           0
Fetch        5      7.68      25.42      29533      13021        565           5
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        8      7.68      25.49      29533      13021        565           5

Rows     Row Source Operation
-------  ---------------------------------------------------
      5  SORT ORDER BY
1000000   TABLE ACCESS FULL BIG_TABLE

Basically - the where rownum <= 5 query KNEW "he only wants the top 5 rows".
So, what it did was to set up a "array" of 5 slots if you will.  It fetched the
first 5 rows from big_table and put them sorted into this array.

Then, it continued fetching the rows from big_table and added them sorted into
this 5 slot array.  If it was to go into slot 6 -- the row was discarded.  If it
was to go into slot 2, slots 2, 3, 4 were bumped up one (to become 3, 4, 5) and
row 5 that was there "disappeared".

So, the sort was easier.  The amount of temp -- trivial (just 5 slots after
all).

(this is conceptual ok -- that is "basically how this works")

The other query on the other hand -- read every row in big_table, sorted it with
respect to every other row and then plopped it all out to temp.  We read 5 rows
and stopped.  Tons more work -- lots more CPU and due to the increased IO we did
(for temp writes) -- lots more elapsed time.

If you want to get maximum performance, you will use the database feature set as
much as you can (and avoid tools that don't allow you to do so to boot!)
November 26, 2002 Oleksandr -- Thanks for the question regarding "How to work with Anydata type over JDBC", version 9.0.1
You Asked
Hi Tom,

Sorry for troubling you, but I am so excited to find your site for the first
time in  a"Accept question" mode, that simple can't resist.

Really I have to questions (that seem to be unrelated, but really are related
tightly).

1. Can I access from Java (JDBC) parse error offset?  It is piece of with OCI or
DBMS_SQL and possible with native dynamic SQL in PL/SQL
(dbms_sql.last_error_offset do that job for native dynamic SQL while it is not
documented), but for JDBC I'm unable to find a direct way.

2.I wish to path from Java stored procedure to PL/SQL collection with items of
Anydata type (this may be or table or PL/SQL indexed table).
How can I accomplish this?

The my real needs is to collect all possible error information about statement,
and from PL/SQL I'm unable to access error parameters (withot parsing an error
message text), and from Java I'm unable to find parse error position, and
ONSERVERERROR trigger doesn't fire if exception is handled. So I try to pass
statement from Java to PL/SQL,
obtain parse error position (if any), re-raise exception, catch it in Jave,
access exception parameters (Oracle extension for server-side JDBC) and combine
all this information. It works perfectly without bind variables or with
variables of known type, but I wish to handle any number of bind variables of
any type.

Yours sincerely,

Oleksandr Alesinskyy
and we said...
1) i know of know exposed way.  But, since errors are so "rare" in general
(syntax errors), writing a small java class that simply executes a block that
parses and returns the error offset would be easy.

ops$tkyte@ORA817DEV.US.ORACLE.COM> variable stmt varchar2(255)
ops$tkyte@ORA817DEV.US.ORACLE.COM> variable error_offset number
ops$tkyte@ORA817DEV.US.ORACLE.COM> variable x char(1)
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec :stmt := 'select x from dual where dummy
= :x'

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_cursor number := dbms_sql.open_cursor;
  3  begin
  4      begin
  5          dbms_sql.parse( l_cursor, :stmt, dbms_sql.native );
  6          :error_offset := 0;
  7      exception
  8          when others
  9          then
 10              :error_offset := dbms_sql.LAST_ERROR_POSITION;
 11      end;
 12      dbms_sql.close_cursor( l_cursor );
 13  end;
 14  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> print :error_offset

ERROR_OFFSET
------------
           7

so, just stuff that block of code in the java app -- wrap it in a utility
routine for example -- and you can bind in the statement/error code after
getting a parse error to get this....

2) I think the above obviates that.  The code to pass a collection of anydata
types would be obscure and long at best.  I don't think you need to pass the
binds back and forth with that...
Review & Followup
Rating: 5
How to work with Anydata type over JDBC November 26, 2002
Reviewer: Oleksandr Alesinskyy from Germany
Thank you a lot!

The idea to separate parsing from execution in this case is great one. It would
double the parsing, but I definitely can afford it. BTW, can you anyway provide
example of working with Anydat from JDBC (or refer me to such example),
just for curiosity?

Yours sincerely,

Oleksandr Alesinskyy

Followup:
It would ONLY double the parse IN THE EVENT of an error!!!

don't call this by default, make this the exception.  That is what my comment to
the effect of:

But, since errors are so "rare" in general
(syntax errors), writing a small java class that simply executes a block that
parses and returns the error offset would be easy.

was to convey -- you'll HARDLY ever have to do this second parse, so it is OK.

As for the java thingy -- no example from me.  I do things simple, especially
when it comes to Java.  Anydata + collection types + java = yuck.  sorry --
don't have one

Rating: 2
How to work with Anydata type over JDBC November 27, 2002
Reviewer: Oleksandr Alesinskyy from Germany
Ok, just in this specific case erronous statements may come even more often,
then normal ones (this is a main reason to have diagnostic as such
comprehensive, as possible), but, as I have said, I can tolerate double parsing
even on every statement as performance isn't issue.

About simplicity - you are right, it is much better to do things simple and this
is possible with 99.999%likehood, but I prefer to know how to do complex things
just for the sake of safety in last 0.00%

Sincerely yours,

Oleksandr Alesinskyy

November 26, 2002 Matthew -- Thanks for the question regarding "Modifying sequence numbers", version 8.1.6
You Asked
It seems that there is no way to directly modify the next
sequence number.  You can drop the sequence and recreate it with
a new start number, but this means that all dependencies will
become invalid and permissions will be lost.  Since every one of
our tables has a trigger which depends on the sequence number,
this is an annoying issue.  I would like to be able to modify
the sequence number when loading a newly created database with
data (something we do a lot--we use a data modeling tool to keep
our schema model and periodically "unload" the existing DB,
create a fresh schema from the data model tool, and reload the
old data.

Experimenting with Toad, I found I was able to modify one of the
system tables and change the next sequence number and it appears
to work.  I could create a SP to make this modification without
having to edit the system table directly, and this would
accomplish the immediate goal of being able to reset the
sequence number without dropping it.

Is this acceptable or is there a better way to accomplish the
same task?

Matthew Cromer
and we said...
Not acceptable.  Never modify the system tables under any
circumstances (unless support tells you to).  You can get into
serious problems there.  The database uses the data dictionary
like your application would -- it sometimes caches results and
assumes "hey, since I own this table, I KNOW no one is modifying
it so I don't have to query it again to find the right value".
You will hit ora-600 (internal errors) and experience other bad
side effects from updating the data dictionary.

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1119633817597
for how to reset a sequence.  This is safe and this logic
can be put into a stored procedure.
Review & Followup
Rating: 3
what bout when we want to use it in a procedure ? November 26, 2002
Reviewer: shalini from CA,USA
Hi Tom,

I think this is an excellent idea to reset the sequence,
But when I try to use it within procedure,
the statement to alter the sequence gives me the error...

PLS-00103: Encountered the symbol "ALTER" when expecting one of the following:
begin declare end exception exit for goto if loop mod null
pragma raise return select update while <an identifier>
<a double-quoted delimited-identifier> <a bind variable> <<
close current delete fetch lock insert open rollback
savepoint set sql execute commit forall
<a single-quoted SQL string>

why so and whats the workaround ?

thanx in advance for help

Followup:
execute immediate 'alter .....';

DDL needs to be done dynamically in plsql

November 26, 2002 Jack -- Thanks for the question regarding "pctincrease having 0", version 8.1.7
You Asked
Hi,

we have one dba who keep insist on having  PCTINCREASE 10. He claims having
pctincrease 10 will keep SMON coalescing the fragmentation I read some where
having pctincrease 0 itself won't create any fragmentation. Can you elaborate
with examples who is right?
thanks
Jack
and we said...
ugg.

In 817 we should all be using or migrating to locally managed tablespace.  there
pctincrease = meaningless_parameter_that_is_thankfully_totally_ignored.

If you do the right thing with dictionary managed tablespaces which is:

o never drop or truncate

OR

o initial = next = constant and pctincrease = 0

you never never never need SMON to waste your CPU cycles on coalescing adjacent
free extents.....

Basically -- if they NEED to set pctincrease <> 0 -- they are doing it wrong.
November 27, 2002 Balaji -- Thanks for the question regarding "Dynamic Sql,Bind Variables,TK PROF", version 8.1.17
You Asked
Hi Tom,
 I am lucky to ask these questions. I read about how to use bind variables in
Dyanmic Sql.With this i am sending the eg which tried.

declare

br_code varchar2(4);
version_date date;
ls_sql  varchar2(1000);

begin

br_code :='0781' ;
version_date :='20020930';

/* Method 1, This is  the method we are using now. */
ls_sql := 'update t set br_code= '|| ''''||br_code || ''''|| ' ,version_date = '
|| ''''||version_date||'''';
dbms_output.put_line('SQL ' || ls_sql);
execute immediate ls_sql;

/* Method 2 , We are planning to use this method*/
ls_sql := 'update t set br_code= :br_code ,version_date = :version_date';
dbms_output.put_line('SQL ' || ls_sql);
execute immediate ls_sql using br_code,version_date;

end;
/

The following are the report generated from the TKPROF.

***********************************************************

update t set br_code= '0781' ,version_date = '20020930'

call  count  cpu  elapsed    disk      query    current        rows
---- ------  ---- -------- ---------- ---------- ------- ----------
Parse   1    0.02   0.02     0         0           0              0
Execute 1   10.42  11.32    92      2925       30810          12504
Fetch   0    0.00   0.00     0         0           0              0
------- -- -------- ------ ------  ------      ------        ------
total   2   10.44  11.34    92      2925       30810          12504

Misses in library cache during parse: 1

***************************************************************

***************************************************************

update t set br_code= :br_code ,version_date = :version_date

call  count   cpu    elapsed     disk   query  current  rows
---- ------  ------  --------    ------ ------ -------  ----------
Parse   1     0.01      0.01       0      0         0         0
Execute 1     6.88      8.59     165    323     26340   12504
Fetch   0     0.00      0.00       0         0         0       0
------ ----  ------   -------    -----  -----  -------  ----------
total   2    6.89       8.60     165    323     26340   12504

Misses in library cache during parse: 1
**************************************************************

Cau let me know, why the no's under disk column is high when i use the bind
variables. I can see the CPU time is less when compared to the first. But i was
wondering why the I/O is more values when compared to the first.

2)
 In our application we have Normal Tables and IOT Tables. The Table
ME_CUST_CA_REV_DETAILS, ME_TXN_CA_ENTRIES which are IOT Tables.Both has around
70 columns .We are facing problems while inserting the records into these
tables. It takes lot of time to insert. Is there any way we increase the speed.
The Select Statement which is used to insert the rows into the tables takes only
5 mins to retrieve all the records (Around 200,000 records), but when combined
with the Insert into Table it takes around 40 min's to 1 hr to insert into these
tables.
   Pls advice.

Thanks and Regards
M.Balaji
.
and we said...
1) you compared apples to flying toaster ovens that is why.  I'll ASSUME that t
has various indexes on br_code/version_date and/or br_code/version_date is
NULLable or shorter then the values you update to.  You ran an update with
constants.  It did serious damage to the indices/migrated rows.  you ran the
SAME update again with binds -- which did not (cause that damage, migrate rows
-- the damage was done, the rows migrated).

Here is a way to see the effects, run this script:

drop table t;
create table t ( br_code varchar2(4), version_date varchar2(8) );
insert into t select null, null from all_objects where rownum <= 12504;

alter session set sql_trace=true;
update t first set br_code= '0781' ,version_date = '20020930';
update t second set br_code= '0781' ,version_date = '20020930';
alter session set sql_trace=false;

drop table t;
create table t ( br_code varchar2(4), version_date varchar2(8) );
insert into t select null, null from all_objects where rownum <= 12504;
create index t_idx1 on t(br_code);
create index t_idx2 on t(version_date);
create index t_idx3 on t(br_code,version_date);
create index t_idx4 on t(version_date,br_code);

alter session set sql_trace=true;
update t third set br_code= '0781' ,version_date = '20020930';
update t fourth set br_code= '0781' ,version_date = '20020930';
alter session set sql_trace=false;

and then review the tkprof (ignore ELAPSED, we can tune that) :

update t first set br_code= '0781' ,version_date = '20020930'

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      1.68       9.05         47      10233      47902       10849
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      1.68       9.05         47      10233      47902       10849

tons of MIGRATED rows thanks to me inserting NULL, NULL to begin with!!!

update t second set br_code= '0781' ,version_date = '20020930'

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.94       4.91         59         50      38532       10849
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      0.94       4.91         59         50      38532       10849

my, that is much better, lots less row migration going on here, less query,
less current, less CPU.  Now, let's add some indexes:

update t third set br_code= '0781' ,version_date = '20020930'

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.01          0          0          0           0
Execute      1      5.42      38.39        241      10589     137728       10849
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      5.43      38.40        241      10589     137728       10849

wow, add some indexes and watch out!  but wait -- is it so bad?

update t fourth set br_code= '0781' ,version_date = '20020930'

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      1.14       4.80         52         50      38531       10849
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      1.14       4.80         52         50      38531       10849

hmmm, didn't have to migrate rows, didn't have to actually BUILD the index --
last update did the work. Much faster, lots less LIO's

This is what you are seeing, the first update did all of the work -- the second,
just plowed along.

try dropping and recreating T in between the updates -- then see (that they are
the same)

2) an IOT is used to increase QUERY performance.  To physically COLOCATE rows
together that you query together.

Insert performance on an IOT, like a b*tree cluster or a hash cluster -- which
all force PHYSICAL PLACEMENT on data -- will be much much much slower.

The concept here is - I query the data a gazillion times during the day -- hence
I want the data "physically" ordered to increase performance.  I'll pay the
performance tax (taxes, like death, are unfortunately unavoidable) during the
LOAD, instead of every time I query.

70 columns sounds like *alot* for an IOT.  Are you sure you are using it
"right"?? (if you have my book "expert one on one oracle" -- i describe when I
believe you would want to use them)
Review & Followup
Rating: 5
Excellent November 26, 2002
Reviewer: Balaji.M from Singapore
Hi Tom,
  After reading this i realise still there are lot to learn in Oracle. Thanks
for the Answer

Regards
M.Balaji

Rating: 5
How November 27, 2002
Reviewer: Barry
Tom

"
tons of MIGRATED rows thanks to me inserting NULL, NULL to begin with!!!"

How can you tell there was tons of migrated rows
form the TKPROF output?

Followup:
I used my knowledge of how Oracle works.

I know when I inserted (null,null) the row took very very little room.  We had
many hundreds of rows/block.

I know when I updated (null,null) to br_code= '0781' ,version_date = '20020930'
the row would grow, we can fit only a couple dozen rows/block.  The other rows
on that block will migrate.

Then, I compared the IO's:

update t first set br_code= '0781' ,version_date = '20020930'

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      1.68       9.05         47      10233      47902       10849
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      1.68       9.05         47      10233      47902       10849

tons of MIGRATED rows thanks to me inserting NULL, NULL to begin with!!!

update t second set br_code= '0781' ,version_date = '20020930'

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.94       4.91         59         50      38532       10849
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      0.94       4.91         59         50      38532       10849

The ONLY difference between these two was that "first" had to MIGRATE the
(null,null) rows -- I just KNOW that (it has to happen).  It is what accounts
for the additional query and current mode gets.

November 27, 2002 Venkatesh -- Thanks for the question regarding "Migration from MS SQL to Oracle", version 8.1.7
You Asked
Dear Tom,

I am in a critical situation that I need to convert all the procedures which are
in MS SQL to Oracle8i in a project i am involved in.

In a particular procedure in MS SQL they have used temporary table (which is
usual in MSSQL). but here, in this procedure they have updated a that temporary
table according to the values on other tables... ( I hope that u get my
problem)...

how can i go about doing the same, in Oracle ..... this is the only procedure
pending right now...

thanx in advance Tom.
Regards,
Venkatesh
and we said...
we have global temporary tables -- so that is not an issue.

to update data in T1 from T2 -- you need only use ANSI syntax:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:273215737113
has two different methods for updating a Join that should work in all databases.

(i would actually be looking to REMOVE the silly temp table from the procedure
all together.  Just join.  I cannot tell you how many multi-page t-sql things
I've turned into a VIEW in Oracle)
Review & Followup
Rating: 2
How silly is the temp table in SQL Server? November 26, 2002
Reviewer: Henry Xie from NYC, USA
I know you have probably written on this before.
But I don't quite see why temp table is
evil.  I do think that temp table is sometimes
a good way to simplify the logic by dividing
a complex procuedure into smaller jobs.

I would compare it to the use of procedures
in, say, the C language as opposed to the assembly
languages.  You can certainly do all the things
one can imagine in an assembly language without
using procedures, and more efficient you bet.
However the point is that procedures do make
the job of people, especially dump people like me,
much easier.  I, for one, find it hard to
write and debug a 40-line select statement.
I'd rather write five 2-line select/update statements
with a couple of temp tables to link them together.
This way I can debug each of the five statements
easily.

In other words, I'd rather regard the use of
temp table as a matter of taste.

Followup:
Inline views do the same thing -- break the logic into smaller (more efficient)
jobs.

I'd rather regard it as a matter of performance, the more you use them - the
slower you go.

Rating: 3
Migration from MS SQL to Oracle November 27, 2002
Reviewer: Venkatesh from Chennai, India
Ya, i agree with you in using temporary table.
But what happens when more than one user executes the procedure at the same
time.

Suppose we create temporary table in the procedure using EXECUTE IMMEDIATE.
obviously when two people executes the procedure, the name of the temporary
table conflicts. ain't it?  what could be the alternative to this?

regards,
venkatesh

Followup:
Umm, global temporary tables are always 100% session specific.

You cannot see my data, I cannot see your data.  Only YOUR session can see YOUR
data, period.

do not create a table in a procedure, that would be the wrongest way to do it in
Oracle.

Rating: 5
Really great... November 27, 2002
Reviewer: Venkatesh from Chennai, India
Its really great that I had a great response from you. Thank you very much, Tom
and co.

Regards,
venkatesh

November 27, 2002 Sitarama -- Thanks for the question regarding "Killing a session", version 9.0.1
You Asked
Hi Tom,
I have a query. We are using TOAD to debug the stored procedures. Sometimes the
team performs the debugging operation and forgets to rollback or commit the
information and initiates another debug session. While we make sure that such
things dont happen frequently, it does happens sometimes. This causes a
deadlock. Whenever a session having a deadlock is killed, Oracle either kills
the session immediately or marks the session as "killed" during which it
rollbacks the information generated by the application. However, in the case
mentioned above, it is always marked for killed. Our observation is that system
is taking a very long time to do this rollback sometimes to the order of 30 mins
to 1 hr. Can you please tell if this is a normal behavior as we cannot afford to
such long disruptions in development. If this is not a normal behavior what is
that we can do to reduce this time of rollback? Thanks in advance.

Have a great day.
Chakri

Following up on the above question...

Thanks Tom, for your response. I should have put more information for the
purpose of clarification. Thing is, whenever a dead lock occurs, we logon to the
database as SYSTEM and issue the command "alter system kill session" with the
respective serial# and sid. This is what i meant when I said "Whenever a session
having a deadlock is killed". I am give an example here...

09:39:19 ccil:SQL> @e:\chakri\oracle\sql\locks

OS_USER_NAME                   SESSION_ID OBJECT_NAME                    PROCESS
  LOCKED_MODE
------------------------------ ---------- ------------------------------
--------- -----------
desc
---------------------------------------------------------------------------------
------
Administrator                          24 SGL_ACNT_TXN
1164:2304           3
Lock table in Row Exclusive Mode - TM(RX)

Administrator                          23 ACTVTY_LOG
1164:2304           3
Lock table in Row Exclusive Mode - TM(RX)

Administrator                          23 GLBL_SCRTS_BLNCS
1164:2304           3
Lock table in Row Exclusive Mode - TM(RX)

Administrator                          23 SGL_ACNT_TXN
1164:2304           3
Lock table in Row Exclusive Mode - TM(RX)

09:39:55 ccil:SQL> alter system kill session '23,597';
alter system kill session '23,597'
*
ERROR at line 1:
ORA-00031: session marked for kill

09:42:03 ccil:SQL> /
Enter value for n: PH2DEV
old   3:  where schemaname = '&n'
new   3:  where schemaname = 'PH2DEV'

       SID    SERIAL# MACHINE              STATUS   OSUSER
SCHEMANAME
---------- ---------- -------------------- -------- --------------------
--------------------
        23        597 CCIL\SITARAM         KILLED   Administrator        PH2DEV
        24        150 CCIL\SITARAM         INACTIVE Administrator        PH2DEV
        32       1911 CCIL\SITARAM         INACTIVE Administrator        PH2DEV

09:42:07 ccil:SQL> /
Enter value for n: PH2DEV
old   3:  where schemaname = '&n'
new   3:  where schemaname = 'PH2DEV'

       SID    SERIAL# MACHINE              STATUS   OSUSER
SCHEMANAME
---------- ---------- -------------------- -------- --------------------
--------------------
        23        597 CCIL\SITARAM         KILLED   Administrator        PH2DEV
        24        150 CCIL\SITARAM         INACTIVE Administrator        PH2DEV
        32       1911 CCIL\SITARAM         INACTIVE Administrator        PH2DEV

09:46:53 ccil:SQL> /
Enter value for n: PH2DEV
old   3:  where schemaname = '&n'
new   3:  where schemaname = 'PH2DEV'

       SID    SERIAL# MACHINE              STATUS   OSUSER
SCHEMANAME
---------- ---------- -------------------- -------- --------------------
--------------------
        23        597 CCIL\SITARAM         KILLED   Administrator        PH2DEV

18:19:19 ccil:SQL> /
Enter value for n: PH2DEV
old   3:  where schemaname = '&n'
new   3:  where schemaname = 'PH2DEV'

       SID    SERIAL# MACHINE              STATUS   OSUSER
SCHEMANAME
---------- ---------- -------------------- -------- --------------------
--------------------
        23        597 CCIL\SITARAM         KILLED   Administrator        PH2DEV

If you see the above statistics, i simulated a deadlock at around 9:40 AM
(please see session id 23 and 24)....and killed the session 23 as mentioned
above. Since then i have been monitoring the v$session. AS you see, the session
is marked as "killed" even at 18:20 hrs.

1. Why is this happenning??? To my knowledge, PMON should have cleaned up this
process. Please explain.

2. Also, i was not able to modify the data in SGL_ACNT_TXN table till 09:59 AM
(getting an error saying that the record is being modified by other
user)...However, i tried to modify the data at 18:20 hrs....even though the
session is marked killed, system allowed me modify the data...What does this
mean???

Thanks in advance.

Chakri
and we said...
read
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1058832409881
the session will stay in the KILLED state potentially forever.

If you open a session in sqlplus

And then goto another window and kill that session

That first session will stay there forever UNTIL that session tries to do
something in the database -- then that session will get the message "your
session has been killed" and will be physically disconnected at that point.

Else -- if we didn't do that -- the first session would get "3113 EOF on
communication channel" -- meaning that user would immediately call the help
desk.  The only way we can gracefully handle the kill is to wait for the client
to contact us.

Now, whilst in the killed state -- the transaction IS ROLLED back.  LOCKS ARE
given up.  That session isn't blocking anyone after it is done rolling back (and
the progress of the roll back can be monitored in v$transaction -- as talked
about in that link)

PMON should not, will not, have "cleaned up" this process.  PMON cleans up after
abnormal termination -- this is not an abnormal termination.

You were able to modify it because the killed session had completed its
rollback.
Review & Followup
Rating: 4
November 27, 2002
Reviewer: Chakri from Mumbai, India
Thanks Tom for the info...

November 27, 2002 Hrishy -- Thanks for the question regarding "select within select", version 8.1.7
You Asked
Hi Tom

sometimes i have seen that you tend to write very very smart sql code with
analytics ,inline view etc...there are couple ofd examples available on your
site for the above ...they are excellent...however i sometimes seen you writing
sql code somethin like this

a)select empno,name ,(select deptno,..
from emp

now my question can you please gimme some exmaples of select within select and
also exaplin me the situation for which you would like to use these sql's..just
like the way you have explianed for non square result sets..

b)I am interested in job scheduling capabilites of dbms_job ..could you give an
example of a job scheduling where in i have to execute jobs in a defined order

like
p1
p2
p3
and the condition being even if p1 fails p2 and p3 should be executed..

regards
Hrishy
and we said...
a) it is useful in place of an outer join.  To get the count of emps by DEPT, if
you just code:

  1  select dept.deptno, count(*) from emp, dept where emp.deptno=dept.deptno
  2* group by dept.deptno
scott@ORA920.US.ORACLE.COM> /

    DEPTNO   COUNT(*)
---------- ----------
        10          3
        20          5
        30          6

we are NOT getting the correct answer - we are missing deptno=40.  So, we could:

  1  select dept.deptno, count(EMPNO)
  2    from emp, dept
  3   where emp.deptno(+)=dept.deptno
  4*  group by dept.deptno
scott@ORA920.US.ORACLE.COM> /

    DEPTNO COUNT(EMPNO)
---------- ------------
        10            3
        20            5
        30            6
        40            0

Or alternatively:
  1  select dept.deptno, (select count(*) from emp where emp.deptno=dept.deptno)
cnt
  2*   from dept
scott@ORA920.US.ORACLE.COM> /

    DEPTNO        CNT
---------- ----------
        20          5
        30          6
        40          0
        10          3

scott@ORA920.US.ORACLE.COM>

Sort of like a correlated subquery.

b) schedule this job:

begin
     begin
         p1;
     exception
         when others then
            log_error( 'p1', sqlerrm );
     end;
     begin
         p2;
     exception
         when others then
            log_error( 'p2', sqlerrm );
     end;
     begin
         p3;
     exception
         when others then
            log_error( 'p3', sqlerrm );
     end;
end;

or better yet, create that as a procedure 'p4' and just schedule p4 to run.
Review & Followup
Rating: 5
cool November 27, 2002
Reviewer: hrishy from PA
Hi Tom

Well as usual coolest response ;-D..you rock

November 27, 2002 PARTHA -- Thanks for the question regarding "Restrict to All_source Table", version 8.1.7
You Asked
Tom,

Can you tell me how do i give users to see only the stored procedures that they
created not any thing else in all_source table.

When user queries like following..

Select * from all_source;

They should be able to see only his source not anybody source.

My solution is i will create a view as follows..

create view test as select * from all_source where owner = user;

or

Revoke the privilleges from using all_source

Above one will work or not and can you give me your thoughts.

Thanks.

Partha
and we said...
Well, first and foremost -- I would ask "why do you care?"  They can only see
the source of code they are allowed to execute.

But if you care, and you use packages, they will see ONLY the package
specification, not the body (the code).  Packages are the only thing to use in a
real production system as well.  It breaks the dependency chain and promotes
much better coding practices.

Playing games with the Oracle data dictionary like that can have adverse side
effects.  I can imagine many third party tools -- and even many Oracle tools --
being very upset at the lack of the "all_source" view.  You need not create that
other view you described, we already have that -- it is "user_source".
Review & Followup
Rating: 3
All_source Table Restriction June 05, 2002
Reviewer: A reader
Good

Rating: 4
Missing privilege for all_source through PL/SQL? November 27, 2002
Reviewer: Graham Halsey from UK
Hi Tom,

First things first, I know there are much better ways of extracting PL/SQL
source code than what I'm about to suggest, e.g. exp or your own getcode.sql
scripts. However, I'd appreciate your response anyway, here goes >>

I can execute a SQL statement that extracts source code for package spec and
package body from ALL_SOURCE. But when I run the same SQL statement inside
PL/SQL, I only get the package spec and not the body. Is there a privilege
somewhere I need to grant to the PL/SQL owner or is it just not possible to get
package body through PL/SQL hitting all_source?

Here's some code to show you what I mean.

@connect sys@&&connectstring
create user tom identified by tom default tablespace users temporary tablespace
temp;
grant dba to tom;

@connect tom@&&connectstring
create or replace package tom_pkg as
procedure tom_proc;
end;
/
create or replace package body tom_pkg as
procedure tom_proc is
begin
    null;
end;
end;
/
grant execute on tom_pkg to public;

@connect sys@&&connectstring
create or replace procedure dump_tom_code as
var_code    all_source.text%type;
var_type    all_source.type%type;
cursor    cur_tom is
select    text,
    type
from    all_source
where    owner='TOM'
order by
    lower(type),
    lower(name),
    line
;
begin
    open cur_tom;
    fetch cur_tom into var_code,var_type;
    while cur_tom%found
    loop
        dbms_output.put_line(substr(var_code,1,255));
        fetch cur_tom into var_code,var_type;
    end loop;
    close cur_tom;
end;
/

set lines 150 pages 500
col text format a80 wrap
set serveroutput on size 20000
select    text,
    type
from    all_source
where    owner='TOM'
order by
    lower(type),
    lower(name),
    line
;
exec dump_tom_code;

As SYS, when I run the SQL statement I get the spec and body, but when I exec
the PL/SQL procedure I just get the spec.

BTW, Oracle 8.1.6. on Linux.

TIA,
Graham.

Followup:
STOP -- sys is ours, consider it sacred.  STOP USING IT.  Don't touch it, don't
log in as it.  Just avoid it.  You never never never need to log in as sys (use
AS SYSDBA just to start up, shutdown and that is IT)

Then, read
http://asktom.oracle.com/~tkyte/Misc/RolesAndProcedures.html
roles are never in place during the execution of a definers rights procedure (as
you have).  Hence, the ALL_SOURCE view - as described above -- hides the
implementation from you -- the code.

You can use DBA_SOURCE to bypass that check.  The owner of the procedure -- who
had better NOT BE sys -- would need SELECT granted directly on DBA_SOURCE. Then
they can see ALL code from ALL users.

Rating: 5
Sorted November 27, 2002
Reviewer: Graham Halsey from UK
Thanks Tom & stop panicking!

I only used SYS because
a) it made my example script shorter so I didn't have to tell you what account I
was really using and what privs I'd granted to it
b) I figured SYS would have just about all privs granted so I couldn't have not
granted something required in order to get at ALL_SOURCE properly.

Anyway, now I'm using DBA_SOURCE as my non-SYS user, it's working perfectly

Graham.

Followup:
problem is -- things work differently for SYS then for other users.

SYS is the single worst account to pick for examples!  you'll never know if it
works cause of the magic of sys or what.  so, even for an example, using sys is
a bad idea.

November 27, 2002 -- Thanks for the question regarding "How to identify connection/server errors when sending email using utl_smtp?", version 9.0.2
You Asked
I have been using utl_smtp to send emails from Oracle.
And I followed your advice and made it run as a job.

Now I have a question about the exception handling
of the utl_smtp package.  Basically I don't want
the email job to be re-tried for 16 times if
the exception is caused by the erroneous use
of utl_smtp such as wrong parameters are passed.
One such example is when the sent-from email address
is bogus -- this is possible since I read it from database
-- utl_smtp would raise an exception like "invalid domain".
In other words, I only want the email
job to be re-tried only when there is a connection issue,
or the smtp server is temporarily jammed, or
other situations that re-tries might overcome.

However, I don't know if it is possible to catch those
exceptions caused by errors (or those that
worth re-trying).  I have the list of all possible
excpetions from utl_smtp, but I cannot figure out how
I can do what I need reliably.  I wonder if you
have any solutions or tips on this issue.

Thanks a lot!  I really appreciate your help.
and we said...
yes, you can catch them.

here is an example straight from the utl_smtp specification:

  *
  * EXAMPLES
  *   Retrieve the home page from
http://www.acme.com/
  *
  *   DECLARE
  *     c utl_smtp.connection;
  *
  *     PROCEDURE send_header(name IN VARCHAR2, header IN VARCHAR2) AS
  *     BEGIN
  *       utl_smtp.write_data(c, name || ': ' || header || utl_tcp.CRLF);
  *     END;
  *
  *   BEGIN
  *     c := utl_smtp.open_connection('smtp-server.acme.com');
  *     utl_smtp.helo(c, 'foo.com');
  *     utl_smtp.mail(c, 'sender@foo.com');
  *     utl_smtp.rcpt(c, 'recipient@foo.com');
  *     utl_smtp.open_data(c);
  *     send_header('From',    '"Sender" <sender@foo.com>');
  *     send_header('To',      '"Recipient" <recipient@foo.com>');
  *     send_header('Subject', 'Hello');
  *     utl_smtp.write_data(c, utl_tcp.CRLF || 'Hello, world!');
  *     utl_smtp.close_data(c);
  *     utl_smtp.quit(c);
  *   EXCEPTION
  *     WHEN utl_smtp.transient_error OR utl_smtp.permanent_error THEN
  *       BEGIN
  *         utl_smtp.quit(c);
  *       EXCEPTION
  *         WHEN utl_smtp.transient_error OR utl_smtp.permanent_error THEN
  *           NULL; -- When the SMTP server is down or unavailable, we don't
  *                 -- have a connection to the server. The quit call will
  *                 -- raise an exception that we can ignore.
  *       END;
  *       raise_application_error(-20000,
  *         'Failed to send mail due to the following error: ' || sqlerrm);
  *   END;
  */
Review & Followup
Rating: 4
Thanks! November 27, 2002
Reviewer: Henry Xie
Tom,

Thanks for your prompt reply!

So I just need to re-raise utl_smtp.transient_error
and suppress utl_smtp.permanent_error, after
logging all of them, inside the email job, so that
the email job will re-try upon transient_error only.
Cool.  Thanks again.

November 27, 2002 Senthil -- Thanks for the question regarding "Efficient & secure way of Deleting rows from a Table", version 8.1.6
You Asked
Hi Tom,
When i want to delete all the rows from a table in the developer
forms either i have to use "Delete from table xyz " or
FORMS_DDL('TRUNCATE TABLE XYZ');
The efficient way of deleting the entire table  is TRUNCATE
TABLE COMMAND , but the user sholud have <<DROP ANY TABLE >>
privilege. <<DROP ANY TABLE >> privilege cannot be given to all
the users.
I would like to know what is the effcient way of deleting the
rows from a Table through <<Sql*Forms>> without giving
<<DROP ANY TABLE >> privilege to the user.

Thanks and Regards
M. Senthil Kumar

I want to delete the rows from a table.
RIghtnow we are using
the FORMS_DDL() in forms.
The user has to be give with a privilege of 'DROP ANY TABLE'
and we said...
Have the owner of the table create a procedure:

create procedure truncate_that_table
as
begin
   execute immediate 'truncate table XYZ';
end;

Have that owner grant execute on this procedure to the
appropriate people or roles.

Now, your form can simply execute

begin
   truncate_table_table;
end;

and it'll be done.  They need no additional access to that table
in order to do that.  The stored procedure runs with the base
privilege set of the definer of the procedure.
Review & Followup
Rating: 3
Inline view and Sql Forms November 18, 2002
Reviewer: A reader from India
Dear Tom,
  Is it not possible to use inline view in Sql Forms?I am getting the following
error
PL/SQL error 103 at line 7,column 6:
encountered the symbol "(" when expecting one of the following:
a pl/sql variable or double-quoted string
an expanded name
an expanded name link
a table reference expression
a key word
resuming parse at line 12,column 15.

Please help.

Followup:
sql forms -- that would be the name of a really really really old product.
anyway, if you have a really old production (eg: predates version 7.1 of the
database when inline views were added) you might have to use a VIEW in the
database or dynamic sql (if such a thing exists in the version of the tool you
are using) to hide this construct from the parser which predates this feature.

Rating: 5
question regarding temp tablespace when using definer procedure November 27, 2002
Reviewer: A reader
hi

user X has temp tablespace TEMPX
user Y has temp tablespace TEMPY

user X has a procedure to create index and grant execute privilege to user Y

user Y runs the procedure and create a big index but we get error saying TEMPX
is full, shouldnt user Y use TEMPY for this process?

Also I have this procedure

CREATE OR REPLACE procedure tablon(nom_tabla varchar2)
as
begin
execute immediate 'create table '||nom_tabla||' as select * from bscs.emp';
end;
/

if I grant execute to user Y when he runs it the table is created under my
schema... how so?

Followup:
in a procedure running with definers rights -- the current schemaid is X (the
owner).  So, the temp comes from the current schemaid and should be TEMPX.

As for the second question -- the procedure RUNS AS OWNER, the current schema in
the procedure is the OWNER.  It is as if the OWNER of this procedure ran the
create table statement themselves.

The how so is "by design", thats the way they were intended to work.

Rating: 5
so it is like run alter session set current schema? November 27, 2002
Reviewer: A reader
Hi

So if I am user Y and execute a procedure whose owner is user X it's like I
become user X?

We have a problem in our datawarehouse, we have a ETL user who has a temporary
tablespace for himself but to create an index he must use a package due to
security reasons (ETL user has minimum privilege) however it is NOT using his
own temporary tablespace which you said it's a normal behaviour, is there any
way to make him use his own temporary tablespace?

I always thought that the invoker inherit system privileges only, not temporary
tablespaces and default tablespaces and certainly not the schema! Seems that I
am totoally wrong :-(

Followup:
Yes, exactly -- that is the very definition of "definers rights" procedures (as
opposed to INVOKERS rights routines which we also do)

The only way to get the temp tablespace you want to use get used would be:

o grant the CREATE INDEX to the user Y (which if you ask me would HAVE to be in
the minimal set of privs for the ETL user.  Hey -- I'm all over this minimal
privs concept but you seem to have taken it to an illogical extreme)

o alter user X to have that default temporary tablespace

When Y runs that procedure, it is as if X were running it (or Z for that matter,
they are all X)

November 27, 2002 Catherine -- Thanks for the question regarding "ref cursor", version oralce8i
You Asked
Hi Tom,
I need to create a procedure which will return multiple
recordset
with multiple records. Can Oracle manage it and how can I create
the stored procedure? Since this procedure is going to be called
in java program, how can I get the cursors?

Thanks
and we said...
Yes, you can return as many result sets as you like, see
http://osi.oracle.com/~tkyte/ResultSets/index.html
for an example of how to do this in java and many other
languages.
Review & Followup
Rating: 4
ref cursor March 16, 2001
Reviewer: Catherine Tieu from USA
This is very helpful thank you very much.

Rating: 3
Multiple ResultSet return November 26, 2002
Reviewer: Michael from Irvine, CA USA
Can you go over a sample procedure that returns multiple ref cursors and how to
scroll through the many ref cursors (ResultSet in JDBC)?
Thank you.

Followup:
You will just have more then one OUT parameter.  You just call getObject for as
many as you have.

      conn = DriverManager.getConnection(connect_string, "scott", "tiger");

      CallableStatement cstmt = conn.prepareCall(query);
      cstmt.registerOutParameter(1,OracleTypes.CURSOR);
      cstmt.registerOutParameter(2,OracleTypes.CURSOR);
      cstmt.execute();
      ResultSet rset1 = (ResultSet)cstmt.getObject(1);
      ResultSet rset2 = (ResultSet)cstmt.getObject(2);

      while (rset1.next ())
        System.out.println( rset1.getString (1) );
      while (rset2.next ())
        System.out.println( rset2.getString (1) );

Rating: 4
very good. November 27, 2002
Reviewer: jlandzpa from CHN
thank.

Rating: 4
This is great! November 27, 2002
Reviewer: Michael from Irvine, CA USA
Hi Tom,

Thank you very much.  This was very helpful.  However, it did take me a few
tries.  I notice that if my sp takes other parameters, they have to be declared
after the IN OUT ref cursors.  For example:

   PROCEDURE sp_get_login_info
   ( pcur_user   OUT inetowner.inet_types_pkg.ref_cursor
   , pcur_menu   OUT inetowner.inet_types_pkg.ref_cursor
   , pcur_rollup OUT inetowner.inet_types_pkg.ref_cursor
   , p_emp_id    IN VARCHAR2
   );

The other question I would have for you is whether or not this is the
recommendation to do or split that into three separate sp and call them
separately in the Java class.

Thank you again.

Followup:
No you don't?  Take this package:

create or replace package demo_pkg
as
    type rc is ref cursor;

    procedure p( p_input1 in number,
                 p_rc1    out rc,
                 p_input2 in number,
                 p_rc2    out rc,
                 p_input3 in number,
                 p_rc3    out rc );
end;
/

create or replace package body demo_pkg
as
    procedure p( p_input1 in number,
                 p_rc1    out rc,
                 p_input2 in number,
                 p_rc2    out rc,
                 p_input3 in number,
                 p_rc3    out rc )
    is
    begin
        open p_rc1 for select p_input1 from dual;
        open p_rc2 for select p_input2 from dual;
        open p_rc3 for select p_input3 from dual;
    end;

end;
/

and this java:

import java.sql.*;
import java.io.*;
import oracle.jdbc.driver.*;

class RefCursor
{

public static void main (String args [])
throws SQLException, ClassNotFoundException
{
   String query  = "begin "+
                        "demo_pkg.p( ?, ?, ?, ?, ?, ? ); "+
                    "end;";

   DriverManager.registerDriver
        (new oracle.jdbc.driver.OracleDriver());

   Connection conn=
       DriverManager.getConnection
       ("jdbc:oracle:thin:@aria-dev:1521:ora817dev",
         "scott", "tiger");

   Statement trace = conn.createStatement();

   CallableStatement  cstmt = conn.prepareCall(query);

   cstmt.setInt(1,55);
   cstmt.registerOutParameter(2,OracleTypes.CURSOR);
   cstmt.setInt(3,66);
   cstmt.registerOutParameter(4,OracleTypes.CURSOR);
   cstmt.setInt(5,77);
   cstmt.registerOutParameter(6,OracleTypes.CURSOR);
   cstmt.execute();

   ResultSet rset1 = (ResultSet)cstmt.getObject(2);
   ResultSet rset2 = (ResultSet)cstmt.getObject(4);
   ResultSet rset3 = (ResultSet)cstmt.getObject(6);

   for(int i = 0;  rset1.next(); i++ )
        System.out.println( "rset1 " + rset1.getString(1) );

   for(int i = 0;  rset2.next(); i++ )
        System.out.println( "rset2 " + rset2.getString(1) );

   for(int i = 0;  rset3.next(); i++ )
        System.out.println( "rset3 " + rset3.getString(1) );

}
}

and you get this result:

$ java RefCursor
rset1 55
rset2 66
rset3 77

the order is not relevant at all.

As for the last one, if they logically belong together as outputs from the same
routine, by all means -- keep them together.  Consider them just as you would
ANY out parameter from the procedure, split them up only if you would split them
up if they were numbers.

Rating: 5
Thank you! December 06, 2002
Reviewer: Michael, Irvine Ca

Rating: 5
Thank Heaven for Tom! December 14, 2003
Reviewer: Melissa from Orlando, FL USA
This is exactly what I've been looking for!  A comprehensive example that shows
the pl/sql and the Java for a procedure returning multiple cursors.  Wonderful!

Rating: 5
Very Useful December 28, 2003
Reviewer: Sami

November 27, 2002 William -- Thanks for the question regarding "Locally Managed System Tablespace", version 9.2.0
You Asked
Tom,

Can you share any observations or experiences with Locally Managed System
tablespaces?  Our shop will be upgrading to 9.2 next year, and while some of us
would like to use LMT's for the System tablespace, many of the senior DBA's feel
that it's the sort of feature we'd be better off letting other people "test in
production" for a year or two before using it.  I've used non-system LMT's in
8.1.7 for about a year and think they're great.

Thanks.
and we said...
I've not heard of any issues.  I haven't had any issues -- I won't use anything
but.

By the time you upgrade to 9.2 next year, people would have been testing it for
over a year -- one way to look at it.

I wouldn't consider anything else on a newly created instance personally.  The
neat thing is that is system is LMT, it is physically IMPOSSIBLE to create a
DMT!!!  (one mans feature is anothers shortcoming I'm sure -- but I really think
that is cool).
Review & Followup
Rating: 4
Thanks, Tom! December 02, 2002
Reviewer: William Rachal from New Orleans, LA USA
Thanks for the fast response.  Great information, as always.

November 29, 2002 Reddi -- Thanks for the question regarding "default users", version 8.1.7
You Asked
When I do a search on dba_users, I see a long list of users( a couple of them
which I created), actually how many of them do I actually need to run a
database.

and who are the * marked users( i have put a * against these user names).

SQL> select username from dba_users;

USERNAME
------------------------------
SYSTEM
OUTLN*
AURORA$JIS$UTILITY$ *
AURORA$ORB$UNAUTHENTICATED*
ORDPLUGINS*
CTXSYS*
ADAMS
CLARK
TEST
MIKE

USERNAME
------------------------------

MTSSYS*
BLAKE
JONES
SCOTT
MDSYS
ORDSYS*
OSE$HTTP$ADMIN*
DBSNMP*
and we said...
adams, blake, clar, jones should be dropped.  They are demo accounts. if you
installed demos, they got installed.

outln -- for the Query Plan stability feature.  Should have the password
altered.

AURORA$JIS$UTILITY$
AURORA$ORB$UNAUTHENTICATED is an internal account used by the builtin java
capabilities.

ctxsys is the interMedia text owner.  It owns the context packages.  You should
change its password.

mdsys is the spatial (part of interMedia) owner, like ctxsys you should change
the password.

ordplugins and ordsys -- same as mdsys.  part of intermedia, generic stuff used
by them all.

dbsnmp is part of the Oracle intelligent agent.  If you change this, pick up
<Note:70174.1> from support first.  It goes through the steps for doing this and

what needs to be updated.

mtssys - account for microsoft transaction server support.

tracesvr - used for Oracle trace.  This password should be changed.  If you are
using Oracle trace, remember to update it as well.

OSE$HTTP$ADMIN -- oracle servlet engine http administrative account.
Review & Followup
Rating: 4
May 14, 2001
Reviewer: A reader

Rating: 5
All documentation should read this clearly July 31, 2001
Reviewer: Rob Kellington from Edmonton, Alberta
Great!

Rating: 5
Clear and concise December 18, 2001
Reviewer: A reader
Thank you for the clear and concise information provided.

Rating: 5
One more question on this November 29, 2002
Reviewer: Tajana from Austria, Vienna
Thanks, I searched for this matter ...

To complete the list of users: There are also
WKSYS
HR,OE,PM,SH
OLAPDBA,OLAPSVR,OLAPSYS
OSE$HTTP$ADMIN
QS,QS_ADM,QS_CB,QS_CBADM,QS_CS,QS_ES,QS_OS,QS_WS
PORTAL30,PORTAL30_DEMO,PORTAL30_PUBLIC,PORTAL30_SSO,PORTAL30_SSO_PS,PORTAL30_SSO_
PUBLIC

What are those users all for ? Some of them seem to contain example/demo data
(HR,OE,PM,SH).

What about tablespaces EXAMPLE, CWMLITE, DRSYS ?

Which of that users and data (=> tablespaces) can I remove from a production DB
?

Followup:
see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:5796841276215
(found by searching for some of the names:
QS_ADM,QS_CB,QS_CBADM,QS_CS,QS_ES,QS_OS
on this site.  contains pointers to the DOC that tells you what these are.
hence a search on OTN in the 9.2 /whatever doc set would have found the same)

November 30, 2002 Bule -- Thanks for the question regarding "Datafile error and Log Archive", version 8.1.7
You Asked
Dear Tomacle,

Hope you are well.
I have 2 questions:
1. If I want to backup Database in Archive log mode, do I need to backup only
archive logs and control files? Do I need redo log files?
But I must backup full database ( Offline backup) but I need not backup
Datafiles daily.
I only need to backup archive logs and control files daily. Will I backup
archive logs and control files after shutting down Database?
If I don't shutdown Database, will I able to get last information from control
file?

2. I have 2 Database servers s1 and s2. One time , an error message ocurrs that
no data is retrived from a table. An error message was shown ' I/O error'. I
thought that error was occured due to block level corruption. I shut down the
Database. I copy the all Datafiles, archive logs, control files, redo log files
to another server s2 and start up that new server s2 and the production Database
is running smoothly. But when I want to startup the original server S1, the
database can not startup due to one datafile FMS_DATA01.DBF :

ORA-01157: cannot identify/lock data file 14 - see DBWR trace file
ORA-01110: data file 14: 'D:\ORA81DATA\FMS_DATA01.DBF'
ORA-27048: skgfifi: file header information is invalid
OSD-04004: invalid file header

But I copied this datafile into another server S2, and that s2 server is running
well without any error. But what is the problem? I delete Oracle software
including registry from the Problemetic server S1 and install Oracle freshly
again on this server. I Copied all required Oracle files from backup into
appropriate locations and tried to startup the database. But the same error
ocurred.

Please tell what is the problem.

Thanks and regards,
and we said...
1) you NEVER want to backup online redo logs -- NEVER (archive or noarchivelog).

The only thing that could happen by backing up online redo logs is you
accidently restore them over the current online redo logs -- wiping out any
chance for a complete recovery.

Just don't back them up.

You need to read:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76993/toc.htm
from cover to cover.  There are NO SHORTCUTS here.  The single most important
job of a DBA is backup and recovery.  If you don't know this stuff inside-out,
backwards and forwards -- you are not doing your job.

NEVER do a cold backup in archive log mode.  That is just an utter and total
waste.  OFFLINE backups are for losers.  Hot is the only way to backup an
archive log mode database.  Nonarchivelog mode databases -- don't know where
they fit in in a production environment personally so they don't really exist.

read
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3151992035532
2) yup, thats what I would do -- erase the software and reinstall because a
datafile has gone bad. (not).  You had a datafile corruption, it frankly sounds
like your disk was going bad (had a bad sector).  You got really lucky when you
copied it the first time and the OS was able to read it.  Then the disk went
kaput.  No more reading of that sector for you anymore.  That is why the
database could not start.

You should have gone to a backup of the file BEFORE the disk error showed
itself.  That is the file you should have restored (after determining WHY the
disk could not be read) and recovered.  That would have been the correct
approach -- take your backups (thats what they are there for) and use the
archived logs to catch that good file up to the rest of the system.  That is
what MEDIA recovery is all about.

Please -- read the backup and recovery guide -- read it a couple of times.
Understand how all of the pieces work together.  Do this if you value your data.
Review & Followup
Rating: 3
Thanks November 24, 2002
Reviewer: Blue from Bangladesh
Good suggestion and tips , Dear Tom.
Do you suggest for Hot back up?
In archive log mode, I must do hot backup?

What is the main reason not to do cold backup in Archive log mode?

Followup:
read the second link above for my thoughts on why cold backup in archive log
mode is a bad idea.

Rating: 3
What should be done? November 25, 2002
Reviewer: Blue from Bangladesh
Thanks Tom for your good idea.
But my query is: If I use Database with archive log mode, I should and must use
hot backup and I will never do cold or OS backup?

Followup:
asked and answered, from above:

....
NEVER do a cold backup in archive log mode.  That is just an utter and total
waste.  OFFLINE backups are for losers.  Hot is the only way to backup an
archive log mode database.  Nonarchivelog mode databases -- don't know where
they fit in in a production environment personally so they don't really exist.

......

Rating: 3
Good Tips November 26, 2002
Reviewer: Blue from BD
Dear Tom,
Thank you so much for your good and useful tips.
But I have some curisity, don't mind.
When I need to do cold backup?
Soppose, I backup full all my Datafiles, control files to Tape and I regularly
backup my all archivelogs and controlfiles daily. One day my database is lost
but I have all datafiles, upto-date controlfiles, archive logs in my Tape. I
restore all datafiles, control files and archivelogs from Tape into my Server
and startup the Database.
Can I not get last backup information?
What do think, what is this technique and do you think that this technique is
not good and this technique should not follow?

Followup:
quote: When I need to do cold backup?
answer: only when you are in NO archive log mode. never any other time

quote: Can I not get last backup information?
answer: if you have the last hot/cold backup, all archives and all online redo
logs -- you can recover fully.  if you lose an(or more) online redo log, you may
lose some committed transactions - unless you are also using data guard in 9i,
then you WON'T lose the online redo logs.

Rating: 3
Backup and Recovery November 27, 2002
Reviewer: Blue from BD
Dear Tom,
Thanks a lot. Sorry to bother you again and again.
I describe a Backup and restore method, I need to know that whether this
technique is applicable or not:
-----------------------------------------------
I backup full all my Datafiles once a time to Tape and I regularly backup my all
archivelogs and controlfiles daily.
I don't want to backup Datafiles daily, because it takes a huge time thats why I
backup (OS backup) all Datafiles one time to Tape. But I backup archivelogs and
control files regularly.
One day my database is crashed.
I install the same version of Oracle and the same Database name again on that
server and copy Datafiles, controlfiles and archive logs from Tape to the exact
locations of the Server.
If I need, I create password file.
Finally I startup the Database and restore the Data from archivelogs which it
needs.
Now my questions is: Is it possible to restore Database by this technique?
-----------------------
I believe that I will get your valuable comments on it.

Followup:
Yes -- but you do want to backup your database more then just once.  It would
take a really long time to apply all of those archives (and lots of tapes to
hold) .

This sounds like a weekly schedule - once a week you backup (hot) all datafiles
and during the week you backup archives (and a controlfile to TRACE)

Rating: 3
Thanks and Review November 27, 2002
Reviewer: Blue from BD
Dear Tom,
I am astonished that how you give answer so fast!!!!
Thanks again.
I can tell simply:
1. If I want to do daily backup I should and must use Hot(Online) backup of
Datafiles, control files and redo logs.
When I backup controlfiles in this situation , do we get last information in
controlfiles? Do I need online redo logs?
2. If We want as weekly backup, We can do cold/OS backup of datafiles. But this
takes huge time to recovery data from archive logs.
Am I correct or wrong ?

Followup:
1) you need to read:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76993/toc.htm
and absorb 100% of that material.  Until you can answer all of these questions
in your sleep -- you are not ready to be the primary "keeper of the backups".

you do not backup ONLINE redo logs.  you do backup ARCHIVES.

you need to read about the controlfiles in order to understand that you really
want a backup of it to TRACE (a textual "create control file" statment).  A
binary backup is only useful really to create a textual "create controlfile"
statment after the fact.

2) weekly would be NO DIFF then daily -- just more archives to apply is all.

You would *not* backup cold.

Rating: 5
Reader November 28, 2002
Reviewer: A reader
"
A
binary backup is only useful really to create a textual "create controlfile"
statment after the fact.
"

Does this imply that you can create a textual control file
from th backup of binary control file ?

Followup:
yup.
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76993/recoscen.htm#11872
describes what you would do if you lose all current controlfiles.  Specifically:

...
Have not backed up the control file using the TO TRACE option, but used the TO
filename option of ALTER DATABASE BACKUP CONTROLFILE

Use the control file copy to obtain SQL output. Copy the backup control file and
execute STARTUP MOUNT before ALTER DATABASE BACKUP CONTROLFILE TO TRACE
NORESETLOGS. If the control file copy predated a recent structural change, edit
the TO TRACE output to reflect the structural change.
........

that is a really wonderful manual that if you call yourself a DBA but haven't
read it -- then you are not a DBA.

Rating: 4
Reader November 29, 2002
Reviewer: A reader
That relies on the init.ora file. In a total disaster
recovery scenario, if you forget the database name
( database != SID ) or the DB_BLOCK_SIZE, the database
will not mount and trace file can not be generated

Followup:
well, there is a super finite number of block sizes -- so that is not an issue.

and getting the dbname is pretty easy -- strings SYSTEM.DBF | more, it'll pop to
the top...

but yes -- your init.ora files should be part of your backup set and if you use
9ir2 rman and above, it'll make it so for you so you cannot forget it.

(but, use a backup controlfile to trace, that binary one just complicates
things)

Rating: 5
Thanks November 30, 2002
Reviewer: A reader

November 30, 2002 Randy -- Thanks for the question regarding "Sequences in Logical Standby database", version 9.2.0.1
You Asked
If you insert a record in your primary database using a sequence how does this
code get applied to your standby database.   Does Oracle ensure that the same
value is inserted in both databases or will the result be different in both?

INSERT INTO TEST(ID,TEXT)
VALUES(TEST_SEQ.nexval,'X')
/
and we said...
That will be converted into:

   insert into test (id, text) values ( 12345, 'X' )

assuming the sequence nextval was 12345 of course.  The sequence isn't used in
the apply at the logical standby, rather the value is applied AND the test_seq
sequence is maintained (kept caught up to the production instance).
November 30, 2002 Pierre -- Thanks for the question regarding "Long raw statistics", version 8.1.7
You Asked
I need to generate a statistics report on the size of long raws ex:

   size 1 to 100kb   : < number of records >
   size 101 to 200kb : < number of records > .... etc

Is this possible with Oracle ?.... apparently you can query using
UTL_RAW.LENGTH() but it will fail with sizes above 32k ?!

Thanks in advance
and we said...
It will not happen with LONG RAWS (which are deprecated and have not been
enhanced or touched since version 7.3 in 1995!!)

It will happen easily with the infinitely more functional BLOB datatype
(dbms_lob.getlength).

There is not anything you can do unless the application itself stored the LENGTH
in a column when it inserted the data (like ALL_VIEWS has for example)
December 01, 2002 Ananth -- Thanks for the question regarding "Job_queue_process ", version 8.1.7
You Asked
Hi Tom

    I have three questions :

    i ) How do we determine a optimal value for JOB_QUEUE_PROCESS, and how it is
related to the no of jobs running ?

    ii ) I heard that Oracle applications ( ver 10.7 ) tables never used to have
primary key, foreign key. Is there any specific reason for that ?

    iii ) I heard that Oracle's direction is Java for users who are currently
using PRO*c, but then Java code will not run as fast as a pro*c executable ?!

Thanks
Anantha Krishnan
and we said...
1) job_queue_processes determines the maximun number of concurrently running
jobs.  Suppose you have 5 snapshots (materialized views) that refresh at 3am.
Each one takes 15 minutes to execute serially  You have a 4 cpu machine and
discover that if all ran at the same time, it takes 20 total minutes for all 5.

If you have job queue_processes = 1, then one at a time will run.  It will take
1 hour, 15 minutes -- 5*15 minutes.

If you have job queue processes = 2, then two will run at the same time, it will
take about 45+ minutes -- 15plus minutes (for two) + 15plus minutes (for next
two) + 15 minutes for one

If you have job queue processes = 3, then three can run, it will take about 1/2
hour (little more then then)

if you have job queue processes = 5, then all 5 run at the same time, it'll take
20 minutes.

So, you need to determine how many jobs you want running at the same time and
set appropriately.

ii) they were written using version 6.0 of the database which did not have such
a construct.  Legacy coding practice.

iii) Oracle's direction is to support every programming construct under the Sun.
 We support cobol, C, java, C++, VB, plsql -- just to name a very few.

If only there were one language appropriate for each and every task -- forever.
Wouldn't that be nice.

C is fully supported, in fact 9i released new functionality *just for C* in the
form of OCCI (OCI for C++ for example).  Pro*c continues to be enhanced.

C was never a "front line language".  We never had a C/C++ IDE for example.  It
is not being "desupported" -- quite the contrary.
December 01, 2002 -- Thanks for the question regarding "temp table,cursors,arrays", version 8.1.7
You Asked
Tom,
I'm working with oracle database which our bosses want coded identical to sql
server. In oracle I am using implicit cursor for retrieving result sets. In sql
server they use temp tables which the sql server developer said is microsoft's
version of an array in the database.The sql server developer also mentioned that
cursors were much slower then the temp tables.  In most cases they have code
which
1) builds temp table
2)populates temp table
3)loops through data in the temp table
4) does some work on the each data set
5)deletes a row
6)commit work
7)ends loop
Because both backends support one front end they are trying to  have a coding
standard between the two.
Besides  putting step 5 and 6 after the loop so they are done only once I wanted
your opinion on whether to go with an array,cursor or temp tables. When would
one be more appropriate then the other.
In this application we will be having anywhere from 100 to 300 users.
In regards to the array's(there would be quite  a few) I am concerned about
memory usage, temp table (oracle's) is fine but it's still a matter of loading
the table then processing(not sure about how these are affected when alot users
acces the same procedure all using the same temp table). This make's me lean
toward cursors which I already have because it seems to have less steps involved
and simplifies things(no having to load anything just select loop and do
something). It seems impossible to me to have both databases written exactly the
same considering they are totally different and in the end I'm sure one of them
will suffer at best. Any advice or ideas here would be greatly appreciated.

Thanks,
and we said...
Your bosses are not being smart.  They think they are

o saving time
o saving resources
o saving energy
o being more "flexible"

I think they are

o wasting time
o wasting resources
o expending untold energies
o being very confined as to what they can and cannot do
o and worst of all -- wasting TONS of money (all you guys looking for jobs,
  well, now you see where the money for salaries is going -- down the tubes)

Micorsoft SQLServer is not Oracle
Oracle is not Microsoft SQLServer

If they were synonymous -- why would there be two?

Things that work great in one database -- stink in another.
Things that work bad in one database (cursors for example in SQLServer -- added
for nominal compliance with industry standards "but don't use them really") work
great and are in fact the correct and proper way in the other.

How about this -- if the bosses want to have the code be the same in SQLServer
as in Oracle -- start with the ORACLE code and make SQLServer behave like us.
Only fair isn't it??

The above logic should be a SINGLE UPDATE statement optimally and at worst would
be:

   for x in ( select ... from <however many tables you want> )
   loop
      process;
   end loop;
   commit;

Note the location of the commit -- it is in the proper location (you see --
Oracle and SQLServer -- they have fundementally different concurrency and
locking models).

Notice the lack of extra work to simulate cursors -- why?  Because Oracle
doesn't need to simulate cursors!!!

Now, since PLSQL and TSQL are fundementally different languages and you are
using stored procedures -- why all of this fuss about "they should be exactly
the same".  That is foolishness -- plain and simple.  Rather -- you should have
a spec that says "procedure FOO does accepts this input, produces this output
and has these side-effects".  Then FOO should be coded properly for the target
database -- using each and every special, proprietary feature of each database
-- this goes for SQLServer as well as Oracle.  You paid for it -- USE IT

That is my advice.  Don't do what the bosses say -- educate them (if they are
technical, they will understand.  If they are the pointy haired guy from
Dilbert, it doesn't matter if they understand or not).

Before anyone flames me with "well, great for you but in the real world...."
stuff -- this is the real world...
December 01, 2002 -- Thanks for the question regarding "Natuer of Jobs when taking Hot Back Ups", version 9.1.1
You Asked
Hi tom,
We are currently moving from a cold back up scenario to a hot backuo scenario,
In our Application we have jobs being executed at the EOD,
Which creates some tables drop some tables etc.

How many sets of backs will we require to do a consistent recovery,
At the most the user can reenter 1 days data,

We also have some jobs being executed at the eod,
Which does a lot of updates inserts etc..Is it okay if the backup is done during
that time.

In case the database crashes durinmg the job and needs to be recovered, after
doing a point in time recovery will the database proceed to complete the rest of
the job.

Thanks for you time

Nihal
and we said...
You can do whatever you like during the backup -- you need take only one backup.
It is not like an OS backup where you might need to make pass after pass after
pass to get all of the stuff.

You want to read (and understand -- read it till you understand it) this
document from cover to cover:
http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/nav/docindex.htm#index-BAC
as I've no idea what version you are really using -- 9.1.1 isn't one we shipped
-- thats a pointer to the 9iR2 doc set.

You do want to keep at least 3 backups available of course, to protect from bad
tapes, other errors and such.

The end user should never have to re-enter data.

You should use RMAN to perform the backups as the tablespaces do not have to be
in backup mode and no additional redo is generated during the backup process.
Other then the fact that RMAN will read every datafile -- and this will increase
the load on the server -- you need not consider what you are doing during that
period really.  You'll have contention for disk -- but beyond that, it will be
"as before" -- normal.

I don't know what you mean by:

"In case the database crashes durinmg the job and needs to be recovered, after
doing a point in time recovery will the database proceed to complete the rest of

the job."

first -- an instance failure does instance crash recovery, not point in time
recovery (PITR).  PITR is something YOU would do in order to perform an
incomplete recovery (to purposely LOSE committed changes -- due to an
application error for example).

The database will recover fully from this and put you back exactly where you
were when the instance failed for whatever reason.
December 01, 2002 Gaurang -- Thanks for the question regarding "Overhead of the MONITORING option in db objects ", version 8.1.7
You Asked
Hi Tom,

What is the overhead of monitoring table updates for tables/indexes created with
the MONITORING option?

I also heard that if you update table it will get reflected after  three hours
in *_tab_modifications. What to do in order to update
*_tab_modifications immediately.

Thanks
and we said...
I have not noticed, nor have I heard of any measurable overhead associated with
this feature.

Index monitoring is a new feature of Oracle9i R1 and up -- and tells you if an
index is being used, it is not used for statistics.

In 9iR1 and up - you can use

DBMS_STATS.FLUSH_DATABASE_MONITORING_INFO

to flush the stats.  There isn't any way I know of to get them reflected
immediately prior to that release.
December 01, 2002 Anthony -- Thanks for the question regarding "Shared Pool", version 8.1.6
You Asked
Hi Tom,

How true that if you size your shared_pool larger than it should, you will just
experience more library cache contention? What causes cache buffer chains
contention?

tnx and more power.

Cheers,
and we said...
you will not necessarily get more library cache contention -- but you can run
into severe performance issues.

Let's say you are tasked with managing a heap of memory.  It grows and grows and
grows and gets really big.  Then, you are asked to "garbage collect it".  That
will take some amount of time and whilst you are doing that, others must wait
(therein lies the contention issue).  So, you run and run and run and then stop
-- big backlog, lots of thrashing about and then you start running again -- run
run run -- stop, run run run -- stop.

This happens when you develop a system that doesn't make good use of the shared
pool -- that doesn't use bind variables -- and you "attempt" to fix it by making
the shared pool really large.  All you've done is pushed the wall back a couple
of inches (put the problem off for a couple of minutes longer) but made the
problem more magnified.

So, what do people do?  they flush the shared pool at some threshold, say 50%.
Well, guess what?  they could pretty much achieve the same by setting the shared
pool to 50% of its current size!

anyway, if you have a "good" system, that uses the shared pool, then having it
large just wasted ram.  If you have a "bad" system, that does not use the shared
pool wisely (no binds, lots of concurrent activity, OLTP type system) -- then
have a large shared pool can bring you to a screeching halt.

cache buffers chains -- hot blocks generally do that.  hundreds or thousands of
people trying to latch onto the same blocks in the buffer cache at the same
time.
Review & Followup
Rating: 4
Sizing your shared pool December 01, 2002
Reviewer: anton from Phil.
Tom,

suppose apps is using bind variables and the dba just wants to have a large
shared pool (of course the server has enough memory and dont have to worry about
paging), is there any performance gain in doing this?

Cheers..

Followup:
No, it is -- as i said -- a waste of perfectly good ram.  If you have that much
extra, send it over to me, I never seem to have enough.

December 02, 2002 Om -- Thanks for the question regarding "Kill session and SID in oracle", version 8.1.7
You Asked
Hi Tom,

Q1) Generally we nake use of DBA login to kill user sessions whenever required.

Is it any way (shell script etc) which we can write and which can be used by the
developers to kill there sessions.

Q2) Is there any way to get SID apart from looking at v$session
    or any other V$ views in ORACLE.

Many thanks for always helping us.

Best regards,
Om
and we said...
Have someone who has been granted ALTER SYSTEM and SELECT on v_$session directly
create the following script (read
http://asktom.oracle.com/~tkyte/Misc/RolesAndProcedures.html
for why "directly" is key here)

create or replace procedure kill_session( p_sid in number, p_serial# in number )
as
begin
    for x in ( select *
                 from v$session
                where username = USER
                  and sid = p_sid
                  and serial# = p_serial# )
    loop
        execute immediate 'alter system kill session ''' ||
                 p_sid || ',' || p_serial# || '''';
        dbms_output.put_line( 'Alter session done' );
    end loop;
end;

Now, grant execute on that to whomever.  Now, anyone can kill a session owned by
the same username as they are logged in as (eg: their sessions)
Review & Followup
Rating: 5
Kill sessions November 26, 2002
Reviewer: Om from India
Excellent.It's what I was looking for.

Rating: 4
December 01, 2002
Reviewer: Daniel Liu from East Brunswick NJ USA
We have implemented similar procedure to kill long running query in Web EJB
environment.(user tired of waiting,change
search criteria,launch new query,the previous query get killed.)
Since binding is not allowed in DDL,will thousands statement
like this trash the shared pool?

Followup:
If you have to do this thousands of times, you have what I would characterize as
a "bug" in your application.

I would be looking at the cause here -- why do you beans (which should be short,
sweet tiny transactions) take so long that the user gets "bored"?

Yes, just as thousands of unique inserts could, thousands of anything could be
bad for you.

December 02, 2002 Shankar -- Thanks for the question regarding "ETL -- warehousing", version 8.1.7.2
You Asked
Hi tom,

surprise that i got a chance to post after a very long time.

anyways, we have system basically a hybrid of oltp and a warehouse database,
wherein data gets pushed to warehouse every half hour through a batch process.
i'd like to know what are my other options of doing this better.

currently we aggregate the data from oltp to a file for each table in the format
by which warehouse tables are stored and then do the same for tables in
warehouse. though we run this batch process every half hour, data will be
collected for the entire day for both operations. so on every batch run, batch
process does a unix diff on those files and parses the diff output to coin
insert/update/delete statements and data gets pushed accordingly. its obvious
that this opeartion gets slower and slower when there are huge loads on the oltp
for a day or towards end of the day.

however, i reckon to run the batch process once a day during off peaks hours
thereby avoiding shared sqls getting flushed out of sharedpool during the day
and if possible run on parallel. so do you see unix diff approach being a
scalable one for these tasks and what are my other options.

thanks your response.
Shankar
and we said...
wow, did you even consider that you own a database?  And that databases are born
to manipulate data (that is one of their primary goals after all)?  And that you
have this powerful thing called SQL that can help you get newly added rows? And
that with a little foresight -- your OLTP system would be designed to supply the
changes to the warehouse easily?

Sorry if I sound sarcastic, it just seems that no one where you work ever
considered "hey, we got this database thing, lets use it".

If you have the horse power to unix DIFF your database every half hour and the
other end has the power to parse and execute all of those
inserts/updates/deletes that most likely aren't using binds -- you probably
bought at least 2 to 3 times as much hardware as you actually need.

I suggest you look at some of the features the database has to offer.  I would
be looking at a total re-design from the ground up with some thought as to "what
can this database thing do for me to help me do my job".  There are so many
opportunities here I cannot even begin to enumerate them.

If you take this to heart and do decide to follow that path, consider 9iR2 with
streams:
http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/nav/docindex.htm#index-STR
if not, even in good old 8172 there are a gazillion other options for you to
look at.  That data doesn't ever have to leave the database -- I would never be
dumping and diffing and loading every 1/2 hour.  Basic replication is more
appealing then that (snapshot logs -- super simple timestamp mechanism).
Review & Followup
Rating: 5
materialized views December 01, 2002
Reviewer: Shankar from CA, USA
thanks for your suggestion tom. i wish i could redesign the system from ground
zero but we would completely loose our vendor support (not to mention).

9iR2 with streams looks appealing for this kind of tasks but again its a long
way to get there. only recently we got the support for 8.1.7 databases so 9i i'd
say at least a year

'Basic replication is more appealing then that (snapshot logs -- super simple
timestamp mechanism)' -- did you mean materalized views here. i know they are
only avaliable on enterprise editions but we have databases on both standard /
enterprise editions

Followup:
MV's are also known as snapshots and are available with SE as well.  they are
called "basic replication"

December 02, 2002 -- Thanks for the question regarding "difference between parse count (total) and parse count (hard)", version 8.1.7
You Asked
Hi Tom,

I appreciate if you help in understanding differences between parse count
(total) and parse count (hard). Does it have any impact on parse to execute
ratio. What is the best way of calculating Parse to Execute ratio, and what is
the optimum value for Parse to Execute Ratio.

Thanks.

Sudhakar.
and we said...
A hard parse is the parse of a query for the very first time -- it is not in the
shared pool.  This is very very expensive.  In most systems, over time as the
database runs- the hard parse count should go down to ZERO.  If not, it
indicates that you are not useing bind variables and that is the NUMBER 1
performance killer.

parse_count_total - parse_count_hard = soft parses.  soft parses are much better
then hard but should be minimized as well.  Your parse/execute ratio should be
as near zero as you can get it.  Hopefully, you can open a cursor (prepared
statement) once and reuse it over and over an over.  Instead of executing:

  prepare statement
  bind statement
  execute statement
  close statement

in your code, you should prepare the statement ONCE per execution of your
application and use it over and over.
Review & Followup
Rating: 4
need help! May 05, 2001
Reviewer: sivababu from india
Hello sir,
 How can i post the questions?...
I didn't find anything to post...

id : a_sivababu@hotmail.com

thanks.

with regards,
sivababu

Rating: 5
April 19, 2002
Reviewer: Randy Richardson from St. Louis, MO
I would like a query that gets hard parse and soft parse counts for sql.  I now
have:

SELECT a.sorts, a.executions, a.parse_calls, b.username,
a.sql_text
  FROM v$sqlarea a, all_users b
 WHERE a.parsing_user_id = b.user_id
   AND a.executions > 0
   AND b.username NOT IN ('SYS', 'SYSTEM')

I assume parse_calls is both hard and soft.  where can I get them appart?  I
would like to show for each SQL used by "user"  the executions, hard_parse,
soft_parse, sorts.

I use this report to help look for tuning areas during development.

Thanks.

Followup:
Well, you don't really want to look at that at the STATEMENT level do you? You
want to look at that in the AGGREGATE.

Consider:

a statement is hard parsed when not found in the shared pool
it is soft parsed when found in there

hence -- the existence in the shared pool means it'll be soft parsed, not hard.
It'll be hard parsed once (or once per invalidation), soft parsed otherwise.

It doesn't make sense really to look at the hard parse count at the statement
level!

statspack gives you the soft/hard parse ratio, that is what is important.

Rating: 4
number of soft parses November 27, 2002
Reviewer: A reader
hi

according to note: 208918.1

soft parse = parse count (total) - parse count (hard) - session cursor cache
hits

but my understanding of session cursor cache hits is parse calls doesnt even
occur if the cursor is cached in session, is my understanding wrong? Or parse
calls always occur no matter what

Followup:
with session cached cursors there are now what I will call 4 types of parses:

hard parse (library cache miss, do the entire thing)
soft parse (library cache hit, do less work then hard)
softer soft parse (session cursor cache hit, less work then soft parse)
NO PARSE (well written program that reused cursors -- NO work performed)

A "softer" soft parse still incurrs (avoidable) overhead.  I would say the
support note is a little aggresive in its math.  Maybe something like:

soft parse = parse count(total) - parse count(hard) - 0.5 * session cursor cache
hits

(0.5 is arbitrary, illustrative)....

Consider -- we'll do 10,000 softer soft parses vs no parse and see that the
softer soft parse still lots of extra (avoidable) work:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int primary key )
organization index;
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 1 );
1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> commit;
Commit complete.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set session_cached_cursors =
100;
Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5
  6      l_cnt   number;
  7  begin
  8      insert into run_stats select 'before', stats.* from stats;
  9
 10      l_start := dbms_utility.get_time;
 11      for i in 1 .. 10000
 12      loop
 13          execute immediate 'select count(*) from t' into l_cnt;
 14      end loop;
 15      l_run1 := (dbms_utility.get_time-l_start);
 16      dbms_output.put_line( l_run1 || ' hsecs' );
 17
 18      insert into run_stats select 'after 1', stats.* from stats;
 19      l_start := dbms_utility.get_time;
 20
 21      for i in 1 .. 10000
 22      loop
 23          select count(*) into l_cnt from t;
 24      end loop;
 25      l_run2 := (dbms_utility.get_time-l_start);
 26      dbms_output.put_line( l_run2 || ' hsecs' );
 27      dbms_output.put_line
 28      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 29
 30      insert into run_stats select 'after 2', stats.* from stats;
 31  end;
 32  /
279 hsecs
206 hsecs
run 1 ran in 135.44% of the time

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value-a.value run1,
c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11   order by abs( (c.value-b.value)-(b.value-a.value))
 12  /

NAME                                 RUN1       RUN2       DIFF
------------------------------ ---------- ---------- ----------
...
STAT...session cursor cache hi      10000          3      -9997
ts

STAT...opened cursors cumulati      10005          6      -9999
ve

STAT...parse count (total)          10005          6      -9999
LATCH.shared pool                   10117        108     -10009

34 rows selected.

so that shows parse count does get incremented for each cursor cache hit -- and
that there is some latching going on....

It is better then a soft parse for sure - but not as good as NO parse.

Rating: 4
how to achive NO PARSE December 02, 2002
Reviewer: A reader
Hi

How can we avoid parses at all (after first parse)? It depends on Applications?

Followup:
It depends on the coder of the application using best practices, yes.

If they code:

  loop
      parse insert
      bind insert
      execute insert
      close insert
  end loop

they did it wrong they should have coded

   parse insert
   loop
       bind insert
       execute insert
   end loop
   close insert

Or, if they have a routine they know will be called many times, they should code
something like:

  ...
  if first_time then
      parse insert
  end

  bind insert
  execute insert
  ....

and just never close it.  Best of all -- if you use PLSQL, this happens
automagically, no extra code necessary.  PLSQL does all of this for you
transparently.

December 02, 2002 Sathish -- Thanks for the question regarding "Dynamic query containing Blob, which contains different field.", version 8i
You Asked
I have a table with a blob field, and type among other details. Say,
    Create table trx (
        trxId Number(18),
        trxType Varchar2(20),
        objValue    Blob,
        ...
    )

Blob Fields contains a java serialised object, different objects based on types,
though all of them implements same interface.

We access this object always thru' j2ee container, so it works fine so far.

Now users want to use reports using sqlplus, crystall reports etc.  So they want
a solution to this blob issue.

Assuming users know the fields that in blob, I was thinking of writing a stored
procedure, which accepts a (out )ref cursor, and query.  Query would contain
some predefined prefix, say trx_view.xx so that Stored Proc would know its part
of blob field.

    for example,
        exec ReportUtil.executeReport(:refcursor, "select trxId,
trx_view.BillPayee, trx_view.transferedAmount, companyName from trx, company
where trx.companyId=company.companyId");

    Where BillPayee, transferedAmount are the blob fields( Note that these
fields won't be there always, this will be based on trxType)

    The Stored procedure executeReport would call a java stored procudure, to
parse and give a query like this: (No particular reason to use java stored proc.
My developer found it easier to parse using java)

    Select trxId, ReportUtil.Blob2Varchar(serialisedBlob,1),
ReportUtil.Blob2Varchar(serialisedBlob,2), companyName from trx, company, (
select ReportUtil.getSerialisedBlob(objValue) from trx ) where
trx.companyId=company.companyId.

Implementation of ReportUtil.getSerialisedBlob:
        getSerialisedBlob(objValue in Blob, fields in Varchar_table) return
Varchar2.

    This will accept a blob field, a varchar table ,which is
{"BillPayee","transferedAmount" in this case} and return a pipe separted
values.( For example, "ABC Corp|100.00"). This is implemented in java stored
Proc

Implementation of ReportUtil.Blob2Varchar:
        ReportUtil.Blob2Varchar(pipeSepartedValue Varchar2, int Number) return
varchar

    Returns the proper value, based on the pipe field.

Rational Behind:
    I want to deserialised only once for each row, as its a costly operation.

    I thought of inserting into a temp table, and then fetching it but don't
want to perform the operation twice. Also my output would contain a huge result
set. Thought it would be inefficient(not sure).

    I also thought of returning a table ( instead of ugly pipe separated values
), but it gives end of connection error once in both sqlplus and plsql
procedure.

Requirements:
    Returns a lot of records.
    Avoid quering twice, means,

        select blobValue from <query>
        then select required fields..

        In this way, processing will be easier, but there is over load. ( Is
there really matter)

Assumptions:
    No blob field in where clause
    no functions to use with blob field( like
trx_view.BillPayeeAmount+trx_view.tra nsferAmount -- This is for ease of
parsing. May be can do it later.
        Not allowed to do any DDL in runtime.

Questions:
    Looks very messy approach to me, your suggested way? Consider there will be
lot of records, need to efficient.
    How to cater for complex objects? ( My ObjValue would not jsut contain  a
flattened structure,but also some dept ). May be we can assume one level depth.
    How to cater for other types.( above just assumes Varchar..Atleast I need to
support number, date)

Platform:
    Oracle 8i on Solaris.

Thanks a lot.
and we said...
I see no way to avoid this "overhead" and do not have a simple clean answer for
you.  Sorry.

But hey -- this is a perfect example.

A perfect example of why you want to store data in the database in the format
that the database natively supports.

A perfect example of why you don't want to use the database as a bit bucket as
you have done!

So, you'll take this serialized java object, parse it, return a string which
needs yet MORE parsing.....

Sorry -- don't know what to tell you.  There is going to be overhead for all of
this parsing, calling Java from SQL, etc.  If only you had stored the data in a
format understandable to the database itself.  If it as if you asked me to
memorize a book written in French.  I could do it -- but I would get nothing
from it.  I would not be able to answer a single question anyone asked about the
book.  The best I could do is regurgitate verbaitim the contents of the book --
but provide no meaning, no inflection, nothing.

But this is a great example of what I've been saying for a long time.  If you
tie the logic all up in the application -- if you make that fatal mistake of
assuming "all access via my application only" -- you'll find out the hard way
you were wrong.  Don't get me wrong, your in great company -- 3278 Green Screen
applications on a mainframe, many of them did this.  Client Server applications
-- famous for it.

Year after year, the latest greatest programming paradigm comes along -- only to
be upsurped by the latest greatest next year.  and then what?  that really cool
thing you did last year is the monkey on your back now.

The data is key here, the applications are almost secondary.  The data will be
repurposed, reused, shared.  If you do it all via your application -- you will
remove the ability to repurpose, reuse, share the data.

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1372403341772
and search (ctl-f) for disagreements.  Very similar thread but talking about
identification, authorization, access control, auditing (you know, those things
a database normally does).  I'll bet you suffer from that issue as well - so
even if you accomplish this goal -- how do you secure the data since I'm sure
all security is done at the application level?
December 02, 2002 Darshan -- Thanks for the question regarding "Pause in SQL*Plus", version 8.1.7
You Asked
Hello Tom,
          Is there any way to bring about a definite pause of a predefined
interval,say 2 seconds,in SQL*Plus just as it is possible in Forms 5.0 using
when_timer_expired trigger ? My problem is that i want to execute two
queries,but the next query should be executed 5 - 10 seconds after the first
query is fired.

Thanking you in Anticipation,
Darshan D. Mhatre
and we said...
exec dbms_lock.sleep( N );

where N= number of seconds to pause.  You may need execute on dbms_lock granted
to you.  If that is an issue, you can have the DBA setup a procedure:

create procedure sleep( n in number )
as
begin
    dbms_lock.sleep( n );
end;
/

so you can call that without having to have access to the full dbms_lock
package.
Review & Followup
Rating: 4
Thanks for ur timely help December 02, 2002
Reviewer: Darshan D. Mhatre from Bombay,India
Hello Tom,
          Thanks for ur valuable and timely help.It solved my problem.

Thanks and Regards,
Darshan

December 02, 2002 Eugene -- Thanks for the question regarding "converting user specified date into oracle date", version 8.1.7
You Asked
Hi Tom,
I wonder if you have a script handy or you can just point me to the right
direction. I am looking for a way to write a proc or function that will convert
the date inserted by the user (from any format) into the format that I can pass
into Oracle. For example: user can enter 12.01.2002 OR 12/01/2002 OR 12-01-2002
OR 20021201 ..... and I'd be able to make it work for Oracle.

Thank you,
Eugene
and we said...
Well, sure you can do that but -- big question for you -- is that the 12th of
January 2002 or the 1st of december 2002.... hmmm....

see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1365403168188
for a method but be careful.  DD and MM can cause you great heartburn.
Review & Followup
Rating: 5
thanks alot December 02, 2002
Reviewer: Eugene from Seattle
Thanks Tom,
I will look into the example that you've provided.

Eugene

December 02, 2002 -- Thanks for the question regarding "Control File Too Old", version 8.1.7
You Asked
Sir,
  I was practising backup and recovery. The database in in archive log mode. I
took an Offline backup of the control files, datafiles and redo log files. Then
i created an table on 'users' tablespace and then  after this i deleted the
users datafile. Then i tried to restore the files from the backup and then tried
to start again. It gave me an error saying that 'Control file ids old'. I am
struck here and dont know how to proceed.
Thanks
akhil mahajan
and we said...
You want to read:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76993/toc.htm
from cover to cover until you fully understand all that it says.

Basically, when you backup your control files, you really want to use the "to
trace" option so as to create a "CREATE CONTROLFILE" statement.  Binary control
file backups are not very useful, only really useful to create a CREATE
CONTROLFILE statment in a pinch when you lost yours.

When doing a restore, it is best to have the ONLINE control files and ONLINE
redo log files.  This is why we multi-plex them (keep more then one copy on more
then one mount point/physical device).  It is common to lose a disk, it is less
common to lose 2 disks at the same time, it is very very rare to lose 3 disks at
the same time so 3 is a good number for control file copies and online redo log
copies.

When you restore -- you would restore the DATAFILES to the damaged disk(s).  You
would use the current control files and current online redo logs to perform a
full recovery.

If you choose to restore the control files from the backup -- you'll get that
control file too old (why?  cause the control file is TOO OLD).  You would use a
create controlfile statement to recreate a controlfile that isn't.  It is as if
you were doing this scenario:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76993/recoscen.htm#11872
losing all copies of the current control file.

This also points out why backing up online redo logs is BBBBBAAAAADDD.  The only
thing you might do with them is OVERWRITE the good online redo log files --
making a full recovery IMPOSSIBLE.  Never never never backup online redo logs.
It is never necessary with a ARCHIVELOG MODE backup -- the only thing that could
happen is you wipe out your online redo logs losing committed work.  It is never
necessary if you do a normal or immediate shutdown in NOARCHIVELOG mode.  You
shouldn't be backing up a shutdown aborted instance so it is NEVER necessary.

See also
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6601495804013
for a recent discussion on this topic.
Review & Followup
Rating: 4
December 03, 2002
Reviewer: A reader

December 02, 2002 -- Thanks for the question regarding "Query/Insert ", version 9ir2
You Asked
Hi Tom ,

how could i improve the performance of the following query.
I have an index on date_col and it's being used when i do the
select part

The aim is  to get Total counts for a given Month ,  but as you can see ,the
counts are obtained by going to the child TABLE first ,  do some matching  there
and the Date_col is on the parent Table.

So, i was  thinking of putting  the daily counts into append_table as shown
below  and later on people could query this table to get the SUMs  for a given
Day/Month .

Asumme:  parent_table  has  200+ Million rows
         child_table   has  parent_rows * 7
         Look_up       has  100  Rows

INSERT      /*+ APPEND */
       INTO append_table
 SELECT (SELECT op.NAME
             FROM look_up op
            WHERE op.pk = a.ref_key) NAME  ,
TRUNC (a.date_col) date_col,
          (SELECT COUNT (*)
             FROM child_table b
            WHERE col_1 LIKE '_match%'
              AND col_2 LIKE 'XVI%'
              AND a.pk_col = b.fk_col)  count1  ,
          (SELECT COUNT (*)
             FROM child_table b
            WHERE col_1 LIKE '_match%'
              AND col_2 LIKE 'XD13%'
              AND a.pk_col = b.fk_col)   count2   ,
          (SELECT COUNT (*)
             FROM child_table b
            WHERE col_1 LIKE '_match%'
              AND col_2 LIKE 'XD2%'
              AND a.pk_col = b.fk_col)    count3  ,
            .
            .
            .some more  counts here similar to above
            .
     FROM parent_table a
    WHERE date_col >= TO_DATE (:b1, 'MMYYYY')
      AND date_col < LAST_DAY (TO_DATE (:b1, 'MMYYYY')) + 1
      AND a.some_col  IS NOT NULL

Thanks in advance .
and we said...
insert /*+ append */ into t
select op.name,
       trunc(da.date_col),
       sum( case when b.col_1 like '_match%' and b.col_2 like 'XVI%'
            then 1
            else 0
            end ),
       sum( case when b.col_1 like '_match%' and b.col_2 like 'XD13%'
            then 1
            else 0
            end ),
       sum( case when b.col_1 like '_match%' and b.col_2 like 'XD2%'
            then 1
            else 0
            end ),
       .....
  from parent_table a,
       loop_up op,
       child_table b
 where a.date_col >= to_date( :b1, 'mmyyyy')
   and a.date_col < last_day( to_date(:b1,'mmyyyy')) +1
   and a.some_col is not null
   and a.ref_key = op.pk (+)  /* only do the (+) if NEEDED!!! */
   and a.pk_col = b.fk_col (+) /* only do the (+) if NEEDED!!!! */

use the outer join if and only if there are rows in A with ref_keys that are not
in OP.

use the outer join if and only if there are rows in A with pk_col values that
are not in B.
Review & Followup
Rating: 5
Need Group By ? December 02, 2002
Reviewer: Pasko
Hi Tom ,
Thanks for your prompt Response .

I have tried your query but i think i need a Group By clause .

I  had also tried this option before but it doesn't use my Index on a date
column.

Followup:
yes,

group by on op.name, trunc(da.date_col)

and as for the INDEX thingy -- the question I ask you is "which performs
better?"

indexes do not imply, infer or mean "better"
indexes can mean "worse"

So, the lack of use of an index -- meaningless.

Since this is an insert /*+ append */, meaning you execute this once in a blue
moon (like once a day, or a week), you can use literals in the query instead of
binds -- that'll give the optimizer more information to decide whether it wants
to use an index or not.

I only takes me 7seconds to full scan 1,000,000 rows on my laptop (114 meg of
data).  Indexes are not necessary here maybe...  Assuming you have a slightly
faster machine then my laptop.  indexes in fact could be "slow=true"

December 02, 2002 Richard -- Thanks for the question regarding "How to get the record count of an OS file in the database?", version 8.1.7
You Asked
We load between 4 and 15 flat files from 2 different legacy systems into our
data warehouse nightly.  As part of our regular daily audit of the data loads,
we need to:

1. verify the record count of the files (sent via ftp from AS/400 systems to the
HP where our database resides),

2. check the number of records loaded via sqlldr, and

3. check the load status from the Warehouse Builder audit tables.

Parts 2 and 3 are simple and taken care of by our existing plsql procedures.

All files are fixed-length.  Record lengths vary from 100 characters to over
3500 characters.  All files are in US7ASCII caharacterset.  The number of
records for the "narrower" files ranges from  under 50 to 250,000; the number of
records for the "wider" files ranges from 10,000 to 4 million.  Overall file
sizes range from several kilobytes to over 100MB.  Executing "wc -l" on our
largest files typically runs in about a minute.

Step 1, getting the actual OS flat-file record count is the stumper for me.
I've seen the examples of getting a directory listing into a database table via
java, and examples of executing OS commands from plsql via java as well... but
I'm not a java programmer and need to somehow combine these 2 example cases so
that I can get a record count of all files in a given directory and have the
results stored in a database table.

So far I've worked around this using a unix shell script that spools the results
of wc -l *.TXT to a file and then reads that file in a loop passing the count
and filename as parameters to a sqlplus script executed in the loop.  It works
but is highly dependent on the OS and requires it to be run from either a
scheduled batch or manually.

It would be much simpler if we could simply make a call from plsql, such as:
  get_dir_rec_cnt(p_directory=>'DATA_DIR',
                  p_files=>'*.TXT',
                  p_target_tbl=>'FILE_STATUS');

Any suggestions?
and we said...
Since they are fixed length files, all we need to do is get the FILESIZE and
then divide by the record size.

That is easy.

ops$tkyte@ORA920.US.ORACLE.COM> create or replace directory
  2  MY_FILES as '/tmp/';

Directory created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create or replace
  2  function number_of_records( p_filename in varchar2, p_lrecl in number )
  3  return number
  4  as
  5      l_bfile    bfile;
  6      l_recs     number;
  7  begin
  8      l_bfile := bfilename( 'MY_FILES', p_filename );
  9      dbms_lob.fileopen( l_bfile );
 10      l_recs := dbms_lob.getlength( l_bfile ) / p_lrecl;
 11      dbms_lob.fileclose( l_bfile );
 12      return l_recs;
 13  end;
 14  /

Function created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> !wc -l /tmp/foo.dat
     18 /tmp/foo.dat

ops$tkyte@ORA920.US.ORACLE.COM> exec dbms_output.put_line( number_of_records(
'foo.dat', 20 ) );
18

PL/SQL procedure successfully completed.

ta-dah....

if you do not KNOW the lrecl, we could go a step further:

ops$tkyte@ORA920.US.ORACLE.COM> create or replace
  2  function number_of_records( p_filename in varchar2 )
  3  return number
  4  as
  5      l_bfile    bfile;
  6          l_lrecl    number;
  7      l_recs     number;
  8  begin
  9      l_bfile := bfilename( 'MY_FILES', p_filename );
 10      dbms_lob.fileopen( l_bfile );
 11          l_lrecl := dbms_lob.instr( l_bfile, utl_raw.cast_to_raw( chr(10) )
);
 12      l_recs := dbms_lob.getlength( l_bfile ) / l_lrecl;
 13      dbms_lob.fileclose( l_bfile );
 14      return l_recs;
 15  end;
 16  /

Function created.

ops$tkyte@ORA920.US.ORACLE.COM> exec dbms_output.put_line( number_of_records(
'foo.dat' ) );
18

PL/SQL procedure successfully completed.

that'll look for the first newline (chr(10)) in the file and assume that is the
logical record length (lrecl)
Review & Followup
Rating: 5
A Real Life-saver! November 03, 2002
Reviewer: Richard from Bensenville, IL USA
Thanks for the quick response.  Exactly what I needed.

Rating: 1
There are ads popping up from your site November 04, 2002
Reviewer: A reader
Mr. Kyte I dont know whether it is from you or not,
but there are pop up windows appearing from your site.

Followup:
That is *quite* impossible -- 100% impossible.

Not me -- it is you.  sounds like you have some issues with your ISP.

Rating: 4
pop up windows.... November 04, 2002
Reviewer: robert from PA
>>....pop up windows appearing from your site.

what popup windows ? there isn't even an animated gif
here...ever...

Followup:
;)

I cannot tell you how much i HATE animated gifs!

I'm really digging mozilla as a browser.  Coolest features:

o popup killer, I see *no* popups
o in the "security" section -- you can disable animated gifs
o you can right click on an image and choose "block images from this server".
over time -- I see *no* ads whatsoever

Not only is browser faster (i don't download most of a page anymore, most of
many public pages out there are ads), but it doesn't give me a headache with
flashing gifs, moving things and I'm never chasing popups anymore.

Rating: 4
Wow November 04, 2002
Reviewer: amol from India
This makes me think do i really need anything but Oracle.

Rating: 4
Very useful indeed November 04, 2002
Reviewer: andrew from ca, usa

Rating: 5
December 02, 2002
Reviewer: A reader
Excellent!

Can you also please show how to get the row counts from the Sqlldr logfiles? (I
want to get "rows successfully loaded", "rows not loaded due to ..... various
error conditions.). Thanks very much.

Followup:
use utl_file (or a bfile)

fopen the log file
read the lines and look for what you want.

it is an exercise in simple "parsing" of a text file.

December 02, 2002 Alejandro -- Thanks for the question regarding "Date function", version 8.1.7
You Asked
 How can I do to calculate the Hours and Minutes transcurred between 2 Dates???

  For example,

   11-30-2002 14:30

   and

   12/02/2002 16:35

   = 50:05
and we said...
http://asktom.oracle.com/~tkyte/Misc/DateDiff.html
given D1 and D2 as dates -- it would be:

ops$tkyte@ORA920.US.ORACLE.COM> select trunc(abs(d1-d2)*24) hours,
  2         mod(abs(d1-d2)*24*60, 60 )  minutes
  3    from (
  4  select to_date( '11-30-2002 14:30', 'mm-dd-yyyy hh24:mi' ) d1,
  5         to_date( '12/02/2002 16:35', 'mm/dd/yyyy hh24:mi' ) d2
  6    from dual
  7         )
  8  /

     HOURS    MINUTES
---------- ----------
        50          5

Review &
      Followup

      Rating: 5
It's amazing
      !  December 02, 2002
Reviewer:  Alejandro
      Almaraz  from Monterrey, MEXICO.
It was very very useful tu me, I greet you very much your Help!!


Subject | Article | Last Updated (East coast time)
BLOB's | article only | Dec 03, 2002 07:20 am
REMOTE user calls to a distributed database | article only | Dec 03, 2002 08:40 am
Sql query | article only | Dec 03, 2002 09:26 am
locking problems | article only | Dec 03, 2002 12:05 pm
How to improve union query | article only | Dec 03, 2002 01:27 pm
Alter table add column | article only | Dec 03, 2002 04:28 pm
function similar to add_months | article only | Dec 04, 2002 07:50 am
This strange file | article only | Dec 04, 2002 08:00 am
Maintenence activities for DBA | article only | Dec 04, 2002 08:02 am
Regarding REF CURSOR | article only | Dec 04, 2002 08:27 am
Locking w/ Direct-Path INSERT | article only | Dec 04, 2002 09:31 am
Procedure works in 8.1.5 but not in 8.1.7 | article only | Dec 04, 2002 12:14 pm
Restricting the cascade delete on child tables. | article only | Dec 04, 2002 12:29 pm
Union All to return one row in a SubQuery | article only | Dec 04, 2002 02:05 pm
Locking Related Question | article only | Dec 04, 2002 02:22 pm
CALCULATE THE QUARTER IN THE FISCAL, YEAR | article only | Dec 04, 2002 04:07 pm
Bind Variables, SQLTrace and TKPROF | article only | Dec 04, 2002 04:39 pm
procedure | article only | Dec 04, 2002 07:12 pm
Generic procedure for creating aggregated data | article only | Dec 04, 2002 07:15 pm
Where can I get detail information on "Analytic Functions" with examples. | article only | Dec 05, 2002 08:06 am
A simple Question for you | article only | Dec 05, 2002 08:11 am
control file parallel write wait event | article only | Dec 05, 2002 10:03 am
Current as well as Previuos Fiscal Year Code | article only | Dec 05, 2002 10:51 am
Step to tune an existing application | article only | Dec 05, 2002 11:14 am
SQL Question - wanting PK of a subquery containing aggregate function | article only | Dec 05, 2002 12:35 pm
ref cursor with or with out dynamic sql | article only | Dec 05, 2002 06:40 pm
Oracle and Java | article only | Dec 05, 2002 07:34 pm
Dump blob into file | article only | Dec 06, 2002 07:06 am
Lots of (Sum) s in a Group By | article only | Dec 06, 2002 07:23 am
Why all links to your site are all of sudden so slow!!! | article only | Dec 06, 2002 07:26 am
Index use | article only | Dec 06, 2002 08:07 am
Scalibilty... & Procedures issue... | article only | Dec 06, 2002 08:17 am
How to view long raw data | article only | Dec 06, 2002 08:37 am
Pivot Tables from Expoert one on one. | article only | Dec 06, 2002 09:47 am
How to call Label Security functions in procedures defined in a package? | article only | Dec 06, 2002 03:34 pm
Rounding error in Oracle 7.3.4 | article only | Dec 06, 2002 04:08 pm
BLOB column creates an UNIQUE index | article only | Dec 06, 2002 04:27 pm
Meaning of sql_address column | article only | Dec 06, 2002 04:31 pm
'df -k' won't show the temp file | article only | Dec 07, 2002 09:35 am
Query tables: joining or loop or join ref_cursor? | article only | Dec 07, 2002 10:05 am
Should next extent be equal to initial for a table | article only | Dec 07, 2002 10:17 am
Problem with buffer overflow | article only | Dec 07, 2002 10:23 am
row chaining | article only | Dec 07, 2002 02:21 pm
maximum number of joins | article only | Dec 07, 2002 03:15 pm
tuning a batch program | article only | Dec 08, 2002 09:55 am
File creation using UTL_File | article only | Dec 08, 2002 10:18 am
Null ? ? ? | article only | Dec 08, 2002 10:20 am
Staging Database | article only | Dec 08, 2002 12:07 pm
Multiple joins vs. function based view columns | article only | Dec 08, 2002 12:49 pm
What is the sql file to create dbms_redefinition package body in oracle 9i | article only | Dec 08, 2002 04:37 pm
Multiple instances vs Single instance per App. | article only | Dec 08, 2002 07:27 pm
how to pass text value with a quote(s) to insert thru a procedure | article only | Dec 08, 2002 08:16 pm
Generating a view script for an existing table | article only | Dec 09, 2002 08:57 am
Partition pruning/elimination -reg.. | article only | Dec 09, 2002 10:26 am
Analyze Vs DBMS_STATS | article only | Dec 09, 2002 12:59 pm
Query to retreive rows to produce a report like PIVOT query | article only | Dec 09, 2002 01:05 pm
pl/sql -- can it run sqlldr. | article only | Dec 09, 2002 07:13 pm
row(s) 1 - 57 of 57
December 03, 2002 Roland -- Thanks for the question regarding "BLOB's", version 8.1.5
You Asked
Hi'

On an Oracle 8.1.5 database with the Application Server 8.0.4.1
I have built an application to store and index documents.

The format I am using to upload the files to is BLOB.

I can index the BLOB, create the filters, and search for content
in the BLOB.

I retrieve the filename using the htp.anchor function which all
works well.

I must now just write a procedure which uploads the word
document on the client side.

My idea was ik I could get the filename plus extention
(word.doc)and in the browser I would select it, the application
(MSWORD) would open.

On WebDB this works fine, because I construct the URL to point
to the document table, which is seen as a directory on the
client.

How can I get a word document in the browser frob within a BLOB?

Roland Daane
and we said...
First, I would suggest using the native features of OAS and
allowing it to manage the content.  This will result in the
least amount of code for you.

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:242014628882
for details on that.

In Order to upload into a blob, what I would suggest is to allow
OAS4.x to upload into a LONG RAW as it does and then use the
to_lob() function to put the long raw they upload into
websys.ows_content into your own table.

You will need to use dynamic sql to do this as PLSQL currently
does not understand the TO_LOB() function.  So, you might
execute an insert like:

execute immediate 'insert into my_table ( c1, c2, theBlob )
                   values
                   select :c1, :c2, to_lob(content)
                     from websys.ows_content
                    where <predicate to id your row>'
                  using your_plsql_variable_for_c1,
                        your_plsql_variable_for_c2;

That'll copy the long raw into your blob.  You could then remove
the data from the content table if you wanted.

To download the blob, you'll use plsql as shown at
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:232814159006
Short of that -- you would have to either write your own
cartridge or use the one at
http://osi.oracle.com/~tkyte/owarepl/doc/index.html
but bear in mind -- its not supported by support.
Review & Followup
Rating: 3
Good but I can't load files (.doc) in my table July 06, 2001
Reviewer: Pierrot from France
Your response is very good but can you explain me how to load a word document
into a table and create the index to find all documents which contains words.

Please answer me at :

jirotype14@hotmail.com

Thanks

Rating: 2
use different methods December 03, 2002
Reviewer: roland daane from Netherlands
What I do is call a perl or cgi script using the owa_http package, thus dumping
documents onto the file system.
the cgi script then calls a procedure that inserts the document into a blob or
clob column (document or picture) and then deletes the file from disk!

works fine and all the code is actually here at asktom (except the cgi bit)

Followup:
and that is easier how?

December 03, 2002 Ivan -- Thanks for the question regarding "REMOTE user calls to a distributed database", version 8.1.7
You Asked
Hi Tom,

I am having trouble understanding database links / synonyms in distributed
databases. What is happening is that the user (REMOTEKEYID) that created the
database link and procedures is always the caller to database 'B'.  This is
incorrect because I want the user (lets say FOO and all his friends) who logs in
to be the caller to database 'B' using the procedure created by REMOTEKEYID. FOO
has all the rights to update the record in database 'B' so it is essential that
FOO does the call. I tested my code on database 'B' and without a distributed
transaction it works fine but when I use a distributed transaction I notice that
the caller is always REMOTEKEYID instead of FOO.

I create a PRIVATE database links and synonyms for REMOTEKEYID to be used to
connect to database 'B'.

create database link remotekeyid connect to mev identified by confused using
'KEYID';

CREATE SYNONYM remote_mev_common  FOR mev.mev_common@remotekeyid;
CREATE SYNONYM remote_mev_co      FOR mev.mev_co@remotekeyid;

I create a procedure on database 'A' that is a shell to a procedure on database
'B' using REMOTEKEYID.

CREATE OR REPLACE PACKAGE keyid_co3
AUTHID CURRENT_USER
IS

PROCEDURE set_company_name
  (
    i_company_id                 IN  companies.company_id%TYPE,
    io_name                      IN  OUT companies.name%TYPE
  );
END keyid_co3;
/

CREATE OR REPLACE PACKAGE BODY keyid_co3
IS
PROCEDURE set_company_name
  (
    i_company_id               IN      companies.company_id%TYPE,
    io_name                    IN  OUT companies.name%TYPE
  )
IS
BEGIN
  remote_mev_common.set_distributed_xctn(TRUE);
  remote_mev_co.set_company_name(i_company_id,
                                 io_name);
  COMMIT;
EXCEPTION
  WHEN others THEN
    ROLLBACK;
END set_company_name;

My question is how

Can you provide an example from scratch of how this should work.  Somehow I
think that a database link using CURRENT_USER should be used.

Thanks as always,

Ivan
and we said...
you created the database link with a hard coded username and password:

create database link remotekeyid connect to mev identified by confused using
'KEYID';

any access to that database link will ALWAYS be "mev" -- there is no chance of
it being anyone else.

If you want the current user to be authenticated against the remote source, just
leave the connect to/identified by out of the database link

create database link remotekeyid using 'KEYID';

that will take the current users USERNAME and PASSWORD and use that to log into
the remote site.

Don't let the synonyms confuse you -- they are just aliases and are not relevant
to the problem at all -- the only problem you had was you created the database
link as a "proxy" link -- it always used that username/password.
Review & Followup
Rating: 5
Problem solved as usual December 03, 2002
Reviewer: Ivan from USA
I tried the change of dropping the current db link and creating a new one
"create database link remotekeyid using 'KEYID';" under the REMOTEKEYID scheme.
But this did not work so I dropped the db link and created a new one under
SYSTEM but this time as a PUBLIC db link "create public database link
remotekeyid using 'KEYID';"

It worked!!!!!

Thanks,

Ivan

December 03, 2002 Lakshmi -- Thanks for the question regarding "Sql query", version 8.1.7
You Asked
Hi Tom
           I have an audit table as follows.

SQL> DESC AUDIT;
 Name                                      Null?    Type
 ------------------------------------- -------- -------------
 CASENO                                      NOTNULL NUMBER(9)
 audit_date                                           date
 Event                                                 varchar2
 username                                              varchar2

For a particular caseno all audit events are stored into this table.

SQL> select * from audit;

 caseno audit_date     event          username
------- ------------- ----------- --------------------
2100      10/10/2002   REGISTER       UK123
2100      12/10/2002   LOCK           IK123
2100      20/11/2002   WITHDRAW       ADMIN

I want to write a sql query to find out all case no in the audit table whose
last Event is Withdraw.

Many thanks for taking time to answer my question

Cheers !!!
Lakshmi
and we said...
select distinct caseno
  from (select caseno,
               last_value(event)
               over ( partition by caseno order by audit_date ) last_event
          from audit )
 where last_event = 'WITHDRAW';

or

select caseno
  from audit
 group by caseno
having max( to_char(audit_date,'yyyymmddhh24miss') || event )
       like '______________WITHDRAW';
Review & Followup
Rating: 5
SQL QUERY December 04, 2002
Reviewer: lakshmi from bangalore india
Tom
      Excellent query !!!!

December 03, 2002 raju -- Thanks for the question regarding "locking problems", version 9.2.0.1
You Asked
Hi Tom,

I am facing some deadlocks in the application and the problem is with the FK's
not being indexed. I am running the application and, as and when I hit the
deadlocks I index that particular FK.

The question is ... should I take a decision to index all the FKs irrespective
of whether they cause any deadlocks now ? What is the downside of indexing all
the FKs ?

Thanks in advance for your help.

Raju
and we said...
downsides:

o potentially different query plans
o more storage
o increased update/insert/delete times

upsides:

o no deadlocks

You should index a fkey if:

o you update the parents primary key OR you delete from parent
o you query from parent to child (eg: show me the EMPs that work in DEPT 10)

You need not index a fkey if, even in light of the above if:

o the child table is not or very infrequently modified
o the parent table is a read only/read mostly reference table (code lookups
  for example)
Review & Followup
Rating: 5
Thanks December 03, 2002
Reviewer: raju from london
Thanks Tom ! That's a perfect answer !

December 03, 2002 josh -- Thanks for the question regarding "How to improve union query", version 9.2
You Asked
Greetings Tom,

How can I improve this data validation procedure? I have been toying with
analytics but haven't been able to get anything to produce the right data.

test_name := 'VII.A. Acct Sldto vs. acct prfl';
--  dbms_output.put_line(test_name);
   Select '<=', expctd, actl, decode(sign(expctd-actl), 1, 'N', 'Y'),
   decode(sign(actl-expctd), 1, 0, expctd-actl),
   decode(expctd, 0, 0, DECODE(sign(actl-expctd), 1, NULL,
round((((actl-expctd)/expctd)*100),2)))
   , decode(decode(sign(expctd-actl), 1, 'N', 'Y'), 'N', 'N', 'N')
    into test_condition, expected_result, actual_result, pass_fail,
variance_value, variance_percent_val,
        critical_indicator
  from (
      SELECT sum(expctd) as expctd, sum(actl) as actl
      FROM
      (select count(distinct KEY_ACCT_SLDT_DIM_ID) as expctd, 0 as actl
      from dm.sls_ordr_fact
      where KEY_ACCT_SLDT_DIM_ID not in (0,1)
      union
      select 0, count(distinct KEY_ACCT_PRFL_DIM_ID)
      from dm.sls_ordr_fact
      where KEY_ACCT_PRFL_DIM_ID not in (0,1)
      and KEY_ACCT_SLDT_DIM_ID not in (0,1)));

  insert into BUS_RULE_VALDTN_RSLT values
    (current_run_id, schema_name, table_name, test_name, test_condition,
expected_result, actual_result,
     variance_value, variance_percent_val , pass_fail, null, critical_indicator,
sysdate, USER, sysdate, sessn_run_id);
and we said...
Just make a single pass.  It'll be a full scan on SLS_ORDR_FACT with a not in
anyway (or an index on SLS_ORDR_FACT if you have one on (KEY_ACCT_PRFL_DIM_ID,
KEY_ACCT_PRFL_DIM_ID) -- it'll fast full scan that index instead of the table)

This should do it:

Select '<=',
       expctd,
       actl,
       decode(sign(expctd-actl), 1, 'N', 'Y'),
       decode(sign(actl-expctd), 1, 0, expctd-actl),
       decode(expctd, 0, 0, DECODE(sign(actl-expctd), 1, NULL,
                             round((((actl-expctd)/expctd)*100),2))),
       decode(decode(sign(expctd-actl), 1, 'N', 'Y'), 'N', 'N', 'N')
  into test_condition, expected_result, actual_result, pass_fail,
       variance_value, variance_percent_val, critical_indicator
  from ( select count(distinct (case when KEY_ACCT_SLDT_DIM_ID not in (0,1)
                                     then KEY_ACCT_SLDT_DIM_ID
                                     else null
                                 end)) as expctd,
                count(distinct (case when KEY_ACCT_PRFL_DIM_ID not in (0,1)
                                      and KEY_ACCT_SLDT_DIM_ID not in (0,1)
                                     then KEY_ACCT_PRFL_DIM_ID
                                     else null
                                 end)) as actl
           from dm.sls_ordr_fact )
Review & Followup
Rating: 5
Excellent December 03, 2002
Reviewer: Josh
I was not familiar with using case statements in that fashion. This solution
returns the same answer as the original one and reduces the cost on my system by
85%.

I do have a followup question:

Why don't the two distinct count statements affect each other? The way I
understood such statements before was that a

count distinct (col_a), count distinct (Col_b) would return counts based upon
the number of distinct combinations of col_a/col_b.

The way this query works now is that the two count distinct statements are
independent of each other. They return counts that reflect the same results as
running two seperate queries:

select distinct count(col_a)

select distinct count(col_b)

How does that work?

Followup:
there is

select <ALL|DISTINCT> .......

and there is

select count( DISTINCT .. ), count( DISTINCT ... )

when you use the first version "select DISTINCT ...." -- the distinct is applied
to the result set AFTER all rows were returned.

When you use the second version, distinct as a modifier INSIDE of the count --
it counts( distinct EXPRESSIONS ) for that expression.  It does not return a
distinct result set.

Consider:

scott@ORA920> select count(job)
  2    from emp
  3   group by deptno
  4  /

COUNT(JOB)
----------
         3
         5
         6

counts the NUMBER of non-null job values by deptno -- if two deptno's had the
same number of non-null jobs -- this query would show both

scott@ORA920> select DISTINCT count(job)
  2    from emp
  3   group by deptno
  4  /

COUNT(JOB)
----------
         3
         5
         6

this query does the same as the first HOWEVER -- it would have removed
duplicates from the result set -- so if two deptno's had "3" as the number of
non-null jobs, "3" would appear in the result set only once

scott@ORA920> select count(DISTINCT job)
  2    from emp
  3   group by deptno
  4  /

COUNT(DISTINCTJOB)
------------------
                 3
                 3
                 3

this query counts the number of DISTINCT JOBS by deptno.

scott@ORA920> select DISTINCT count(DISTINCT job)
  2    from emp
  3   group by deptno
  4  /

COUNT(DISTINCTJOB)
------------------
                 3

scott@ORA920>

 and this one returns a distinct result of of the count of DISTINCT jobs by
deptno

Hope that doesn't confuse it more!  basically -- distinct INSIDE OF COUNT() is a
count of distinct elements by the group by clause.  DISTINCT after select says
"remove duplicates from the entire result set after you compute it"

December 03, 2002 Surya -- Thanks for the question regarding "Alter table add column", version 8.1.7
You Asked
Hi,
I want to add a column in table not at the last but in between the table without
dropping and recreating the table.

Say my table structure is
Code
Date
Status
Actiondate

I want to add acolumn after Status
Code
Date
Status
new column
Actiondate

Is it possible to do that  if yes how to do that
and we said...
The physical order of columns is not relevant here. (i hope your code doesn't
rely on it!!! very shaky)

Anyway, what you can do is this:

alter table t add new_column number;
<revoke on T_TABLE from everyone>
rename table T to T_TABLE;
create view T as
select code, date, status, new_column, actiondate
  from t_table;
<grant on T to everyone that you revoked from>

Views are a great device for doing something like this -- but -- it is scary
that you want columns in "some order" as that concept is foreign to a relational
database.
Review & Followup
Rating: 1
View is not required December 03, 2002
Reviewer: Surya Prakash from London
How can i alter a physical definition of database table.
Actually we create dynamic Insert statement where last first columns are
reserved so only option left are to add new additional table in between (in my
example after code and date and before actiondate)

Followup:
actually a view is required -- no idea why you say "view is not required".

what the heck is "where last first" -- last first??

Use the view, short of dropping and recreating the table from scratch that is
your only option -- period.

Your method of this "dynamic insert" sounds very suspicious and fragile --  this
simple exercise here is showing it's fragileness.  You might rethink that logic.

But -- all else aside -- the view is required, unless you want to rebuild the
table which you said "i don't want to rebuild the table".

Rating: 5
"View is not required" December 03, 2002
Reviewer: Tony Andrews from London, UK
> Actually we create dynamic Insert statement where last first columns are
reserved

I take this to mean something like:

v_sql := 'INSERT INTO t VALUES ('
|| v_code || ',' || v_date
|| <<other values go here>>
|| ',' || v_action_date ')';

... in which case a solution would be to correct the insert statement to:

v_sql := 'INSERT INTO t( code, date'
|| <<other column names go here>>
|| ',action_date) '
|| VALUES ('
|| v_code || ',' || v_date
|| <<other values go here>>
|| ',' || v_action_date ')';

Or maybe this is bad third-party code and so cannot be fixed?

Rating: 2
Related question December 03, 2002
Reviewer: She from CT, US
How would I modify the length of a varchar column in a table, without dropping
the table...

Followup:
you can make it BIGGER

alter table t modify c varchar2(BIGGER)

you cannot make it "smaller", but you can add a check constraint to make it so
that only the smaller field can be put into there.

Rating: 3
Possible=Y; advisable=N December 03, 2002
Reviewer: Andrew from ca, usa
Well I guess it IS possible to effectively juggle column orders by adding tmp
columns, populating them, and finally dropping the unwanted ones. Similar for
re-sizing a column smaller (move the data out, resize, move it back again). By
the time you've worked out all the DDL and DML, it would have been quicker to
just rebuild though.

Being a fan of having a clean schema without "patches" to work around
limitations, I'd personally prefer rebuilding the tables.

Followup:
views man -- this is what views are all about.

it would take about 5 seconds with a view -- no muss, no fuss.

Never will I understand the FUD surrounding the use of views.

Rating: 4
An easy way to add a column wherever you want !! just a logic December 03, 2002
Reviewer: Kumar from San Diego
If your table is not a huge then I would use this below logic, this helps if
your database is 8i and latter only. See below, what is your table structure
call this table as t1

t1 ( Your table existing structure)
====
Code
Date
Status
Actiondate

Now you need to add column "newcolumn" after "status". See the math below to
achieve this:-

alter table t1 add(newcolumn <datatype> ,actiondate_TEMP <whatever datatype>)
/
update t1 set actiondate_TEMP = actiondate
/
commit ( no need actually this commit)
/
alter table t1 drop(actiondate)
/
alter table t1 add(actiondate <whatever datatype>)
/
update t1 set actiondate = actiodate_TEMP
/
commit
/
alter table t1 drop(actiodate_TEMP)
/

now you run the command desc t1 you can see the structure of your table as :-

Code
Date
Status
new column
Actiondate

Note:- I mentioned these update statements to take care the data if any exists.

Followup:
Congratulations!

You win my first ever Rube Goldberg Award.  Outstanding...

"The purpose of a Rube Goldberg Machine is to build the most complicated machine
possible to perform a simple everyday task."
http://www.rube-goldberg.com/html/today.htm

Rating: 4
Excellent December 03, 2002
Reviewer: Surya Prakash from London
Thanks Kumar. I got my answer.

Followup:
you are kidding -- you won't recreate the table -- but you'll use the "curly
shuffle" to totally scramble it and make it as bad as possible.

wow, amazing.  amazing what hoops people will go through when the answer is
staring them -- literally staring them -- in the face.  totally amazing.

you'll write, rewrite, write, rewrite and totally scramble the table but you
won't use a view, you won't just recreate the table as select -- both of which
(in order) are infinitely superior options.  Ok.....

Rating: 3
An option is an option but no measure like superior n inferior -- choice is yours December 03, 2002
Reviewer: kumar from San Diego
Hi Tom,

Well I did not present this option to achieve any award nor to show how sperior
my option. What I mentioned is another option ( that is why I wrote another way)
and those may fit or not fit based on the criteria. For eg:- How about sometimes
your create re create may not be the choices for small kind of environments like
users are not allowed to do create, drop rename such previleges and the table
had some foreign key constraints on other keys. However they may be allowed to
alter table then this could be an easy option.

I am sure to agree with your the bullet proof option that you have presented. I
am neither interested to highlight mine nor inferior the other methods of
options. I Just spread or throw another way in the thread.

Oracle 8i did not had undo tablespace even then many use 8i as their production.
It's an option whether we can use or not? choice is yours. I know this time
perhaps you are ready to throw another award to me.
Thanks

Followup:
I think Andrew, who put the comment right above yours -- before you put it up
here, hit the proverbial nail on the head.

possible  = y
advisible = n

Rating: 3
Reducing the varchar2 datatype column size December 03, 2002
Reviewer: Pablo Rovedo from Argentina
just a comment regarding modifying varchar2 columns ...

from 9i SQL Reference:

"You can reduce the size of a column s datatype as long as the change does not
require data to be modified. Oracle scans existing data and returns an error if
data exists that exceeds the new length limit."

Cheers
Pablo

Followup:
cool -- stealth feature!! that one totally slipped by me, thanks so much:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x varchar2(25) );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t values ( 'x' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table t modify x varchar2(5);
alter table t modify x varchar2(5)
                     *
ERROR at line 1:
ORA-01441: column to be modified must be empty to decrease column length

versus

ops$tkyte@ORA9I.WORLD> create table t ( x varchar2(25) );

Table created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> insert into t values ( 'x' );

1 row created.

ops$tkyte@ORA9I.WORLD>
ops$tkyte@ORA9I.WORLD> alter table t modify x varchar2(5);

Table altered.

December 04, 2002 rajesh -- Thanks for the question regarding "function similar to add_months", version 8.1.6
You Asked
hi tom

please help me in writing a function using sql statements which can be able
to return date of the next month according to following creteria.
 if the month belongs to apr,jun,sep,nov  then the function should return
30-mon-yyyy  of the next month (for eg input is 30-jun-2002 then 30-jul-2002 ,
if the input date is 15-jun-2002 then 15-jul-2002 as in add_months function).
for all other cases except feb the function should return
31-mon-yyyy  of the next month.

now what i am doing is checking using

if elseif method by cheking the date.

sincerely

rajesh
and we said...
add_months(sysdate,1) - decode( to_char(sysdate,'mmdd'),
                        '0430', 1, '0630', 1, '0930', 1, '1130', 1, 0 )

will do that.
Review & Followup
Rating: 4
Awesome! December 03, 2002
Reviewer: Abhijit from Mumbai
Hi Tom,
This solution is simply superb.

Cheers!
Abhijit

Rating: 4
excellent December 03, 2002
Reviewer: depesh
hi

why avoiding february ? how it can be changed also for
february with 28 and 29 days

depesh

Followup:
well, they didn't ask for feb

what do you believe it should do in feb.

Rating: 5
nice December 03, 2002
Reviewer: rajesh
hi tom

thaks for this wonderful answer.

ok depash while asking i think it will be difficult to include months less than
30 days a single statement.
(feb might have 28 and 29 days)
ok if the input date is 28-feb-2002 then result will be
31-mar-2002 in toms answer also.
can it be changed to 28-mar-2002 ..

thanks dep for this  comment.

thanks tom

sincerely

rajesh

Followup:
nah, we can do it.  Here are two ways -- the add_month2 is the original answer,
the add_month1 is a "new" answer that basically says:

if adding a month to the current date results in a day in the next month that is
GREATER than the day in the current month -- return a fixed up date in next
month, else just return add_months results.

ops$tkyte@ORA920.US.ORACLE.COM> select dt, add_month1, add_month2, decode(
add_month1, add_month2, 'same', 'different' )
  2  from (
  3  select dt,
  4  decode(sign(to_char(dt,'dd') - to_char(last_day(add_months(dt,1)),'dd')),
  5      -1, to_date( to_char(dt,'dd-')||
                          to_char(add_months(dt,1),'mon-yyyy'), 'dd-mon-yyyy' ),
  6          add_months(dt,1) ) add_month1,
  7          add_months(dt,1) - decode( to_char(dt,'mmdd'),
  8                                  '0430', 1, '0630', 1, '0930', 1, '1130', 1,
0 ) add_month2
  9    from (
 10  select add_months( last_day( to_date('01-jan-2000') ), rownum-1 ) dt from
all_objects where rownum <= 24
 11  union all
 12  select add_months( last_day( to_date('01-jan-2000') ), rownum-1 )-1 dt from
all_objects where rownum <= 24
 13         )
 14  )
 15  order by 1
 16  /

DT        ADD_MONTH ADD_MONTH DECODE(ADD_MONTH1,ADD_MONTH2,'
--------- --------- --------- --------------------------------
30-JAN-00 29-FEB-00 29-FEB-00 same
31-JAN-00 29-FEB-00 29-FEB-00 same
28-FEB-00 28-MAR-00 28-MAR-00 same
29-FEB-00 29-MAR-00 31-MAR-00 different
30-MAR-00 30-APR-00 30-APR-00 same
31-MAR-00 30-APR-00 30-APR-00 same
29-APR-00 29-MAY-00 29-MAY-00 same
30-APR-00 30-MAY-00 30-MAY-00 same
30-MAY-00 30-JUN-00 30-JUN-00 same
31-MAY-00 30-JUN-00 30-JUN-00 same
29-JUN-00 29-JUL-00 29-JUL-00 same
30-JUN-00 30-JUL-00 30-JUL-00 same
30-JUL-00 30-AUG-00 30-AUG-00 same
31-JUL-00 31-AUG-00 31-AUG-00 same
30-AUG-00 30-SEP-00 30-SEP-00 same
31-AUG-00 30-SEP-00 30-SEP-00 same
29-SEP-00 29-OCT-00 29-OCT-00 same
30-SEP-00 30-OCT-00 30-OCT-00 same
30-OCT-00 30-NOV-00 30-NOV-00 same
31-OCT-00 30-NOV-00 30-NOV-00 same
29-NOV-00 29-DEC-00 29-DEC-00 same
30-NOV-00 30-DEC-00 30-DEC-00 same
30-DEC-00 30-JAN-01 30-JAN-01 same
31-DEC-00 31-JAN-01 31-JAN-01 same
30-JAN-01 28-FEB-01 28-FEB-01 same
31-JAN-01 28-FEB-01 28-FEB-01 same
27-FEB-01 27-MAR-01 27-MAR-01 same
28-FEB-01 28-MAR-01 31-MAR-01 different
30-MAR-01 30-APR-01 30-APR-01 same
31-MAR-01 30-APR-01 30-APR-01 same
29-APR-01 29-MAY-01 29-MAY-01 same
30-APR-01 30-MAY-01 30-MAY-01 same
30-MAY-01 30-JUN-01 30-JUN-01 same
31-MAY-01 30-JUN-01 30-JUN-01 same
29-JUN-01 29-JUL-01 29-JUL-01 same
30-JUN-01 30-JUL-01 30-JUL-01 same
30-JUL-01 30-AUG-01 30-AUG-01 same
31-JUL-01 31-AUG-01 31-AUG-01 same
30-AUG-01 30-SEP-01 30-SEP-01 same
31-AUG-01 30-SEP-01 30-SEP-01 same
29-SEP-01 29-OCT-01 29-OCT-01 same
30-SEP-01 30-OCT-01 30-OCT-01 same
30-OCT-01 30-NOV-01 30-NOV-01 same
31-OCT-01 30-NOV-01 30-NOV-01 same
29-NOV-01 29-DEC-01 29-DEC-01 same
30-NOV-01 30-DEC-01 30-DEC-01 same
30-DEC-01 30-JAN-02 30-JAN-02 same
31-DEC-01 31-JAN-02 31-JAN-02 same

48 rows selected.

December 04, 2002 Martin -- Thanks for the question regarding "This strange file", version 9.2.0
You Asked
Hi tom:
I have a short question:
I'm guessing for what is used the file lkSID that is in $ORACLE_HOME/dbs.
Thank You,
Martin.

PS: Your new book will be focused in performance & optimization, right?
PS2: Can you comment shortly your experience setting shmmax for Oracle on Linux?
and we said...
it's a lock file -- ala unix, it just means "database up" don't bother trying to
start again.

It is about best practices -- everything from "it is a database, not a bit
bucket" to "these are the tools you need to use" to "hints -- good or evil".
About 100 different items.

I use a shell script:

[tkyte@tkyte-pc-isdn init.d]$ cat kernel.sh
echo 100 32000 100 100 > /proc/sys/kernel/sem
echo 2147483648 > /proc/sys/kernel/shmmax
echo 4096 > /proc/sys/kernel/shmmni
echo 2097152 > /proc/sys/kernel/shmall
echo 65536 > /proc/sys/fs/file-max
ulimit -n 65536
echo 1024 65000 > /proc/sys/net/ipv4/ip_local_port_range
ulimit -u 16384

linked in my /etc/init.d/rc3.d directorie

[tkyte@tkyte-pc-isdn rc3.d]$ ls -l S*ker*
lrwxrwxrwx    1 root     root           19 Aug 29 15:32 S50kernel.sh ->
../init.d/kernel.sh

to get that setup.  hope thats what you wanted...
Review & Followup
Rating: 5
when is the new book due out December 03, 2002
Reviewer: Steve Montgomerie from Dallas/Orlando
enjoy your current title, when is the next one due out?

Followup:
spring time, 2003

December 04, 2002 Vineet -- Thanks for the question regarding "Maintenence activities for DBA", version 8.1.7.2
You Asked
Hi,

I am using Oracle 8.1.7.2 on AIX. RMAN is used for complete daily hot backups.

What are the detailed administration tasks that a DBA has to follow on a daily,
weekly and a monthly basis? I usually run the utlbstat & utlestat reports on a
weekly basis. Also i do a regular check on the space availability, max extents,
etc.
If there is a scheduled maintenence window of say 1 hour every month, then what
should be done during that time? Are there any activities to be done with the db
in restricted mode?
Providing scripts would be a great help.

Thanks in advance.
and we said...
utlbstat and estat are soooo obsolete as to be useless.

Use statspack
http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96533/statspac.htm#34837
infinitely superior, part of the database since 816.

There are no checklists of what a DBA must or must not do on a day to day basis.
 It all depends on the environment.  Every place is different.

I have no, repeat NO, scheduled things to be done in a maintenance window.  You
do not rebuild indexes, reorg tables on a schedule.  During a maintenance window
you might apply a patch to resolve an issue.  You would upgrade application
software.  There is no regularly scheduled "housecleaning" things you need to
do.  You would figure out if you even needed your maintenance window from month
to month.

I cannot provide scripts -- since your job is your job and what you need to do
is 100% dependent on where you work, what they do and how they do it.
Review & Followup
Rating: 3
December 03, 2002
Reviewer: A reader
Thanks.

Could you give please a list of tasks we should analyze if we must do?

Which are the most important tasks of maintenance that most of the dba does?

Only to know if we are not missing to see if we need it.

Thanks.

Followup:
backup and recovery.

Everything else is incidental.

If there is one thing a DBA must do and do without error --totally and
completely -- it is this backup and recovery thing.

Rating: 4
What not to do..? December 03, 2002
Reviewer: Kishan
Dear Tom:

This may sound tad frivolous, but what tasks in your opinion that DBAs do or
users ask the DBAs to do, that are totally useless or don't have a useful
cost/benefit ratio (like renaming a column in a table for example, you can use
views can't you..). Do you have any list like Top N useless/could be done in a
better way tasks, time doing which can be spent in something better (like
reading Expert one-on-one or fishing maybe...;)

If you could put them out here, maybe people can figure out how they can be
better in their jobs.

Regards,
Kishan.

Followup:
rebuild indexes on a schedule

reorg tables on a schedule

reorgs in general (eg: rename columns, reorder columns -- see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6700238804926
for a classic example)

cold backups of an archive log mode database.

December 04, 2002 Mohammed Abdul -- Thanks for the question regarding "Regarding REF CURSOR", version 6i
You Asked
Hello!
As we all know that we can lock the rows which we fetch from the explicit cursor
in the declaration section with the help of FOR UPDATE OF clause, and can refer
to the same row in the executeable section with the WHERE CURRENT clause. So
likewise can we lock rows which results from REF CURSOR and use FOR UPDATE OF
and WHERE CURRENT OF clause with this REF CURSOR, and how.

Please reply me soon.

Faheem.
and we said...
You can use select for update with the ref cursor but UPDATE where CURRENT OF is
supported only with "regular" cursors.

You'll need to select out the ROWID in the refcursor to simulate the where
current of:

tkyte@TKYTE816> declare
  2          type rc is ref cursor;
  3
  4          l_cursor rc;
  5          l_dname  dept.dname%type;
  6          l_rowid  rowid;
  7  begin
  8          open l_cursor for select dname, rowid from dept for update;
  9          loop
 10                  fetch l_cursor into l_dname, l_rowid;
 11                  exit when l_cursor%notfound;
 12
 13                  update dept set dname = lower(l_dname)
 14                   where rowid = l_rowid;
 15          end loop;
 16          close l_cursor;
 17  end;
 18  /

PL/SQL procedure successfully completed.
Review & Followup
Rating: 4
December 04, 2002
Reviewer: Robert from Ireland
Tom,
Would you use this if you were passing out a reference cursor and wanted to
update the rows that you have passed.  For example we have a ref cursor that
selects based on processed value of 1.  We need to update that value to 2 after
we have passed the rows back.  (we are using 9.2)

Is the method above valid for this?

Followup:
certainly.  The row's will be locked by the client process that calls this
procedure, they will fetch the row, process it and update it by rowid (fast).

December 04, 2002 Twin -- Thanks for the question regarding "Locking w/ Direct-Path INSERT", version 8.1.7
You Asked
Tom,

In Oracle documentation 'Database Concepts', it says

"Locking Considerations with Direct-Path INSERT

During direct-path INSERT, Oracle obtains exclusive locks on the table (or on
all partitions of a partitioned table). As a result, users cannot perform any
concurrent insert, update, or delete operations on the table, and concurrent
index creation and build operations are not permitted. Concurrent queries,
however, are supported, but the query will return only the information before
the insert operation."

I have a couple of questions,

1) I was able to do following tests (INSERTs) in two different ORACLE SQL
sessions before commiting. It seems that the table FOO2 is not locked by
'session 1' since I was able to insert into the same table in 'session 2'.

2) Notice 'REDO size' are same comparing the INSERT statements w/ APPEND hint
vs. wo/ APPEND hint. Why? Is that suppose DIRECT PATH INSERT has less 'REDO'
than regular INSERT? BTW, the table FOO2 is NOLOGGING table.

Thanks

Twin Dolphins

********************************************************************
Session 1 :
==========

SQL> set autotrace on statistics;
SQL> insert /*+ APPEND */ into foo2 values (34);

1 row created.

Statistics
----------------------------------------------------------
          0  recursive calls
          3  db block gets
          1  consistent gets
          0  physical reads
        440  redo size
        453  bytes sent via SQL*Net to client
        414  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> insert into foo2 values (34);

1 row created.

Statistics
----------------------------------------------------------
          0  recursive calls
          3  db block gets
          1  consistent gets
          0  physical reads
        440  redo size
        454  bytes sent via SQL*Net to client
        400  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          1  rows processed

Session 2:
==========

SQL> set autotrace on statistics;
SQL> insert /*+ APPEND */ into foo2 values (23);

1 row created.

Statistics
----------------------------------------------------------
          0  recursive calls
          3  db block gets
          1  consistent gets
          0  physical reads
        440  redo size
        455  bytes sent via SQL*Net to client
        414  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> insert into foo2 values (23);

1 row created.

Statistics
----------------------------------------------------------
          0  recursive calls
          3  db block gets
          1  consistent gets
          0  physical reads
        440  redo size
        454  bytes sent via SQL*Net to client
        400  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          1  rows processed

*******************************************************************
and we said...
the documentation also states that /*+ APPEND */ works only with

INSERT as SELECT

you did not do a direct path insert.

do this:

ops$tkyte@ORA920.US.ORACLE.COM> create table t ( x int );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into t values ( 1 );

1 row created.

and then in another session:

ops$tkyte@ORA920.US.ORACLE.COM> insert /*+ append */ into t select 1 from dual;

you'll find the second session blocks.  If you go back to the first and do this:

ops$tkyte@ORA920.US.ORACLE.COM> commit;

Commit complete.

ops$tkyte@ORA920.US.ORACLE.COM> insert into t values ( 1 );

you'll find that insert is blocked until you commit the direct path insert..

Direct path inserts are for large, bulk operations only.  Never ever for single
row inserts.
December 04, 2002 Harri -- Thanks for the question regarding "Procedure works in 8.1.5 but not in 8.1.7", version 8.1.7.2
You Asked
I use following procedure for analyzing tables:

create or replace procedure analyze_tables(
v_owner in dba_tables.owner%TYPE,
v_estimate in number default 0,
v_days in number default 0) as

v_sqllause varchar2(200);
start_time varchar(30);
stop_time varchar(30);
e_rolli exception;
 PRAGMA EXCEPTION_INIT(e_rolli, -1555);

CURSOR c_taulu IS
select table_name, num_rows from dba_tables where owner=v_owner and
 (last_analyzed<sysdate-v_days or last_analyzed is NULL);

BEGIN

select TO_CHAR(sysdate, 'DD-MON-YYYY HH24:MI:SS') into start_time from dual;
DBMS_OUTPUT.PUT_LINE('Schema: ' || v_owner || ' analyze started: ' ||
start_time);

for asd in c_taulu LOOP
begin
if asd.num_rows is null then
        asd.num_rows:=0;
   end if;
  if asd.num_rows < v_estimate or v_estimate=0 then
        v_sqllause := 'analyze table '|| v_owner ||'.'|| asd.table_name || '
compute statistics';

  else
        v_sqllause := 'analyze table '|| v_owner ||'.'|| asd.table_name || '
estimate statistics sample 25 percent';

  end if;
     EXECUTE IMMEDIATE v_sqllause;
   EXCEPTION
     when e_rolli then
      DBMS_OUTPUT.PUT_LINE('Table ' || v_owner ||'.'|| asd.table_name || ' not
analyzed - snapshot too old');

  end;
END LOOP;

select TO_CHAR(sysdate, 'DD-MON-YYYY HH24:MI:SS') into stop_time from dual;
DBMS_OUTPUT.PUT_LINE('Scheman ' || v_owner || ' analyze and: ' || stop_time);

DBMS_OUTPUT.PUT_LINE('---');

END analyze_tables;

-------

it works fine in 8.1.5.0.0 db. Exeption also works ok.

but when I run this in 8.1.7.2 db:

SQL> Schema: LIHASDB analyze started: 08-SEP-2002 02:15:03
BEGIN analyze_tables('LIHASDB', 10000000, 7); END;

*
ERROR at line 1:
ORA-01555: snapshot too old: rollback segment number 5 with name "RBS4" too
small
ORA-06512: at "SYS.ANALYZE_TABLES", line 21
ORA-06512: at line 1

Exception is not working! Why?
and we said...
What is line 21 on your system.

On mine, line 21 is:

 21
 22  for asd in c_taulu LOOP

that is, it looks like it is the cursor FOR LOOP (eg: your SELECT is throwing
this error, NOT the analyze)

Please confirm.  If it is the select, then -- hey -- your RBS is waaaayyyy to
trivially small.

The select is not in the exception block, hence the code is doing what the code
should be doing.

You should be using DBMS_STATS and ALTER TABLE MONITORING to gather just stats
on tables by schema using the "gather stale" option.  It'll remove the need for
your procedure and only analyze those objects that need it.
Review & Followup
Rating: 5
... September 11, 2002
Reviewer: Harri from Finland
Thanks for your answer!

line 21 is:
for asd in c_taulu LOOP

is it so that select:
select table_name, num_rows from dba_tables where owner=v_owner and
    (last_analyzed<sysdate-v_days or last_analyzed is NULL);

throws the error? How it is possible. RBS are quite small (15 megs each) but
still how this small select needs so much undo data.

Followup:
It is based on HOW LONG the query needs to go back in time -- not how "small"
the select is.

You open the query at 9pm (for example)

After each fetch, you analyze some table.  That takes a bit of time.

So, you are running and now it is 11pm.  Your RBS have wrapped a couple of times
by now.

We need to do a consistent read on the data dictionary -- but -- we cannot -
some rollback data that was generated at 9:15pm (someone dropped a table for
example, causing a data dictionary update) is *gone*.  No consistent read --
bamm -- end of query.

I can show you an ora-1555 with a 1 row, 1 block table -- it is trivial.  You
just need enough elapsed time between the OPEN and the FETCHES.

Rating: 5
Very Interesting September 12, 2002
Reviewer: Chuck Jolley from Oklahoma City, OK
Thanks Tom!

Rating: 5
September 13, 2002
Reviewer: Harri from Finland
I understand now that 1555 can raise very easily in cursor for loop. However
there is no drop or create clauses in v$sql. Can it be the procedure itself
doing updates to data dictionary and causing need for reading data from rb?

How can I bypass this - maybe reading table names through temporary table?

Well maybe I start using dbms_stat and monitoring, but still this is interesting
issue and we learn how things work.

Followup:
Yes -- recursive SQL does data dictionary updates all of the time.

But -- show me a drop or create statement in V$SQL and I'll show you a database
that isn't ORACLE ;)  DDL is never in there.  (so there may have been thousands
of creates and drops)

Use DBMS_STATS with GATHER STALE as I said.  It'll reduce your procedure to one
line of code.  You cannot beat that.

Rating: 5
September 13, 2002
Reviewer: jmodic from Slovenia
Harri,

There need not be any drop/create table executed during the time your procedure
needs to finish, for your cursor loop to blow with ORA-1555. Your own procedure
itself is causing it, by making updates to data dictionary tables while
analyzing tables.

Each ANALYZE TABLE will update SYS.TAB$ to record the time when the table was
last analyzed. Since DBA_TABLES is based on SYS.TAB$ (among others), your
rollback segments will soon be unable to provide a read consistent view for your
cursor.

Rating: 5
DBMS_STATS December 04, 2002
Reviewer: Harri from Finland
Hi,

I am finally going to use DBMS_STATS and gather stale for analyzing.

If my traditional analyze commands are:
ANALYZE TABLE <table>
 ESTIMATE STATISTICS
 SAMPLE 30 PERCENT;

ANALYZE TABLE <table>
    ESTIMATE STATISTICS
    SAMPLE 30 PERCENT
    FOR ALL INDEXED COLUMNS
        SIZE 50;

What options I should use with DBMS_STATS to do same statistics?

Is this ok(part of procedure):

dbms_stats.gather_schema_stats
          (OWNNAME => v_schema,
           ESTIMATE_PERCENT => 30,
           METHOD_OPT => 'for all indexed columns size 50',
           CASCADE => TRUE,
           GRANULARITY => 'ALL',
           OPTIONS=>'GATHER STALE',
           DEGREE => 4,
           OBJLIST=>a);

...or do I need 2 procedures. (with and without method_opt)

Second thing. Are different analyze methods isolated. I mean example if
histograms are once collected using 'analyze table...for all indexed columns'
and then later I use DBMS_STAT+gather stale without gathering histograms...what
happens. Do I have histograms at all or old(not updated) histograms. How to
check when histograms are collected?

Followup:
before you did it table by table

now you appear to be doing it schema by schema.

If you do it schema by schema, you won't be using different options on different
tables.  So, you would have one call, one method.

If you gather histograms once and then don't gather them again -- the old values
stay in there.  I know of know way to determine when the histograms where last
collected.

December 04, 2002 Francisco -- Thanks for the question regarding "Restricting the cascade delete on child tables.", version 8.1.6
You Asked
Hi Mr. Tom:

The company where I am working has an application where the foreign key
constraints were defined with the delete cascade option.

So, now my user is asking me if there is any posibility to restrict the
elimination of the child records.

The only solution that I gave him is to recreate the foreign constarints without
the delete cascade option, with this in mind the parent record couldn´t be
eliminated if there are dependent records.

Is there any other way to do the same without to recreate the constraints?

The user wants to be able to catch the parent table and dependent tables and
then to advice to the final user that it is necesary to eliminate the child
records first .......all of this with the delete cascade option in the foreign
constraints.

I would appreciate any advice.

Best regards.
and we said...
So, this sounds like a user interface issue then -- if they want to tell the end
user "hey, this'll delete some records in this other table" and then let them
decide.

If this is the case, why not just have the UI run a query like:

  select count(*) into l_cnt
    from dual
   where exists ( select null from child_table_1 where fk = pk )
      or exists ( select null from child_table_2 where fk = pk )
      .....

if the count comes back 0 -- no children, if the count comes back 1 -- there are
children.

Then let the user decide?

I don't see why you need to touch the existing constraint if that is all you
want to achieve.
Review & Followup
Rating: 5
Thanks Mr. Tom. October 28, 2001
Reviewer: Francisco Mtz. from Monterrey, N.L. Mexico
Thanks Mr. Tom for the quick response.

Your answer was very useful, but what about if the user wants to know the name
of the dependant tables?

I appreciate your help.

Best regards.

Followup:
then simply do this:

 select 'CHILD_TABLE_1'
    from dual
   where exists ( select null from child_table_1 where fk = pk )
 union all
 select 'CHILD_TABLE_2'
    from dual
   where exists ( select null from child_table_2 where fk = pk )
      .....

that'll return a list of child tables with rows.

Rating: 5
Other "cascades" features... October 29, 2001
Reviewer: Andre Nasser from Rio de Janeiro, Brazil
Tom,

I know Oracle implements ON CASCADE DELETES up to release 8i.
Other forms have to be implemented through code (i.e. trigers).
Do you know if in the next versions (9i,...) there will be other cascade options
like:

- ON UPDATE CASCADES;
- ON DELETE NULLIFIES;
- etc...

that already exist in other rdms's a long time ?

Thanks again !

Followup:
Well, with regards to ON UPDATE CASCADE, I hope we never implement it.  You
should never need it (if you do, you've done your design utterly wrong).

See
http://osi.oracle.com/~tkyte/update_cascade/index.html
for a way to do it in 7.x and up however (in 90% of the cases).  Should give you
an appreciation of how nasty this is.

Additionally, you could create your constraints DEFERRABLE, set them to be
deferred and easily accomplish the update cascade via a procedure (but again, if
you are finding a constant, recurring need for this -- YOU'VE DONE YOUR JOB
WRONG).

For the ON DELETE SET NULL

ops$tkyte@ORA815.US.ORACLE.COM> create table p ( x int primary key );

Table created.

ops$tkyte@ORA815.US.ORACLE.COM> create table c ( x references p on delete set
null );

Table created.

works in Oracle8i release 1 (815) and up.

As for the "etc" -- I'm not aware of any "etc's" to be handled beyond these two.

Rating: 5
Thanks October 29, 2001
Reviewer: Andre Whittick Nasser from Rio de Janeiro, Brazil
I was now aware of the ON DELETE SET NULL feature, starting in 8.1.5.

Rating: 4
ON UPDATE CASCADE nasty ? October 29, 2001
Reviewer: Andre Whittick Nasser from Rio de Janeiro, Brazil
Tom,

Sorry, but I did not quite get why this is so bad.

Just a note... If I am not mistaken, DB2 has this feature, which set the
standard for SQL. By the way I studied DBMSs using Date's book based on DB2's
SQL.

I imagine a very simple case: I want to update the DEPTNO primary key on DEPT
table, and I want all chldren in EMP to have the DEPTNO foreign key updated.

What could be so bad ?

Locking ? Let us set up the right indexes.

Followup:
It depends on what flavor of DB2 you have -- some got it, some don't (like some
have hash partitioning, some have range, some don't have hash, some don't have
range).  You'd have to specify which one of the umpteen DB2's you were talking
about.

The first rule of the relational model is that primary keys are immutable.  If
you need to update the primary key of the dept table -- its not a primary key,
it might be a surrogate key, it might be a column that needs to be unique, but
it is not the primary key.

Its just a design issue, if the key might change, its not the key.  Something
else must be the key (even if that something else is a system generated key).

Rating: 4
December 04, 2002
Reviewer: Sagi from India
Hi Tom,

As usual learning alot from your site.

I never new that you can do "SET NULL" in ON DELETE CASCADE constraint.

Which Data Dictionary will tell me whether it was

ON DELETE CASCADE or
ON DELETE SET NULL

I looked into USER_CONSTRAINTS and DBA_CONSTRAINTS.

It did not show any information there. The Column DELETE_RULE in both shows "NO
ACTION".

Is there any method or only method is insert a row in parent & child and delete
it and see what happens?

Thanx in Advance.

Regards,
Sagi

Followup:
It is an oversight -- they forgot to add it to the decode in the *_constraints
views.

It is fixed in 902 and up.

If you read out the view text for *_CONSTRAINTS, you'll see a decode on
c.refact.  It should be:

       decode(c.type#, 4,
              decode(c.refact, 1, 'CASCADE', 2, 'SET NULL', 'NO ACTION'),

In prior releases the decode on 2 was missing.  Now -- DO NOT MODIFY
dba_constraints, user_constraints, all_constraints -- but you can create
"my_dba_constraints" using this modified decode.

December 04, 2002 Jay -- Thanks for the question regarding "Union All to return one row in a SubQuery", version 8.1.7
You Asked
Thank you for this website.

My 2 part question:

I working with a one-to-many relationship of data.

The puesdo-logic I want to follow is
If there is a site_use_id get that record
else get the record without any site_use_id

There is a unique index on CUST_ACCOUNT_ID, SITE_USE_ID.
There is always a record where SITE_USE_ID is null.

SELECT cp.CUST_ACCOUNT_ID, cp.SITE_USE_ID, cp.COLLECTOR_ID
FROM (-- collector - site level
      SELECT cp1.CUST_ACCOUNT_ID, cp1.SITE_USE_ID, cp1.COLLECTOR_ID
      FROM hz_customer_profiles cp1
      WHERE 1 = 1
        AND cp1.CUST_ACCOUNT_ID = 1
        AND cp1.SITE_USE_ID = 22
    UNION ALL
      -- collector - customer level
      SELECT cp2.CUST_ACCOUNT_ID, cp2.SITE_USE_ID, cp2.COLLECTOR_ID
      FROM hz_customer_profiles cp2
      WHERE 1 = 1
        AND cp2.CUST_ACCOUNT_ID = 1
        AND cp2.SITE_USE_ID IS NULL
     ) cp
WHERE 1 = 1
  AND ROWNUM < 2

Question 1:  Will the top half of the union will always be returned first?

Question 2:  How can I rewrite this as used in a subquery? (I get a invalid
column name)

select v.*
from view v
where 1 = 1
  and v.collector_id =
(SELECT cp.COLLECTOR_ID
FROM (-- collector - site level
      SELECT cp1.CUST_ACCOUNT_ID, cp1.SITE_USE_ID, cp1.COLLECTOR_ID
      FROM hz_customer_profiles cp1
      WHERE 1 = 1
        AND cp1.CUST_ACCOUNT_ID = v.ship_TO_CUST_ACCOUNT_ID
        AND cp1.SITE_USE_ID = v.SHIP_TO_SITE_USE_ID
    UNION ALL
      -- collector - customer level
      SELECT cp2.CUST_ACCOUNT_ID, cp2.SITE_USE_ID, cp2.COLLECTOR_ID
      FROM hz_customer_profiles cp2
      WHERE 1 = 1
        AND cp2.CUST_ACCOUNT_ID = v.ship_TO_CUST_ACCOUNT_ID
        AND cp2.SITE_USE_ID IS NULL
     ) cp
WHERE 1 = 1
  AND ROWNUM < 2)

Thank you.

1 = 1 if I need to comment out parts of the where clause I don't have to worry
about it being the first line.  I will end up putting this in an oracle report
with lexicals to handle user parameters. Never have to worry about the lexical
being null and invalidating the select.

I would be interested in both but the subquery is most important.
Why won't the union work in general (if time permits)

Very sincere Thank You!
and we said...
The union all by itself won't work because you are dependent on the order of
rows (where rownum <2) and a union all does not promise to return rows in any
order at all !  We are free to scramble them up as we see fit.  It might work
today and not tomorrow.  It might work in 8i but not 9i.  It is nothing you can
count on.

So, query 1 can be written as:

select *
  from ( SELECT cp2.CUST_ACCOUNT_ID, cp2.SITE_USE_ID, cp2.COLLECTOR_ID
           FROM hz_customer_profiles cp2
          WHERE 1 = 1
            AND cp2.CUST_ACCOUNT_ID = 1
            AND (cp2.SITE_USE_ID IS NULL or cp2.site_use_id = 22)
          order by cp2.site_use_id NULLS LAST )
 where rownum = 1
/

And query two -- well, the scope of the variables in the outer query only goes
down ONE subquery -- the second inner subquery cannot see them.  So, what can we
do, we can do this instead (query against EMP to show the "trick" we are using:

scott@ORA920.US.ORACLE.COM> select *
  2  from (select ename
  3          from emp
  4         order by comm desc nulls last
  5          )
  6  where rownum = 1
  7  /

ENAME
----------
MARTIN

scott@ORA920.US.ORACLE.COM>
scott@ORA920.US.ORACLE.COM> select substr( max(
rpad(to_char(nvl(comm,0),'999999999'),10) || ename ), 11 ) from emp
  2  /

SUBSTR(MAX
----------
MARTIN

select v.*
  from view v
 where 1 = 1
   and v.collector_id =
     (SELECT substr( max(rpad(to_char(nvl(site_use_id,0),'999999999'),10) ||
COLLECTOR_ID), 11)
        FROM hz_customer_profiles cp2
       WHERE 1 = 1
         AND cp2.CUST_ACCOUNT_ID = 1
         AND (cp2.SITE_USE_ID IS NULL or cp2.site_use_id = 22)
      )

That assumes

o site_use_id is positive
o site_use_id isn't ZERO

we would have to adjust for them...
Review & Followup
Rating: 5
Perfect December 04, 2002
Reviewer: Jay Kiefer from Windsor, CT
Exactly what I was looking for.
Was unfamiliar with NULLS LAST keyword

Wouldn't of thought of the scope trick either.

Thank you.

December 04, 2002 Nilesh -- Thanks for the question regarding "Locking Related Question", version 8.1.7
You Asked
Greetings Tom,

This issue I have is related to Row Level locking.
Hardware/Software:
 Oracle - 8.1.7.2.0 Installed on Sun - OS 5.8

Spec:
 Need to generate an extension column value based on existing         values

Set up:
    TABLE_ITEM

    Item Key(*)    Node(*)        Ext(*)    Other columns
----------------------------------------------------------------
    1234        NODE1        001    ...............
    1234        NODE2        001    ...............
    1234        NODE2        002    ...............
    1234        NODE3        001    ...............

NOTE * DENOTES COMPOSITE PK

Environment:
Front end tool allows to make data entry into this table.
As you can see that we increment the extension every time within a group (Item
Key, NODE).
For example 1234,NODE2 second row has Ext as 002.If end user adds 1234,NODE3 we
derive the Ext as 002 and make an Insert into above table with Ext as 002.

Currently we are doing this using MAX(Ext) for given Item Key and IN combination
and then add 1. But with this logic there is a potential problem to have two
sessions generate the same Ext number for a given combination of Item Key and
Node. We can not use sequnce here because
there could be N number of combinations for Item Key and Node
and for each combination we have to work
with Extensions. Secondly,Ext will need to be in the order of
001,002,003 as and when front end makes entries in the table.

Problem: Will row level lock help to make sure that no two
sessions generate the same extension for given combination of Item and Node?

Something like this
 Lock row(s) exclusive for given Item Key, Node;
 Get Max Ext and add one;
 Release Lock;

Can you please give yr expert advice as to what is the ideal way to do this?

Thanks in advance. Really appreciate all your help so far with issues I had
previoulsy.

Regards
Nilesh
and we said...
Yuck.  I hate these sort of artificial constraints on a system "must be
sequential in a group starting with 1" -- thats the pits.  almost as bad as the
futile attempt for "gap free sequences".

I suppose they are supposed to be gap free as well? What happens when you
delete?  do you resequence them all?

sigh, it should be a sequence, the numbers 1, 2, 3, ... could easily be
generated on the fly if the end users demand it.  You just need an increasing
number in ext, nothing more, nothing less.

(expert advice = change requirements, use row_number() upon select if you
actually need such a number)

You will have to serialize at the NODE LEVEL.  Your insert logic will have to be
something like:

   begin
      insert into t ( item_key, node, ext, .... ) values
      ( :item_key, :node, '001', .... );
   exception
       when dup_val_on_index then
           select * into l_rec
             from t
            where item_key = :item_key
              and node = :node
              and ext = '001'
            FOR UPDATE;  /* wait for row to become available then lock */

           insert into t ( item_key, node, ext, .... )
           select item_key, node, to_char(max(ext)+1,'fm000'), :other_cols...
             from t
            where item_key = :item_key
              and node = :node;
    end;
Review & Followup
Rating: 3
That Helped me! January 05, 2003
Reviewer: Nilesh from Parsippany, NJ
Tom,
   I totally agree with your comments.
   Tx a lot for your help.
   Your solution works perfectly.

December 04, 2002 Shujath -- Thanks for the question regarding "CALCULATE THE QUARTER IN THE FISCAL, YEAR", version 8.1.7
You Asked
HI TOM

I AM TRYING TO ROLLUP DATA FOR FISCAL YEAR QUARTER.
I want to count the number of customers enrolled in each fiscal year quarter.
based on custumer enrollement date.

Iam trying some thing like this which not correct.

select to_number(to_char(enrollment_dt,'Q')
where Enrollment_dt between 07/01/yyyy and 06/30/(yyyy+1)

WHERE MY FISCAL YEAR is between 1st JULY and 30th June following year.

I would Appreciate your help

Thanks in advance.
and we said...
what is wrong with it?

True -- your q1 = 3, q2 = 4, q3 = 1, q4 = 2  --but that is easy to fix:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select dt, to_char(dt,'Q'),
                                         mod(to_char(dt,'Q')+1,4)+1
  2    from (select add_months(trunc(sysdate,'y'), rownum-1 ) dt from
all_objects where rownum <= 12 )
  3  /

DT        T MOD(TO_CHAR(DT,'Q')+1,4)+1
--------- - --------------------------
01-JAN-02 1                          3
01-FEB-02 1                          3
01-MAR-02 1                          3

01-APR-02 2                          4
01-MAY-02 2                          4
01-JUN-02 2                          4

01-JUL-02 3                          1
01-AUG-02 3                          1
01-SEP-02 3                          1

01-OCT-02 4                          2
01-NOV-02 4                          2
01-DEC-02 4                          2

12 rows selected.

hope that answers the question -- since it wasn't really very clear?
Review & Followup
Rating: 4
its works December 04, 2002
Reviewer: shujath from Iselin,NJ usa
Thank you Tom for your Timely help!! :-)

December 04, 2002 Luis -- Thanks for the question regarding "Bind Variables, SQLTrace and TKPROF", version 8.1.7
You Asked
Hi, Mr. Kyte. How are you?

Given the query below, why is it being parsed 20 times? Isn't it using bind
variables? The count for the "execute" column is correct, I mean the query
really is supposed to run 20 times, each of them with different values for
"COD_CLASSIF_TRIBUT" (meaning tax_code), "COD_SUB_CLASSIF_TRIBUT" (tax_sub_code)
and "COD_ITEM_CLASSIF_TRIBUT" (tax_item).
Could you please give me a hint of what's going on?
Thanks in advance.

Luis C. L. Nobre
Brasilia, Brazil.

*********************************************************************

SELECT *
FROM FOLHA.FL_SERV_CLASSIF_TRIBUT_VALOR
WHERE ANO_BASE = :1
AND   MAT_SERVIDOR = :2
AND   COD_CLASSIF_TRIBUT = :3
AND   COD_SUB_CLASSIF_TRIBUT = :4
AND   COD_ITEM_CLASSIF_TRIBUT = :5
AND   VERSAO = :6

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse       20      2.20       2.32          0          0          0           0
Execute     20      0.01       0.02          0          0          0           0
Fetch       20      0.00       0.03          3         60          0          20
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       60      2.21       2.37          3         60          0          20

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 46  (FOLHA)

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  TABLE ACCESS BY INDEX ROWID FL_SERV_CLASSIF_TRIBUT_VALOR
      2   INDEX UNIQUE SCAN (object id 34681)

Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: CHOOSE
      1   TABLE ACCESS (BY INDEX ROWID) OF 'FL_SERV_CLASSIF_TRIBUT_VALOR'
      2    INDEX (UNIQUE SCAN) OF 'PK_SERV_CLASSIF_TRIBUT_VALOR' (UNIQUE)

********************************************************************
and we said...
because the programmer who wrote the code parsed it 20 times.

I would need to see the code -- but they are probably opening a cursor, parsing
the query, binding, executing it, closing -- each and every time.

I have a saying "parse once, execute many... don't close that cursor!"

if you used plsql, this would be done transparently for you -- plsql caches
cursors all by itself without any programmer intervention.  All the programmer
has to do is make sure that they only parse the stored procedure call once per
session then!
Review & Followup
Rating: 5
How to keep ref_cursor open December 04, 2002
Reviewer: Arun Gupta from Harrisburg, PA USA
Tom
As I described my application architecture in another post, we have a web based
application where COM+ components call stored procs in packages. The stored
procedures use ref cursor to return multi-row records to COM+ components. Once
the COM+ components get the ref cursor, how can we keep it open? We are not
closing ref cursor within the packages. As a result, I also see multiple parses
for queries and sometimes the parse count is almost as high as execute count.
The performance monitoring tools complain about a poor parse to execute ratio.
Please let me know if we can do something about it.
Thanks

Followup:
Ref cursors -- by definition -- will be parsed, must be parsed -- cannot be
cached.

Why?

Because they are pointers to result sets, The cannot be cached.

You can obviate most of the performance hit by setting session_cached_cursors to
some appropriate number (say 100).  it'll not fix the ratio (there is only one
ratio I care about -- soft parse ratio, the ratio of soft to hard parses and it
should be darn near 100%).  but it will fix (mostly) the performance hit of the
soft parse.

Rating: 5
Thank You December 05, 2002
Reviewer: Luis C L Nobre from Brasilia, Brazil
I'll talk to the Delphi programmer.

Thanks again.

December 04, 2002 -- Thanks for the question regarding "procedure", version 805
You Asked
Hi Tom,

I have a procedure 'my_proc' in a package 'my_pak'. I need to track the no. of
times this procedure 'my_proc' is being executed every day, by whom and what
time it's being executed. How can I track the above details?

Thankx for your time,
SB
and we said...
I would:

create table audit_tbl( pkg_name varchar2(30), proc_name varchar2(30),
                        username varchar2(30), when date );

and simply add:

   insert into audit_tbl (pkg_name, proc_name, username, when )
   values ( 'MY_PAK', 'MY_PROC', user, sysdate );

before you say "but that'll add overhead", read through:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6690299776039
so you can see how much overhead I add to my programs in order to tune, debug,
maintain, report on them and so on.

Using that audit_tbl, you'll achieve your goals.
Review & Followup
Rating: 5
Thankx Tom !!!! December 04, 2002
Reviewer: SB from CA
Thank you for the reply.
My customer is not willing to modify his procedure, so, Is there any view where
I can look for loads/executions of procedures like we have for DML statements
(in v$SQL). Thankx again for your time.

Followup:
well, all top level calls will be in there -- all of the anonymous blocks -- but
if "p2" is never called by a client -- just from "p1" and "p1" is called from
the client -- "p2" will not appear in v$sql.

but, you can suck something useful out of v$db_object_cache:

select owner, name, namespace, executions
  from v$db_object_cache a
 where namespace in ( 'TABLE/PROCEDURE', 'BODY' )
   and executions > 0
   and exists ( select null from dba_objects
                 where object_type in
                  ( 'PACKAGE', 'PACKAGE BODY', 'PROCEDURE', 'FUNCTION' )
                   and object_name = a.name and owner = a.owner )
/

Now, aging from the shared pool -- flushing of the shared pool will lose
"counts" as will a shutdown.  but if you capture that table from time to time -
you can tell about how many executes you have over time.

Rating: 5
way cool !!!!!!! December 04, 2002
Reviewer: SB from CA
Thankx, Tom

December 04, 2002 Darren -- Thanks for the question regarding "Generic procedure for creating aggregated data", version 8.1.7
You Asked
Hi Tom, love your work - I hope I can make this clear enough.

I have looked at your "printtbl" code and think I should be able to use
something like it but I'm buggered if I can get it to work.

Basically I need to

1. loop through all the records in a source table
2. check if a value derived from a function applied to the one of the columns
exists in a target table
3. if it does not then insert it plus all the other column values from the
source
4. if it doesn't then I need to update the existing row by applying some
function to the column value

For example

Source row: (TIME_KEY,OBJECT_ID)

TIME_KEY        OBJECT_ID    N_CALLS        N_ANSWERED     MAX_TIME_WAITING
200210220900    100_0_108    6        78            12
200210220900    100_0_109    4        7            16

Target row:

TIME_KEY    TIME        N_CALLS        N_ANSWERED     MAX_TIME_WAITING
200210        0900        10        85        16

As you can see, the new time key and time is are derived from the original time
key (based on some rounding of dates and times etc). The target rows are
aggregated values from the source with columns named "MAX%" being maximums,
"MIN%" being minimums and anything else being summed.

I need to be able to do this for any given table i.e. I can't hard code column
names or anything in cursors.

The insert part is easy - I can't figure out how to do the update. It looks like
I need to generate an update statement like

update target_table set (n_calls,n_answered,max_time_waiting) = (n_calls +
v_n_calls,n_answered + v_n_answered,
greatest(max_time_waiting,v_max_time_waiting))

where the v_ values come from the current row in the source table.

There can be as many as 30 - 40 million rows in the source table which is why
I'm not just doing a big group by (which works perfectly well for small source
tables). We would like to be able to run this nightly to update with source data
from during the day.

It's all basically to speed up queries currently based on these large source
tables. Oh, and it's a packaged database so we're not really free to play with
table structures. We can index etc but that's about it.

Any ideas? Thanks for your time.
and we said...
tell me -- why can't you hard code column names?  do you change your schema
every night?  why is this dynamic (if the structure ISN'T YOURS - hence won't be
changing until an upgrade -- at which point you would need to fix it anyway...)

But anyway - assuming that

o time_key is always in SOURCE
o time_key, time is always in TARGET
o you are only interested in N_ and MAX_ columns
o you update by time_key from SOURCE

I believe the following procedure will work.  Instead of updating existing and
inserting new -- I take the lazy way, just insert.  It is one time a temporary
table proves useful.  Consider:
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t1
  2  ( time_key varchar2(12),
  3    object_id varchar2(12),
  4    n_calls int,
  5    n_answered int,
  6    max_time_waiting int
  7  );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t2
  2  ( time_key varchar2(6),
  3    time varchar2(4),
  4    n_calls int,
  5    n_answered int,
  6    max_time_waiting int,
  7    primary key(time_key,time)
  8  );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t1 values (
'200210220900','100_0_108',   6 ,      78 ,          12 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t1 values (
'200210220900','100_0_109',   4 ,      7  ,         16 );

1 row created.

so, that is your SOURCE (t1) and TARGET (t2) tables.  For each target table, I
would issue:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create global temporary table temp_t2 on
commit delete rows
  2  as select * from t2 where 1=0;

Table created.

Now a procedure:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure refresh( p_source
in varchar2,
  2                                       p_target in varchar2,
  3                                       p_timekey in varchar2 )
  4  as
  5      l_stmt  long default 'insert into temp_' || p_target || ' ( time_key,
time ';
  6      l_cols  long;
  7      l_aggs  long;
  8
  9      procedure p ( p_str in varchar2 )
 10      is
 11         l_str   long := p_str;
 12      begin
 13         loop
 14            exit when l_str is null;
 15            dbms_output.put_line( substr( l_str, 1, 250 ) );
 16            l_str := substr( l_str, 251 );
 17         end loop;
 18      end;
 19  begin
 20      for x in ( select column_name
 21                   from user_tab_columns
 22                  where table_name = upper(p_target)
 23                    and (column_name like 'N\_%' escape '\'
 24                         or
 25                         column_name like 'MAX\_%' escape '\' )
 26                  order by column_id )
 27      loop
 28          l_cols := l_cols || ', ' || x.column_name;
 29          if ( x.column_name like 'N%' )
 30          then
 31              l_aggs := l_aggs || ', SUM(' || x.column_name || ') ' ||
x.column_name;
 32          else
 33              l_aggs := l_aggs || ', MAX(' || x.column_name || ') ' ||
x.column_name;
 34          end if;
 35      end loop;
 36      l_stmt := l_stmt || l_cols || ') select time_key, time ' || l_aggs ||
 37               ' from ( select substr( time_key, 1, 6 ) time_key, substr(
time_key, 9, 4 ) time ' ||
 38                 l_cols ||
 39               '  from ' || p_source ||
 40               ' where time_key = :x1
 41                 union all
 42                 select time_key, time ' || l_cols || ' from ' || p_target ||
 43               ' where time_key = :x2 and time = :x3 ) group by time_key,
time ';
 44
 45      p(l_stmt);
 46      execute immediate l_stmt using p_timekey, substr( p_timekey, 1, 6 ),
substr( p_timekey, 9, 4 );
 47      execute immediate 'delete from ' || p_target ||
 48                        ' where time_key = :x1 and time = :x2'
 49        using substr( p_timekey, 1, 6 ), substr( p_timekey, 9, 4 );
 50      execute immediate 'insert into ' || p_target || ' (time_key, time ' ||
l_cols || ') ' ||
 51                        'select time_key, time ' || l_cols || ' from temp_'
|| p_target;
 52      commit;
 53  end;
 54  /

Procedure created.

can be run to "refresh" target from source ( i just print out the insert for
debugging purposes, you can remove that):

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t2;
no rows selected

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec refresh( 't1', 't2', '200210220900' );
insert into temp_t2 ( time_key, time , N_CALLS, N_ANSWERED, MAX_TIME_WAITING)
select time_key, time , SUM(N_CALLS) N_CALLS,
SUM(N_ANSWERED) N_ANSWERED, MAX(MAX_TIME_WAITING) MAX_TIME_WAITING from ( select
substr( time_key, 1, 6 ) time_key, substr( ti
me_key, 9, 4 ) time , N_CALLS, N_ANSWERED, MAX_TIME_WAITING  from t1 where
time_key = :x1
               union all

select time_key, time , N_CALLS, N_ANSWERED, MAX_TIME_WAITING from t2 where
time_key = :x2 and time = :x3 ) group by tim
e_key, time

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t2;

TIME_K TIME    N_CALLS N_ANSWERED MAX_TIME_WAITING
------ ---- ---------- ---------- ----------------
200210 0900         10         85               16

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t1 values (
'200210220900','100_0_110',  24 ,      56  ,        66 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec refresh( 't1', 't2', '200210220900' );
insert into temp_t2 ( time_key, time , N_CALLS, N_ANSWERED, MAX_TIME_WAITING)
select time_key, time , SUM(N_CALLS) N_CALLS,
SUM(N_ANSWERED) N_ANSWERED, MAX(MAX_TIME_WAITING) MAX_TIME_WAITING from ( select
substr( time_key, 1, 6 ) time_key, substr( ti
me_key, 9, 4 ) time , N_CALLS, N_ANSWERED, MAX_TIME_WAITING  from t1 where
time_key = :x1
               union all

select time_key, time , N_CALLS, N_ANSWERED, MAX_TIME_WAITING from t2 where
time_key = :x2 and time = :x3 ) group by tim
e_key, time

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t2;

TIME_K TIME    N_CALLS N_ANSWERED MAX_TIME_WAITING
------ ---- ---------- ---------- ----------------
200210 0900         44        226               66

ops$tkyte@ORA817DEV.US.ORACLE.COM>

Hope that helps you get started.
Review & Followup
Rating: 4
Thanks December 04, 2002
Reviewer: Darren Belcher from Brisbane, Australia
The administrators of the application which use this database are able to create
new source tables at will as part of their report definitions.

I guess I was just trying to write one generic procedure to save us having to
modify the procedure or write new code every time they did this (not that they
do it that often).

Maybe I am trying to be too smart for my own good!?

I will be interested to see how this solution performs for our big tables.

If you have time I'd also like your thoughts regarding the fact I could write a
single group by to perform this (easily generated too!) but, while it worked in
about 20 minutes for a 3 million rowtable, it used over 10GB of temp space in 5
hours on a 35 million row, 15 column  table.

Thanks again for your time.

Followup:
what was your sort_area_size (one of the most "under set" parameters for "big
things")

read
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6501620260742
and consider using an alter session to up the sort area size to speed up these
large "builds".

December 05, 2002 -- Thanks for the question regarding "Where can I get detail information on "Analytic Functions" with examples.", version Oracle 8.1.7
You Asked
Hi Tom,

Where can I get detail information on "Analytic Functions" with examples.

Thanks.
and we said...
Well, I have a whole chapter on them in my book....

There is also
http://technet.oracle.com/doc/oracle8i_816/server.816/a76994/analysis.htm#11237
and
http://technet.oracle.com/doc/oracle8i_816/server.816/a76989/functio2.htm#81409
as well.
Review & Followup
Rating: 2
Range Clase in Sum Analytic function December 05, 2002
Reviewer: Vidya from India
I am lpooking for range clause in the Sum Analytic fnction and th3e syntax and
eg are not vailable I tried and it took me to the 8i SQL Refereence where in
sufficient eg are not given So Pls update on that

Followup:
I think I have a pretty nice write up of it in my book "Expert one on one
Oracle"

Sorry if you find the explanation in the SQL Reference manual "insufficient".
Both links above talk about "windowing" and that is where range is explained
(and is where I learned how to do it myself).

I find setting up simple examples and testing to see how they work to be useful
- maybe that would work for you?  Eg: I did little tests like this:

ops$tkyte@ORA920.US.ORACLE.COM> select ename,
  2         sal,
  3         first_value(ename) over ( order by sal
  4                     range between 300 preceding and 300 following ) eprec,
  5         first_value(sal) over ( order by sal
  6                     range between 300 preceding and 300 following ) sprec,
  7         last_value(ename) over ( order by sal
  8                     range between 300 preceding and 300 following ) efoll,
  9         last_value(sal) over ( order by sal
 10                     range between 300 preceding and 300 following ) sfoll
 11   from emp
 12  /

ENAME             SAL EPREC           SPREC EFOLL           SFOLL
---------- ---------- ---------- ---------- ---------- ----------
SMITH             800 SMITH             800 ADAMS            1100

that shows when you take SMITH as the current row and add + or - 300 to their
salary (range = 500 .. 1100), the "lowest" salary (sprec) is 800 in that window
and the highest (sfoll) is 1100

JAMES             950 SMITH             800 WARD             1250

when you do the same to james, the window is 650..1250.  In that range, the
lowest sal is SMITH's at 800 and the highest is WARD's at 1250

ADAMS            1100 SMITH             800 MILLER           1300

now adams with 1100, the range is 800..1400 -- do the same.... and so on

MARTIN           1250 JAMES             950 TURNER           1500
WARD             1250 JAMES             950 TURNER           1500
MILLER           1300 ADAMS            1100 ALLEN            1600
TURNER           1500 MARTIN           1250 ALLEN            1600
ALLEN            1600 MILLER           1300 ALLEN            1600
CLARK            2450 CLARK            2450 CLARK            2450
BLAKE            2850 BLAKE            2850 SCOTT            3000
JONES            2975 BLAKE            2850 SCOTT            3000
FORD             3000 BLAKE            2850 SCOTT            3000
SCOTT            3000 BLAKE            2850 SCOTT            3000
KING             5000 KING             5000 KING             5000

14 rows selected.

EMP is a great table to test with.  Experimentation is a great way to learn
analytics.  If you get my book -- I spend quite a few pages trying to explain
windowing -- which is a hard concept to get across on paper.

December 05, 2002 Om -- Thanks for the question regarding "A simple Question for you", version 8.1.7
You Asked
Dear Tom,

Thanks very much for your previous response.It was really
very useful.

Question
=========

If we have a table T1 with a single C1 column

T1

c1 number;

and this C1 column contains some values between 1 to 100.

Is it possible to write a SQL statement to find out the
from C1 column the numbers which are missing between 1 and 100.

For Example if C1 contains

1
2
3
4
5
6
7
8
9
10

The Query should simply print numbers from 11 12 13 ...100
which are not contained by C1.

Best regards,
Om
and we said...
  1  select *
  2    from ( select rownum r from all_objects where rownum <= 100 ) a,
  3         t1
  4   where a.r = t1.c1 (+)
  5*    and t1.c1 is null
ops$tkyte@ORA817DEV.US.ORACLE.COM> /

         R         C1
---------- ----------
        11
        12
        13
        14
        15
...
        98
        99
       100

90 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
Review & Followup
Rating: 4
December 04, 2002
Reviewer: Robert from Memphis, USA

Rating: 5
excellent use of all_objects December 05, 2002
Reviewer: Georg from Germany

Rating: 3
Simple question December 05, 2002
Reviewer: Om from India
Tom,

this is nice solution but then it has a dependency on all_objects table and also
if I want to make this generalized i.e. let's say search numbers between 1 to
300
instead of 1..100.

Could you please advise on this.

Best regards,
Om

Followup:
so what if it is dependent on all_objects?  that is one table i can promise you
will certainly be available, thats why I picked it. If all objects isn't
there, you have a broken database.

all_objects has 1000 or better objects generally.  In 8i, generally 20-30,000
objects in fact.

Just bind:

  1  select *
  2    from ( select rownum r from all_objects where rownum <= :N ) a,
  3         t1
  4   where a.r = t1.c1 (+)
  5*    and t1.c1 is null

Rating: 5
Great article in Oracle Mag concerning this... December 05, 2002
Reviewer: reader
See
http://otn.oracle.com/oramag/oracle/02-sep/o52sql.html
for a complete article.

Rating: 4
Possible solution in 8i December 05, 2002
Reviewer: Dan Kefford from New Haven, CT
The article cited above mentions a pretty cool solution for those using 9i. For
those interested in one for 8i, perhaps the following would be acceptable:

SQL> CREATE OR REPLACE TYPE number_tab_type AS TABLE OF NUMBER
  2  /

Type created.

SQL> CREATE OR REPLACE FUNCTION get_number_range_tab
  2    (p_begin_num IN NUMBER,
  3     p_end_num   IN NUMBER)
  4     RETURN number_tab_type
  5  IS
  6     l_number_range_tab   number_tab_type := number_tab_type() ;
  7  BEGIN
  8     FOR l_num IN p_begin_num .. p_end_num
  9     LOOP
 10        l_number_range_tab.EXTEND ;
 11        l_number_range_tab(l_number_range_tab.COUNT) := l_num ;
 12     END LOOP ;
 13     RETURN l_number_range_tab ;
 14  END get_number_range_tab ;
 15  /

Function created.

SQL> SELECT * FROM TABLE(CAST(get_number_range_tab(&begin_num, &end_num) AS
number_tab_type))
  2  /
Enter value for begin_num: 1
Enter value for end_num: 10
old   1: SELECT * FROM TABLE(CAST(get_number_range_tab(&begin_num, &end_num) AS
number_tab_type))
new   1: SELECT * FROM TABLE(CAST(get_number_range_tab(1, 10) AS
number_tab_type))

COLUMN_VALUE
------------
           1
           2
           3
           4
           5
           6
           7
           8
           9
          10

10 rows selected.

SQL> /
Enter value for begin_num: 10
Enter value for end_num: 20
old   1: SELECT * FROM TABLE(CAST(get_number_range_tab(&begin_num, &end_num) AS
number_tab_type))
new   1: SELECT * FROM TABLE(CAST(get_number_range_tab(10, 20) AS
number_tab_type))

COLUMN_VALUE
------------
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20

11 rows selected.

December 05, 2002 John -- Thanks for the question regarding "control file parallel write wait event", version 8.1.7
You Asked
Hi Tom,

I am loading tables in a database. I can't figure out why the
control file parallel write event is always so high.
The DB is only used as a staging area for loading/converting data. No users. I
tried to minimize checkpoints.
We don't care about recovery for this DB.

log_archive_start                    boolean FALSE
log_checkpoint_interval              integer 0
log_checkpoint_timeout               integer 0
log_buffer                           integer 512000

      GROUP#        BYTES ARC STATUS
------------ ------------ --- ----------------
           1   2147483648 NO  ACTIVE
           4   2147483648 NO  CURRENT

Top 5 Wait Events
~~~~~~~~~~~~~~~~~                                             Wait     % Total
Event                                               Waits  Time (cs)   Wt Time
-------------------------------------------- ------------ ------------ -------
control file parallel write                         2,350       33,154   67.13
log file parallel write                             2,499       12,044   24.39
single-task message                                     2        4,002    8.10
db file parallel write                                221           63     .13
db file sequential read                                31           41     .08
and we said...
we write to the control files even more then we write to logs.

While you say "recovery isn't important" -- we would beg mightly to differ.
Checkpoints are NOT something you want to minimize - in fact, you want them to
happen continously (they happen in the background -- the only time your
foreground process has to wait for them is when they didn't have time to FINISH
in the background!!! if you make them "not happen" -- you'll just create  a
large backlog and have to wait for it)

You tune the control file writes in the same way you do logs

o dedicated devices to remove contention
o never on raid 5
o if you are just using UFS, mount with forcedirectio for logs and control files

  if you can (or check out your logical volume vendors docs for how to mount
  and bypass buffer cache).  use raw if push comes to shove -- slice up a small
  partition.

It looks like you need to tune your control/log devices here.  A bigger log
buffer and FAR FAR FAR fewer commits (you are probably committing way way too
much -- that is causing extra control file writes and log waits - the only time
you want for logs; when you commit or the redo log buffer is totally full).

Stop committing so much (I'm guessing on that -- people "love to commit" for
some reason).

Make a much much larger redo log buffer.

Tune the control and log devices.
Review & Followup
Rating: 4
August 21, 2003
Reviewer: A reader

Rating: 5
meant to select 5 stars August 21, 2003
Reviewer: A reader

December 05, 2002 Dharu -- Thanks for the question regarding "Current as well as Previuos Fiscal Year Code", version 8.1.7
You Asked
Hi Tom,
I am wondering like, is this the right approach to code Fiscal year as follows.
DATE BETWEEN
TO_DATE('07/01/'||decode(sign(to_number(to_char(sysdate,'MM'))-6),1,to_char(ADD_M
ONTHS(sysdate, 0),'YYYY'),to_char(Add_Months(sysdate,-12),'YYYY')),'MM/DD/YYYY')
AND
TO_DATE('06/30/'||decode(sign(to_number(to_char(sysdate,'MM'))-6),1,to_char(Add_M
onths(sysdate, 12),'YYYY'),to_char(ADD_MONTHS(sysdate,
0),'YYYY')),'MM/DD/YYYY').
I would appreciate if you can show me any efficient way of coding for the same.
Thank you in advance..
and we said...
this way is a little shorter to type:

select dt,
add_months( trunc(dt,'y'),
decode(to_char(dt,'mm'),1,-6,2,-6,3,-6,4,-6,5,-6,6,-6,6) ) f1,
add_months( trunc(dt,'y'),
decode(to_char(dt,'mm'),7,18,8,18,9,18,10,18,11,18,12,18,6) )-1 t1
from ( select created dt from all_objects )

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.01          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch     1960      2.70       2.74          0     145074          0       29380
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total     1962      2.72       2.75          0     145074          0       29380

********************************************************************************

select dt,
TO_DATE('07/01/'||decode(sign(to_number(to_char(dt,'MM'))-6),1,
         to_char(ADD_MONTHS(dt,
0),'YYYY'),to_char(Add_Months(dt,-12),'YYYY')),'MM/DD/YYYY'),
TO_DATE('06/30/'||decode(sign(to_number(to_char(dt,'MM'))-6),1,
          to_char(Add_Months(dt,
12),'YYYY'),to_char(ADD_MONTHS(dt,0),'YYYY')),'MM/DD/YYYY')
  from (select created dt from all_objects )

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch     1960      3.28       3.26          0     145074          0       29380
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total     1962      3.28       3.26          0     145074          0       29380

and uses less CPU horsepower and gets the same results...
December 05, 2002 Antoine -- Thanks for the question regarding "Step to tune an existing application", version 8.1.7
You Asked
Hi Tom,

I am wondering do you have any step or approache to tune an existing
application?

Like, always run statspack first after tkprof?

Thanks,

Antoine
and we said...
You got it backwards.

Run tkprof to find low hanging fruit such as:

o queries with high logical IO's -- tune them
o queries with high CPU times -- tune them
o queries with EXECUTE = PARSE > 1.  That is, queries you parse and execute over

  and over -- make that be parse = 1, execute > 1 instead
o queries that do not use bind variables -- if you see the same query over and
  over that only differs in the use if literals, fix it.
o queries that are executed hundreds of thousands of times.  Look at the
  algorithm to see what you can do to prevent that.  Eg: if you code:

   for x in (select * from t1)
   loop
      for y in ( select * from t2 where t2.fk = t1.pk )
      loop
         ....

  recode that as a JOIN as it should always have been.

Then, you can run statspack to tune at the instance level.

If you have my book "Expert one on one Oracle" -- chapter 10 is all about this
topic.
Review & Followup
Rating: 3
Step to tune an existing application December 03, 2002
Reviewer: Antoine Bocti from Montreal, Canada
Hi Tom,

 thanks again for you're response, but let say the application is on SAP on
another application that you cannot see the sql and the end user call you
because it slow!

what are the steps in this case?

Followup:
same as before

yes, you can see the queries
yes, you can even help tune them (indexing, changing optimizer_index_*
parameters)

and then statspack to find any low hanging fruit (disk contention, log file
waits, what are the bad "waits" that you can influence)

Rating: 5
Step to tune an existing application December 04, 2002
Reviewer: Antoine Bocti from Montreal, Canada
Thanks Tom!

Rating: 5
How to run tkprof in another user session December 04, 2002
Reviewer: Arun Gupta from Harrisburg, PA USA
Tom
We have a web based application where transactions are managed by
MS-DTC(Microsoft Distributed Transaction Coordinator).

All the SQLs to do select/insert/update/delete to all tables are in stored procs
which are in packages. These stored procs are called by COM+ components.

There is no commit/rollback within the stored procs. The commit/rollback is
managed by MS-DTC, controlled by the COM+ components. The COM+ components use
one username (app_user) to log in to the database and execute these packages.
Connection pooling is managed by IIS and depending upon the users hitting the
web site, I can see multiple app_user sessions connected to the database. In
this scenario, how can I run tkprof ?

Thanks

Followup:
you tell me -- you've subverted all identification and access control in the
database.  You've sort of crippled us here.

You have to do it yourself.

Your com controls have to have some way to know "who the user is".

Your com controls will have to have some flag that says "developer needs to
tune, set sql_trace=true when the user = 'BOB'"

That is -- your developed code should have been developed with tuning and
debugging in mind.  There should be some parameter you can flip in order to get
performance information.  It is something that (since you aren't using the
databases I&A) you need to instruement your code to do.

Rating: 5
December 05, 2002
Reviewer: Arun Gupta from Harrisburg, PA USA
The application is web based, we cannot know/control who is connecting and
performing the search. However, any user who connects, will run queries
contained only within the packages. That is the only way the users can access
the database. If I can tune queries within the packages, then no matter who runs
them, we will get good performance. I use statspack reports to identify the
queries which have most buffer gets, most I/O, most CPU etc. and then tune them.
Please let me know if this is a good strategy or not for our situation.

Followup:
It is the way I would do it myself.  I think the best client applications are
those that have "begin ... end;" in them and never had SELECT, INSERT, UPDATE or
DELETE ;)

December 05, 2002 Carol -- Thanks for the question regarding "SQL Question - wanting PK of a subquery containing aggregate function", version 9i
You Asked
1.

If I have a query like the following which returns for each department, the max
hire date of all employees within the department.  If the department has no
employees in it, the department is displayed with a null hire date:

select dept.dept_name,
       emp.hire_date
from dept,
     emp
where dept.id_dept = emp.id_dept(+)
  and (emp.id_emp is null
       or emp.hire_date =
         (select max(e.hire_date)
          from employees e
          where e.id_dept = emp.id_dept))

If the hire date column contains no duplicates within a department, this would
be fine but I get more than one row for a some departments.

Is there any way to have the sub-select return the PK ID of the record with the
max hire_date?  I have tried using the FIRST and FIRST_VALUE functions but the
queries ran very slowly.  In other words, what is the best way to write
something like this?

2.

Also, if the emp table has an ACTIVE_YN flag how would I write the query if I
wanted the subquery to return the PK ID of the following:
- If an active employee exists for the department return the ID of the active
employee with the max(hire_date)
- If no active employees exist, return the ID of the employee with the
max(hire_date)

The query could be something like the following - although this doesn't work
because of a scope problem:

select dept.dept_name,
       emp.hire_date
from dept,
     emp
where dept.id_dept = emp.id_dept(+)
  and (emp.id_emp is null
       or emp.id_emp =
        (select id_emp
         from (select id_emp
               from employees e
                   where e.id_dept = emp.id_dept
               order by active_yn desc, hire_date desc)
         where rownum <= 1)

Hope this makes sense.  Thanks so much.
and we said...
1) either of

select dept.dname,
       to_date(substr(
               max( to_char( emp.hiredate, 'yyyymmddhh24miss' ) || emp.empno ),
               1, 14 ),'yyyymmddhh24miss') hiredate,
       to_number( substr(
               max( to_char( emp.hiredate, 'yyyymmddhh24miss' ) || emp.empno ),
               15 ) ) empno
  from dept left outer join emp on ( dept.deptno = emp.deptno )
 group by dept.dname
/

select distinct dept.dname,
       first_value(emp.hiredate) over (partition by dept.dname order by hiredate
DESC) hiredate,
       first_value(emp.empno) over (partition by dept.dname order by hiredate
DESC) empno
  from dept left outer join emp on ( dept.deptno = emp.deptno )
/

does that.

2) building one the same exact concept:

ops$tkyte@ORA920.US.ORACLE.COM> create table emp as select * from scott.emp;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> create table dept as select * from scott.dept;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> alter table emp add active_yn char(1);

Table altered.

ops$tkyte@ORA920.US.ORACLE.COM> update emp set active_yn = decode(
mod(rownum,2), 0, 'Y', 'N' );

14 rows updated.

ops$tkyte@ORA920.US.ORACLE.COM> select dept.dname,
  2         to_date(substr(
  3                 max( decode(active_yn,'Y','1','N','0') ||
  4                 to_char( emp.hiredate, 'yyyymmddhh24miss' ) || emp.empno ),
  5                 2, 14 ),'yyyymmddhh24miss') hiredate,
  6         to_number( substr(
  7                 max( decode(active_yn,'Y','1','N','0') ||
  8                 to_char( emp.hiredate, 'yyyymmddhh24miss' ) || emp.empno ),
  9                 16 ) ) empno
 10    from dept left outer join emp on ( dept.deptno = emp.deptno )
 11   group by dept.dname
 12  /

DNAME          HIREDATE       EMPNO
-------------- --------- ----------
OPERATIONS
RESEARCH       19-APR-87       7788
SALES          03-DEC-81       7900
WASHINGTON     23-JAN-82       7934

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select distinct dept.dname,
  2         first_value(emp.hiredate) over (partition by dept.dname order by
active_yn DESC, hiredate DESC) hiredate,
  3         first_value(emp.empno) over (partition by dept.dname order by
active_yn DESC, hiredate DESC) empno
  4    from dept left outer join emp on ( dept.deptno = emp.deptno )
  5  /

DNAME          HIREDATE       EMPNO
-------------- --------- ----------
OPERATIONS
RESEARCH       19-APR-87       7788
SALES          03-DEC-81       7900
WASHINGTON     23-JAN-82       7934

Now, I loaded up a table with 10,000 dept's and 100,000 empnos -- I found the
performance of both to be very similar.  I can tell you which query is more
readable, understandable and easier to write.....
Review & Followup
Rating: 4
Another possible option December 05, 2002
Reviewer: A reader
I've used the construction at the end of this example when I need to effectively
"outer-join" to a subquery.

Here is the sample data:

SQL> SELECT  d1.dname,
  2          e1.hiredate
  3  FROM    scott.dept d1,
  4          scott.emp e1
  5  WHERE   d1.deptno = e1.deptno(+)
  6  /

DNAME          HIREDATE
-------------- -----------
ACCOUNTING     09-JUN-1981
ACCOUNTING     17-NOV-1981
ACCOUNTING     23-JAN-1982
RESEARCH       17-DEC-1980
RESEARCH       23-MAY-1987
RESEARCH       03-DEC-1981
RESEARCH       19-APR-1987
RESEARCH       02-APR-1981
SALES          20-FEB-1981
SALES          01-MAY-1981
SALES          28-SEP-1981
SALES          03-DEC-1981
SALES          08-SEP-1981
SALES          22-FEB-1981
OPERATIONS

15 rows selected.

Here is the query which is not quite right because it is missing the department
with no employees:

SQL> SELECT  d1.dname,
  2          e1.hiredate
  3  FROM    scott.dept d1,
  4          scott.emp e1
  5  WHERE   d1.deptno = e1.deptno
  6  AND     e1.hiredate =
  7         (SELECT  MAX(e2.hiredate)
  8          FROM    scott.emp e2
  9          WHERE   e2.deptno = e1.deptno)
 10  /

DNAME          HIREDATE
-------------- -----------
ACCOUNTING     23-JAN-1982
RESEARCH       23-MAY-1987
SALES          03-DEC-1981

And here is a modified version of the query above which _will_ get what we want:

SQL> SELECT  d1.dname,
  2          e1.hiredate
  3  FROM    scott.dept d1,
  4          scott.emp e1
  5  WHERE   d1.deptno = e1.deptno(+)
  6  AND     NVL(e1.hiredate, TO_DATE('01-JAN-1900', 'DD-MON-YYYY')) =
  7         (SELECT  NVL(MAX(e2.hiredate), TO_DATE('01-JAN-1900',
'DD-MON-YYYY'))
  8          FROM    scott.emp e2
  9          WHERE   e2.deptno(+) = e1.deptno)
 10  /

DNAME          HIREDATE
-------------- ---------
ACCOUNTING     23-JAN-1982
RESEARCH       23-MAY-1987
SALES          03-DEC-1981
OPERATIONS

Followup:
avoid the correlated subquery unless you have to.  Do you really want to run
that subquery once for each row in the outer query?

(also -- it is not getting the stuff requested -- where is the empno?)

December 05, 2002 raju -- Thanks for the question regarding "ref cursor with or with out dynamic sql", version 8.1.6
You Asked
tom,

your assisatance is needed regarding the following
i have a stored procedure that returns a result set using ref cursor.
but it has to accept a parameter i_sort_by, this parameter tells me on which
column result
set data to be sorted.
without the sorting i would have written my proc in the following way.

create or replace procedure myproc(o_rec_cur as t_rec_cur)
as
begin
  open o_rec_cur for select
t1.code,t1.desc,t2.sht_name,t2.title,t3.comp,t4.benefits
                     from t1,t2,t3,t4 where t1.code = t2.code
                                            t2.id = t3.id
                                            t3.no = t4.no;
end;

now with sorting, i have to write in this way

create or replace procedure myproc(i_sort_by varchar2,o_rec_cur as t_rec_cur)
as
begin
  open o_rec_cur for 'select
t1.code,t1.desc,t2.sht_name,t2.title,t3.comp,t4.benefits
                     from t1,t2,t3,t4 where t1.code = t2.code
                                            t2.id = t3.id
                                            t3.no = t4.no' order by ||
i_sort_by;
end;

only for the sort_by i have to return the ref cursor with a dynamic query, so is
there a way to avoid this dynamic query
with still able to sort the data. i thought i will go for a global temporary
table, but in our company we are not supposed to
use temporary tables.

thanks in advance
raju
and we said...
Why the fear of dynamic SQL here?  All sql is dynamic under the covers -- the
best way to accomplish this is in fact using native dynamic sql.

You'll have the same number of parses as you would with a ref cursor (those
cursors cannot be cached).  You'll get the most optimal plan for each order by
(if some can use an index -- they will while others will not)

You can use a decode -- eg:

  order by decode( i_sort_by, 'CODE', t1.code, 'DESC', t2.desc, .... )

that'll work if all of the fields are STRINGS -- if you have dates:

to_char(date_field, 'yyyymmddhh24miss' )

will make them sortable -- numbers are a problem if they can be negative, if
they are just postive:

to_char( num_field, '000000000000000009.999999999999' )

or some appropriate, fixed width format will work.

Again though -- I would just use the native dynamic sql.
Review & Followup
Rating: 5
thanks for the reply November 16, 2001
Reviewer: raju from usa
thanks for the answer tom,
1.why fear dynamic sql?
do you mean to say the below bot will be more less same in occupying time.

  oper o_ref_cur for select * from emp where empno = :empno using i_empno;

  oper o_ref_cur for 'select * from emp where empno = :empno' using i_empno;

2.wity native dynamic sql, i uderstand it is better, but how do you return a
result set? like we do with ref cursor

please followup.

regards
raju

Followup:
Marginal difference (and if the order by results in a different plan -- the
dynamic cursor might totally blow away the static one!)

ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_start number;
  3
  4      l_rec   emp%rowtype;
  5          l_empno number default 0;
  6
  7          type rc is ref cursor;
  8          l_cursor rc;
  9  begin
 10      insert into run_stats select 'before', stats.* from stats;
 11
 12      l_start := dbms_utility.get_time;
 13      for i in 1 .. 1000
 14      loop
 15          open l_cursor for select * from emp where empno > l_empno;
 16          loop
 17              fetch l_cursor into l_rec;
 18              exit when l_cursor%notfound;
 19          end loop;
 20          close l_cursor;
 21      end loop;
 22      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 23
 24      insert into run_stats select 'after 1', stats.* from stats;
 25
 26      l_start := dbms_utility.get_time;
 27      for i in 1 .. 1000
 28      loop
 29          open l_cursor for 'select * from emp where empno > :x' using
l_empno;
 30          loop
 31              fetch l_cursor into l_rec;
 32              exit when l_cursor%notfound;
 33          end loop;
 34          close l_cursor;
 35          end loop;
 36      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 37
 38      insert into run_stats select 'after 2', stats.* from stats;
 39  end;
 40  /
203 hsecs
219 hsecs

2) in EXACTLY the same way -- there is no difference to the client whether you
use open for <static> or open for <string>

Rating: 5
December 05, 2002
Reviewer: Don
Tom,

I am using a package (RPT_UTIL.EXEC_SQL) to exec a SQL string built by
application reporting tool.  The results are returned via ref cursor, formatted
and displayed by the application.

We would like to log what queries get run and how long they take.

I have built a log table and I capture the user, time, and query string.  But I
am having trouble with the execution time.

I tried doing something like this in the EXEC proc:

PROCEDURE RPT_EXEC (
   P_MOD_ID                   IN       NUMBER,
   P_RPT_ID                   IN       NUMBER,
   P_QUERY                    IN       VARCHAR2,
   P_ERRSTRING                OUT      VARCHAR2,
   P_RESULT                   OUT      NUMBER,
   P_CUR                      OUT      MY_REFCUR
) IS
   /*  USED TO EXECUTE A SAVED REPORT OR SQL */

   V_SQL     VARCHAR2 (4000) := P_QUERY;
   V_ID      NUMBER;
   V_START   NUMBER;
BEGIN
   P_RESULT := 0;

   SELECT RPT_LOG_SEQ.NEXTVAL
     INTO V_ID
     FROM DUAL;

   INSERT INTO CMR.RPT_LOG
               (RPT_LOG_ID, RPT_USER, RPT_RUNDATE, RPT_RUNTIME_SEC, RPT_ID,
RPT_SQL)
        VALUES (V_ID, P_MOD_ID, SYSDATE, 0, P_RPT_ID, NULL);

   V_START := DBMS_UTILITY.GET_TIME;
   OPEN P_CUR FOR V_SQL;

   UPDATE CMR.RPT_LOG
      SET RPT_RUNTIME_SEC = (DBMS_UTILITY.GET_TIME - V_START) / 100
    WHERE RPT_LOG_ID = V_ID;

   COMMIT;
   P_RESULT := 1;
END;

But no matter how big the query, my runtime value was always under a sec.  Even
ones that take 20 -30 sec in SQL/Plus.

 Is this the way to do it or am I missing the big picture?

Thanks for all the help.

Followup:
you are not timing the time it takes to fetch -- which for many queries is all
of the time.

When you open a query - we don't get all of the data together somewhere
(consider that opening "select * from one_million_row_table where rownum = 1"
takes as much time as "select * from one_million_row_table").  We get data as
requested.

You'll have to have the called UPDATE this row with the end time -- but then
you'll be timing the time it takes to draw on the screen and all as well.

December 05, 2002 Piotr -- Thanks for the question regarding "Oracle and Java", version 9.2.0.1.0
You Asked
Hello,

I have 3 questions:

1. Is there a way in Oracle to implement callbacks on the client side?
For example the following scenario:
Session A selects a row from table. Session A hypotetically registers a
"callback on update". Session B updates that row and on commits. This fires
asynchronously (say: TCP/IP out-of-band data) the callback in the session A,
something like a Java listener class or a true "C" callback function. The
callback than unregisters itself. Could you, please, give an example or kind a
workaround if that won't work?

2. If I have a complex java GUI app that lets users scroll and update data in a
JTree, JTable, JGraph view. Which of the two is better way to save their
changes:
a) cache and track all the changes than make a big insert/update/delete at the
end of the processing
b) issue individual SQL statements as soon as users' edits happen with "select
for update" possibly.

3. I have written a thumbnail generator in Java and I want to deploy it directly
in DB by setting a trigger on the image table and have it called whenever a user
inserts or updates an image. Problem is that this program (class) uses Java
Swing although it doesn't display any components just uses ImageIcon and
JPEGEncoder. Is it OK for Oracle to use Swing classes? If so how can I most
easily and securely upload Java classes into DB (which schema, what Oracle
permissions, what Java permissions, synonyms if ever). I'd like potentially
everybody to be able to use that class.

Thank you in advance and regards.
and we said...
1) AQ can be used for this.
http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/appdev.920/a96587/qopface.htm#64237
is an example of how to register for a non-persistent queue in a call back
fashion.  The table would have a trigger that queues a message, program gets the
callback upon commit.

See also:
http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/appdev.920/a96584/oci16m43.htm#555732
2) if you have a stateful connection, (b) is preferred.  It ensures the user
isn't wasting their time (you lock the data from others - others are told
"someone else is working on this -- go away, come back later".

The other method (a), doesn't require a stated connection but the end user might
discover "their" data was updated by someone else and their work was for nothing
-- they have to start over.

3) oh -- you didn't know we already did that.  bummer.
http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/appdev.920/a88786/mm_imgre.htm#1044452
It might work (your class)-- might not.  I've seen some gui things work OK, some
not so.  You might consider just using the built-in interMedia datatype that
gives you lots of functionality.
Review & Followup
Rating: 5
RTFM December 06, 2002
Reviewer: Piotr Jarmuz from Braunschweig, Germany
1) Excellent! I should have read docs before getting the idea of convincing our
management to use PostgreSQL. Sorry! Now we definitely stay with Oracle.
2) Hmm... hard to say. I have a Java applet and Perl proxy server that talks to
the database through a dedicated connection so I am not sure here. There are
only few (2-3) people that will use that (and probably not in overlapping
periods). So I guess it should be a.) Altough I could program the server so that
it guarantees that the same process and thus Oracle connection and thus
transaction management would be used for each applet's POST or XML-RPC request
and then it could be b). Don't know here...

3) No I didn't... But these are just few lines of code. Java is great! Were it
not so slow would it be (close to) perfect!

bummer - <according to
http://www.m-w.com/dictionary.htm
>
an unpleasant experience (as a bad reaction to a hallucinogenic drug)

An Oracle drug? ;)

But this is a pleasent experience and <definitely> good reaction, in any case!

Thank you very much.
Cheers!

P.S.
This RTFM is for me :-) I know. It doesn't hurt.

December 06, 2002 M -- Thanks for the question regarding "Dump blob into file", version 9.0.1
You Asked
We can dump data into the flat text file, but it seems that blob data is not
included (am I right?). If we don't use export utility, do we have to dump blob
data into file one by one ? (then if we have thousands blobs, we will get
thousands dumped files).

Another question is about SQL*PLUS. When we use SQL*PLUS to run queries, it
takes a quite while to display the returned row result. I was wondering if there
is an option to turn DISPLAY off.

Thanks for any help.
and we said...
In Oracle9iR2 you can use PLSQL to put a blob to a file as UTL_FILE supports
binary IO as well as TEXT IO.

Before that, a java stored procedure can do the same.

I don't know where you are going with:

"If we don't use export utility, do we have to dump blob
data into file one by one ? (then if we have thousands blobs, we will get
thousands dumped files)."

since if you are not using exp, you are using something you wrote -- hence it
can do whatever you program it to.

I use

SQL> set autotrace traceonly

it runs the query, fetches the data -- does not print it but shows me the
explain plan and the statistics.  This is good for tuning.  If you are not
tuning, just dumping data out then put this in a scrip:

set termout off
spool filename
select * from t;
spool off
set termout on

and run that script.
Review & Followup
Rating: 2
Reading Blob data December 06, 2002
Reviewer: Daya from India
Hi Tom!
  I have a problem in reading the Blob data from the database.
After storing the data to database, i have a procedure to read
the data from database as given below:
  We are using Database version 8.1.7.

create or replace procedure read_lob( p_id in demo.id%type )
is
    l_lob    blob;
    l_amt    number default 30;
    l_off   number default 1;
    l_raw   raw(32767);
begin
    select theBlob into l_lob
        from demo
         where id = p_id for update;
        begin
           loop
              dbms_lob.read( l_lob, l_amt, l_off, l_raw );
              dbms_output.put_line( utl_raw.cast_to_varchar2( l_raw ) );
              l_off := l_off+l_amt;
              l_amt := 4096;
           end loop;
        exception
           when no_data_found then
             dbms_output.put_line(sqlerrm);
        end;
end;

When i try to execute this procedure, i get a error like:
SQL> set serveroutput on size 100000
SQL> exec read_lob(2);

PL/SQL procedure successfully completed.

SQL> exec read_lob(2);
spool e:\woc\woctabledel.lst
BEGIN read_lob(2); END;

*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error
ORA-06512: at "SYS.DBMS_OUTPUT", line 57
ORA-06512: at "WOC.READ_LOB", line 14
ORA-06512: at line 1

  What could be the problem? For another file which has got no
space or null values, the same procedure works fine.

Thanks
Daya

Followup:
search this site for

woctabledel

and read the answer I already gave you on the other question you put this exact
text on already....

Rating: 4
March 03, 2003
Reviewer: Sage from AVO
I met the same question before,and "exp" is the exact answer. :) "exp" can dump
data into a binary file, includes the blob data.

December 06, 2002 John -- Thanks for the question regarding "Lots of (Sum) s in a Group By", version 8.0.6
You Asked
Hi, Tom

I've got a problem creating a temporary table from a large table with 1.5
million records.
The temp table should have about 40000 records

Create table temp as
(select loc, team, year,
  sum(a) sales1,
  sum(b) sales2,
      ....
  sum(z) sales12,
  sum(a) - sum(aa) - sum(aaa) margin1,
      .....
  sum(z) - sum(zz) - sum(zzz) margin12
from master
group by loc, team, year)

The problem looks like a workspace problem as it doesn't even finish the query
in 4 hours, and that's all the spare time I have overnight to do housekeping
before the applications restart.
Is the problem the 24 (Sum)s or am I missing something

Thanks

John
and we said...
could be indicative of being ultra conservative on your sort_area_size.  Here is
an example on a modest Solaris box

two CPU:

Oct 24 15:37:33 aria-dev unix: cpu1: SUNW,UltraSPARC-II (upaid 1 impl 0x11 ver
0xa0 clock 400 MHz)
Oct 24 15:37:33 aria-dev unix: cpu3: SUNW,UltraSPARC-II (upaid 3 impl 0x11 ver
0xa0 clock 400 MHz)

and 2gig of ram (quite a few years old, definitely not state of the art, has
about 6 instances of Oracle running on it -- nothing fancy with the disks --
just "plain old disks"

So, I created a table T:

drop table t;

create table t ( loc varchar2(10), team varchar2(10), year int,
a int, b int, c int, d int, e int, f int, g int, h int, i int, j int, k int, l
int, m int,
n int, o int, p int, q int, r int, s int, t int, u int, v int, w int, x int, y
int, z int );

begin
    for l_loc in 1..200
    loop
        for l_team in 1..40
        loop
            for l_year in 1998..2002
            loop
                insert into t values ( 'loc' || l_loc, 'team' || l_team, l_year,

1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26);
            end loop;
        end loop;
    end loop;
end;
/

insert /*+ append */ into t select * from t;
commit;
insert /*+ append */ into t select * from t;
commit;
insert /*+ append */ into t select * from t;
commit;
insert /*+ append */ into t select * from t;
commit;
insert /*+ append */ into t select * from t;
commit;

select count(*) from t;

1,280,000 records...

and then:

ops$tkyte@ORA806.WORLD> set timing on
ops$tkyte@ORA806.WORLD> alter session set sort_area_size = 104857600
  2  /

Session altered.

Elapsed: 00:00:00.00
ops$tkyte@ORA806.WORLD> create table t2
  2  as
  3  select loc, team, year,
  4             sum(a) sales1, sum(b) sales2, sum(c) sales3,
  5             sum(d) sales4, sum(e) sales5, sum(f) sales6,
  6             sum(g) sales7, sum(h) sales8, sum(i) sales9,
  7             sum(j) sales10, sum(k) sales11, sum(l) sales12,
  8             sum(m) sales13,
  9             sum(q)-sum(r)-sum(q) margin1,
 10             sum(a)-sum(f)-sum(w) margin2,
 11             sum(k)-sum(d)-sum(e) margin3,
 12             sum(c)-sum(s)-sum(r) margin4,
 13             sum(e)-sum(z)-sum(t) margin5,
 14             sum(j)-sum(y)-sum(y) margin6,
 15             sum(l)-sum(x)-sum(u) margin7,
 16             sum(o)-sum(w)-sum(i) margin8,
 17             sum(n)-sum(v)-sum(o) margin9,
 18             sum(j)-sum(u)-sum(p) margin10,
 19             sum(g)-sum(t)-sum(g) margin11,
 20             sum(n)-sum(g)-sum(h) margin12,
 21             sum(w)-sum(f)-sum(j) margin13
 22   from t
 23   group by loc, team, year
 24  /

Table created.

Elapsed: 00:02:28.50
ops$tkyte@ORA806.WORLD>

took about 2 1/2 minutes.  Normally, the sort area size is 102,400 bytes
(100k).  For this test, using a temp tablespace with 1m extents (that never
actually got used!) and a 100m sort area size.

Now, trying the same thing with a 10m sort area size, it is very different, I
let it run for 1/2 hour -- it took up about 1.4 gig of temp and I stopped it
(got bored, proved my point)...

So suggestion: increase sort_area_size in your batch session to some large
number to improve the performance of your very very large sort.  The smaller the
sort area size -- the smaller the swap pieces - the more temp you need -- the
longer it will take (lots of extra IO and all).

People tend to way undersize this and never change at the session level for
batch jobs.  It can make a TREMENDOUSLY huge difference though!
Review & Followup
Rating: 5
Illustrative; shows up with materialized views, too November 16, 2002
Reviewer: Daryl
Thanks for the nice example. I've seen this with refreshing materialized views
as well. With a default sort_area_size, it takes *forever*. Upping it to 100m
cut it to under 30 seconds.

Rating: 5
How about 9i December 06, 2002
Reviewer: A reader from India
Hi Tom

If I am using 9i with workarea size policy = AUTO. Then would Oracle take care
of dynamically sizing the sort_area_size according to the need of the session?

Thanks

Followup:
yes, according to the need of the session AND the currently available resources.

December 06, 2002 Tony -- Thanks for the question regarding "Why all links to your site are all of sudden so slow!!!", version 8.1.6
You Asked
Hi Tom,
From last 24 hours, I have observed that accessing your site, clicking on 'ask
question', 'read question','Review Question' etc., all gone slow (taking around
2-3 minutes instead of couple of seconds before). Are others facing the same
problem?
Thanks
and we said...
Here are my site stats:

Last 24 Hours:
Page Views: 27,348
Page Views/Sec: 0.317
IP Addresses: 2,552
Views per IP Address: 10.716
Users: 2,986
Views per User: 9.159
Distinct Pages: 20
Avg Elap/Page (seconds): 0.65
Slowest/fastest Page (seconds): 16.19/ 0.13

Last 60 Minutes:
Page Views: 759
Page Views/Sec: 0.211
IP Addresses: 130
Views per IP Address: 5.838
Users: 147
Views per User: 5.163
Distinct Pages: 16
Avg Elap/Page (seconds): 0.90
Slowest/Fastest Page (seconds): 9.20/ 0.14

Last 60 Seconds:
Page Views: 9
Page Views/Sec: 0.150
IP Addresses: 4
Views per IP Address: 2.250
Users: 6
Views per User: 1.500
Distinct Pages: 3
Avg Elap/Page (seconds): 0.77
Slowest/Fastest Page (seconds): 1.20/ 0.28

So, my time in the database is always under a second.

is anyone else seeing slow times? (i'm not)

could be a network issue.  If you see this page and feel so inclined, add where
you are (country/city) and if performance is "normal".  thanks!
Review & Followup
Rating: 4
Still very slow! December 05, 2002
Reviewer: A reader
Tom,
As always, thanks for your quick response.
I'm from Sydney/Australia. Even clicking on 'Letus Know' takes more time!
May be network issue, others can through some more information.

Rating: 1
works fine for me December 05, 2002
Reviewer: donkey
from oregon

Rating: 5
Seems OK December 05, 2002
Reviewer: A reader from Arizona, USA

Rating: 5
Seems OK December 05, 2002
Reviewer: Raghu from Arizona, USA

Rating: 4
Fine in Sydney, Aus at "13:30pm" local time December 05, 2002
Reviewer: Gary from Sydney, Australia
Though I think you need to decide which format you are going to use to display
time. 16pm and 19pm looks a little odd. :)

"originally submitted on 5-dec-2002 16pm eastern time, lasted updated 5-dec-2002
19pm"

Followup:
vbg -- no one else has noticed that.

I always use hh24, must have thrown a "pm" in the format as well.  We are
rolling a "new release" out soon, we'll fix that...

Rating: 4
speed December 06, 2002
Reviewer: A Reader from Germany
speed is OK

Rating: 3
December 06, 2002
Reviewer: Sagi from India
Hi Tom,

The speed looks ok to me.

How do you record the hits to this site. I mean you use and tools, if so Can you
let us know which one is that.

Regards,
Sagi

Followup:
see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6690299776039
it is all internal to the framework (project marvel) that asktom is built on.
It is all builtin to the stuff that builds asktom.

Rating: 5
It is fine now (Sydney, Australia, Local time - 10PM) December 06, 2002
Reviewer: Tony
Thanks everybody.

Rating: 3
December 06, 2002
Reviewer: Phil from UK
Works fine from the UK

December 06, 2002 -- Thanks for the question regarding "Index use", version 8.1.7.1.0
You Asked
Hi Tom,
I am using a Oracle database ver 8.1.7.1.0
If I create a table
create table
abc(
a number,
b number,
c number,
d number
)
primary key abc_pk (a, b, c)

The in a query

select * from abc where b = 50;

And we know that Primary key is itself an index.
So will the above query use the index??

In which Oracle version the column sequence in an index does not matter for
index use?

Could you help me in this ?

Thanks and Regards,
Jitendra
and we said...
It could use the index in any release -- in 8i and before, it could use the
index as a "skinny" table.  Consider:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t
  2  ( a number, b number, c number, d number,
  3    constraint t_pk primary key(a,b,c) )     pctfree 80 pctused 20;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t select mod(rownum,3), rownum,
rownum, rownum from all_objects;

23653 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table t compute statistics
  2  for table
  3  for columns a, b size 254
  4  /

Table analyzed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t where b = 50;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=48 Card=1 Bytes=19)
   1    0   TABLE ACCESS (FULL) OF 'T' (Cost=48 Card=1 Bytes=19)

ops$tkyte@ORA817DEV.US.ORACLE.COM> select /*+ index( t t_pk ) */ * from t where
b = 50;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=3 Card=1 Bytes=19)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=3 Card=1 Bytes=19)
   2    1     INDEX (FULL SCAN) OF 'T_PK' (UNIQUE) (Cost=26 Card=1)

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off

In 9i, it gets really interesting as it can "skip scan" the index as well:

ops$tkyte@ORA920.US.ORACLE.COM> create table t
  2  ( a number, b number, c number, d number,
  3    constraint t_pk primary key(a,b,c) )     pctfree 80 pctused 20;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into t select mod(rownum,3), rownum,
rownum, rownum from all_objects;

29069 rows created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> analyze table t compute statistics
  2  for table
  3  for columns a, b size 254
  4  /

Table analyzed.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> set autotrace traceonly explain
ops$tkyte@ORA920.US.ORACLE.COM> select * from t where b = 50;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=FIRST_ROWS (Cost=2 Card=1 Bytes=20)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=2 Card=1 Bytes=20)
   2    1     INDEX (SKIP SCAN) OF 'T_PK' (UNIQUE) (Cost=4 Card=1)

search this site for

"skip scan"

for details on that.
December 06, 2002 -- Thanks for the question regarding "Scalibilty... & Procedures issue...", version 8
You Asked
Hi Tom,

Could u pls make the following clear.

1. Oracle claims that it is a very scalable db. How can the same be verified by
an end-user like me?  I have used both SQL Server & Ora. And, I do not feel the
difference....

2. How can it be concluded that Stored Prcoedures are faster than simple PL/SQL
block of code ?

3. Is efficiency / fastness an advantage of using Packages. If yes, how can that
be verified by the end user ?

Rgds....
and we said...
1) do this.

update a row in table ( in sql server, since they despise transactions -- hate
them -- you'll need to begin tran ).

in another session, select count(*) from that table.

Tell me -- which one works faster -- allows for more concurrent users?  Oracle
or Sqlserver in this case?  Have you ever gotten a deadlock while running a
QUERY in sqlserver -- never in Oracle (reads don't block writes, writes don't
block reads -- a write only blocks a write of the SAME exact row)

Now, put a 500 gigabyte data warehouse table (single table) into Oracle.  You
would of course use partitioning to make it managable and materialized views to
make queries against it super fast -- transparently.  Now, put the same table in
sqlserver -- oh wait, no real partitioning, no materialized views -- guess
you'll have to code it yourself.

and so on.  do you do big things?   you'll find Oracle does big things.

2) who said they were?  but really - you don't have to compile a stored
procedure to run it.  You have to compile an anonymous block in order to run it.
 If we find it in the shared pool (a concept that SQL server doesn't have by the
way -- one that leads towards our scalability -- awesome reuse of other peoples
work to avoid having to compile and parse stuff).

The benefits of stored procedures over anonymous blocks is not one of speed but
one of management, automatic dependency tracking, less code in the client,
tuning (i can tune a stored procedure in the database easily -- if you stuff it
into the client code -- I have to rip it out -- tune it -- put it back in, what
a pain)

3) again, it is not SPEED but MANAGEMENT.  See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:286816015990
December 06, 2002 Sandy -- Thanks for the question regarding "How to view long raw data", version 8.1.7
You Asked
Hi,

I have a table with a column whose data type is LONG RAW, which has text data
stored in it. I want to view the data in this column. The database I am using is
Oracle 8i (Release 8.1.7.1.0), and I use sqlplus from the command prompt. Please
let me know if you have any pointers.

Thanks,
Sandy
and we said...
You can see the first 32k this way:

declare
procedure p ( p_str in varchar2 )
is
   l_str   long := p_str;
begin
   loop
      exit when l_str is null;
      dbms_output.put_line( substr( l_str, 1, 250 ) );
      l_str := substr( l_str, 251 );
   end loop;
end;
begin
   for x in ( select * from t )
   loop
       p( utl_raw.cast_to_varchar2( x.long_raw_column ) );
   end loop;
end;
/

Alternatively, you can:

create global temporary table t ( x blob ) on commit delete rows;

and then to see one of the long raws you would:

SQL> insert into t select to_lob( LONG_RAW_COLUMN ) from t2 where ....;
SQL> select utl_raw.cast_to_varchar2(dbms_lob.substr( x, 4000, 1 )) from t;

to see any 4000 character chunk of it.
December 06, 2002 Ajeet -- Thanks for the question regarding "Pivot Tables from Expoert one on one.", version 8.1.7
You Asked
Tom,
I have read through the Analytic function and Pivot tables in your Book.I was
trying to apply those things in following problem.
I have a table having 34 columns and I want to select each column in a seperate
row having value for only that colum.

For example if I have a table
TEMP(object_id,object_name)
and values
1,'TEST'
2, 'TEST2'
3,'TEST4'
etc..
Then I want to select them as following

1
'TEST'
2
'TEST2'
3
'TEST4'

I can use following

Select object_id from TEMP
UNION ALL
Select object_nam from TEMP

but only if object_id and object_name are of same data type.Which is not in my
case.

I tried following also -

Select object_id||chr(10)||object_name from TEMP but it does not solve my
problem.

My Question is - can I use Pivot table features which you have given in your
book.I tried but I was not able to do.
Can you help me .

Thanks
Ajeet
and we said...
something like this perhaps:

ops$tkyte@ORA920> create or replace package demo_pkg
  2  as
  3      type rc is ref cursor;
  4
  5      procedure down( p_cursor in out rc,
  6                      p_tname  in varchar2,
  7                      p_where  in varchar2 := null );
  8  end;
  9  /

Package created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> create or replace package body demo_pkg
  2  as
  3
  4  procedure down( p_cursor in out rc, p_tname in varchar2, p_where in
varchar2 )
  5  as
  6      l_cnt    number := 0;
  7      l_stmt   long;
  8      l_dc1   long := 'decode( x$x.X$R';
  9      l_dc2   long := 'decode( x$x.X$R';
 10  begin
 11      for x in ( select column_name , column_id
 12                   from user_tab_columns
 13                  where table_name = upper(p_tname)
 14                  order by column_id )
 15      loop
 16          l_cnt := l_cnt+1;
 17          l_dc1 := l_dc1 || ', ' || x.column_id || ', ''' || x.column_name ||
'''';
 18          l_dc2 := l_dc2 || ', ' || x.column_id || ', to_char( ' ||
x.column_name || ')';
 19      end loop;
 20      l_stmt := 'select rr, ' || l_dc1 || ') col_name, ' || l_dc2 || ')
col_value
 21                   from (select rownum rr, a.* from ' || p_tname || ' a ' ||
 22                                           p_where || ' ), (select rownum x$r
 23                                              from all_objects
 24                                             where rownum <= ' || l_cnt || ')
x$x ' ||
 25                  'order by rr';
 26
 27      p(l_stmt);
 28      execute immediate 'alter session set cursor_sharing=force';
 29      open p_cursor for l_stmt;
 30      execute immediate 'alter session set cursor_sharing=exact';
 31  end;
 32
 33  end;
 34  /

Package body created.

ops$tkyte@ORA920> drop table dept;

Table dropped.

ops$tkyte@ORA920> create table dept as select * from scott.dept;

Table created.

ops$tkyte@ORA920> variable x refcursor
ops$tkyte@ORA920>
ops$tkyte@ORA920> exec demo_pkg.down( :x, 'dept', 'where rownum < 3' )

PL/SQL procedure successfully completed.

ops$tkyte@ORA920> print x

        RR COL_NAME                         COL_VALUE
---------- -------------------------------- ---------------------------------
         1 DEPTNO                           10
         1 DNAME                            ACCOUNTING
         1 LOC                              NEW YORK
         2 DEPTNO                           20
         2 DNAME                            RESEARCH
         2 LOC                              DALLAS

6 rows selected.

ops$tkyte@ORA920> exec demo_pkg.down( :x, 'dept', '' )

PL/SQL procedure successfully completed.

ops$tkyte@ORA920> print x

        RR COL_NAME                         COL_VALUE
---------- -------------------------------- ---------------------------------
         1 DEPTNO                           10
         1 DNAME                            ACCOUNTING
         1 LOC                              NEW YORK
         2 DEPTNO                           20
         2 DNAME                            RESEARCH
         2 LOC                              DALLAS
         3 DEPTNO                           30
         3 DNAME                            SALES
         3 LOC                              CHICAGO
         4 DEPTNO                           40
         4 DNAME                            OPERATIONS
         4 LOC                              BOSTON

12 rows selected.

ops$tkyte@ORA920>
Review & Followup
Rating: 3
Would this suffice? December 06, 2002
Reviewer: Ik from India
Haven't tried it out...

SELECT
    col
FROM
(Select
    TO_CHAR(object_id) col,
    ROW_NUMBER() OVER (ORDER BY object_id) rnk,
    'A' sort_flg
from TEMP
UNION ALL
Select
    object_name col,
    ROW_NUMBER() OVER (ORDER BY object_id) rnk,
    'B' sort_flg
from TEMP)
ORDER BY
    rnk,
    sort_flg

Rating: 5
Excellent. December 06, 2002
Reviewer: Ajeet from OH,USA
Tom - It worked without a problem.Now there are a number of other requirements I
have but all of them can be derived using this script. which I will try myself.
Please write another Book for Programmers where you can give some insight of
programming using SQL and PL/SQL.
How you come up with such excellent logic--I worked for 6 hours and i was not
able to get it right.
Thanks so much for your help.

December 06, 2002 Xing -- Thanks for the question regarding "How to call Label Security functions in procedures defined in a package?", version 9.2.0
You Asked
In my login package, I want to dynamically define the authorizations and
privileges of users (or set the user's data label when the user logs in). The
problem occurred when I execute functions defined in the SA_USER_ADMIN. For
example, in my pl/sql package I call the following procedures:

EXECUTE IMMEDIATE 'EXECUTE
SA_USER_ADMIN.DROP_USER_ACCESS(''LC_SAMPLE'',''Test_Label'')';
EXECUTE IMMEDIATE 'EXECUTE
SA_USER_ADMIN.SET_USER_LABELS(''LC_SAMPLE'',''Test_Label'',''Ext:A:Bio'')';

It compiles fine, however, when I run my login package, the following error
occurred:

Invalid SQL statement...

I know that we have to run procedures SA_USER_ADMIN.DROP_USER_ACCESS() and
SA_USER_ADMIN.SET_USER_LABELS() as sysdba. Then how I can run my package in
which it calls SA_USER_ADMIN.DROP_USER_ACCESS as sysdba?

Thank you very much!

Xing Li
and we said...
execute is a sqlplus'ism.  it is shorthand for begin ... end;

declare
   l_x  varchar2(30) default 'LC_SAMPLE';
   l_y  varchar2(30) default 'Test_Label';
begin

...

execute immediate
'begin
    SA_USER_ADMIN.DROP_USER_ACCESS( :x, :y );
 end;'  using l_x, l_y;

....

Remember -- ALWAYS USE BIND VARIABLES in order to avoid killing the shared pool.
Review & Followup
Rating: 3
Follow up question December 05, 2002
Reviewer: XingLi from Fremont, CA, USA
Thanks Tom for your timely response. I am rephrasing the second part of my
original question here, i.e., how can I run a pl/sql program in which there are
procedures that have to be run as SYSDBA as a NORMAL USER? Specifically, I need
to run the login package as a normal user. However, some procedures in the login
package need to run as SYSDBA. How can I solve this problem?

Thanks again.

XingLi

Followup:
what could you possibly be doing that needs to be done as "sysdba"??

explain please.

Rating: 3
Followups December 06, 2002
Reviewer: XingLi from Fremon, CA, USA
How can I run a pl/sql program in which there are
procedures that have to be run as SYSDBA as a NORMAL USER? Specifically, I need
to run the login package as a normal user. However, some procedures in the login
package need to run as SYSDBA. How can I solve this problem?
The reason for doing this is the following: In any application, any user has to
go through login package first in order to anything with the database. In the
login package we assign the roles to the user and also assign the data label to
the user for label security. The problem is here. Label security package needs
to be run as SYSDBA. So how can I assign the data label dynamically to each user
whenever they use the application? Thank you very much.

Followup:
You cannot -- you have to connect "AS SYSDBA"

"AS SYSDBA" is special, "AS SYSDBA" is for maintenance.  You cannot turn on "AS
SYSDBA", you have to connect "AS SYSDBA"

December 06, 2002 Vinod -- Thanks for the question regarding "Rounding error in Oracle 7.3.4", version 2.2.4
You Asked
Tom,

I ran the test you suggested and it makes it clear.

I ran it for 3 different values of x (.215, .645, .195) and the results are
below.

Connected to ORACLE as user: /

0.21500000357627869 .215000003576279, .22
n = 1
0.64499998092651367 .644999980926514, .64
n = 0
0.19499999284744263 .194999992847443, .19
n = 0

thanks again for you help,

..Vinod

---
Tom,

I encountered the following problem using round in Pro*C

I had the following statement in my program.

EXEC SQL
UPDATE a_stfreightcosts
SET   ...
      ,freightvolumecost = round(:ST.freightvolumecost,2)
      ...
WHERE CURRENT OF cur_freight;

freightvolumecost is number(11,2) and ST.freightvolumecost is float.

This (and not using round at all) did not work for values like 1.215, 2.315,
4.355 ie values ending in 5 in the third decimal place, these numbers got
rounded to 1.21, 2.31, 4.35. It did work for ALL other values. I could not
replicate this in sql*plus.

I spoke to someone here and she said she had faced the similar problem and had
seen the 'dirty' solution below. I tried it and it worked.

EXEC SQL
UPDATE a_stfreightcosts
SET   ...
      ,freightvolumecost = round(:ST.freightvolumecost + 0.00001,2)
      ...
WHERE CURRENT OF cur_freight;

I was just wondering if you had seen something like this before and if there is
a better solution, I couldn't find anything searching the prior questions.

Appreciate your contribution!!

Vinod
and we said...
I hate floats!!!  I'll publish this one so others can see it.

can you run this on your system:

#include "unistd.h"
#include <stdio.h>

static char *   USERID = "/";

#define SQLCA_INIT
EXEC SQL INCLUDE sqlca;

static void sqlerror_hard()
{
    EXEC SQL WHENEVER SQLERROR CONTINUE;
    printf("\nORACLE error detected:");
    printf("\n% .70s \n", sqlca.sqlerrm.sqlerrmc);
    EXEC SQL ROLLBACK WORK RELEASE;
    exit(1);
}

main( argc, argv )
int argc;
char *  argv[];
{
EXEC SQL BEGIN DECLARE SECTION;
float     x = 1.215;
varchar   y1[48];
varchar   y2[48];
varchar   oracleid[31];
int       n;
EXEC SQL END DECLARE SECTION;

    strcpy( oracleid.arr, USERID );
    oracleid.len = strlen(oracleid.arr);

    EXEC SQL WHENEVER SQLERROR DO sqlerror_hard();
    EXEC SQL CONNECT :oracleid;
    printf("\nConnected to ORACLE as user: %s\n\n", oracleid.arr);

    exec sql select :x, round(:x,2) into :y1, :y2 from dual;
    printf( "%10.50g %.*s, %.*s\n", x, y1.len, y1.arr, y2.len, y2.arr );

    exec sql select count(*) into :n from dual
              where round(:x,2) = 1.22;

    printf( "n = %d\n", n );

    /* Disconnect from ORACLE. */
    /* EXEC SQL COMMIT WORK RELEASE; */
    exit(0);
}

when I do:

$ ./t

Connected to ORACLE as user: /

1.21500003337860107421875 1.2150000333786, 1.22
n = 1

is the output.  bear in mind, FLOATS are notorious.  They are imperfect
representations.  when we convert floats to numbers -- strange things can happen
in the conversion.  float arithmetic is very fragile.  look at the resulting
number from the float conversion above.

you might consider using a STRING for all interactions with floats.  strtod and
sprintf to convert on the way out and the way in.
December 06, 2002 Pushparaj -- Thanks for the question regarding "BLOB column creates an UNIQUE index", version 8.1.7
You Asked
Tom,

When I create a table with BLOB column, it automatically creates
an UNIQUE index. Can you please explain me on this.

SQL > CREATE TABLE BLOB_DEMO
     ( COL1 NUMBER, COL2 BLOB DEFAULT EMPTY_BLOB() )
/
Table created.

SQL> SELECT INDEX_NAME, UNIQUENESS FROM USER_INDEXES
     WHERE TABLE_NAME='BLOB_DEMO';

INDEX_NAME                     UNIQUENES
------------------------------ ---------
SYS_IL0000395823C00002$$       UNIQUE

SQL > SELECT column_name FROM user_ind_columns
      WHERE index_name = 'SYS_IL0000395823C00002$$'
/
no rows selected

Can you please the purpose of this system created unique index, with no column
in the user_ind_columns table.

Thanks
Pushparaj
and we said...
a blob is a bunch of chunks.  The chunks are like a linked list - to be chained
together to make your entire lob.

The index -- that is the physical implementation of the linked list.  For every
lob locator (say "X") you have there is a list of:

"X"  chunk1  dba (data block address)
"X"  chunk2  dba
"X"  chunk3  dba
....

that is the data in that index - a bunch of (lob locators, chunk id) -- the
primary key -- and the dba pointing to where that lob chunk is.
December 06, 2002 amit -- Thanks for the question regarding "Meaning of sql_address column", version 8.1.7.3
You Asked
Hi,

I know that oracle uses a hash function to hash each sql statement into library
cache.
In that respect sql_hash_value does make sense , but what does sql_address
column stand for?
Is it required to locate a sql  in a hash chain since multiple statements could
hash into same hash chain.

thanks
amit
and we said...
The sql_address is the actual "C pointer" to where that sql is.  It is generally
used with the sql_hash to uniquely identify a query in the shared pool (although
the address in itself is sufficient).

as you noted, the hash for "select * from t" will be the same for everyone
submitting "select * from t" -- but there might be many many select * from t's
in the shared pool since:

o scott owns a table t, select * from t is really select * from SCOTT.t for him
o tkyte sees a public synonym T that points to MARY.T
o mary owns a table t

so each query would be "different" in the shared pool -- but the hash would be
the same.  the sql address would differentiate them.
December 07, 2002 Fan -- Thanks for the question regarding "'df -k' won't show the temp file", version 8i
You Asked
Tom,

Would you please to explain:

'df -k' won't show the allocated datafile for temporary tablespace (locally
managed).

Thanks,
and we said...
thats because we are using a quickie OS call that doesn't really allocate the
file until you write to it.  Its the way the OS handles it.

With "permanent files", we allocate and then write each block.  With tempfiles
we just allocate.

It is an operating system nuance, nothing we have control over.

For example (single user system, i'm the only one on)

ops$tkyte@ORA817DEV.US.ORACLE.COM> !df -k /tmp
Filesystem            kbytes    used   avail capacity  Mounted on
/dev/dsk/c0t1d0s0     385615   20585  326469     6%    /tmp

ops$tkyte@ORA817DEV.US.ORACLE.COM> create temporary tablespace ttt tempfile
'/tmp/foo.dbf' size 100m;
Tablespace created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> !df -k /tmp
Filesystem            kbytes    used   avail capacity  Mounted on
/dev/dsk/c0t1d0s0     385615   20585  326469     6%    /tmp

no space increase yet...

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter user ops$tkyte temporary tablespace
ttt;
User altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set sort_area_size=32767;
Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly;

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from all_objects order by
object_name, object_id, created, last_ddl_time;

16907 rows selected.

Statistics
----------------------------------------------------------
          7  recursive calls
         20  db block gets
      77510  consistent gets
        254  physical reads
          0  redo size
    2038315  bytes sent via SQL*Net to client
     125527  bytes received via SQL*Net from client
       1129  SQL*Net roundtrips to/from client
          0  sorts (memory)
          1  sorts (disk)
      16907  rows processed

ops$tkyte@ORA817DEV.US.ORACLE.COM> !df -k /tmp
Filesystem            kbytes    used   avail capacity  Mounted on
/dev/dsk/c0t1d0s0     385615   22609  324445     7%    /tmp

no my space usage increased some, this was the tempfile space we actually used
during my sort.
Review & Followup
Rating: 4
more questions October 09, 2001
Reviewer: Fan from nj
1) If the temp files have not been recognized by OS, can the 'false' space
information introduce allocating other files into the same file system? For
example, if /dbtemp has been allocated for temp datafile (taken 98%), but the
usage (df -k) doesn't show that the space has been taken by Oracle. Is it
possible that OS or Oracle allows the space to be used by other files?
2) It is another performance improvement for locally managed tablespace?

Rating: 5
Options October 09, 2001
Reviewer: Connor from Scotland
You can get into a knot sometimes where other files use the space (that would
have been there for the temp file).  If you are concerned, typically copying the
file somewhere and back (whilst the db isn't using it obviously) can fix the
problem.  Alternately (I haven't tried this), create it as a normal datafile,
drop the tablespace, then create as a tempfile with reuse.

Followup:
thanks for the followup Connor.

Rating: 3
Is there some way to handle this online? December 06, 2002
Reviewer: Bill Rachal from New Orleans, LA USA
Thanks, as always, for the outstanding information, Tom.  I like the suggestions
that Connor has made, but is there some way to guarantee that the files get
their allocated amount of space without shutting down the database?  As I'm
reading things, I should shutdown the database, copy the new tempfile twice, and
then startup everytime I resize temp.

Followup:
Or -- just allocate them on a volume with sufficient free space and let them
allocate themselves as they see fit???  I just let them allocate it as they need
it.

Rating: 4
Solaris /tmp on filesystem? December 06, 2002
Reviewer: Jeremy Smith from Salt Lake City, UT
From your previous questions it looks like most of your examples you do on
Solaris...is this one?  (it looks like it because of the device names).

It's interesting that you (or your dba) uses an actual disk for your /tmp
filesystem instead of the Solaris standard of /tmp as being a special filesystem
that uses ram and/or swap space as necessary for storing data.

For example:
$df -k
Filesystem          kbytes    used   avail capacity  Mounted on
swap                  870552  708408  162144    82%    /tmp

Do you usually create your temporary tablespaces in /tmp?  Is there a
management/performance reason that you use a real filesystem /tmp instead of
tmpfs?  The one reason I've seen is that it prevents a file that goes nuts
writing to /tmp from reducing the swap space you have to page out to.  Are there
other reasons?  Do you back up your /tmp?  Is it for clustering (create a temp
tablespace that everyone can access)?  If so, why not just use a different
filesystem?  The _nice_ thing about the way Solaris uses swap for /tmp is that
if you do have oodles of ram, it doesn't ever bother to flush your writes to
disk, which helps performance.

Is having /tmp on disk a practice you'd recommend for all Solaris/Oracle
machines?

Followup:
solaris and linux.. but this was solaris and this WAS A TEST!!

I do not put Oracle files in /tmp, no -- i was demonstrating how sparse files
work only.

Rating: 5
It's called a sparse file December 06, 2002
Reviewer: Angelo from Albany, NY
Here's what I do:

Make sure that the temp tablespace in question is the default temp tablespace
for the current user.

CREATE GLOBAL TEMPORARY TABLE t ( c1 char(2000) );

begin
while (1 = 1) loop
insert into t (c1) values (' ');
end loop;
end;
/

drop table t;

When you get the space error, the tablespace as been fully allocated.

Rating: 5
Force physical allocation of space for tempfiles on unix January 09, 2003
Reviewer: Jonathan Reinhart from Walnut, CA
In order to guarantee that space allocated for temporary
tablespaces in unix is actually allocated  --
 1. FIRST CREATE THE FILE ON THE OPERATION SYSTEM, then
 2. ALLOCATE THE NEWLY CREATED FILE TO THE TEMP TBLSP, e.g.

$ dd if=/dev/zero of=/temp/oradata/prod/temp02.dbf
   bs=1000k count=1000

 create temporary tablespace temp2
 tempfile '/temp/oradata/prod/temp02.dbf' REUSE
 ...

This way you can be sure that the file won't run out of space i.e. 'allocated
space' will not be allocated somewhere else.
Test the procedure with du and ls.

Hope I helped..

December 07, 2002 Michael -- Thanks for the question regarding "Query tables: joining or loop or join ref_cursor?", version 8.1.7
You Asked
Hi Tom,

Here is my situation:

We are working a web-based project with Java and Oracle 8.1.7. I have a question
regarding table querying. My intention is to create a (or more) ref_cursor of a
simple query for each table instead of using joining. For example, we use
functions to returing ref_cursor for each of the following queries:

SELECT product_id, product_name
FROM product
WHERE product_id = v_product_id;

SELECT product_id, product_detail_size
FROM product_details
WHERE product_id = v_product_id;

There are at least three ways to process the results of these two tables:

1. Java first calls first ref_cursor to get the product_name and use the second
cursor to get product detail information.

2. Join these two ref_cursors as one ref_cursor.

3. Return a cursor of:

SELECT a.product_id, a.product_name,b.product_detail_size
    FROM product a, product_details b
    WHERE a.product_id = v_product_id
      AND a.product_id = b.product_id;

My questions: is 2 possible? which one is better?

Many thanks,
and we said...
Glad you asked before you did it totally wrong.

Databases were born to join.  BORN to JOIN.  It is what we do best.

The only answer that makes sense is #3.

#2 isn't possible.
#1 is the worst thing you can do.

Let the database do AS MUCH WORK AS POSSIBLE.

Never do a join yourself -- let the database do the join (please!!!)
Review & Followup
Rating: 3
December 06, 2002
Reviewer: A reader

Rating: 1
December 06, 2002
Reviewer: A reader

Followup:
always like that -- "i don't like your answer" sort of stuff.  thanks for
backing it up with solid technical reasoning like you did!

Rating: 4
Good Fundamental Philosophical Teaching....... Valuable.... December 06, 2002
Reviewer: Robert from Memphis, USA
Tom,

We just trust you are leading us in the right direction! :^)

Thanks,

Robert.

December 07, 2002 Sing -- Thanks for the question regarding "Should next extent be equal to initial for a table", version 8.0.6
You Asked
Tom,

I have a table Table_A  with the following columns:

      Col1   VARCHAR2(200),
      Col2   VARCHAR2(50),
      Col3   NUMBER(38),
      Col4   NUMBER(38),
      Col5   NUMBER(38),
      Col6   DATE,
      Col7   DATE,
      Col8   FLOAT,
      Col9   FLOAT

Currently, data is loaded into Table_A every 5 minutes with 38
records through a script created from sqlloader throughout the
day. It is 24X7 test facility. But when the facility goes into
production, 10,000 records are loaded once everyday through the
script from sqlloader. Data is always inserted into this table
and never updated. Data deletion cycle start after one year of
inserting data into the table.

I am trying to figure out the right storage parameters for my
table if 10,000 records are loaded at once into the table.

These are the storage parameter I came up with:

pctincrease 0
pctfree 5
pctused 80
minextents 1
maxextents unlimited

The storage parameters I am unable to come up with are the
initial and next extents. How do I determine these parameters?

Also, if the loaded increases from 10,000 records to 90,000
records what would be my right storage parameters for my table?

Thanks very much,

Sing
and we said...
Its mostly a matter of opinion.  I myself prefer to have
uniformly sized extents (all extents = same size in a given
tablespace).  Makes it so much easier to manage.  The myth that
a table should have as few extents as possible is that -- a
myth.  Many extents affect 2 operations:

o ddl on the table.  a table with 1,000 extents will take a
while to "drop"

o certain parallel operations on a highly active table.  Again,
it takes lots of extents to see this.

For full scans and (especially) indexed reads -- 1, 10, 100,
1000 -- its OK.

So, that aside -- Your row width looks to be about ~300 bytes
give or take.  10,000 rows is 3,000,000.  A months worth of data
is about 90meg give or take.  Try an extent size of about 100meg
(you want the extents to be multiples of your multi block read
count -- say about 64k typically so make it be a multiple of
that).  That should give you 12 extents at the end of the year.

You might consider partitions if you are going to age data out
of here at the end of the year.  It would make it really easy to
get rid of a months worth of data and slide in the new month.
You could convert the oldest partition to a "table" and archive
it easily.
Review & Followup
Rating: 5
June 06, 2001
Reviewer: Tony Sangvacharakul from Kansas City, KS

Rating: 3
July 25, 2001
Reviewer: Pichaimani Balasubramanian from San Jose, CA USA

Rating: 4
If not using partitioning, how to size pctfree and pctused December 06, 2002
Reviewer: Beth from USA
If you decide not to use partitioned tables, how would you size pctfree and
pctused, considering that you are constantly inserting, and only deleting once a
year.

What other purging/archiving options do you have?

Followup:
see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6407993912330
CTAS is often an excellent approach.  create table as select rows_to_keep --
rather then deleting a huge percentage of the table -- can be very efficient.

as for pctfree/pctused.  The delete doesn't affect the pctfree at all (insert +
update activity determines that).  If you never update -- pctfree=0.

as for pctused -- if you want the space to be reused ASAP (eg: no updates)--
pctused=100-pctfree

December 07, 2002 lisa -- Thanks for the question regarding "Problem with buffer overflow", version oracle 7.3.4
You Asked
I run a script from NT using plus80.exe, in this script it combines several
steps including procedures, sqls. After it run about 7-8 min
I got this error ORA-20000 ORU-10027 buffer overflow, limit of 1000000 bytes,
and then the step aborted.
Could you explain what is this overflow, and how to correct ?
and we said...
You are generating more then 1,000,000 characters of output via
dbms_output.put_line calls.  That is the maximum.

You can:

set serveroutput off

and then run your scripts but you will not get any dbms_output output.  There is
no way to up this limit.

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:146412348066
for a different implementation of dbms_output that doesn't have this limit.

followup to comment

grep your scripts for "serveroutput" and check your code for dbms_output.enable.

Someone must be turning it back on on you.
Review & Followup
Rating: 4
Problem with buffer overflow July 03, 2001
Reviewer: A reader from USA
Very useful info.
But after I "set the serveroutput off" at the beginning, the error still there.
Then I tried to
go into the code to physically remove the sys.dbms_output.PUT_LINE statements
then I have a complete run.
Does this make sense to you ?

Lisa

Rating: 4
line length Overflow July 24, 2002
Reviewer: Rahul from India~
Hi Tom

BEGIN SP_COPYAUDITDATA_V1; END;

*
ERROR at line 1:
ORA-20000: ORU-10028: line length overflow, limit of 255 bytes per line
ORA-06512: at "SYS.DBMS_OUTPUT", line 99
ORA-06512: at "SYS.DBMS_OUTPUT", line 65
ORA-06512: at "FPIMLVER103.SP_COPYAUDITDATA_V1", line 112
ORA-06512: at line 1

this error is coming when I try to print something in variable but value is vaey
large...
how can we overcome from this prob ?

Rating: 5
ORU-10027, December 07, 2002
Reviewer: Arabinda from Bangalore,India
This is a very useful info.I am getting this error on our application.Recently
this has started coming.By running the appl. (in Forms 6i) we are not setting
serveroutput.The packages have dbms_output to print debugging information.I am
running my application in Forms6i,and after some time I start getting the
problem.I close the session and start another ,after some time this repeats.If I
run a big process , it comes immediately.Does it mean,inside all the
procedures/triggers code some where dbms_output.enable is there and creating the
trouble?

Followup:
yes, someone has a dbms_output.enable in there somewhere.

Turn on SQL_TRACE and trace a session -- you might find it there.

December 07, 2002 jamie -- Thanks for the question regarding "row chaining", version 8.1.7
You Asked
Tom,
I have a question about row chaining. I am still new to this so
I hope I'm not wasting your time here by asking a silly question.
First I am assumming that row chaing is a row of data stored in multiple
adjacent blocks.
So if this is the case then I have a table with columns a,b,c
and insert colA('this'),colB('is'),colC('row chaining').
Ok so this could cause 2 blocks with block 1 having 'this is ro' and block 2
with 'w chaining'. Querying a chained row is supposed to be less efficent
because we're reading multiple blocks. This leads me to wondering if I can query
this row of information and do it in a way that would avoid the overhead. I'm
thinking if I query select row1(colABC) from tab where colA = 'this' then I am
wanting colC so I have to look in an extra block. if I query select row1(colABC)
from tab where colC = 'this' with colC being in two blocks and used in the where
clause would this be even slower again. Last if I query select colA,colB(of
row1) from tab where ColB = 'is' then I would be avoiding colC read so row
chaining would not be a factor here.
Could you please clarify this for me because at this point I'm getting things
somewhat confused and am in need of a logical explanation.

Thanks in advance,
and we said...
row chaining is a row stored in multiple blocks.  the blocks might be miles
apart from eachother.

A row such as:

insert colA('this'),colB('is'),colC('row chaining').

would never chain.  We won't chain a row that fits on a block.  We might
"MIGRATE" it after an update -- but during an insert, a row will never ever
chain if it fits on a block (and that would fit on a block in any oracle
instance - too small).

Actually -- I just covered this in detail in my latest print article in Oracle
Magazine -- you can read it online here:
http://otn.oracle.com/oramag/oracle/02-nov/o62asktom.html
And if you goto www.oracle.com/oramag, you can sign up for a free subscription
as well.
Review & Followup
Rating: 5
December 07, 2002
Reviewer: A reader from nfld,canada
Tom,
Thanks for the link its very informative. As far as the row I used I know it's
to small to chain I was just trying to keep things brief. Besides my main
concern is understanding how different types of queries on a chained row are
affected.

Thanks!

December 07, 2002 Budhi -- Thanks for the question regarding "maximum number of joins", version 8.1.7
You Asked
Hi Tom,

What is the maximum number of table joins or tables we can have in a SQL
statement?

Thanks

Budhi
and we said...
infinite.

I've done over 300 as a test (takes a bit of time to PARSE the query ;)

There is no documented limit, only practical limits (eg: a 500 table join -- you
are going waaayyyy beyond what I call practical.  A 20 table join -- no worries,
well, except that I cannot imagine the data model that would make me do that!)
December 08, 2002 Budhi -- Thanks for the question regarding "tuning a batch program", version 8.1.7
You Asked
Hi Tom,

In our project, for generating reports, we are planning to run a batch process
every night, which will populate some report tables.

We are using INSERT statements in the batch using /*+ APPEND */ hint.

Could you please tell me what is the equivalent hint or syntax when we are using
UPDATE statements. What we are planning is , like /*+ APPEND */ hint prevent the
logging and writing to rollback segments during inserts, won't it be fast if we
would be able to prevent the same while update?

Would, creating the report tables as NOLOGGING help?

Now all the report tables are created in a huge tablespace REPORT_DATA. Is it
better to set the NOLOGGING option of this tablespace itself to TRUE?

One last doubt is in our batch we have Updates which resembles following
syntax:-

UPDATE EMP
SET SAL = SAL + (Select sum(Fn_computed_sal(empno))
                From   sal_desc
                Where  sal_desc.deptno = emp.deptno);

Here fn_computed_sal is a function and sal_desc any other table.

I am unable to call the above statement in PL/SQL and is presently using EXECUTE
IMMEDIATE.

Could you please reason this?

Thanks

Budhi
and we said...
If you insert into a table with indexes, there will still be tons of rollback
and redo generated.

There is no corresponding "feature" for updates -- there cannot be.  An insert
append can bypass the generating of UNDO (rollback) for the newly inserted data
(but not the indexes it modifies and in an archive log mode database, it doesn't
skip the REDO generation at all unless the table is in nologging mode).  An
insert can do this because it writes ABOVE the high water mark for the table.
If we need to undo the insert it is simple, just don't advance the HWM!!  it is
as if the insert didn't take place (we have to UNDO the indexes of course
though!)

An update does work just above the hwm -- in fact, it works pretty much UNDER
the hwm.  Therefore, when we need to rollback -- we NEED the rollback.

NOLOGGING WILL HAVE zero effect on this with updates.

You can:

  update emp
     set set = (select EMP.SAL + sum(fn.....)
                  from sal_desc
                 where ....
Review & Followup
Rating: 4
December 07, 2002
Reviewer: Budhi
Hi Tom,

So will the following sequence halp:-

Alter Index ik_1 unusable;
Alter Session set skip_unusable_indexes=TRUE;

Do the bulk insert using /*+ APPEND */

Alter Index ik_1 rebuild;
Alter Session set skip_unusable_indexes=FALSE;

Do the updates.

Thanks for that response.

Followup:
If rebuilding the indexes takes less time then maintaining -- perhaps. (if not,
then no, it wouldn't)

Depends on what you are doing -- if the tables are empty (eg: you just truncated
them) then yes.

Rebuild in parallel if they are big.
Rebuild with nologging regardless.

Also, postpone UNTIL AFTER the updates -- all indexes you can. You probably have
lots of indexes that are there just for the reports -- that aren't used by the
update (but have to be maintained).  If you can defer that maintanence....

Rating: 4
December 07, 2002
Reviewer: Allan
Hi Tom,

I did n't understand  that

  update emp
     set set = (select EMP.SAL + sum(fn.....)
                  from sal_desc
                 where ....
statement.

think u mistyped SAL as SET right?

Followup:
yes,

set sal = ( select EMP.SAL + sum( .....

Rating: 4
December 07, 2002
Reviewer: A reader
Hi Tom,

What if i want to implement this PL/SQL

update emp
     set sal = nvl(EMP.SAL,0) +
                  nvl((select EMP.SAL + sum(fn.....)
                  from sal_desc
                 where ....),0

Becuse the select statement may return null and hence update my already existing
emp.sal vlaue to null.

Followup:
set sal = ( select nvl(emp.sal,0)+nvl(sum(fn...),0)

Rating: 3
Tuning Batch Program December 08, 2002
Reviewer: Ramehs from U.K
We are using Rule-Based optimizer and hints are good for only cost-based
optimizer. Can we use Insert into /*+ APPEND */ in rule-based environement
aswell?

Followup:
the append hint doesn't kick in the CBO -- tables need to be analyzed (or
optimizer goal set, etc).  consider:

ops$tkyte@ORA920.US.ORACLE.COM> create table t ( x int );
Table created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into t values ( 1 );
1 row created.

ops$tkyte@ORA920.US.ORACLE.COM> alter system flush shared_pool;
System altered.

ops$tkyte@ORA920.US.ORACLE.COM> delete from plan_table;
0 rows deleted.

ops$tkyte@ORA920.US.ORACLE.COM> explain plan set statement_id = 't1' for
  2  insert /*+ APPEND */ into t select * from t t1;

Explained.

ops$tkyte@ORA920.US.ORACLE.COM> select plan_table_output from
table(dbms_xplan.display('plan_table',null,'serial'));

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------
--------------------------------------------------

--------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost  |
--------------------------------------------------------------------
|   0 | INSERT STATEMENT     |             |       |       |       |
|   1 |  LOAD AS SELECT      |             |       |       |       |
|   2 |   TABLE ACCESS FULL  | T           |       |       |       |
--------------------------------------------------------------------

Note: rule based optimization

10 rows selected.

ops$tkyte@ORA920.US.ORACLE.COM> analyze table t compute statistics;
Table analyzed.

ops$tkyte@ORA920.US.ORACLE.COM> delete from plan_table;
3 rows deleted.

ops$tkyte@ORA920.US.ORACLE.COM> explain plan set statement_id = 't2' for
  2  insert /*+ APPEND */ into t select * from t t2;

Explained.

ops$tkyte@ORA920.US.ORACLE.COM> select plan_table_output from
table(dbms_xplan.display('plan_table',null,'serial'));

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------
--------------------------------------------------

--------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost  |
--------------------------------------------------------------------
|   0 | INSERT STATEMENT     |             |     1 |     2 |     2 |
|   1 |  LOAD AS SELECT      |             |       |       |       |
|   2 |   TABLE ACCESS FULL  | T           |     1 |     2 |     2 |
--------------------------------------------------------------------

Note: cpu costing is off

10 rows selected.

and you can see that since only the second one has COST/BYTES/ROWS -- only it
used the CBO to optimize the select.

December 08, 2002 Subhash -- Thanks for the question regarding "File creation using UTL_File", version 9.0.1
You Asked
Hi,

   Is it possible to create a file ,using UTL_File, in the client machine ?

Regards

Subhash
and we said...
Nope, not unless the clients file system is mounted and accessible read/write to
the Oracle softwares userid.

UTL_FILE, as all stored procedures, run in the dedicated/shared server on the
server -- they do not run in the client process at all.
December 08, 2002 Amar -- Thanks for the question regarding "Null ? ? ?", version 8.1.7
You Asked
The question looks silly but .....
What is Null and how it is handled/stored in ORACLE ?
and we said...
I've got a book you are dying to read

Specifically, your question is addressed in it:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c08schem.htm#2848
This book is the Oracle Concepts guide:
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/toc.htm
and I like to say that if you read it from cover to cover, retain just 10% of
it, you'll already know 90% more then most people about Oracle.
Review & Followup
Rating: 4
You are correct December 08, 2002
Reviewer: Ajeet from OH,USA
I am reading your book expert one on one since last 2 months and I feel that I
understand oracle a lot better than what I have learned in last 6 years.
In Your book too you have suggested to read concept mannual and it is  very
honest guideline you gave us.
Thanks .

December 08, 2002 Krishna -- Thanks for the question regarding "Staging Database", version 8.1.6.0.0
You Asked
Tom,
Can you please tell me what we are supposed to have on a staging database? What
kind of data and what kind of privileges are supposed to be given to the
developers? Is it a replication of Production data?. If so how would you
maintaint the security?. Can you in general tell me  about the process ( Dev ->
Staging -> Prod)?
Thanks
and we said...
what is your idea of "staging"

It sounds like "test" to me.  "staging" is generally a data warehousing term
when I've heard it used.

Test should be a mirror of production.  The developers hand off the "updates" to
the DBAs who apply it to what appears to be PROD to ensure that PROD can be
updated successfully.

Test would have the same privs as Prod -- really nice if test is a restore of
prod.
December 08, 2002 Jose Luis -- Thanks for the question regarding "Multiple joins vs. function based view columns", version 9.2.1
You Asked
Scenario:
Package/Procedure returning data from big table ("Costumers" ex.).

Seven or eight small tables referenced to costumer columns,
(country,state,city,costumer_group,...).

These tables data is language sensitive, it depends on web client language "in"
parameter.

In some case,(some language input),outer joins will be needed;

Wich is the best way to face this kind of query ?

Declaring cursor with :

1.- Several outer joins and filtering with language "in" parameter.
2.- Function based column: fn_country(costumer.id_country,p_inlang).
3.- Only costumer columns in cursor, open it and aply fn_country on every fetch
from ready and filtered costumer data.

Thanks in advance, your opinion is very important for us.
and we said...
#1 is the only really good choice and there is more then one way to code it.

#2 is an attempt to "out smart" the database -- to say "I can do an outer join
faster then you".  Well, you cannot.  Your fn_country() approach would cause SQL
to call PLSQL that is going to call SQL that will either find a row or not.
Much less efficient then just letting SQL do it's job.

#3 is even worse as it involves a client round trip.

Outer joins are not *evil*.  When used appropriately, they are no faster or
slower then a regular join really.

You can either:

  select t.*, t2.c1, t3.c2
    from t, t2, t3
   where t.key1 = t2.key1(+)
     and t.key2 = t3.key2(+)

or
   select t.*, (select c1 from t2 where t2.key1 = t.key1) c1,
               (select c2 from t3 where t3.key2 = t.key2) c2
     from t;

If you EVER find yourself coding:

  select t.*, t2.c1, t3.c2
    from t, t2, t3
   where t.key1 = t2.key1(+)
     and t.key2 = t3.key2(+)
     and t2.SOME_COLUMN some_operator SOME_VALUE

and the some_operator isn't "IS NULL" -- then you are using the outer join
inappropriately -- you don't need to outer join to t2 in that case and should
drop the outer join (that is when performance can get killed -- when you outer
join UN-NECESSARILY)
Review & Followup
Rating: 4
Multiple joins vs. function based view columns/bis December 07, 2002
Reviewer: Jose Luis Navarro from Spain
Thanks for your anwser.
In #3 choice I tried to do all the job before return any data out my
procedure.Just open,(maybe few rows on costumer after first filtering), fetch
every row into any cursor or pl/slq table,call fn_country(), ending loop,closing
and returning data to client. Pl/sql calling pls/ql?. If cursor really retrieves
few rows from big table, is this still even worse than choice #1?.
Thanks again.

Followup:
Use a REF CURSOR
http://asktom.oracle.com/~tkyte/ResultSets/index.html
return that to the client -- do NO procedural (eg: slower, more code, more work,
more for you to maintain, debug, write, etc) coding whatsoever.

#1 is the only correct approach.  There are terribly rare occasions when a plsql
function can help you tune a badly performing query.  This is NOT one of them.

The less code you write, the more you let sql do for you -- ultimately the
better off you are.

Why are you trying to second guess the database engine so much -- it was born to
query data (and insert it and join and so on -- it is like tigger in winnie the
pooh and this is what tiggers do best...)

Rating: 4
OuterJoins vs Functions December 08, 2002
Reviewer: G.V.N. from Houston
<Quote>
#1 is the only really good choice and there is more then one way to code it.
#2 is an attempt to "out smart" the database -- to say "I can do an outer join
faster then you".  Well, you cannot.  Your fn_country() approach would cause SQL

to call PLSQL that is going to call SQL that will either find a row or not.
Much less efficient then just letting SQL do it's job.
</Quote>

You demonstrate most of what you say with examples and numbers.
I would appreciate it if you can show an example or two which can prove the
above statements.

Thank you

Followup:
I dream of the day when people just take runstats:
http://asktom.oracle.com/~tkyte/runstats.html
and DIY

Ok, so here is my setup -- and remember for EVERY TABLE you add -- the
"function" runtimes will get worse and worse and worse.....

ops$tkyte@ORA920.US.ORACLE.COM> create table t1 as select * from all_objects;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> create table t2 as select * from all_objects
where rownum <= 15000;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> alter table t1 add constraint t1_pk primary
key(object_id);

Table altered.

ops$tkyte@ORA920.US.ORACLE.COM> alter table t2 add constraint t2_pk primary
key(object_id);

Table altered.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> analyze table t1 compute statistics
  2  for table for all indexes for all indexed columns;

Table analyzed.

ops$tkyte@ORA920.US.ORACLE.COM> analyze table t2 compute statistics
  2  for table for all indexes for all indexed columns;

Table analyzed.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create or replace function get_data( p_object_id
in number ) return varchar2
  2  is
  3      l_object_name t2.object_name%type;
  4  begin
  5      select object_name into l_object_name from t2 where object_id =
p_object_id;
  6      return l_object_name;
  7  exception
  8      when no_data_found then
  9          return NULL;
 10  end;
 11  /

Function created.

Now, I test:

ops$tkyte@ORA920.US.ORACLE.COM> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5
  6      l_seq   number;
  7  begin
  8      insert into run_stats select 'before', stats.* from stats;
  9
 10      l_start := dbms_utility.get_time;
 11      for x in ( select a.object_id, a.object_name oname1, b.object_name
oname2
 12                   from t1 a, t2 b
 13                  where a.object_id = b.object_id(+) )
 14      loop
 15          null;
 16      end loop;
 17      l_run1 := (dbms_utility.get_time-l_start);
 18      dbms_output.put_line( l_run1 || ' hsecs' );
 19
 20      insert into run_stats select 'after 1', stats.* from stats;
 21      l_start := dbms_utility.get_time;
 22      for x in ( select object_id, object_name oname1, get_data(object_id)
oname2
 23                   from t1 )
 24      loop
 25          null;
 26      end loop;
 27      l_run2 := (dbms_utility.get_time-l_start);
 28      dbms_output.put_line( l_run2 || ' hsecs' );
 29      dbms_output.put_line
 30      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 31
 32      insert into run_stats select 'after 2', stats.* from stats;
 33  end;
 34  /
81 hsecs
1726 hsecs
run 1 ran in 4.69% of the time

PL/SQL procedure successfully completed.

5% of the time -- pretty impressive eh?

ops$tkyte@ORA920.US.ORACLE.COM> select a.name, b.value-a.value run1,
c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11   order by abs( (c.value-b.value)-(b.value-a.value))
 12  /

NAME                                 RUN1       RUN2       DIFF
------------------------------ ---------- ---------- ----------
...
STAT...session uga memory           65464          0     -65464
STAT...consistent gets - exami         90      73745      73655
nation

STAT...consistent gets               8288     103152      94864
STAT...session logical reads         8332     103226      94894
LATCH.cache buffers chains          16838     134120     117282
LATCH.shared pool                     381     117939     117558
LATCH.library cache pin               246     117860     117614
LATCH.library cache                   501     177013     176512

92 rows selected.

and look at that -- 95,000 more logical IO's.  A half of a million more latches
(those are also known as "locks")

the database was born to join -- it was invented to do this stuff, really - it
was!

Just to go the extra mile -- here is the output with a table T3 created just
like T2 was:

ops$tkyte@ORA920.US.ORACLE.COM> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5
  6      l_seq   number;
  7  begin
  8      insert into run_stats select 'before', stats.* from stats;
  9
 10      l_start := dbms_utility.get_time;
 11      for x in ( select a.object_id, a.object_name oname1, b.object_name
oname2, c.object_name oname3
 12                   from t1 a, t2 b, t3 c
 13                  where a.object_id = b.object_id(+)
 14                                    and a.object_id = c.object_id(+) )
 15      loop
 16          null;
 17      end loop;
 18      l_run1 := (dbms_utility.get_time-l_start);
 19      dbms_output.put_line( l_run1 || ' hsecs' );
 20
 21      insert into run_stats select 'after 1', stats.* from stats;
 22      l_start := dbms_utility.get_time;
 23      for x in ( select object_id, object_name oname1, get_data(object_id)
oname2,
 24                            get_data2(object_id) oname3
 25                   from t1 )
 26      loop
 27          null;
 28      end loop;
 29      l_run2 := (dbms_utility.get_time-l_start);
 30      dbms_output.put_line( l_run2 || ' hsecs' );
 31      dbms_output.put_line
 32      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 33
 34      insert into run_stats select 'after 2', stats.* from stats;
 35  end;
 36  /
96 hsecs
3210 hsecs
run 1 ran in 2.99% of the time

PL/SQL procedure successfully completed.

as promised -- the function based approach gets *worse* as you add more
overhead -- to  boot:

ops$tkyte@ORA920.US.ORACLE.COM> select a.name, b.value-a.value run1,
c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11   order by abs( (c.value-b.value)-(b.value-a.value))
 12  /

NAME                                 RUN1       RUN2       DIFF
------------------------------ ---------- ---------- ----------
...
STAT...consistent gets               6480     176903     170423
STAT...session logical reads         6526     176977     170451
LATCH.cache buffers chains          13668     207899     194231
LATCH.library cache pin               182     235440     235258
LATCH.shared pool                     224     235494     235270
LATCH.library cache                   377     353406     353029

91 rows selected.

is even more horrible then before

Rating: 5
Multiple Joins vs. functions December 09, 2002
Reviewer: G.V.N.Rao from Houston
Excellent!!

December 08, 2002 Govindasamy -- Thanks for the question regarding "What is the sql file to create dbms_redefinition package body in oracle 9i", version 9i
You Asked
Hi Tom -

I need to use the online redefinition feature in oracle9i. For this, i need to
create dbms_redefinition packge and package body. There is  a sql file
dbmshord.sql for creating dbms_redefinition packge in oracle9i. But i do not
know, what is the sql file to create the dbms_redefinition package body in
oracle 9i.  Can you please tell me the name of the sql file to create
dbms_redefiniton package body.

Thanks for your help.

Govind
and we said...
That package is installed by default, it is already there.  You may need to have
someone connected "as sysdba" to grant on it to you -- do NOT attempt to install
this in your own schema.

By default, execute is granted to:

GRANT EXECUTE ON dbms_redefinition TO execute_catalog_role

now, that is a ROLE, so if you are trying to make a stored procedure, you'll
need a DIRECT grant -- see
http://asktom.oracle.com/~tkyte/Misc/RolesAndProcedures.html
for why.
December 08, 2002 Krishna -- Thanks for the question regarding "Multiple instances vs Single instance per App.", version 8.1.6.0.0
You Asked
Tom,
I have read your article in Ask tom about why we should use one Oracle instance
in OLTP  not one per App. I think the same way and I am convinced too. Can you
please give me some more doc and white papers which talk about this topic ?
Thanks
and we said...
Not aware of any -- the benefits seem obvious to me:

o the ability to run MORE on LESS.  If you have 10 apps, you will need a bigger
box with more CPU's to run if you do an instance/app then if you do a single
instance.

o the ability to manage a SINGLE VERSION.  You will either upgrade all or
upgrade none with a single instance.  I give you 3 years with a 10 instance
machine before you are managing 10 versions of Oracle.  Good luck with that.

o the ability to actually tune.  Try getting 10 Oracle instances to "play nice"
on a single box.  You have no ability (resource manager) for example to restrain
the users in instance 1 from taking over the machine.  Each instance only sees
itself.

o the old argument "i need to be able to restore App1 to a different point in
time" hasn't applied for years with tablespace point in time recovery.

Ask the people who want an instance/app to COST justify it.
Review & Followup
Rating: 4
well it depends December 08, 2002
Reviewer: A reader
hi

it depends, if everything is in the same server then if the instance fails then
many apps fails not just one, if the application is very critical or very loaded
it's better isolate them in seperate server/instance

Followup:
If availability is your concern -- RAC (clustering) is the answer.

We can get a failed instance of any size up in seconds.

If the application is very loaded and you are on a single machine (that is
what we are talking about) you will only be increasing the load by starting
more then one instance on that server.

December 08, 2002 Gil -- Thanks for the question regarding "how to pass text value with a quote(s) to insert thru a procedure", version 9.2.0
You Asked
We have a web app that needs to do an insert into oracle table.  The problem is
there can be ' marks within the text.  I tried the replace, but still got the
error.  Seems like this would be a common problem?  We need a way to do the
insert regardless of whether one or more ' is there or not in the text. The user
or the developer will not be making any substitutions of characters.  The
following is my test case info.

Thanks !
--===============================
Oracle 9.2.0.1
--=====================================

CREATE TABLE webin_insp
    (afiid                          VARCHAR2(5),
    locid                          VARCHAR2(15))
  PCTFREE     10
  PCTUSED     40
  INITRANS    1
  MAXTRANS    255
  TABLESPACE  users;
--=====================================
CREATE OR REPLACE PROCEDURE insert_webin
   ( theLOCID IN varchar2)
   IS

v_locid varchar2(15);
BEGIN
v_locid:= REPLACE(theLOCID, '''','|| CHR(39) ||') ;

    INSERT INTO scott.webin_insp(locid)
    VALUES(v_locid);

    commit;

END; -- Procedure insert_webin
--=================================================
SQL> exec conversions.insert_webin('can't');
ERROR:
ORA-01756: quoted string not properly terminated

SQL>
and we said...
You want to use bind variables in your web app.

the procedure should just be:

CREATE OR REPLACE PROCEDURE insert_webin( p_LOCID IN varchar2)
IS
BEGIN
    INSERT INTO scott.webin_insp(locid)
    VALUES(p_locid);
END;

(really bad practice to commit in a procedure, the CLIENT is the only one smart
enough to know when the transaction is OVER)

The caller would PREPARE a statement like this:

  begin conversions.insert_webin( ? ); end;

or
  begin conversions.insert_webin( :bv ); end;

depending on your language (which you neglect to mention).

In sqlplus -- to test -- you can:

SQL> exec conversions.insert_webin( 'cant''t' );

but in a REAL PROGRAM YOU WILL USE BIND VARIABLES in the call to the procedure
-- or we will all make fun of you in addition to watching your system keel over
from a trashed shared pool.
December 09, 2002 Martin -- Thanks for the question regarding "Generating a view script for an existing table", version 8.1.6
You Asked
Tom,

I have a databaseschema that contains some tables. I would like to generate a
view script for each table, for example:

table dummy
 (a number,
  b varchar2(30))

The script should be like:
create or replace view xx_dsc_dummy (a,b)
as select a,b from dummy;

Is there an easy way to do this in a script which would run in sql*plus?

Thanks for always sharing your great knowlegde of Oracle!

Martin
and we said...
just

begin
  for x in ( select table_name from user_tables )
  loop
      execute immediate 'create or replace view xx_dsc_' || x.table_name ||
                        ' as select * from ' || x.table_name;
  end loop;
end;
/

we'll expand the * out.  If you wanted the "create script" after that - search
this site for

getallviews

that will extract all of the verbose view definitions.
Review & Followup
Rating: 5
Very quick response December 09, 2002
Reviewer: Martin from The Netherlands
Wow Tom,

Thanks for the quick response...
very usefull, thank you very much!

December 09, 2002 rahul -- Thanks for the question regarding "Partition pruning/elimination -reg..", version 8.1.7
You Asked
Dear Tom,

Sorry for the delay.

1  CREATE TABLE emp (no number, name VARCHAR2(10) ,
  2  PRIMARY KEY (no, name))
  3  partition by hash(no)
  4  (
  5  PARTITION PART1   TABLESPACE "TS1"  ,
  6  PARTITION PART2   TABLESPACE "TS2"
  7* )
  8  /

Table created.

insert into emp(no,name) values (&1,'&2');-- insert about 10 rows
CREATE INDEX emp_IDX
ON emp(no)
LOCAL
(PARTITION PART1 TABLESPACE TS1_INDX_N,
 PARTITION PART2 TABLESPACE TS2_INDX_N)

exec DBMS_STATS.GATHER_SCHEMA_STATS(ownname => 'EMP_SCHEMA')
                       OR
ANALYZE TABLE EMP PARTITION(PART1) COMPUTE STATISTICS;
ANALYZE TABLE EMP PARTITION(PART2) COMPUTE STATISTICS;
ANALYZE INDEX EMP_IDX PARTITION(PART1) COMPUTE STATISTICS;
ANALYZE INDEX EMP_IDX PARTITION(PART1) COMPUTE STATISTICS;

EXPLAIN PLAN SET STATEMENT_ID='NO_PRUNE'
FOR
SELECT * FROM EMP
WHERE NO IN(100,455,777)

SELECT PARTITION_START, PARTITION_STOP FROM PLAN_TABLE WHERE
STATEMENT_ID='NO_PRUNE';

KEY(INLIST)---------> IS THE DISPLAYED VALUE.(NO EKIMINATION OCCURS)

EXPLAIN PLAN SET STATEMENT_ID='PRUNE'
FOR
SELECT * FROM EMP
WHERE NO IN(100);

SELECT PARTITION_START, PARTITION_STOP FROM PLAN_TABLE WHERE
STATEMENT_ID='PRUNE';

A PARTITION(start/stop) VALUE IS AVAILABLE. (ELIMINATION OCCURS)

This is the actual problem. It appears that for multiple values in the IN list,
there is no elimination.

Please advise,
Thanks,
Rahul.
and we said...
In your case, you have a primary key on (NO,NAME).

This primary key created an index.  It is by default a GLOBAL index.  It it not
partitioned.

When it sees "where no in ( x, y, z )", it is (rightly) choosing the primary key
index to answer the query.  No partition elimination needed (or desired in
fact).

Lets try this without the key:

tkyte@TKYTE816> CREATE TABLE emp (no number, name VARCHAR2(10)  )
  2  partition by hash(no)
  3  (
  4    PARTITION PART1   TABLESPACE "P2"  ,
  5    PARTITION PART2   TABLESPACE "P3"
  6  )
  7  /

Table created.

tkyte@TKYTE816>
tkyte@TKYTE816> insert into emp select empno, ename from scott.emp;

14 rows created.

tkyte@TKYTE816>
tkyte@TKYTE816> select * from emp partition(part1);

        NO Tablespace Name
---------- ----------------
      7369 SMITH
      7499 ALLEN
      7654 MARTIN
      7698 BLAKE
      7782 CLARK
      7839 KING
      7876 ADAMS
      7934 MILLER

8 rows selected.

tkyte@TKYTE816> select * from emp partition(part2);

        NO Tablespace Name
---------- ----------------
      7521 WARD
      7566 JONES
      7788 SCOTT
      7844 TURNER
      7900 JAMES
      7902 FORD

6 rows selected.

tkyte@TKYTE816>
tkyte@TKYTE816>
tkyte@TKYTE816> CREATE INDEX emp_IDX
  2  ON emp(no)
  3  LOCAL
  4  (PARTITION PART1 TABLESPACE p2,
  5   PARTITION PART2 TABLESPACE p3)
  6
tkyte@TKYTE816> analyze table emp compute statistics;

Table analyzed.

tkyte@TKYTE816>
tkyte@TKYTE816> alter tablespace p3 offline;

Tablespace altered.

tkyte@TKYTE816> select * from emp where no in (7369);

        NO Tablespace Name
---------- ----------------
      7369 SMITH

tkyte@TKYTE816> select * from emp where no in ( 7369, 7499, 7654 );

        NO Tablespace Name
---------- ----------------
      7369 SMITH
      7499 ALLEN
      7654 MARTIN

tkyte@TKYTE816>
tkyte@TKYTE816> select * from emp where no in ( 7521 );
select * from emp where no in ( 7521 )
              *
ERROR at line 1:
ORA-00376: file 9 cannot be read at this time
ORA-01110: data file 9: 'C:\ORACLE\ORADATA\TKYTE816\P3.DBF'

tkyte@TKYTE816> select * from emp where no in ( 7521, 7566, 7788 );
select * from emp where no in ( 7521, 7566, 7788 )
              *
ERROR at line 1:
ORA-00376: file 9 cannot be read at this time
ORA-01110: data file 9: 'C:\ORACLE\ORADATA\TKYTE816\P3.DBF'

tkyte@TKYTE816> alter tablespace p3 online;

Tablespace altered.

As you can see -- partition elimination takes place with "where no in ( x)" and
"where no in (x,y,z)"....

watch out for those global indexes.
Review & Followup
Rating: 2
partition elimination January 03, 2002
Reviewer: Rahul from Sunnyvale , CA, USA
Tom,

I dropped the global index and created a local index:
alter table emp
add constraint local_idx
primary key(no,name)
using index local

select * from emp partition(part1)
select * from emp partition(part2), both return values from their appropriate
partitions.

Now when i do a select * from emp where no in(x) results in start partition and
stop partition in the plan table with the partition value-- say 2.
select * from emp where no in(x,y) results in key(inlist)in plan_table.(Note:
x,y belong to same partition)

Is there a way to tell the optimizer to use the partition ?

Thanks

Followup:
It won't do it unless it feels its worth the effort.  We can "trick it" into
thinking it is in fact worth the effect:

ops$tkyte@ORA817DEV.US.ORACLE.COM> CREATE TABLE emp (no number, name
VARCHAR2(10)       )
  2  partition by hash(no)
  3  (
  4    PARTITION PART1   TABLESPACE testing_lmt  ,
  5    PARTITION PART2   TABLESPACE users
  6  )
  7  /

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into emp select empno, ename from
scott.emp;
14 rows created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from emp partition(part1);

        NO NAME
---------- ------------------------------
      7369 SMITH
      7499 ALLEN
      7654 MARTIN
      7698 BLAKE
      7782 CLARK
      7839 KING
      7876 ADAMS
      7934 MILLER

8 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from emp partition(part2);

        NO NAME
---------- ------------------------------
      7521 WARD
      7566 JONES
      7788 SCOTT
      7844 TURNER
      7900 JAMES
      7902 FORD

6 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> CREATE INDEX emp_IDX
  2  ON emp(no)
  3  LOCAL
  4  (PARTITION PART1 TABLESPACE testing_lmt,
  5   PARTITION PART2 TABLESPACE users)
  6  /

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> analyze table emp compute statistics;
Table analyzed.

here is the part where we trick it into thinking its "bigger than a bread
basket"

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_stats.set_table_stats( user, 'EMP',
'PART1', numrows => 1000000, numblks => 100000 );

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_stats.set_table_stats( user, 'EMP',
'PART2', numrows => 1000000, numblks => 100000 );

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_stats.set_table_stats( user, 'EMP',
numrows => 2000000, numblks => 200000 );

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter tablespace users offline;
Tablespace altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from emp where no in (7369);

        NO NAME
---------- ------------------------------
      7369 SMITH

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from emp where no in ( 7369, 7499,
7654 );

        NO NAME
---------- ------------------------------
      7369 SMITH
      7499 ALLEN
      7654 MARTIN

It obviously did partition elimination here, if it didn't, we would get the
errors like we do below:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from emp where no in ( 7521 );
select * from emp where no in ( 7521 )
              *
ERROR at line 1:
ORA-00376: file 7 cannot be read at this time
ORA-01110: data file 7: '/d04/oradata/ora817dev/users.dbf'

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from emp where no in ( 7521, 7566,
7788 );
select * from emp where no in ( 7521, 7566, 7788 )
              *
ERROR at line 1:
ORA-00376: file 7 cannot be read at this time
ORA-01110: data file 7: '/d04/oradata/ora817dev/users.dbf'

ops$tkyte@ORA817DEV.US.ORACLE.COM> alter tablespace users online;

Tablespace altered.

Rating: 5
Partition Pruning and Performance. December 09, 2002
Reviewer: Anthony from Phil.
Hi tom,

Can find more info on your site that talks about partition pruning and
performace. Look at this query for instance.

tst_usr@tst>  select sum(num_rows) from user_taB_partitions where
table_name='PART_TBL1';

SUM(NUM_ROWS)
-------------
      1884708

tst_usr@tst> SELECT INDEX_NAME,COLUMN_NAME FROM USER_IND_COLUMNS WHERE
INDEX_NAME LIKE 'PART%'

INDEX_NAME                     COLUMN_NAME
------------------------------ -----------------------------
PART_PK                         INTERNAL_KEY

tst_usr@tst> select PARTITION_NAME,HIGH_value from user_tab_partitions where
table_NAME='PART_TBL1';

PARTITION_NAME                 HIGH_VALUE
------------------------------ ------------------------------
PART_PART3                      1500001
PART_PART4                      2000001
PART_PART5                      2500001
PART_PART6                      3000001
PART_PART1                      500001
PART_PART2                      1000001

tst_usr@tst> select * from PART_TBL1 Partition (PART_PART1) where internal_key
between 1 and 50000

49089 rows selected.

Elapsed: 00:00:52.00

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1322 Card=48656 Byte
          s=4768288)

   1    0   TABLE ACCESS (FULL) OF 'PART_TBL1' (Cost=1322 Card=48656 B
          ytes=4768288)

Statistics
----------------------------------------------------------
         21  recursive calls
         12  db block gets
     216826  consistent gets
       9356  physical reads
     395224  redo size
    4480574  bytes sent via SQL*Net to client
     265394  bytes received via SQL*Net from client
       3274  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
      49089  rows processed

I'm expecting that ORACLE will only read 1 partition. But based on the explain
plan it did an FTS... Can you explain why?

Cheers,
NOTNA

Followup:
It is just the default level of information autotrace gives you.  It is not
showing you the partition start/stop.  Consider:

ops$tkyte@ORA920> CREATE TABLE t
  2  (
  3    x int primary key,
  4    collection_year int
  5  )
  6  PARTITION BY RANGE (COLLECTION_YEAR) (
  7    PARTITION PART_95 VALUES LESS THAN (1996) ,
  8    PARTITION PART_96 VALUES LESS THAN (1997) ,
  9    PARTITION PART_97 VALUES LESS THAN (1998) ,
 10    PARTITION PART_98 VALUES LESS THAN (1999) ,
 11    PARTITION PART_99 VALUES LESS THAN (2000) ,
 12    PARTITION PART_00 VALUES LESS THAN (2001) ,
 13    PARTITION PART_01 VALUES LESS THAN (MAXVALUE)
 14  )
 15  ;

Table created.

ops$tkyte@ORA920>
ops$tkyte@ORA920>
ops$tkyte@ORA920> insert into t
  2  select rownum, 1995+mod(rownum,7) from all_objects;

29345 rows created.

ops$tkyte@ORA920> set autotrace traceonly
ops$tkyte@ORA920> select * from t partition(part_98);

4192 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=82 Bytes=2132)
   1    0   TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=82 Bytes=2132)

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        295  consistent gets
          0  physical reads
          0  redo size
      74679  bytes sent via SQL*Net to client
       3568  bytes received via SQL*Net from client
        281  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       4192  rows processed

ops$tkyte@ORA920> set autotrace off

full table scan -- but missing the partition start/stop information

ops$tkyte@ORA920> explain plan set statement_id = 't1' for
  2  select * from t partition(part_98);

Explained.

ops$tkyte@ORA920>
ops$tkyte@ORA920> select plan_table_output from
table(dbms_xplan.display('plan_table',null,'serial'));

PLAN_TABLE_OUTPUT
------------------------------------

---------------------------------------------------------------------------------
--
| Id  | Operation            |  Name  | Rows  | Bytes | Cost  | Pstart| Pstop |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |        |    82 |  2132 |     2 |       |       |
|   1 |  TABLE ACCESS FULL   | T      |    82 |  2132 |     2 |     4 |     4 |
-------------------------------------------------------------------------------

Note: cpu costing is off

9 rows selected.

But the more "verbose" output of the explain plan shows the partitions that
will be accessed...

ops$tkyte@ORA920>
ops$tkyte@ORA920> delete from plan_table;

2 rows deleted.

December 09, 2002 Tony -- Thanks for the question regarding "Analyze Vs DBMS_STATS", version 8.1.6.0
You Asked
Hi Tom,
Merry Christmas!
I'm working on a data warehouse database running on 4 CPUs machine. One of the
tables contains 200 million rows, and every month almost 15 million more rows
are loaded. Currently, once the rows are loaded, the table (along with other
small dimension tables)is analyzed with 30% sample. On reading the benefits of
dbms_stats with monitoring over analyze, I'm thinking of changing analyze script
to dbms_stats.gather_table_stats procedure.  My questions are:
1. Can I assume that If I use degree=>4 (number of CPUs on the machine) with 30%
estimate_percent, I will get some loading performance benefits over analyze
command?
2. If I use compute statistics, Can I get some query performance benefits? If
yes, how much more time I may be looking for gathering statistics?

Thanks in advance!

Tony
and we said...
1) you might want to use degree=>8.  Whilst some processes are reading -- others
will be processing, while some are processing, others will be reading.  In order
to max out the machine, you generally need to use more PQ slaves then CPU's.

You will get analyze performance boosts, this will not affect your load time
(unless you count the analyze as part of load?)

2) possibly, the more information you gather, the better.

For such a large table, I would consider partitioning by month, load the data
into a new partition everymonth, analyze just the new stuff (a fraction of the
original).
Review & Followup
Rating: 4
Analyze Vs DBMS_STATS December 26, 2001
Reviewer: Tony from Sydney - Australia
The answer was very useful as it has increased my confidence to go ahead with
the proposal of replacing analyze script.
Once again thanks Tom!
Cheers!
Tony

Rating: 3
May 16, 2002
Reviewer: Deborah Harris from Washington, DC
You recommend analyzing just the new partition, which we are doing.

However, from Metalink, Note 166215.1: Unless the query predicate narrows the
query to a single partition, the
optimizer uses the global statistics.

So, what if a query requires global-level stats?
[For example, SAS runs on top of our 8.1.7.3 database, and does not necessary
use native Oracle SQL, as it has its own compatible SQL, and makes use of the
SAS data warehouse engine for its queries.]

Should we simply set up Table Monitoring with GATHER STALE instead?

Followup:
Yes, this is true.  You need to consider how the table will be accessed.  I
could have been more verbose in this one.  That is a good note to reference and
goes into more detail then I did here.

Rating: 5
Rqst Additional Infor on DBMS_STATS and Gather Stale December 09, 2002
Reviewer: Mike Jones from Columbia, MD USA
Hi Tom,

I have been using DBMS_STATS to gather table stats for some time now.  I have
several (DBMS) jobs which gather table stats using several combinations of
percentages, etc depending on table size/use.  After seeing monitoring and
'gather stale' mentioned in several of your replies, I am looking into using
that.  I've read the documentation on DBMS_STATS and some info in a tuning book.
 However, I can only find Gather Stale at the Gather SCHEMA level, not the table
level.  That means that my one and only Gather Stale job has to have parameters
that fit each and every monitored table or object.  Is that right?  Can you turn
on monitoring only for the tables you want to gather stale on and leave the
other tables (all within one schema) to be done by other independent jobs?

I have been reading the DBMS_STATS info in the Supplied Packages book.  Is there
a better manual with more detailed info on how to use the monitor/gather stale
facility?
Thanks

Followup:
see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:5792247321358
and ctl-f (search) for

please guide

for an example of LIST STALE.  You can use that in turn to find out WHAT needs
to be analyzed in a schema and then do it in the manner you see fit for each
segment.

December 09, 2002 Sunil -- Thanks for the question regarding "Query to retreive rows to produce a report like PIVOT query", version 8.1.7
You Asked
Hi Tom,

I have a table Tabl_Report the structure of which is given below:

desc Tabl_Report

NAME                            Null?     Type
------------------------------- --------- -----
DATE_APPLICATION_SIGNED                   DATE
NBS_UNDERWRITER_ID              NOT NULL  NUMBER(12)
UNDERWRITER_NAME                          VARCHAR2(100)
TEAM_NAME                                 VARCHAR2(20)
WORK_BASKET_ID                  NOT NULL  VARCHAR2(3)
STATUS                                    VARCHAR2(40)
WORK_BASKET_FROM_DATE           NOT NULL  DATE
WORK_BASKET_TO_DATE                       DATE
POLICY_ID                       NOT NULL  NUMBER(12)
BENEFIT_ID                                NUMBER(12)
ENTITY_ID                       NOT NULL  NUMBER(12)
REQUIREMENT_ID                  NOT NULL  NUMBER(12)
REQ_TYPE                                  VARCHAR2(100)
LOB                                       VARCHAR2(100)
COMPANY_NAME                              VARCHAR2(100)

The table contains data like this:

UNDERWRITER_NAME      STATUS        WORK_BASKET_FROM_DATE   WORK_BASKET_TO_DATE
LOB
=================================================================================
======
SUNIL               FINAL ACTION    20-Dec-2002             31-Dec-2002
H
SUNIL               FINAL ACTION    12-Dec-2002             14-Dec-2002
H

I have written a query like this:

select STATUS,
         max( decode( DIFF, 0, cnt, null ) ) "0",
         max( decode( DIFF, 1, cnt, null ) ) "1",
         max( decode( DIFF, 2, cnt, null ) ) "2",
         max( decode( DIFF, 3, cnt, null ) ) "3",
         max( decode( DIFF, 4, cnt, null ) ) "4",
         max( decode( DIFF, 5, cnt, null ) ) "5",
         max( decode( DIFF, 6, cnt, null ) ) "6",
         max( decode( DIFF, 7, cnt, null ) ) "7"
    from ( select STATUS, ABS(TO_NUMBER(TO_CHAR(WORK_BASKET_FROM_DATE,'DD') -
TO_CHAR(WORK_BASKET_TO_DATE,'DD'))) DIFF,
           count(*) cnt from Tabl_Report
           group by STATUS, ABS(TO_NUMBER(TO_CHAR(WORK_BASKET_FROM_DATE,'DD') -
TO_CHAR(WORK_BASKET_TO_DATE,'DD'))) )
   group by STATUS;

STATUS          0   1   2   3   4   5   6   7
================================================
FINAL ACTION    0   0   1   0   0   0   0   0

This is the output generated by the above query, but i want the output like:

STATUS          0   1   2   3   4   5   6   7   8+
====================================================
FINAL ACTION    0   0   1   0   0   0   0   0   1

The "8+" column displays all the counts for which DIFF > 7. (all the values
greater than 7 should be printed in
this column).

Can you please suggest a query for this.

Thanks & Regards,
Sunil.
and we said...
 1* select diff, sign(7-diff) from ( select rownum diff from all_objects where
rownum < 15 )
ops$tkyte@ORA920> /

      DIFF SIGN(7-DIFF)
---------- ------------
         1            1
         2            1
         3            1
         4            1
         5            1
         6            1
         7            0
         8           -1
         9           -1
        10           -1
        11           -1
        12           -1
        13           -1
        14           -1

14 rows selected.

so, just add

  max( decode( sign(7-diff), -1, cnt, null ) ) "8+"
Review & Followup
Rating: 4
Query to retreive rows to produce a report like PIVOT query December 09, 2002
Reviewer: Sunil from Bangalore, India
Hi Tom,

The response was very useful & it solved my problem.

Thank you very much,
Sunil.

Rating: 5
What about unknown columns values ? December 09, 2002
Reviewer: Karl
Tom, congratulations on your extremely useful site.

In the SELECT above, we assume all DIFF values are known (1 to 7).

What if I simply do not know the column values beforehand ? Let us say they are
"dynamic"... Let us assume these values change often in the table and you just
cannot hardcode them in the SELECT statement. ( I imagine you would not use
DECODE in this case )

Could you give me some advice on how to change the code (SQL or PL/SQL) so it
works for such unforeseen values ?

I believe this function is called TRANSFORM in some languages and acts upon
previously unknown values. Maybe it exists in Oracle and I don't know. Or maybe
there is a script ready for it already.

It would help me in my work. Thank you very much.

Followup:
If you have my book "Expert one on one Oracle" -- i demonstrate in there how to
have a generic stored procedure that returns a result set that is transposed --
one of the columns VALUES is dynamically changed into a set of columns for the
row.

December 09, 2002 shalini -- Thanks for the question regarding "pl/sql -- can it run sqlldr.", version 8i release 2
You Asked
Hi Tom,

Thanks for ur help yesterday...
I have one more query ..
I need to call sql-loader through a pl/sql stored procedure..
currently i am using a batch file and exec "sqllldr ..." in
it..now i want to embed this into a stored proc ..is there
anyway to do this ???

Thanks a lot in advance,

Shalini
and we said...
Not directly, PLSQL runs inside the server -- sqlldr is a
command line OS tool.

Via an external procedure (in C) or by using a java stored
procedure, you can interface with the OS from the server.  There
are no native PLSQL features to do this however (in Java its
fairly easy, Java has an OS set of APIs to start and run other
programs)

If the data you are loading is smallish in volume -- it is more
then possible to allow PLSQL to load the data directly.  You can
use UTL_FILE or BFILEs to access the OS file system and load the
data.
Review & Followup
Rating: 4
December 09, 2002
Reviewer: A reader
Tom, Can you please show an example of this. I am struggling to do the same.
Thanks a lot!

Followup:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:952229840241
just replace /usr/bin/ps with sqlldr.


Subject | Article | Last Updated (East coast time)
clustering with Linux | article only | Dec 10, 2002 10:24 am
Contradiction in Concepts | article only | Dec 10, 2002 12:08 pm
Hints on Select with Joins | article only | Dec 10, 2002 08:28 pm
email in PL/SQL | article only | Dec 10, 2002 08:36 pm
SQL to extract varchar column-based hierarchy | article only | Dec 10, 2002 08:50 pm
IN_LIST using decode / case | article only | Dec 11, 2002 10:34 am
Changing expired password | article only | Dec 11, 2002 10:12 pm
Can interMedia index creation be done in parallel? | article only | Dec 12, 2002 10:05 am
Creating public synonyms | article only | Dec 12, 2002 12:44 pm
Autonomous Transactions | article only | Dec 12, 2002 02:09 pm
creating a view on a partitioned table | article only | Dec 12, 2002 03:34 pm
Latches and serialization | article only | Dec 12, 2002 04:08 pm
Simplifying a query | article only | Dec 13, 2002 07:34 am
simplyfy a query its really simple for you | article only | Dec 13, 2002 07:37 am
About tablespace initialization | article only | Dec 13, 2002 10:18 am
Loading Image Problem | article only | Dec 13, 2002 12:59 pm
Indexes | article only | Dec 14, 2002 12:51 pm
Informing other remoter users after stand by converting Primary DB | article only | Dec 14, 2002 01:14 pm
Flagging rows for read only and merging data | article only | Dec 14, 2002 01:43 pm
Development Environment/Methodolgy | article only | Dec 14, 2002 04:28 pm
Compiling invalid Objects / execute immediate issue | article only | Dec 15, 2002 01:37 pm
Bitmap indexes and Group BY | article only | Dec 15, 2002 07:46 pm
Help on Update | article only | Dec 16, 2002 07:31 am
query | article only | Dec 16, 2002 07:52 am
pseudo column called level | article only | Dec 16, 2002 07:53 am
how to mount 2 different databases | article only | Dec 16, 2002 07:58 am
DBMS_STATS.alter_schema_table_monitoring doesn't work | article only | Dec 16, 2002 08:03 am
Question in DYNAMIC SQL | article only | Dec 16, 2002 08:38 am
interrupt of query execution and calling an external function | article only | Dec 16, 2002 01:54 pm
What does N/A mean in the status of an Index? | article only | Dec 16, 2002 02:58 pm
row(s) 1 - 30 of 30
December 10, 2002 Chris -- Thanks for the question regarding "clustering with Linux", version 9i
You Asked
Is it possible to install oracle in a linux clustering environmnet such as
Project Beowulf?  We want to achieve faster responses to our database queries,
will a Beowulf setup work?
and we said...
Not with beowulf, that is a scientific lab way of sharing data between small
machine in a message passing sort of way.

We cluster on linux using the Oracle cluster manager -- on disk 1 of the 9ir1/r2
install disks (so we do cluster, not with beowulf)

Now, and this is important -- unless you are looking at this to scale out
(eg: the queries run great with 1 user, 2 users, 3 users, N users -- but at n+1
the system gets bogged down, and at n+M -- it it really bad) RAC won't make your
queries faster (unless you are looking for massively parallel processing of
queries across nodes in your cluster, rare).

If your queries are slow with 2 users, they will be slow with 2 users each on
their own instance.
Review & Followup
Rating: 5
IEEE 1394 December 10, 2002
Reviewer: Jeremy Smith from Salt Lake City
I'd read in a press release from Oracle that they'd developed some kernel
changes to allow two machines to use one or more shared 1394 (Firewire) disk
instead of a much-more-expensive SCSI or Fiberchannel array.  Got any good links
on how that actually works?  The drivers can be downloaded at
http://otn.oracle.com/tech/linux/open_source.html
but I've heard nothing about integration with the cluster manager or how close
to ready for prime time it is.  Is there a whitepaper or something?

This sounds like it'd be a great way for a DBA to learn about RAC cheaply.  You
could build a working, real, Oracle cluster for about $1000.

December 10, 2002 Manoharan -- Thanks for the question regarding "Contradiction in Concepts", version 8.1.7
You Asked
Hi Tom,

    I would like to clear some of my basic doubts.

When i entered into the oracle (Developer/DBA) world.
I read/heard some basic concepts about the development/administration like

    1.Don't create indexes for small tables.
    2.Give More attention to Physical I/O...
(These are all some examples)

But when i read some expertise guides,i feel there are lot of contradictios to
my old beliefs.

  Not only me but also for many of dba's facing the same problem.What my doubt
is

   1.Were we started with bad guidelines?
   2.What we are reading now is the real or we may get contracitions to the
current expertise gudelines when we read better guidelines

  Where we are lacking? how to approach?

Thanks/regards
Jeyaseelan.M
and we said...
This is a great question.  In the book I am working on now, I have a section
entitled "question authority".  That is what we all need to do

The problem with basic concepts as you have given, such as "don't create
indexes...", "PIO's are what to pay attention to..." is that they are all Rules
of Thumb (ROT).  I despise ROT.

Don't create indexes for small tables -- why not? Well, suppose this is a
frequently updated small table with a foreign key to a parent table and the
parent table is frequently deleted from.  If you don't index it - you'll have
massive - literally massive - concurrency issues related to unindexed foreign
keys.  What if there is a primary key that needs to be enforced?  What if this
table is really small -- and we full scan it constantly -- could indexing help
us?  Yes it can -- we can index full scan or even index fast full scan even less
data (skipping the N blocks we get in current mode for a full scan for example).

Pay more attention to PIO's -- that one I think they just got wrong, or at least
(as with MOST ROT), they left something out.  It should be "after getting your
queries to run with the LEAST amount of LIO's possible -- the next thing to
attack is PIO's.  We can spread IO's out over disks, we can look at our buffer
cache.  The important thing here though is to reduce LIO's -- so the plans are
known and then reduce the PIO's if needed"

It is not that you were given 100% bad guidelines it is that guidelines (ROT)
only apply in some typical cases and frequently in the real world -- do not
apply to your case.

You will get apparent contradictions constantly!  Continously!  Do they
contradict?  Not necessarily -- they might just apply to two different cases.

What you need to do is read the advice...

Make sure the advice giver BACKS IT UP with solid evidence (discount anything
that starts with "I think...", "I feel....", "It is my opinion...." and isn't
followed by some test use cases showing the point!)

Test the advice in your environment...

Make a sensible decision as to the validity of the advice in your case...

If this all (our jobs) were easily codified into a set of ROT -- the software
would just "do it".  We would be obsolete.
Review & Followup
Rating: 5
Great ! Free expert consultancy... ;-) December 09, 2002
Reviewer: A reader

Rating: 5
December 09, 2002
Reviewer: A reader from Barcelona

Rating: 5
My opinion December 09, 2002
Reviewer: Cefers.br from Brasil
This little thread reminds me of the polemic old question about keep index and
tables in separate disks to gain system performance. One of the greatest ROTs.

I think then best way to get "up to date" and get away from old concepts is to
read expert´s books, search for brand new information on Internet, etc.

Rating: 3
Your book December 09, 2002
Reviewer: ken chiu from toronto, Cdn.
Tom, when will the book be out and what's the content ?

Followup:
Spring 2003

here is a blurb from the intro:

Introduction

This is a book that relates my personal experiences and knowledge about building
scalable and performant applications that make use of an Oracle database.  It
has been my longstanding experience that applications dependent on the database
for processing - and today that is pretty much every application of note -
succeed or fail based on how they use the database.

There are many schools of thought out there today that describe how to build
applications that will scale and be performant but the one thing they all have
in common is they use a database to store and process data and, if you strip
away the GUI at the end of the day - it is really about data and processes
accessing and using that data.  Therefore, it is my premise that regardless of
the tools you use, the approaches you take to implementing your application -
sound and proper use of the database from day one will increase your odds of
success many times over.

I am not going to promote the use of one development technique over another but
rather will stress that database design and implementation and the choices you
make as to what features to use in the database versus do yourself outside of
the database in the application will affect you.

What is this book about?

This is not a book about "tuning after the fact" or "fixing a broken database" -
although all of the concepts and ideas in here are applicable in that
environment.  You are at that point looking for how to fix things and that is in
this book - however, unlike many other performance books out there, I will not
show you the dozen scripts you need to run to find little ratios that tell you
if your database is "OK".  This book is about building it right from the start.

Who should use this book?

The target audience of this book is the development TEAM - the group of people
that have 100% of the control over the overall performance of the system.  This
is contrary to the popular myth that the DBA is solely responsible and has total
control over application performance inside of the database.  The best way to
understand that this is a myth would be to use a car racing analogy.  The DBA is
the pit stop guy who changes the tires, makes sure the engine is gassed up, that
the car functions.  If you give the pit stop guy (DBA) a Lincoln Navigator (a
truly huge truck) and tell them to race the Indy 500 with it - what'll happen?
The DBA can make sure the truck runs as fast as it can but he cannot affect the
performance of the truck on a tight corner at 100+ miles per hour.  There is
really very little to be done after the car has been designed and built (short
of throwing out the car and starting over - the car is the application here).
That analogy is frightening in its applicability to so many systems.  A Lincoln
Navigator was built where a high performance Indy car was needed or vice versa.
Remember - we need 18 wheelers, minivans, race cars - everything.  There is no
one size fits all here.
.......

we are shooting for about 600 pages.  It is less "reference" then Expert one on
one is -- more best practices (eg: why you need a test environment, why you need
to benchmark, question authority ;), the tools you'll want and need, when to use
MTS (shared server), ....

Rating: 4
How BIG is the book gonna be ? December 09, 2002
Reviewer: Robert from PA
>> Tom, when will the book be out and what's the content ?

How think or (app. how many pages) is the book gonna be comparing to "Expert" ?

Followup:
600 pages is the target (but then again, E-1/1 was to be 800 pages ;)

Really, shooting for 600 pages on this one.

Rating: 5
Very Nice December 09, 2002
Reviewer: Sikandar Hayat Awan from Pindi Bhattian - Pakistan
The developers and DBA's need the books/experties of experts like you.......

What will be the title of your book?

Followup:
thats the last thing we decide ;)

we write them and then see what fits...

Rating: 5
what's the title for new one? December 09, 2002
Reviewer: sean bu from atlanta

Rating: 5
December 10, 2002
Reviewer: A reader
is that your book or you're on of the authors

Rating: 5
I'll wait for the next Book, eventhough I have'nt finished reading E-1/1 :-) December 10, 2002
Reviewer: Anthony from Phil.

Rating: 4
wasnt your next book about 10i? December 10, 2002
Reviewer: A reader
Hi

I read from one of old threads that you are gonna skip 9i R1 and R2 in your next
book and focus on some stuffs on 10i... but if the book is going to be published
on Spring 2003 I guess it wont have anything on 10i? Or you plan to write
another book for 2004?

Followup:
2004 is sooo far away ;)

But anyway -- this current book I am working on for 2003 is a book for all
versions.  Consider it version independent.  It applies to Oracle 6.0 as much as
9i.

Rating: 5
Dittos! December 10, 2002
Reviewer: Robert from Memphis, USA
Professor Tom,

Thanks for your great Oracle classroom.
Please don't ever stop teaching!

Thanks,

Robert.

Rating: 5
The book should be called .... December 10, 2002
Reviewer: Sanjay Raj from NJ, USA
Excellent Tom. I have learnt more from this forum than from the $2500- $3000
Oracle books that I have accumulated. Just a suggestion for the name of the book
- "Design it right".
Thanks

Rating: 4
December 10, 2002
Reviewer: A reader
Or may be the title should be

Expert on Experts  :-)

Rating: 5
New Book January 07, 2003
Reviewer: Umesh from Bangalore India
Tom,
Thanks so much for all the work. Till date I was in training but shifted to s/w
development, but  with part of your book's knowledge with me. I did not find any
thing new till date apart from what ever you have discussed . Thanks.

But for your New Book I request you to ensure that Book be made available in
India simulataneously

December 10, 2002 Betty -- Thanks for the question regarding "Hints on Select with Joins", version 9.1.2
You Asked
I have a select and was told to add the hint
 /* USE_NL (program call text) */  to increase the speed, which it did do.
However, I do not understand why this hint was choosen?
Table PROGRAM has foreign key to CLIENT.
Table CALL has foreign key to PROBLEM.
Table TEXT has foreign key to CALL.

SELECT  /*+ USE_NL(problem call text) */
  EMPLOYEE.DIVISION,
  DIVISION.MANAGER  AS DIRECTOR,
  PROBLEM.CLIENT,
  PROBLEM.ID,
  PROBLEM.STATUSCHGDT,
  DECODE (PROBLEM.ASSIGNEDDT,
    Null, (SYSDATE - PROBLEM.STATUSCHGDT),
    (SYSDATE - PROBLEM.ASSIGNEDDT)) AS DaysInPlate,
  PROBLEM.ASSIGNEDDT,
  PROBLEM.STATUS12,
  DECODE(SUBSTR(STATUS12,-1,1),
    'W', 'CLIENT',
    'SCC')         AS WaitingOn,
  PROBLEM."Type",
  PROBLEM.CLASS,
  PROBLEM.PRODUCT,
  PROBLEM.EMPLOYEE,
  PROBLEM.DUEDATE,
  PROBLEM.CREATED,
  (SYSDATE -PROBLEM.CREATED) AS DaysSinceCreated,
  PROBLEM.LASTACTDATE,
  PROBLEM.FLGSAFETYFINAL0,
  PROBLEM.FLGPATIENTSAFETY,
  PSLOGNUM.PSLOGNUM,
  PSLOGNUM.PSSTATUS,
  PSLOGNUM.PSASSESSNUM,
  PSLOGNUM.ENTEREDDT,
  PSLOGNUM.ENTEREDBY,
  PSASSESSNUM.PSLOC AS SafetyLOC,
  PSLOGNUM.PSSTATUSDT,
  PSLOGNUM.PSSTATUS,
  PSASSESSNUM.ENTEREDDT AS PSAssesNumEnteredDt,
  PSASSESSNUM.ENTEREDBY AS PSAssesNumEnteredBy,
  (SYSDATE -PSLOGNUM.ENTEREDDT) AS DaysSinceCreated,
  PSASSESSNUM.PATCHMAND,
  TASKACTIONS.FULLACTIONTEXT,
  TEXT.TEXTLINE
FROM
  TMSODBC.PROBLEM     PROBLEM,
  TMSODBC.EMPLOYEE    EMPLOYEE,
  TMSODBC.DIVISION    DIVISION,
  ADHOC.TASKACTIONS   TASKACTIONS,
  TMSODBC.PSLOGNUM    PSLOGNUM,
  TMSODBC.PSASSESSNUM PSASSESSNUM,
  TMSODBC.CALL        CALL,
  TMSODBC.TEXT      TEXT
WHERE
  PROBLEM.STATUS <> 'C'
  AND
  (PROBLEM.FLGSAFETYFINAL0 <> 'N'  Or PROBLEM.FLGSAFETYFINAL0 Is Null)
  AND
  PROBLEM.FLGPATIENTSAFETY = 'Y'
  AND
  CALL.PR_CA = PROBLEM.AA_ID
  AND
  TEXT.TEXTTYPE = 'F'
  AND
  TEXT.CA_TE = CALL.AA_ID
  AND
  TASKACTIONS.Taskslink = PROBLEM.AA_ID
  AND
  TASKACTIONS.LATESTACTION = 'Y'
  AND
  EMPLOYEE.EMPID = PROBLEM.EMPLOYEE
  AND
  DIVISION.DIVISION = EMPLOYEE.DIVISION
  AND
  PSLOGNUM.CLIENT (+) = PROBLEM.CLIENT
  AND
  PSLOGNUM.ID (+) = PROBLEM.ID
  AND
  PSASSESSNUM.PSASSESSNUM (+) = PSLOGNUM.PSASSESSNUM
ORDER BY
  EMPLOYEE.DIVISION;

I am new to this type of select and could use any help that you can give me.  I
do have your book and also Beginning Oracle Programming, if you know of any
other that would be great.

Oracle version 9.2.0.1.0

I use CBO.

The tables have been analyzed.
and we said...
suggest you see:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6601251003901
and try that approach (that of setting optimizer_index_cost_adj to 25 or 35)

To read about hints and what they do, see:
http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96533/hintsref.htm#8326
I do not like to use them as a matter of fact -- I use them as a last ditch --
totally last ditch resort.

Using nested loops joins can be DEADLY -- but people fall into the myth that
they are "good".  See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6749454952894
for a really good example of that.  Indexes - nested loop joins - can be the
worst idea, they can be good.

As for reading material, see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6829158189168
December 10, 2002 Munzer -- Thanks for the question regarding "email in PL/SQL", version 8i
You Asked
Tom:

1.  If you want to write something in PL/SQL application server that send email
automatically to an administrator requesting approval after a user create a
record, which oracle package would be using.

Would you also use an after insert trigger for it or just include it in the
insert routine of the triggering record?

Thank you,
and we said...
search this site for

UTL_SMTP

it does that.

I myself (as documented in my book if you have it) would use DBMS_JOB to
schedule a call to a stored procedure that will send the mail right after I
commit.  The reason for doing this is twofold:

o sending mail is slow, it takes a bit of time.
o sending mail is not transactional.  if you send the email in the trigger and
then ROLLBACK the transaction -- the email won't get "unsent"

Using DBMS_JOB solves both of those issues.  scheduling the job in the trigger
is very fast (the end user is not affected) and if you rollback, the scheduled
job is "unscheduled" as well.
Review & Followup
Rating: 4
More please January 07, 2002
Reviewer: A reader
Hi, Tom,

Would you give us an example on how to schedule dbms_job
in a trigger?

Thanks

Followup:
create trigger trig
after insert on tab for each row
declare
    l_job number;
begin
   dbms_job.submit( l_job, 'proc;' );
end;
/

In my book -- I have tons of material on this.  Additionally, the supplied
packages guide shows you how to use this documented package.

Rating: 4
notification email December 10, 2002
Reviewer: John
Hi Tom,

I have a table as follow

Table: work_to_do
Columns: work_id, what, work_time.

I need to write a background procedure which sends a notification email whenever
the system time reach one hour before the "work time" for any rows in the table.
The table is not static, any user may add/modify/delete rows in the table.

Any solution?

Thanks in advance.

Followup:
You will either schedule a job via dbms_job to run every N minutes looking for
notifications to send -- or you can schedule a job when they
insert/update/delete the record.

both need another column to be added -- either a "flag" saying the email was
sent or a "job" so we know what job is scheduled.

So, you could write a procedure:

procedure p
is
begin
   for x in ( select rowid rid, work_to_do.*
                from work_to_do
               where work_time <= sysdate+1/24
                 and flag = 'N'
                 for update )
   loop
       send mail...
       update work_to_do set flag = 'Y' where rowid = x.rid;
   end loop;
end;

and schedule that 5 or 10 minutes

dbms_job.submit( :n, 'p;', sysdate, 'sysdate+5/24/60' )

Or, you could use a trigger on the table to schedule a JOB to be run upon
insert, modify the job's when upon update of the time, remove the job upon
delete.  for this you would need to add a "job" column that the insert trigger
would fill in and the update/delete would use.

December 10, 2002 Paul -- Thanks for the question regarding "SQL to extract varchar column-based hierarchy", version 9.2.0.1
You Asked
Tom,

I have an hierarchy defined within the same varchar2 column of a table. Parent
has to exist in the same table, its length has to be less that child's length,
and it has to be entirely contained within child, but be as long as pssible.
For example,

drop table tmp;
create table tmp (child varchar2(10));
insert into tmp values ('A');
insert into tmp values ('AB');
insert into tmp values ('ABCD');
insert into tmp values ('ABCE');
insert into tmp values ('ABCEFG');
insert into tmp values ('B');
insert into tmp values ('BBBB');

select child from tmp;

CHILD
----------
A
AB
ABCD
ABCE
ABCEFG
B
BBBB

I'm trying to write SQL to get something like:

select child,??? parent from tmp;

CHILD      PARENT
---------- ---------
A
AB         A
ABCD       AB
ABCE       AB
ABCEFG     ABCE
B
BBBB       B

I know I can write PL/SQL code to do this, but I'm wondering if there is a
simple SQL solution to this task.

Regards,
Paul
and we said...
That is not a very good way to store that data -- have you considered a more
traditional approach of a child/parent column in the first place?
ops$tkyte@ORA920.US.ORACLE.COM> select child, substr((select max(
to_char(length(child),'fm0000') || child )
  2                          from tmp tmp2
  3                         where instr( tmp.child,tmp2.child ) = 1
  4                           and tmp2.rowid <> tmp.rowid ), 5 )
  5    from tmp
  6  /

CHILD      SUBSTR((SEL
---------- -----------
A
AB         A
ABCD       AB
ABCE       AB
ABCEFG     ABCE
B
BBBB       B

7 rows selected.

is one approach -- it'll have to full scan the table N+1 times where N = number
of rows.

ops$tkyte@ORA920.US.ORACLE.COM> select tmp.child child,
  2         substr(max(
decode(tmp.rowid,tmp2.rowid,'0000',to_char(length(tmp2.child),'fm0000') ||
tmp2.child) ), 5 )
  3    from tmp, tmp tmp2
  4   where instr( tmp.child,tmp2.child ) = 1
  5   group by tmp.child
  6  /

CHILD      SUBSTR(MAX(
---------- -----------
A
AB         A
ABCD       AB
ABCE       AB
ABCEFG     ABCE
B
BBBB       B

7 rows selected.

is another -- a cartesian product tho.

The data is not setup, not modeled, to answer this question.  Bad design if this
question is something you need to ask on a recurring basis.
Review & Followup
Rating: 5
Thanks! December 10, 2002
Reviewer: Paul Druker from Fairfax, VA USA
I know that it's not the right way to store that kind of data, but it's coming
from the legacy system.

Followup:
Now that I've had some coffee as well - we can make this a little better by
adding a LIKE clause.  Assuming an index on CHILD, we can see:

ops$tkyte@ORA920> exec gen_data( 'TMP', 1000 );
PL/SQL procedure successfully completed.

ops$tkyte@ORA920> create index tmp_idx on tmp(child)
  2  /
Index created.

ops$tkyte@ORA920> set autotrace traceonly
ops$tkyte@ORA920> select child, substr((select max(
to_char(length(child),'fm0000') || child )
  2                          from tmp tmp2
  3                         where instr( tmp.child,tmp2.child ) = 1
  4                           and tmp2.rowid <> tmp.rowid ), 5 )
  5    from tmp
  6  /

1007 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'TMP'

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      11156  consistent gets
          0  physical reads
          0  redo size
      24882  bytes sent via SQL*Net to client
       1236  bytes received via SQL*Net from client
         69  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       1007  rows processed

ops$tkyte@ORA920>
ops$tkyte@ORA920> select tmp.child child,
  2         substr(max(
decode(tmp.rowid,tmp2.rowid,'0000',to_char(length(tmp2.child),'fm0000') ||
tmp2.child) ), 5 )
  3    from tmp, tmp tmp2
  4    where instr( tmp.child,tmp2.child ) = 1
  5    group by tmp.child
  6  /

1007 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (GROUP BY)
   2    1     NESTED LOOPS
   3    2       TABLE ACCESS (FULL) OF 'TMP'
   4    2       TABLE ACCESS (FULL) OF 'TMP'

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      11088  consistent gets
          0  physical reads
          0  redo size
      24757  bytes sent via SQL*Net to client
       1236  bytes received via SQL*Net from client
         69  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
       1007  rows processed

ops$tkyte@ORA920> select child, substr((select max(
to_char(length(child),'fm0000') || child )
  2                          from tmp tmp2
  3                         where instr( tmp.child,tmp2.child ) = 1
  4                           and tmp2.child like substr( tmp.child, 1, 1 ) ||
'%'
  5                           and tmp2.rowid <> tmp.rowid ), 5 )
  6    from tmp
  7  /

1007 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'TMP'

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1219  consistent gets
          4  physical reads
          0  redo size
      24925  bytes sent via SQL*Net to client
       1236  bytes received via SQL*Net from client
         69  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       1007  rows processed

ops$tkyte@ORA920> select tmp.child child,
  2         substr(max(
decode(tmp.rowid,tmp2.rowid,'0000',to_char(length(tmp2.child),'fm0000') ||
tmp2.child) ), 5 )
  3    from tmp, tmp tmp2
  4    where instr( tmp.child,tmp2.child ) = 1
  5      and tmp2.child like substr( tmp.child, 1, 1 ) || '%'
  6    group by tmp.child
  7  /

1007 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (GROUP BY)
   2    1     NESTED LOOPS
   3    2       TABLE ACCESS (FULL) OF 'TMP'
   4    2       INDEX (RANGE SCAN) OF 'TMP_IDX' (NON-UNIQUE)

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1084  consistent gets
          0  physical reads
          0  redo size
      24757  bytes sent via SQL*Net to client
       1236  bytes received via SQL*Net from client
         69  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
       1007  rows processed

ops$tkyte@ORA920> set autotrace off

it could cut down seriously on the LIO's

Rating: 4
Very good. Ask a related question. December 10, 2002
Reviewer: Henry Xie from NYC, USA
I remember you said before to avoid using "like" whenever
possible.  I am wondering how you would compare the
performance of the following two ways of pattern matching:

1. select ...... from ... where ... column1 like '%xxxx%'

2. select ...... from ... where ... instr(column1, 'xxxx') > 0

And what would you say if the string 'xxxx' is
constructed by a stored function. E.g.,

1. select ...... from ... where ... column1 like '%' || my_fn() || '%'

2. select ...... from ... where ... instr(column1, my_fn()) > 0

Thanks!

Followup:
I don't think I said that -- you use like when you need to, it is not really a
"choice".

How would I compare them?  I would use:
http://asktom.oracle.com/~tkyte/runstats.html
of course!

create table t as select * from all_objects;

ops$tkyte@ORA920> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5  begin
  6      insert into run_stats select 'before', stats.* from stats;
  7
  8      l_start := dbms_utility.get_time;
  9      for i in 1 .. 500
 10      loop
 11          for x in ( select * from t where object_name like '%xxxxx%' )
 12          loop
 13              null;
 14          end loop;
 15      end loop;
 16      l_run1 := (dbms_utility.get_time-l_start);
 17      dbms_output.put_line( l_run1 || ' hsecs' );
 18
 19      insert into run_stats select 'after 1', stats.* from stats;
 20      l_start := dbms_utility.get_time;
 21      for i in 1 .. 500
 22      loop
 23          for x in ( select * from t where instr( object_name, 'xxxxx' ) > 0
)
 24          loop
 25              null;
 26          end loop;
 27      end loop;
 28      l_run2 := (dbms_utility.get_time-l_start);
 29      dbms_output.put_line( l_run2 || ' hsecs' );
 30      dbms_output.put_line
 31      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 32
 33      insert into run_stats select 'after 2', stats.* from stats;
 34  end;
 35  /
1806 hsecs
2178 hsecs
run 1 ran in 82.92% of the time

PL/SQL procedure successfully completed.

but before you all go out there and decide "hey, instr stinks, lets replace it
universally with like, understand it is data dependent

ops$tkyte@ORA920> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5  begin
  6      insert into run_stats select 'before', stats.* from stats;
  7
  8      l_start := dbms_utility.get_time;
  9      for i in 1 .. 50
 10      loop
 11          for x in ( select * from t where object_name like '%tion%' )
 12          loop
 13              null;
 14          end loop;
 15      end loop;
 16      l_run1 := (dbms_utility.get_time-l_start);
 17      dbms_output.put_line( l_run1 || ' hsecs' );
 18
 19      insert into run_stats select 'after 1', stats.* from stats;
 20      l_start := dbms_utility.get_time;
 21      for i in 1 .. 50
 22      loop
 23          for x in ( select * from t where instr( object_name, 'tion' ) > 0 )
 24          loop
 25              null;
 26          end loop;
 27      end loop;
 28      l_run2 := (dbms_utility.get_time-l_start);
 29      dbms_output.put_line( l_run2 || ' hsecs' );
 30      dbms_output.put_line
 31      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 32
 33      insert into run_stats select 'after 2', stats.* from stats;
 34  end;
 35  /
664 hsecs
702 hsecs
run 1 ran in 94.59% of the time

PL/SQL procedure successfully completed.

(tion is the most frequent "END" of object_name in my database)

ops$tkyte@ORA920> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5  begin
  6      insert into run_stats select 'before', stats.* from stats;
  7
  8      l_start := dbms_utility.get_time;
  9      for i in 1 .. 50
 10      loop
 11          for x in ( select * from t where object_name like '%java%' )
 12          loop
 13              null;
 14          end loop;
 15      end loop;
 16      l_run1 := (dbms_utility.get_time-l_start);
 17      dbms_output.put_line( l_run1 || ' hsecs' );
 18
 19      insert into run_stats select 'after 1', stats.* from stats;
 20      l_start := dbms_utility.get_time;
 21      for i in 1 .. 50
 22      loop
 23          for x in ( select * from t where instr( object_name, 'java' ) > 0 )
 24          loop
 25              null;
 26          end loop;
 27      end loop;
 28      l_run2 := (dbms_utility.get_time-l_start);
 29      dbms_output.put_line( l_run2 || ' hsecs' );
 30      dbms_output.put_line
 31      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 32
 33      insert into run_stats select 'after 2', stats.* from stats;
 34  end;
 35  /
891 hsecs
841 hsecs
run 1 ran in 105.95% of the time

PL/SQL procedure successfully completed.

 (java is the most populate BEGIN of string...)

As for the function question.

If the function was "deterministic", I would try to call the function once and
use the result as a BIND VARIABLE (to avoid having the function called once per
row as might otherwise happen.

ops$tkyte@ORA920> create or replace function foo return varchar2
  2  as
  3  begin
  4          return 'java';
  5  end;
  6  /

Function created.

ops$tkyte@ORA920> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5      l_bind  varchar2(50);
  6  begin
  7      insert into run_stats select 'before', stats.* from stats;
  8
  9      l_start := dbms_utility.get_time;
 10      for i in 1 .. 50
 11      loop
 12          for x in ( select * from t where object_name like '%'||foo()||'%' )
 13          loop
 14              null;
 15          end loop;
 16      end loop;
 17      l_run1 := (dbms_utility.get_time-l_start);
 18      dbms_output.put_line( l_run1 || ' hsecs' );
 19
 20      insert into run_stats select 'after 1', stats.* from stats;
 21      l_start := dbms_utility.get_time;
 22      for i in 1 .. 50
 23      loop
 24          l_bind := '%'||foo()||'%';
 25          for x in ( select * from t where object_name like l_bind )
 26          loop
 27              null;
 28          end loop;
 29      end loop;
 30      l_run2 := (dbms_utility.get_time-l_start);
 31      dbms_output.put_line( l_run2 || ' hsecs' );
 32      dbms_output.put_line
 33      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 34
 35      insert into run_stats select 'after 2', stats.* from stats;
 36  end;
 37  /
3653 hsecs
782 hsecs
run 1 ran in 467.14% of the time

PL/SQL procedure successfully completed.

or, if that won't fly for whatever reason, use a trick to get it to do the
function once per query:

ops$tkyte@ORA920> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5  begin
  6      insert into run_stats select 'before', stats.* from stats;
  7
  8      l_start := dbms_utility.get_time;
  9      for i in 1 .. 50
 10      loop
 11          for x in ( select * from t where object_name like '%'||foo()||'%' )
 12          loop
 13              null;
 14          end loop;
 15      end loop;
 16      l_run1 := (dbms_utility.get_time-l_start);
 17      dbms_output.put_line( l_run1 || ' hsecs' );
 18
 19      insert into run_stats select 'after 1', stats.* from stats;
 20      l_start := dbms_utility.get_time;
 21      for i in 1 .. 50
 22      loop
 23          for x in ( select *
 24                      from t, (select '%'||max(foo())||'%' bind_foo from
dual)
 25                      where object_name like bind_foo )
 26          loop
 27              null;
 28          end loop;
 29      end loop;
 30      l_run2 := (dbms_utility.get_time-l_start);
 31      dbms_output.put_line( l_run2 || ' hsecs' );
 32      dbms_output.put_line
 33      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 34
 35      insert into run_stats select 'after 2', stats.* from stats;
 36  end;
 37  /
3405 hsecs
893 hsecs
run 1 ran in 381.3% of the time

PL/SQL procedure successfully completed.

Rating: 5
Great answer. December 10, 2002
Reviewer: Henry Xie from NYC, USA
I can't believe you went such a length to explain it.
I really appreciate your help.  It would take hours for me
to run the same tests as I am relatively new to PL/SQL,
and didn't get a chance to install your test harness yet.
It seems I need to get a hand on the harness right away.
And sorry for quoting you wrong (I guess you moight have
said something like always use "equal" rather than "like"
if at all possible?)

Thanks again, Tom.

Rating: 5
That's what you expect from this site December 10, 2002
Reviewer: Arup Nanda from Norwalk, CT, USA
Well, that's what you expect from this site, a no-nonsense, no-spin explanation
bolstered by pure facts and results ... pure Tom :-) No wonder this is the most
important stop millions make on the information superhighway for Oracle related
knowledge.

Tom, ever wondered about putting some stats on your site - number of hits,
countries, states, search key words, etc.? Certainly not important but could
satisfy some curious minds, mine included ;)

Followup:
Thanks -- millions of visitors is a tad high ;)

Maybe I'll do that report someday....

hits is easy, states/countries -- hard (all i have are IP addresses after all),
search keywords is easy as well.  I log every search and how successful it
was...

December 11, 2002 Craig -- Thanks for the question regarding "IN_LIST using decode / case", version 8.1.7.4
You Asked
I have a developer who wants to use a decode in an IN clause to pass a list of
values.  If a=ALL then give all records, if a=list_of_values, pass those values
to the IN.

I tried using your procedure str2tbl to pass the list, but when I do, I get an
error.

This is what they are writing:

SELECT * FROM ALL_USERS
WHERE USERNAME IN ((DECODE
           ('&1','ALL', USERNAME,
           'SCOTT', (SELECT * FROM THE
                           (SELECT CAST
                                   (STR2TBL ('SCOTT, SYS, SYSTEM')
                                   AS MYTABLETYPE
                                   )
                           FROM DUAL
                           )
                       ),
           NULL
           )
              ))

but I get:

ORA-01427: single-row subquery returns more than one row

They are programming using powerbuilder which they say cannot use IF statements.

Here's their code:

select  a.sell_id,
  sum(decode(a.week_date,(:a_week1),(a.seconds / 30 ),0))capac_week1
from msa.sid_week_rc_avails a,
         "MSA"."GE_RATECLASS_INVENTORY_MAP"
where a.sell_id in (DECODE(:a_sell_nm ,'ALL SELLING TITLES  ',a.sell_id,
:a_sell_id))
and  a.rateclass_id in (DECODE( :a_rateclass_nm, 'ALL RATECLASS  ' ,
a.rateclass_id,:a_rateclass_id)) and
( a."RATECLASS_ID" IN (SELECT "MSA"."GE_RATECLASS_BUY_MAP"."RATECLASS_ID"
        FROM "MSA"."GE_RATECLASS_BUY_MAP"
        WHERE  MSA."GE_RATECLASS_BUY_MAP"."BUY_ID" in ( 3,4,5,11 )  ) ) and
( a."RATECLASS_ID" = "MSA"."GE_RATECLASS_INVENTORY_MAP"."RATECLASS_ID" ) and
( MSA."GE_RATECLASS_INVENTORY_MAP"."INVENTORY_ID" in ( 3,1 )  )   AND
(a.week_date BETWEEN :a_week1 AND :a_week13 )
group by a.sell_id;

And possible values for the variables:

:a_week1 - date  - '30-sep-02'
:a_week13 - date   - '29-dec-02'
:a_sell_nm - string  - 'Oven Fresh - Prime , Oven Fresh - Day  '
:a_sell_id  - number array - 11,2
:a_rateclass_nm - string   - 'National , Billboard  '
:a_rateclass_id - number array - 3,2

Any insight would be greatly appreciated.
and we said...
but powerbuilder can use REF CURSORS and REF CURSORS can be opened via an if
statement.

create package my_pkg
as
   type rc is ref cursor;
   procedure get_result_set( p_cursor in out rc,
                             p_rateclass_nm in varchar2,
                             p_rateclass_id in number,
                             p_week1 in date,
                             p_week2 in date );
end;
/

create package body my_pkg
as
   procedure get_result_set( p_cursor in out rc,
                             p_rateclass_nm in varchar2,
                             p_rateclass_id in number,
                             p_week1 in date,
                             p_week2 in date )
   is
   begin
       if ( p_rateclass_nm = 'ALL RATECLASS' )
       then
           open p_cursor for select ...... where ....;
       else
           open p_cursor for select ....... where .......;
       end if;
   end;
end;
/

that'll fix that right up -- give you the BEST plans possible.
December 11, 2002 Ahmed -- Thanks for the question regarding "Changing expired password", version Oracle8.
You Asked
We are using PowerBilder 7.X (PB) and Visualbasic (VB)
application as developemnt tools and Oracle 8.X database server
as the backend database. PB and VB supports connecting to oracle
datbase server with a none expired password. The question is how
can I include afunctinality in my application to all users
change thier passwords if they are expired by calling some
external oracle sqlnet functions from my within appliction. I am
sure there is a way since Oracle Sql Plus allows the users to
change thier passwords.
and we said...
All users can change their passwords simply by executing:

alter user <theirname> identified by <newpassword>;

PB allows you to execute arbitrary SQL using dynamic sql so this
would work fine.  You would want your application to query
USER_USERS to determine the expiry_date of the users password to
see if they should change it or not.
Review & Followup
Rating: 5
Changing password expired by a Profile in VB December 11, 2002
Reviewer: Luis from Phoenix, AZ USA
Hi Tom,

We would like to implement Oracle profiles to manage passwords in our VB
applications. so far, we haven't been able to figure out how to change a
password when this is expired by the 'password_life_time' parameter of the
profile attached to the users. we can't use the 'alter user ... identified by'
command, since when the password is expired, the ORA-280001 error comes up and
the connection to the DB is not successful (if not connected, the 'ALTER...'
command cannot be used).

In other thread in your web site, there's an example using Pro*C (using EXEC SQL
CONNECT ... ALTER AUTHORIZATION ...). The question is, do you know how this can
be done (change an expired password) in VB? Is there something like the 'ALTER
AUTHORIZATION' command, for VB? Any other suggestion as to how to get around
with this??

Thanks a lot in advance.

Regards

Luis

Followup:
You know, I've never been tempted to use VB and I just don't know.

You can try otn.oracle.com under "discussion forums"...

December 12, 2002 Daryl -- Thanks for the question regarding "Can interMedia index creation be done in parallel?", version 8.1.7
You Asked
According to the syntax diagrams for CREATE INDEX, if domain index parameters
are specified, the PARALLEL clause can't be used. Is there any way to
parallelize index creation of interMedia indexes (interMedia Text in particular)
so that additional CPUs can actually be brought in to get it done. For some of
our tables, the creation of this index takes several hours, with much of the
delay caused by waiting for the INSO filter timeout to expire (in the event of
unindexable documents). Parallelizing this would seem to help with this problem.
Do you agree?
and we said...
see
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/inter.817/a77063/csql5.htm#19446
it supports parallel create index.
Review & Followup
Rating: 5
Doh. June 26, 2002
Reviewer: Daryl
Well, I've searched and searched Technet and couldn't find that link. Very
helpful. Thanks.

Rating: 1
For a domain index? December 12, 2002
Reviewer: Claudius
how can i use the index attributes in a domain index clause?

Followup:
sometimes wish I could put stars in here too....

0 stars to you for providing really no details about anything.  which
attributes, which domain index.  if it is interMedia/Text -- have you read the
documentation (which has examples and such). it is all online.

Rating: 5
Ups... January 07, 2003
Reviewer: Claudius
Sorry,

i tried it and got this message

ORA-29850 invalid option for creation of domain indexes

Cause: The user specified an invalid option like ASC, DESC, SORT or a parallel
clause, partitioning clause or physical attributes
clause.
Action: Choose one of the valid clauses for creation of domain indexes.

But it was my fault... I used also the NOLOGGING clause.

Rating: 5
Just for the record April 09, 2003
Reviewer: Daryl
Parallel CREATE INDEX for interMedia in 8.1.7 requires the underlying table to
be partitioned.

December 12, 2002 RIchard -- Thanks for the question regarding "Creating public synonyms", version 8.1.7
You Asked
Tom,
I have a few questions about synonyms.

1. When you create a public synonym, why does it grant select to public? I have
tested it out where i create a user with just create session and i am able to
see and select from a table with a public synonym. Also i have seen on older
versions that all privilages have been granted to public after creating a public
synonym.
2. We have developers who want to create public synonyms on every object in a
certain schema, is this a good idea or bad?
3. I have read notes, other sites about using a great deal of synonyms may cause
bad performance. Is this really the case??

Thank you in advance..

Richard
and we said...
1)
It does NOT.
It does NOT.
It does NOT.
Here is the proof:

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop user test cascade;
User dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create user test identified by test;
User created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> grant create session to test;
Grant succeeded.

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table t;
Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t ( x int );
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create public synonym t_synonym for t;
Synonym created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> @connect test/test

test@ORA817DEV.US.ORACLE.COM> select * from t_synonym;
select * from t_synonym
              *
ERROR at line 1:
ORA-00942: table or view does not exist

test@ORA817DEV.US.ORACLE.COM> @connect /

ops$tkyte@ORA817DEV.US.ORACLE.COM> grant select on t to test;
Grant succeeded.

ops$tkyte@ORA817DEV.US.ORACLE.COM> @connect test/test

test@ORA817DEV.US.ORACLE.COM> select * from t_synonym;
no rows selected

test@ORA817DEV.US.ORACLE.COM>

you need explicit GRANT on T in my example.  Check your DBA_SYS_PRIVS and
DBA_TAB_PRIVS to make sure PUBLIC hasn't been granted something it should not
have been (eg: maybe someone did something really bad like "grant select any
table to public" -- if they did, watch out -- revoking that will invalidate your
entire database and it'll take a while to recompile everything!)

2) it would be bad.  I would not recommend it.  They can use "alter session set
current_schema=THAT_APPLICATION" in their apps if they find it too onerous to
prefix the schema name on tables.

What happens next year when new application "Foo" comes along and wants to
create public synonyms for all of its stuff?  Name space clash -- both will have
a table "T" or something and won't be able to coexist.  You should use a bare
minimum number of public synonyms.

On asktom for example -- there is ONE public synonym to a function "f".  No
more, no less.  (we picked F as opposed to "ask_tom_main_entry_point" in order
to make the pages as small as possible)

3) yes.

go here:
http://www.ixora.com.au/home.htm
search for

public synonyms

and see why.
Review & Followup
Rating: 5
Right On! Thanks! August 03, 2002
Reviewer: Dan from Yukon Territory, Canada
Hi Tom - great comment - I think you hit that nail on the head - particularly
point 2.

I've been trying to get our (mostly contract) developers to stop, or at least
curtail their use of public synonyms.  This developer community seems hooked on
public synonyms - I think this is a throwback to some traditional development
strategy, which has never really made sense to me.  Too many public synonyms (as
you point out) contaminates a global namespace, and hinders our ability (as
production DBAs) to consolidate multiple small systems into a single instance.

So thanks for the above comments - I'm going to renew my efforts to stamp out
public synonyms.

Rating: 5
Updated Link November 27, 2002
Reviewer: Andy Nare from CIncinnati, OH
Always helpful.  The link you provided is now out of date.  Here is the updated
one.
http://www.ixora.com.au/newsletter/2001_05.htm

      Rating: 4
View &
      synonym dependency. where to find it ?  December 12, 2002

Reviewer:  Ram  from India
i have a synonym on a view. when the view is dropped & created again the synonym
doesnt recognize it and i get an
"table or view does not exist" error. but when i do a "create or replace view"
instead of drop/create it works ok. obviously the dependency gets broken in the
first case. i think its because the synonym uses the "object_id" of the view to
keep track and when we do a drop/create the view gets a new objectid and so the
link gets broken.
am i right ? if yes, where can i find this objectid synonym/view dependency ?
i dont see it in dba_dependencies.

here is a cut/paste from sqlplus.

ram@ORCL 8.1.7.0.0> create table t ( name varchar2(10))
  2  /
Table created.
ram@ORCL 8.1.7.0.0> create view t_v01 as select name from t
  2  /
View created.
ram@ORCL 8.1.7.0.0> create public synonym t_v01 for RAM.t_v01
  2  /
Synonym created.
ram@ORCL 8.1.7.0.0> grant select on t_v01 to scott
  2  /
Grant succeeded.
ram@ORCL 8.1.7.0.0> @connect scott/tiger
scott@ORCL 8.1.7.0.0> select * from t_v01;
no rows selected

-- now back to user RAM
ram@ORCL 8.1.7.0.0> alter table t add status char(3) null;
Table altered.
ram@ORCL 8.1.7.0.0> drop view t_v01;
View dropped.
ram@ORCL 8.1.7.0.0> create view t_v01 as select name, status from t;
View created.
ram@ORCL 8.1.7.0.0> @connect scott/tiger
scott@ORCL 8.1.7.0.0> select * from t_v01
  2  /
select * from t_v01
              *
ERROR at line 1:
ORA-00942: table or view does not exist

-----------a fresh start here--------------
ram@ORCL 8.1.7.0.0> create table t (name varchar2(10))
  2  /
Table created.
ram@ORCL 8.1.7.0.0> create view t_v01 as select name from t
  2  /
View created.
ram@ORCL 8.1.7.0.0> create public synonym t_v01 for RAM.t_v01
  2  /
Synonym created.
ram@ORCL 8.1.7.0.0> ed
Wrote file afiedt.buf

  1* grant select on t_v01 to scott
ram@ORCL 8.1.7.0.0> /

Grant succeeded.
ram@ORCL 8.1.7.0.0> @connect scott/tiger
Input truncated to 14 characters
scott@ORCL 8.1.7.0.0> select * from t_v01
  2  /
no rows selected

-- back to user RAM

ram@ORCL 8.1.7.0.0> alter table t add status char(3) null;

Table altered.
ram@ORCL 8.1.7.0.0> create or replace view t_v01 as select name, status from t
  2  /
View created.
ram@ORCL 8.1.7.0.0> @connect scott/tiger
Input truncated to 14 characters
scott@ORCL 8.1.7.0.0> select * from t_v01
  2  /

no rows selected

          Followup:

the dependency doesn't get broken.

You lost the GRANTS on the view.  A synonym is just that -- a synonym.  We
replace the referece to the synonym with the view name:

select * from SYNONYM;

is rewritten as:

select * from OWNER.VIEW;

The table or view does not exist comes about simply because when you dropped the
view, the grants went with it to the bit bucket.  You need to grant on the newly
created view.

The create OR REPLACE view syntax is designed so as to NOT drop the grants when
you update the view.  If that is the desired behaviour -- use that syntax.

This has nothing at all to do with synonyms.

December 12, 2002 Mike -- Thanks for the question regarding
      "Autonomous Transactions", version 8.1.7

      You Asked
Spec:
  Need a small lookup table with a b_default column and a trigger that allows 0
or 1 rows in the table to be set to "true" (= -1 for this question).  When a new
row is inserted where b_default = -1 then the trigger should remove update any
existant row where b_default = -1.  In short there should only exist one or zero
rows where b_default is true.  If a new row is inserted where b_default is true
then reset all other rows to b_default false.

Descr:
I have coded this with an before insert trigger with an autonomous transaction
(per your great book).  The only problem is that if I insert a row but rollback
the insert before commiting, the autonomous transaction has already reset all of
the b_default values, in effect loosing my previous b_default.  Note from the
example below that I have a similar problem with updates as inserts.

Question:
Is there a better way to code this?

<code>
SQL> CREATE TABLE x_short
  2      (pk                             VARCHAR2(32) NOT NULL primary key,
  3      val                            NUMBER,
  4      b_default                      NUMBER(1))
  5  /

Table created.
SQL> CREATE OR REPLACE TRIGGER tbi_short
  2  BEFORE INSERT
  3  ON x_short
  4  REFERENCING NEW AS NEW OLD AS OLD
  5  FOR EACH ROW
  6  declare
  7    pragma autonomous_transaction;
  8  begin
  9    -- if creating a new default then set all other default(s) false
 10    if (:new.b_default = -1) then
 11      update x_short set b_default = 0 where b_default = -1;
 12      commit;
 13    end if;
 14
 15    -- generate pk
 16    if :new.pk is null then
 17      execute immediate 'select sys_op_guid() from dual ' into :new.pk;
 18    end if;
 19
 20  End;
 21  /

Trigger created.

SQL> CREATE OR REPLACE TRIGGER tbu_short
  2  BEFORE UPDATE
  3  ON x_short
  4  REFERENCING NEW AS NEW OLD AS OLD
  5  FOR EACH ROW
  6  declare
  7    pragma autonomous_transaction;
  8  begin
  9
 10    if (:new.b_default = -1) then
 11      update x_short set b_default = 0 where b_default = -1;
 12      commit;
 13    end if;
 14
 15  end;
 16  /

Trigger created.

SQL> insert into x_short (val, b_default) values (1, 0);

1 row created.

SQL> insert into x_short (val, b_default) values (2, -1);

1 row created.

SQL> commit;

Commit complete.

SQL> select * from x_short;

PK                                      VAL  B_DEFAULT
-------------------------------- ---------- ----------
94F1CF888CEC526BE034080020C902BB          1          0
94F1CF888CED526BE034080020C902BB          2         -1

SQL> insert into x_short (val, b_default) values (3, -1);

1 row created.

SQL> commit;

Commit complete.

SQL> select * from x_short;

PK                                      VAL  B_DEFAULT
-------------------------------- ---------- ----------
94F1CF888CEC526BE034080020C902BB          1          0
94F1CF888CED526BE034080020C902BB          2          0
94F1CF888CEE526BE034080020C902BB          3         -1

SQL> insert into x_short (val, b_default) values (4, -1);

1 row created.

SQL> rollback;

Rollback complete.

SQL> select * from x_short;

PK                                      VAL  B_DEFAULT
-------------------------------- ---------- ----------
94F1CF888CEC526BE034080020C902BB          1          0
94F1CF888CED526BE034080020C902BB          2          0
94F1CF888CEE526BE034080020C902BB          3          0

SQL> truncate table x_short;

Table truncated.
-- similar problem with inserts.

SQL> insert into x_short (val, b_default) values (1, -1);
1 row created.

SQL> insert into x_short (val, b_default) values (2, 0);
1 row created.

SQL> commit;
Commit complete.

SQL> select * from x_short;
PK                                      VAL  B_DEFAULT
-------------------------------- ---------- ----------
94F1CF888CEA526BE034080020C902BB          1         -1
94F1CF888CEB526BE034080020C902BB          2          0

SQL> update x_short set b_default = -1 where val = 2;
1 row updated.

SQL> commit;

Commit complete.

SQL> select * from x_short;

PK                                      VAL  B_DEFAULT
-------------------------------- ---------- ----------
94F1CF888CEA526BE034080020C902BB          1          0
94F1CF888CEB526BE034080020C902BB          2         -1

SQL> update x_short set b_default = -1 where val = 1;

1 row updated.

SQL> rollback;

Rollback complete.

SQL> select * from x_short;

PK                                      VAL  B_DEFAULT
-------------------------------- ---------- ----------
94F1CF888CEA526BE034080020C902BB          1          0
94F1CF888CEB526BE034080020C902BB          2          0

</code>

      and we said...
Well, I'm sorry that you inferred from my book that this was a "good practice".
I gave an example (auditing related) but also said "this does not mean that we
will use autonomous transactions to 'fix' mutating table error any time we hit
them-- they must be used cautiously and with the knowledge of how the
transaction is really progressing.  In the caveats section later, we will
explore the reasons for this.  The mutating table error is actually for your
protection....'

This is a really super tricky question here! What you need to do is avoid the
mutating table, see:

http://osi.oracle.com/~tkyte/Mutate/index.html
say you have a table T ( id int primary key, ...., b_default int );

you could:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger t_bi before insert
or update on t
  2  begin
  3      if ( not state_pkg.inTrig )
  4      then
  5          state_pkg.modified := state_pkg.empty;
  6      end if;
  7  end;
  8  /
Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger t_bifer before
insert or update on t
  2  for each row
  3  begin
  4      if ( not state_pkg.inTrig and :new.b_default = -1 )
  5      then
  6          state_pkg.modified(state_pkg.modified.count+1) := :new.id;
  7      end if;
  8  end;
  9  /
Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger t_ai after insert
or update on t
  2  begin
  3      if ( not state_pkg.inTrig )
  4      then
  5      begin
  6          state_pkg.inTrig := TRUE;
  7          for i in 1 .. state_pkg.modified.count
  8          loop
  9              update t set b_default = 0
 10               where b_default = -1;
 11
 12              update t set b_default = -1
 13               where id = state_pkg.modified(i);
 14          end loop;
 15          state_pkg.inTrig := FALSE;
 16      exception
 17          when others then
 18              state_pkg.inTrig := FALSE;
 19              raise;
 20      end;
 21      end if;
 22  end;
 23  /

Trigger created.

But frankly, I would rather just create a stored procedure to do this:

procedure update_b_default( p_id in int )
as
begin
   update t set b_default = 0 where b_default = -1;
   update t set b_default = -1 where id = p_id;
end;
/

thats much easier....
Review & Followup

      Rating: 5
just one more
      thing ...  December 13, 2001
Reviewer:  Mike
      Wilson  from Los Angeles, CA
Simple Follow Up:
Where do you define 'state_pkg.inTrig'?  Is it simply an integer in a package
somewhere?  I don't see it in your response.

Thanks for the code but as I have learned by now the complicated solution is
rarely the one to use.  I agree with you and would probably prefer the
procedural version.

I was also thinking about simply submitting a dbms_job to update the recordset
in the trigger and letting the commit from the transaction commit the job also
(I think you mention this in your, and I can't say this enough, *excellent*
book).  Thanks for your time.

          Followup:

doh -- sorry about that -- missed that part of the example on the cut and paste.

It was:

create or replace package state_pkg
as
    type array is table of int index by binary_integer;

    modified array;
    empty    array;
    inTrig   boolean default FALSE;
end;
/

thanks for the feedback on the book -- appreciate it.

      Rating: 3
Slightly
      different variation  July 08, 2002
Reviewer:
      Lisa  from Fairfax, VA USA
This is very similar to a problem that I am facing, although mine is slightly
more complex...
I have a table that specifies many different labels for an organization entity.
One and only one label must be the default at any given time for a particular
org.
E.g. table t has columns org, label, org_default.
If when inserting or updating there is not a current default label, the row on
which I am operating must become the default.  Alternately, if my
:new.org_default='Y' for the label, I need to update the previous default to
'N'.  The application code will never allow a label to change explicity from 'Y'
to 'N', the user will specify another label as the default and the trigger must
reset the old default.
I feel that the answer to my question is here, can you give a an additional
push?

Thanks,

Lisa

          Followup:

This is BEGGING for a stored procedure -- rather then the application doing
"insert into", the application does "begin do_insert( .... ); end;"

It'll be INCREDIBLY easy and straight forward and understandable (as opposed to
the triggers).

sounding redundant here -- as that is the same advice I gave to the other person
;)

You can do it with triggers but it is ugly and hard to understand.  Here is one
approach (requires the table have a primary key!)

ops$tkyte@ORA817DEV.US.ORACLE.COM> create sequence t_seq;
Sequence created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table T
  2  ( id number primary key,
  3    org varchar2(5),
  4    label varchar2(5),
  5    org_default varchar2(1) default 'N' );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create unique index t_idx on
t(decode(org_default,'Y',org,null));

Index created.

that index makes SURE at most one "org_default=Y" value per org.  Best to have
that *just in case* something is wrong in the logic below -- it'll prevent
logically corrupt data

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package state_pkg
  2  as
  3      type array is table of number index by binary_integer;
  4
  5      modified         array;
  6      check_these      array;
  7      empty            array;
  8      inTrig           boolean default false;
  9  end;
 10  /

Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger t_bi before insert
or update on t
  2  begin
  3      if ( not state_pkg.inTrig )
  4      then
  5          state_pkg.modified := state_pkg.empty;
  6          state_pkg.check_these    := state_pkg.empty;
  7      end if;
  8  end;
  9  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger t_bifer before
insert or update on t
  2  for each row
  3  begin
  4      if ( not state_pkg.inTrig )
  5      then
  6          if ( :new.org_default = 'Y' )
  7          then
  8              state_pkg.modified(state_pkg.modified.count+1) := :new.id;
  9              :new.org_default := 'N';
 10          else
 11              state_pkg.check_these(state_pkg.check_these.count+1) :=
:new.id;
 12          end if;
 13      end if;
 14  end;
 15  /

Trigger created.

Keeping the ids of records we need to set to "Y" in modified -- note how we
undo the org_default for a moment here, so as to not violate our unique index
above!

check_these are id's that need to be checked to see if they should default to
'Y' -- we need to check the rows that are just inserted and inserted with 'N'

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger t_ai after insert
or update on t
  2  begin
  3      if ( not state_pkg.inTrig )
  4      then
  5      begin
  6          state_pkg.inTrig := TRUE;
  7          for i in 1 .. state_pkg.modified.count
  8          loop
  9              update t set org_default = decode( id, state_pkg.modified(i),
'Y', 'N' )
 10               where org = ( select org from t where id =
state_pkg.modified(i) );
 11          end loop;

for the ones that were inserted with Y, update all of the other org records to
'N', setting just this one to 'Y'

 12          for i in 1 .. state_pkg.check_these.count
 13          loop
 14              update t set org_default = 'Y'
 15               where id = state_pkg.check_these(i)
 16                 and not exists ( select null
 17                                    from t
 18                                   where org = ( select org from t where id =
state_pkg.check_these(i) )
 19                                     and org_default = 'Y' );
 20          end loop;
 21          state_pkg.inTrig := FALSE;

for the ones we need to check, set them to 'Y' IF no other row exists in that
org with 'Y" already.

This does have a race condition in it whereby if you have two sessions that both
insert the "first" row at the same time -- since they cannot "see" eachothers
inserts -- the unique index t_idx above will serialize them.  ONLY ONE session
will succeed, the other will get "dup_val_on_index" -- which I suppose you could
"ignore" (since the goal of the first row in being 'Y' is achieved) if you
wanted.

 22      exception
 23          when others then
 24              state_pkg.inTrig := FALSE;
 25              raise;
 26      end;
 27      end if;
 28  end;
 29  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t ( id, org, label ) values (
t_seq.nextval, 'a', 'aa' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

        ID ORG   LABEL O
---------- ----- ----- -
         1 a     aa    Y

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t ( id, org, label ) values (
t_seq.nextval, 'a', 'bb' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

        ID ORG   LABEL O
---------- ----- ----- -
         1 a     aa    Y
         2 a     bb    N

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t ( id, org, label ) values (
t_seq.nextval, 'a', 'cc' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

        ID ORG   LABEL O
---------- ----- ----- -
         1 a     aa    Y
         2 a     bb    N
         3 a     cc    N

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t ( id, org, label, org_default )
values ( t_seq.nextval, 'a', 'dd', 'Y' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

        ID ORG   LABEL O
---------- ----- ----- -
         1 a     aa    N
         2 a     bb    N
         3 a     cc    N
         4 a     dd    Y

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t ( id, org, label ) values (
t_seq.nextval, 'b', 'aa' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

        ID ORG   LABEL O
---------- ----- ----- -
         1 a     aa    N
         2 a     bb    N
         3 a     cc    N
         4 a     dd    Y
         5 b     aa    Y

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t ( id, org, label ) values (
t_seq.nextval, 'b', 'bb' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

        ID ORG   LABEL O
---------- ----- ----- -
         1 a     aa    N
         2 a     bb    N
         3 a     cc    N
         4 a     dd    Y
         5 b     aa    Y
         6 b     bb    N

6 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t ( id, org, label ) values (
t_seq.nextval, 'b', 'cc' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

        ID ORG   LABEL O
---------- ----- ----- -
         1 a     aa    N
         2 a     bb    N
         3 a     cc    N
         4 a     dd    Y
         5 b     aa    Y
         6 b     bb    N
         7 b     cc    N

7 rows selected.

ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into t ( id, org, label, org_default )
values ( t_seq.nextval, 'b', 'dd', 'Y' );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from t;

        ID ORG   LABEL O
---------- ----- ----- -
         1 a     aa    N
         2 a     bb    N
         3 a     cc    N
         4 a     dd    Y
         5 b     aa    N
         6 b     bb    N
         7 b     cc    N
         8 b     dd    Y

8 rows selected.

      Rating: 5
Why a trigger
      with autonomous_transaction does not work for multiple updates in one
      statement?  September 12, 2002
Reviewer:  A
      Reader  from USA
We are trying to update another table based on the update result of a table and
have some problems doing it by using a trigger with autonomous_transaction.

create table t1 (Key_value number, sub_key number,CHANGE_VALUE number);
insert into t1 values(1,1,1);
insert into t1 values(1,2,1);
insert into t1 values(1,3,0);
insert into t1 values(2,1,1);
insert into t1 values(2,2,0);
insert into t1 values(2,3,0);
commit;

create table t2(key_value number, monitor_value number);
insert into t2  values(1,0);
insert into t2  values(2,0);
commit;

create table tab_temp (Key_value number, sub_key number,CHANGE_VALUE number);

CREATE OR REPLACE TRIGGER TRg_check
BEFORE UPDATE ON t1
FOR EACH ROW
DECLARE Avg_change NUMBER:=0;
pragma autonomous_transaction;
BEGIN
    IF :NEW.CHANGE_VALUE=0 THEN
      UPDATE t2 SET monitor_value = 0 WHERE Key_value = :NEW.Key_value;
    ELSE
      Insert into tab_temp select Key_value,sub_key,CHANGE_VALUE from t1
   where Key_value=:NEW.Key_value;
      SELECT AVG(CHANGE_VALUE) INTO Avg_change FROM tab_temp;
      IF (Avg_change = 1) THEN
        UPDATE t2 SET monitor_value = 1 WHERE Key_value = :NEW.Key_value;
      ELSE
      UPDATE t2 SET monitor_value = 0 WHERE Key_value = :NEW.Key_value;
      END IF;
    END IF;
    commit;
END;

/

The purpose of using the trigger is to determine the monitor_value in t2 based
on the change_value in t1 (including the newly inserted value).  If the
change_value in t1 for a key_value is 1 for all it's sub_key values, then the
monitor_value in t2 is set to 1; otherwise, if any of the change_value for a
key_value is 0, the monitor_value is set to 0.
The trigger works fine when we update one change_value each time.  However, when
we update two or more change_values, the trigger works incorrectly. Say, "update
t1 where key_value=1 and sub_key in (2,3);"
When we check the tab_temp (which should be global temporary table), we get more
than what we want and the results are wrong.  Can you please help.

Qestion:
1.  Is that the right way to do what we want by using this kind of trigger?  We
tried to remove the "pragma autonomous_transaction" and "commit", we endup with
mutating the table.
2.  Why the trigger works for one update but not for multiple row updates in one
statement?

Thank you very much for your help.

          Followup:

bad use of autonomous transaction.  You do understand that your updates to T2
might succeed whilst the insert into T1 failed and is rolled back (eg: your data
integrity is totally OUT THE WINDOW, gone, bzzzt - game over player one)

Anyway, you need to defer the reading of T1 until AFTER the insert is processed.

see

http://asktom.oracle.com/~tkyte/Mutate/index.html
for some techniques

      Rating: 5
Use an Instead
      of Trigger can do it.  September 13, 2002

Reviewer:  A Reader  from USA
Thank you Tom.  I actually got the idea from your book (one on one p665). I
think I misunderstood your meaning in the book. Now I can resolve it by creating
a view (v1)which is the same as t1 and creating an instead of trigger without
using the temporary table.  It seems to be ok now.  The only thing is that all
the developers and users need to remember just update v1 not t1.
However, I still don't understand why a trigger works fine for one row updating
but not for multiple updating in one statement. In the insert statement, I even
put an extra condition in where clause:
Insert into tab_temp select Key_value,sub_key,CHANGE_VALUE from t1 where
Key_value=:NEW.Key_value and sub_key<>:new.sub_key;
Thank you again for your help.

          Followup:

rename t1 to t1_table;
create or replace view t1 as .....;

grant on t1;
revoke on t1_table;

and no one will be the wiser.

As for the one row vs multiple rows:  we KNOW in a single row insert, that you
can get a consistent, meaningful, "data integrity is preserved" look at that
table in the trigger.  In a multi-row insert -- when you query the table in the
trigger that is being inserted into -- you'll see it in a fuzzy state -- some of
the rows will be inserted, some won't.  And if you execute the SAME exact insert
two times in a row like this:

insert into t select * from another_table;
-- take a look at T
rollback;
alter session set sort_area_size = 10000000;
insert into t select * from another_table;
-- take a look at T

You could end up with two different results if we let you peek at the table to
make decisions (eg: the order of the rows would be/could be totally different,
looking at that mutating table would lead to strange and really incredibly hard
to reproduce issues)

We only distinguish between:

insert into t values ( ... ) <<<<---- single row insert
insert into t SELECT <<<<<---- multi-row insert, regardless of how many rows
                               are actually returned.

      Rating: 5
Getting
      multiple rows inside statement trigger  December 12, 2002

Reviewer:  Sandra
Please Tom,

In a for each row trigger, I can use :new to get the new (individual) line
columns. Suppose I have a statement trigger (which fires once no matter the
number of rows).
I cannot use :new, because it only works in for each row triggers (right?).

Question: How can I get this SET of lines that have just been inserted using a
statement trigger ?

Thank you.

          Followup:

You cannot.  It does not work that way.

see the above link about Mutate.html -- it shows how you could capture the
rowids/primary keys of the newly inserted rows.

December 12, 2002 Angelo -- Thanks for the question regarding
      "creating a view on a partitioned table", version 8.1.7

      You Asked
Tom,

I have a table that is partitioned and would like to create a view on this
table. Here is my problem.  To access a certain partiton in this view I must use
the partition value in my where clause, where as if it was just a straight
select from the table I could use the partition(value) clause.

Is there another way of accessing the partition in the view similar to the
select for the table?

      and we said...
ops$tkyte@ORA920.US.ORACLE.COM> select * from v partition(part_95);
select * from v partition(part_95)
              *
ERROR at line 1:
ORA-14109: partition-extended object names may only be used with tables

No -- and it makes sense if you ponder it, views may be ANY arbitrary query, any
at all.  A synonym might be useful here:

ops$tkyte@ORA920.US.ORACLE.COM> create synonym s for t;

Synonym created.

ops$tkyte@ORA920.US.ORACLE.COM> select * from s partition(part_95);

no rows selected

if you just need a different name for the table.
December 12, 2002 Prince -- Thanks for the question regarding
      "Latches and serialization", version 8.1.7.2

      You Asked
Tom,

When there is latch, is the entire DB operation is serialized or certain
operations are serialized?

Assume there are queries with literals. (consider the case of latches on the
shared pool/library cache)

Ex,
1. select col1, col2 from tab1 where col3 = 'Valx';

2, select col1, col2 from tab2 where col1 = 'Val1';

If the above queries (fresh) are executed exactly at the same point will one
them has to wait for the parsing to be done, even if the tables do not have any
relation to each other?

In summary, will any latch cause every other operation which need the latch to
be serialized?

As you always do, some explanation would be wonderful.

Thanks,

      and we said...
A latch is a lightweight lock used to protect a data structure.  If I have
latched a data structure, no one else will able to (they wait).  It is not the
whole database -- just that structure.

Yes, the hard parses require MANY MANY latches - on data structures in the
shared pool.  They will get in eachothers way.

Yes -- latches = locks, locks = serialization device, serialization devices =
less scalable, slower.

Get my book -- I spend about 1300 pages on this topic!
Review &
      Followup

      Rating:
      4
qu  May 05, 2002
Reviewer:  liz
       from fl
hi
serialization means in queue is it?
liz

          Followup:

serialization  in this case means "one at a time, after one another"

      Rating: 4
your
      book  May 05, 2002
Reviewer:  ab  from
      asia
tom
we r not getting your book here in asia i searched a lot lot since last few
months.
it will cost v.much if i buy it thru mail, in local money.do
do something for us

          Followup:

Asia is a big place, anything more specific then that?

      Rating:
      4
asia  May 06, 2002
Reviewer:  ab
       from pakistan
sorry for being late sir
Its Pakistan I m talking of.
will be thankful if u could do anything for us

          Followup:

I can help out by suggesting you get a new keyboard -- your keyboard seems to be
dropping letters such as Y and O and even A's from time to time!

Take the ISBN for the book to a reputable bookstore, they should be able to get
it for you - It is over there, that I know.

      Rating: 5
It is There in
      Pakistan  May 07, 2002
Reviewer:
      A reader
Sorry to Budge in Like this But thought i would help in something that i know
;-D

Yes the Book is available in Pakistana and the indian subcontinent. It is being
published by shorff Publishers from Bombay.

HTH

Regards,
Ganesh R

      Rating:
      3
thankyou  May 07, 2002
Reviewer:  ab
       from sargodha, pak
thankyou a reader
i am trying, inshallah i will find it from somewhere.

      Rating:
      3
thankyou  May 07, 2002
Reviewer:  ab
       from sargodha, pak
thankyou a reader
i am trying, inshallah i will find it from somewhere.

      Rating: 4
Thanks
      Tom  May 07, 2002
Reviewer:  Prince.

Thank you Tom, for your response.

There are "latches" (V$LATCH) and "latch children" (V$LATCH_CHILDREN). If there
is a latch in the parent level will it latches all the children?

Or, will the latches be always on the child level if the latch has children?

          Followup:

Most of the internal structures are protected by one latch.  Some cases (eg: in
the library cache for example), we have multiple latches each protecting part of
the data structure (different parts of the library cache).  These "multiple"
latches are called CHILD LATCHES.  There is one library cache "parent"  with
many children.

You will see the child latches being taken, it will be very rare to see a parent
latch that has children being taken.

Wanna good book on this topic:

http://www.amazon.com/exec/obidos/ASIN/156592598X
....

      Rating: 4
Shared Pool one
      latch?  May 08, 2002
Reviewer:  Dave
       from CT, USA
Tom is the shared pool protected by one latch?
When we parse? We only can do one at a time?

          Followup:

There are child library cache latches (as indicated by the above comment:

... Some cases (eg: in
the library cache for example), we have multiple latches each protecting part of

the data structure (different parts of the library cache).....

      Rating: 5
latches and
      serialization  May 08, 2002
Reviewer:  prakash
      eranki  from Singapore
That was really superb explanation.

      Rating: 4
Related
      Question  May 08, 2002
Reviewer:  Prince

Tom,

I have another related question.

I have a long running select statement. The wait_stat event shows that one of
the wait was on "library cahe" (event: latch_free).

SELECT name, child#, gets, misses, sleeps
FROM v$latch_children
WHERE addr='aaaa';

NAME                             CHILD#        GETS     MISSES     SLEEPS
-------------------------------- ------- ---------- ---------- ----------
library cache                    3        244270286     663240     185443

select * from v$session_event where sid = xx;

EVENT                            TOTAL_WAITS TOTAL_TIMEOUTS TIME_WAITED
AVERAGE_WAIT   MAX_WAIT
-------------------------------- ----------- -------------- -----------
------------ ----------
latch free                             13799           7492         257
.018624538          6

EVENT                            TOTAL_WAITS TOTAL_TIMEOUTS TIME_WAITED
AVERAGE_WAIT   MAX_WAIT
-------------------------------- ----------- -------------- -----------
------------ ----------
latch free                             14073           7644         268
.019043559          6

Now my question is, once my query is parsed why is it waiting on the "library
cache" latch?.

Can you explain, why I am waiting on this event, once my statement is parsed and
started executing?

my v$sesstat parse count is not varying/increasing. So my session is not parsing
again and again.

Thanks,

          Followup:

why do you believe it was the library cache latch?  More likely the cache
buffers chains latches.

If you have my book see chapter 10, i describe how to generate a trace file with
the wait events.  When the wait even is latch free, the values for:

    * P1 = Latch address
    * P2 = Latch number
    * P3 = Tries

are usful, you can goto

 SELECT * FROM v$latchname
   WHERE latch# = &P2
;

to see what latch it is really waiting on.

      Rating: 3
I traced the
      session during the query execution..  May 08, 2002

Reviewer:  Prince
Tom,

Thanks for the update.

I traced the session while the query was executing.

I used event 10046 on level 12. Is that the right one?

% grep WAIT ora_xxxx.trc > wait.log

WAIT #1: nam='db file sequential read' ela= 0 p1=14 p2=147292 p3=1
WAIT #1: nam='db file scattered read' ela= 0 p1=14 p2=147294 p3=8
WAIT #1: nam='db file scattered read' ela= 0 p1=14 p2=147302 p3=8
WAIT #1: nam='db file scattered read' ela= 0 p1=14 p2=147310 p3=8
WAIT #1: nam='db file scattered read' ela= 0 p1=14 p2=147318 p3=2
WAIT #1: nam='db file scattered read' ela= 0 p1=14 p2=147322 p3=2
WAIT #2: nam='latch free' ela= 0 p1=1677847532 p2=106 p3=0
WAIT #1: nam='db file sequential read' ela= 0 p1=14 p2=147326 p3=1
WAIT #1: nam='db file scattered read' ela= 0 p1=14 p2=147329 p3=8
WAIT #2: nam='latch free' ela= 0 p1=1677847532 p2=106 p3=0
WAIT #2: nam='latch free' ela= 0 p1=1677847532 p2=106 p3=0
WAIT #2: nam='latch free' ela= 0 p1=1677847532 p2=106 p3=0
WAIT #2: nam='latch free' ela= 0 p1=1677847532 p2=106 p3=0
WAIT #2: nam='latch free' ela= 0 p1=1677847740 p2=106 p3=0
WAIT #2: nam='latch free' ela= 0 p1=1677847740 p2=106 p3=1
WAIT #2: nam='latch free' ela= 0 p1=1677847532 p2=106 p3=0
WAIT #2: nam='latch free' ela= 0 p1=1677847532 p2=106 p3=1
WAIT #2: nam='latch free' ela= 0 p1=1677847532 p2=106 p3=0
WAIT #2: nam='latch free' ela= 0 p1=1677847532 p2=106 p3=0
WAIT #2: nam='latch free' ela= 0 p1=1677847532 p2=106 p3=0
WAIT #2: nam='latch free' ela= 0 p1=1677847532 p2=106 p3=1
WAIT #2: nam='latch free' ela= 0 p1=1677847740 p2=106 p3=0
WAIT #2: nam='latch free' ela= 0 p1=1677847740 p2=106 p3=1
WAIT #2: nam='latch free' ela= 0 p1=1677847532 p2=106 p3=0
WAIT #2: nam='latch free' ela= 0 p1=1677847532 p2=106 p3=1
WAIT #2: nam='latch free' ela= 0 p1=1677847740 p2=106 p3=0
WAIT #2: nam='latch free' ela= 0 p1=1677847740 p2=106 p3=0
WAIT #2: nam='latch free' ela= 0 p1=1677847532 p2=106 p3=0
WAIT #2: nam='latch free' ela= 0 p1=1677847532 p2=106 p3=1

SQL> select * from v$latchname where latch# = 106 ;

    LATCH# NAME
---------- ----------------------------------------------------------------
       106 library cache

          Followup:

different cursors there.

#1 and #2.  Don't know what #2 is -- but the first one #1, looks like your
select.

Edit the raw trace file (if you have my book -- chapter 10 describes what you'll
see)....

Look in the raw trace file....  You'll find that cursor #2 is something totally
different (or many many different statements)

      Rating: 4
Latches and
      serialization  May 09, 2002
Reviewer:
      Bharath  from India
Hi Tom,

       Let me thank you for the wonderful book. I have two questions

   1. How to avoid soft parse? One solution is keep the cursor open till it
completes its utility. But If I have a cursor in a formula column of a report
how can I keep it open for all the records returned by the query. Is there any
workaround for this. And what are the other ways to avoid soft parse?

   1. I used the following syntax to generate trace file but no information
related to waits were generated?

Thanks,
Bharath

          Followup:

1) to avoid a soft parse, keep the cursor open.  I don't use reports -- never
wrote one in my life.  I don't know what to tell you there.

1) what syntax.  we only print out wait information WHEN there is a wait.
perhaps you had none.  it happens.

      Rating:
      4
Latches  May 09, 2002
Reviewer:
      Bharath  from India
Sorry. My previous question did not have the command that I had used in my
reports.

SRW.DO_SQL('ALTER SESSION SET EVENTS ''10046 trace name context forever, level
12''');

Thanks,
bharath

          Followup:

You may well not have had any waits.

      Rating: 5
Thanks Tom. But
      the other query uses bind variable  May 09, 2002

Reviewer:  Prince
Thank you very much Tom.

My original query calls a function which calculates the months since 1970.

The original query was like,

"select col1, col2, myfun(col3_date) from big_table."

Now, why does it cause so many latches though I calculate the months from
col3_date to 1970 thru myfun (pl/sql)?

Note: TO_DATE('01-Jan-1970','DD-MM-YYYY'), is not in the right format. Should
that be of any concern?

I have your book and I am still reading it. I have Steve Adma's book as well

Thanks,

*********Begin - Entry from RAW trace file ***************
PARSING IN CURSOR #2 len=89 dep=1 uid=34 oct=3 lid=34 tim=3305195552
hv=3915096024 ad='655b4bfc'
SELECT MONTHS_BETWEEN(TRUNC(:b1,'MONTH'),TO_DATE('01-Jan-1970','DD-MM-YYYY'))
FROM DUAL
END OF STMT
*********End - Entry from RAW trace file ******************

*********Begin - Entry from tkprof ************************

SELECT MONTHS_BETWEEN(TRUNC(:b1,'MONTH'),TO_DATE('01-Jan-1970','DD-MM-YYYY'))

FROM
 DUAL

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        0      0.00       0.00          0          0          0           0
Execute 108042     26.11      25.59          0          0          0           0
Fetch   108042     16.85      17.46          0     108042     432168      108042
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total   216084     42.96      43.05          0     108042     432168      108042

*********End - Entry from tkprof ************************

          Followup:

Because you are calling PLSQL from SQL and that will do that.

You should just call months_between DIRECTLY.  Only use plsql in sql when left
with no other choice (eg: code is way to hairy to do in sql).  Calling that 108k
times is "bad".

      Rating:
      5
Excellent  May 09, 2002
Reviewer:
      Prince
I am wondering, why does a simple "select 'x' from dual" causes "4" db block
gets and "1" consistent get?

Also, is it the context switching between pl/sql and sql causes that many
library cache latches?

Thanks.

          Followup:

the 4 db block gets are the reading of the segment header -- you'll see it for
all full scans of any table.

I'd have to see the entire plsql routine in order to comment.  It shouldn't in
general.  In this case, I would say it was all of the recursive sql being done.

If you compare:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table run_stats ( runid varchar2(15),
name varchar2(80), value int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> grant select any table to ops$tkyte;
Grant succeeded.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace view stats
  2  as select 'STAT...' || a.name name, b.value
  3        from v$statname a, v$mystat b
  4       where a.statistic# = b.statistic#
  5      union all
  6      select 'LATCH.' || name,  gets
  7        from v$latch;

View created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function f1(p_date in date)
return number
  2  as
  3      l_return number;
  4  begin
  5      SELECT
MONTHS_BETWEEN(TRUNC(p_date,'MONTH'),TO_DATE('01-Jan-1970','DD-MM-YYYY'))
  6        into l_return
  7        from dual;
  8      return l_return;
  9  end;
 10  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function f2(p_date in date)
return number
  2  as
  3  begin
  4      return
MONTHS_BETWEEN(TRUNC(p_date,'MONTH'),TO_DATE('01-Jan-1970','DD-MM-YYYY'));
  5  end;
  6  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> declare
  2      l_dummy number;
  3      l_start number;
  4  begin
  5      insert into run_stats select 'before', stats.* from stats;
  6
  7      l_start := dbms_utility.get_time;
  8      for i in 1 .. 1000
  9      loop
 10          l_dummy := f1(sysdate+i);
 11      end loop;
 12      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 13
 14
 15      insert into run_stats select 'after 1', stats.* from stats;
 16      l_start := dbms_utility.get_time;
 17      for i in 1 .. 1000
 18      loop
 19          l_dummy := f2(sysdate+i);
 20      end loop;
 21      dbms_output.put_line( (dbms_utility.get_time-l_start) || ' hsecs' );
 22
 23      insert into run_stats select 'after 2', stats.* from stats;
 24  end;
 25  /
44 hsecs
25 hsecs

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select a.name, b.value-a.value run1,
c.value-b.value run2,
  2         ( (c.value-b.value)-(b.value-a.value)) diff
  3    from run_stats a, run_stats b, run_stats c
  4   where a.name = b.name
  5     and b.name = c.name
  6     and a.runid = 'before'
  7     and b.runid = 'after 1'
  8     and c.runid = 'after 2'
  9     and (c.value-a.value) > 0
 10     and (c.value-b.value) <> (b.value-a.value)
 11   order by abs( (c.value-b.value)-(b.value-a.value))
 12  /

NAME                                 RUN1       RUN2       DIFF
------------------------------ ---------- ---------- ----------
LATCH.done queue latch                  1          0         -1
LATCH.ksfv messages                     1          2          1
LATCH.undo global data                  0          1          1
STAT...free buffer requested            3          2         -1
STAT...parse count (total)              6          5         -1
STAT...opened cursors current           2          1         -1
STAT...opened cursors cumulati          6          5         -1
ve

STAT...enqueue releases                 3          2         -1
LATCH.direct msg latch                  0          2          2
LATCH.session queue latch               0          2          2
STAT...enqueue requests                 4          2         -2
LATCH.redo writing                      1          3          2
STAT...redo entries                    16         13         -3
LATCH.cache buffers lru chain           2          6          4
LATCH.messages                          1          5          4
STAT...db block changes                27         22         -5
LATCH.checkpoint queue latch            2          8          6
LATCH.redo allocation                  14         24         10
LATCH.shared pool                     129        118        -11
STAT...recursive cpu usage             28          4        -24
STAT...redo size                    21488      21432        -56
STAT...buffer is not pinned co       1016         16      -1000
unt

STAT...consistent gets               1011         11      -1000
STAT...execute count                 1005          5      -1000
STAT...table scan blocks gotte       1000          0      -1000
n

STAT...table scans (short tabl       1000          0      -1000
es)

STAT...table scan rows gotten        1000          0      -1000
STAT...no work - consistent re       1008          8      -1000
ad gets

STAT...calls to get snapshot s       1006          5      -1001
cn: kcmgss

STAT...recursive calls               1031         30      -1001
LATCH.library cache                  2281        272      -2009
STAT...db block gets                 4024         17      -4007
STAT...session logical reads         5035         28      -5007
LATCH.cache buffers chains          10093        133      -9960

34 rows selected.

question from me -- why do so many people "select .... from dual" in plsql
when a simple assignment would work??

      Rating: 4
What if it is
      called from within a package?  May 09, 2002

Reviewer:  Prince
Also, I forgot to tell you that this function is a part of a package and is
mostly used within the package.

In that case, will it have much difference if it is a function or direct
"months_between"?

Meantime, let me test the difference.

Thanks,

          Followup:

package/function/procedure -- won't matter.  It's the recursive sql, the context
switching.  DO IT STRAIGHT IN SQL.  It'll be faster, cheaper, better.

      Rating:
      5
Perfect.  May 09, 2002
Reviewer:
      Prince
The explation was wonderful.

Did some testing and straight sql was way faster.

Thanks.

      Rating: 4
Also isn't it
      true we can not always do direct assignment?  May 09, 2002

Reviewer:  Prince
IN some cases, like assigning "NEXTVAL", we have to select it from dual. Or, can
this also we avoided by some means?

          Followup:

That is correct, NEXTVAL must come from the database, you must select it.

I'm talking about the 99.99% of the times when you don't need to!

      Rating: 5
Raw trace file
      waits analysis  June 21, 2002
Reviewer:
      Amit  from Dallas, TX USA
Hi Tom,

I have searched the Oracle docs, metalink articles,
browsed your site, and read the chapter in your book discussing
raw trace files and how to interpret them.

I have several questions related to traces with waits, some of which are already
addressed in this thread,
so thanks are in order already...

Generally- what are the rules regarding appearances of wait
event lines interspersed between the other activity lines in the
trace file, and what are the rules for associating a wait event
with a statement / activity within a statement.

Any algorithm / insight you could give in this regard will be highly
appreciated.

For example-

      :
(01) WAIT #1
      :
     ====================================
(02) PARSING IN CURSOR #1 hv=A dep=0
      :
(03) EXEC #1
      :
(04) WAIT #1
      :
(05) EXEC #1
      :
(06) WAIT #1
      :
     ====================================
(07) PARSING IN CURSOR #2 hv=C dep=1 -- recursive stmt as result of stmt C
      :
(08) WAIT #1
      :
     ====================================
(09) PARSING IN CURSOR #1 hv=B dep=0
      :
(10) PARSE #1
      :
(11) WAIT #1
      :
(12) EXEC #1
      :
(13) FETCH #1
      :
(14) WAIT #1
      :
(15) STAT #1
      :
(16) WAIT #1
      :

1. Associating waits with stmts.
   Consider the waits on cursor #1-
   (01): How can we know if this wait is related to stmt "A" ?
         could it be related to a previous stmt of CURSOR #1 ?
         are there different answers depending on the event itself ?
   (04): Since this wait is between two EXEC operations while the cursor
         is working on stmt "A", is it always true that this wait
         is related to directly to stmt "A" ?
   (06): This wait is between an EXEC operation of stmt "A", and
         PARSE,EXEC operations of stmt "B".
         How can we determine if it is related to stmt "A" or "B" ?
   (08): This wait happens after cursor #2 (which is recursive SQL of stmt "B"
is parsed).
         Does this mean that the wait is related to "B" ?

2. Associating waits with activities.
   In the trace file we see the following different activity entries-
     PARSE/EXEC/FETCH/UNMAP
     BINDS
     STAT
   Assuming we know how to determine that a wait is associated with a statement,
   a) How can we associate the wait with an activity/category
(PARSE/EXEC/FETCH/UNMAP/STAT/BINDS) ?
   b) which are valid categories ?
   c) Are there waits which cannot be assigned directly to an activity /
category
      (related to something that does not appear in the tracefile) ?
   d) For each activity- which waits appear before the activity, and which
appear after the activity ?
      for example "log file sync" comes after the EXEC of a commit, and I/O
waits appear before the EXEC operation.

Thanks

          Followup:

Everything is linear, everything is linear.

(01) is not related to A since A was not parsed until after (01) happened.  That
wait was for some other cursor#1.

(04) yes

(06) by the #1 associated with it.  It is associated with cursor #1

(08) not unless the wait had a #2 with it, which it doesn't, hence it is
associated with #1.

2)

a) Everything is linear.  (waits won't be associated with stat, binds btw.  They
are purely "informational" messages -- not database activity).  Look up at the
last thing that took place for the WAIT #1 (or #2 -- whatever)

b) catagories of what?  I don't know what categories you mean.

c) no.

d) everything is linear.  waits are recorded as they happen.  If a wait happened
before an activity -- it was from the PRIOR activity.  You may wait for log file
syncs during the execution of a statement if you had to wait for room in the
redo log buffer as well.  they can happen anywhere.

      Rating: 4
values for
      cache buffer lru chain  November 12, 2002

Reviewer:  A reader
Hi

I have a question regarding DB_BLOCK_LRU_LATCHES. Oracle suggest it to be 2 *
CPU however, if I have 2 instances can i use that rule too???

For example I have a 8 CPU HP_UX Server with two instances, can I set
DB_BLOCK_LRU_LATCHES to 16 for both of them?

          Followup:

yes (and in current releases, you are freed from having to even consider this --
no such parameter exists anymore.

      Rating: 5
about LRU
      latch, again  November 12, 2002
Reviewer:
      A reader
Hi

Thanks for the reply. I always thought when Notes on Metalink suggest values for
parameters are per server basis, for example for LRU latch it states it has to
be twice the number of CPUs so my logic was telling me that then if there are
more instances I should get that value and divide the number of LRU latches into
number of instances! Something like if I have 8 CPU, 4 instances I would use (2
* 8)/4 and set that value to each instances' LRU latch...
Now, seems my logic was incorrect :-)
We are using Oracle 8i and not going to migrate to any new version probably for
a long time (just migrated the app from oracle 7 to 8i) so I guess I will have
to play with these parameters

thank you

      Rating: 4
latches
      serialize  November 12, 2002
Reviewer:
      A reader
Hi

You said that latches serialize operations, my question is what if I have more
than one child latch for a specific latch? Or that is not possible?

When parse a query one of many latches required by Oracle is library cache
latch. Is there only one parent latch for this one? Or there are several child
latches like cache buffer chain (one per data block in data cache)? If there are
more than one library cache latch then does it parallelize several parses or all
parses are done serially one after another?

          Followup:

we divvy up some chunks of memory -- eg: there is more then one library cache
each protecting different regions of the library cache -- to increase
concurrency.

btw: with the cache buffer CHAIN (chain=list) it is not one per block, but
rather per set of blocks in a chain.

      Rating: 3
how to avoid
      latch free waits  December 12, 2002
Reviewer:
      H  from Pakistan
How do we avoid latch free waits. Any thoughts?

          Followup:

what latches are you waiting on....

99 times out of a 100 its library or shared pool -- indicating you aren't using
bind variables.  use bind variables.

      Rating: 3
Latch
      waits  December 12, 2002
Reviewer:  h
       from Pakistan
lib cache
cache buffer chains
redo copy

I dont know how much these waits are impacting performance. I am gen. the tkprof
hopefully that will give some clues.

          Followup:

tkprof won't, statspack is what you want.

run a 15 minute window (take a snap, wait 15 minutes, take a snap)

then look at your top 5 waits.

December 13, 2002 -- Thanks for the question regarding "Simplifying
      a query", version 8.1.7

      You Asked
Say I have a table emps

1. emp_no
2. boss_emp_no
3. dept

The rule is that all employees in the same department should have the same boss.

I want to find exceptions to this rule i.e. show all the emps/boss/dept
violating this rule.

I tried the following:

select b.dept,a.emp_No,a.boss_emp_No,b.emp_No,b.boss_emp_no
from emps a,emps b
where a.dept=b.dept
and a.emp_No!=b.emp_no
and   a.boss_emp_no!=b.boss_emp_no

This query works fine, but it generates a lot of noise i.e unnecessary rows
because of which I cannot get a total count i.e. select count(*) in the above
query doesnt come back, it takes too long.

The problem is that if I have 5 emps in a dept having different boss's, the
query will return multiple rows like
1,2
1,3
1,4
1,5

and then

2,1
2,3
2,4
2,5
and so on.

Really, I need only the 1st set of rows since they give me all the information I
need. After that first set of rows is returned, I somehow need to mark that dept
as "processed".

I even tried

select ...
from emps a
where exists (select null from emps b
              where a.dept=b.dept
              and a.emp_no!=b.emp_no
              and a.boss_emp_no!=b.boss_emp_no)

this gives me only partial information, but thats fine too.

But this also has the same problem i.e. rows are duplicated.

Any help much appreciated. Thanks

      and we said...
the answer is -- your schema is totally wrong.

boss_emp_no is obviously an attribute of the DEPT table, not the EMP table.  You
have put the data in the wrong place.

but in anycase, here are two approaches:

ops$tkyte@ORA920> create table emp as select * from scott.emp;

Table created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> update emp set mgr = (select mgr from emp where deptno=10 and
rownum=1) where deptno=10;

3 rows updated.

ops$tkyte@ORA920> update emp set mgr = (select mgr from emp where deptno=20 and
rownum=1) where deptno=20;

5 rows updated.

ops$tkyte@ORA920>
ops$tkyte@ORA920> select *
  2    from (
  3  select empno, mgr,
  4         count(distinct mgr) over ( partition by deptno ) dist_mgrs,
  5             deptno
  6    from emp
  7         )
  8   where dist_mgrs > 1
  9  /

     EMPNO        MGR  DIST_MGRS     DEPTNO
---------- ---------- ---------- ----------
      7499       7698          2         30
      7654       7698          2         30
      7521       7698          2         30
      7900       7698          2         30
      7844       7698          2         30
      7698       7839          2         30

6 rows selected.

ops$tkyte@ORA920> select empno, mgr, deptno
  2    from emp
  3   where deptno in ( select deptno
  4                       from emp
  5                      group by deptno
  6                     having count(distinct mgr) > 1 )
  7  /

     EMPNO        MGR     DEPTNO
---------- ---------- ----------
      7499       7698         30
      7698       7839         30
      7654       7698         30
      7900       7698         30
      7844       7698         30
      7521       7698         30

6 rows selected.

But -- you need to fix your model, its broke!

Review &
      Followup

      Rating: 5
And if you
      can't change your model...  December 13, 2002

Reviewer:  Marcel  from Netherlands
select *
from emps b,
(select dept,count(*)
 from
 (select distinct a.dept,a.boss_emp_no
  from emps a
 )
 group by dept
 having count(*)>1
) deps c
where b.dept=c.dept

should do the trick. It may not be pretty though :)

          Followup:

I already gave him two efficient queries that answer his question with his
existing model?

It is not that we cannot answer the question - it is that given his business
rule, his data model is *wrong*

December 13, 2002 Naveed -- Thanks for the question regarding
      "simplyfy a query its really simple for you", version 8.0.5

      You Asked
 hi sir how r u hope f9 :)
i wrote the query

select empno from emp where empno = 55 ;

result is = ( no rows selected )

but i want  0 if there is no rows in a table
empno
--------
 0

Plz help me how can this be done

      and we said...
Acwonyms awe one thing, using wettews fow weguwaw wowds (u fow you and uw fow
youw ow you'we ow w u fow awe you) is something compwetewy diffewent!
Twanswation couwtesey of

http://www.rinkworks.com/dialect/
Oh, dat scwewy wabbit!  the diawectizew!

(that being the Elmer Fudd -- the only really politically correct one I could
use -- dialect translation of:

Acronyms are one thing, using letters for regular words (u for you and ur for
your or you're or r u for are you) is something completely different!

Translation courtesey of

http://www.rinkworks.com/dialect/,
the dialectizer!

well, you could:

select empno from emp where empno = 55
union all
select 0 from dual where not exists ( select empno from emp where empno = 55 );

or:

scott@ORA920> select nvl(emp.empno,0)
  2  from emp, (select 55 empno from dual) d
  3  where emp.empno(+) = d.empno
  4  /

NVL(EMP.EMPNO,0)
----------------
               0

scott@ORA920> select nvl(emp.empno,0)
  2  from emp, (select 7369 empno from dual) d
  3  where emp.empno(+) = d.empno
  4  /

NVL(EMP.EMPNO,0)
----------------
            7369

Review &
      Followup

      Rating:
      4
  December 12, 2002
Reviewer:
      A reader
Hi Tom.
I like your sense of humor.
It is realy fun.

      Rating: 3
Result 0
      Rows  December 13, 2002
Reviewer:  Srini
       from Chennai,India
Hi Tom,

This query will also return 0.

select empno from emp where empno = 55
union all
select 0 from dual

Why you use this below query.

select empno from emp where empno = 55
union all
select 0 from dual where not exists ( select empno from emp where empno = 55 );

Is there any reason to use the above?

          Followup:

when empno=55 does exist -- tell me, what does YOUR query return?

here is yet another way if empno is the primary key

scott@ORA920> select nvl( max(empno), 0 )
  2    from emp
  3   where empno = 55
  4  /

NVL(MAX(EMPNO),0)
-----------------
                0

scott@ORA920> select nvl( max(empno), 0 )
  2    from emp
  3   where empno = 7369
  4  /

NVL(MAX(EMPNO),0)
-----------------
             7369

      Rating: 5
Wascally
      Wabbit  December 13, 2002
Reviewer:  Reader
       from Canada
Thank God you didn't opt for a Yosemite Sam type response.

December 13, 2002 Om -- Thanks for the question regarding "About
      tablespace initialization", version 8.1.7

      You Asked
Dear Tom,

I just read from one of our senior Dba that

"Just in case anyone isn't aware of this : if a tablespace's default
pct_increase is set to 0 (zero), smon will not automatically coalesce the
tablespace. Selecting from dba_free_space_coalesced will show percentage of
coalesce. Setting pct_increase to 1 (or any figure other than 0) will cause smon
to automatically coalesce the tablespace every so often."

Could you please explain this.

I read and whenever there is a problem with TEMPORARY tablespace
and if we set pct_increase to 0,the space is released in TEMPORARY
tablespace and it is reinitialized.

Could upi please elaborate on these points.

Best regards,
Om

      and we said...
You need to send your DBA to an 8i new features class.

If they care about coalescing, they are not using LMT's (locally managed
tablespaces).

If they care about the health and well being of your database, they would be!!!

(ok, dismount soapbox)....

If pctincrease = 0, we assume that you smartly also set INITIAL=NEXT and the
need for coalescing, well, it just doesn't exist!! You need not ever coalesce
since each and every extent is the same size and all can easily be reused.

If you use LMT's -- coalescing is done by the bitmap (0's and 1's -- bunch of
adjacent 0's = bunch of adjacent free space).  No coalescing!

do not set pctincrease = 1!!! that'll cause extents to grow in wierd leaps and
bounds -- causing the EXACT problem this dba seems to be wanting to solve!!!!
You'll fragement your tablespace by having strange sized extents -- leaving
holes of unusable sizes all over the place.  You'll end up with a mess.

Do, if you are FORCED to use DMT's (dictionary managed tblspaces) always set
initial=next and pctincrease=0 and have the initial extents for all objects in
the tablespace be the same.  (and float your resume about, you are working in a
bad place if you are forced to use DMT's for all new stuff)

December 13, 2002 Nuno -- Thanks for the question regarding
      "Loading Image Problem", version 8.1.7

      You Asked
Hi there,
i'm having a problem trying to upload a .tif image into a blob field from a web
page.

i've used the solution you provided to someone else here but the problem is that
the images i want to upload are in my computer instead of in the DB server.

I've created the virtual directory 'MY_FILES' - create or replace directory
MY_FILES as 'D:\WFF\images'; - ( and the file i want to upload -10687.tif - in
fact exists) but still i keep getting the same error message.

ERROR:
ORA-22285: non-existent directory or file for GETLENGTH operation

Is there any way i can upload a file directly from my computer without the use
of a virtual directory?

Thanks

      and we said...
You cannot use dbms_lob.loadfromfile unless the disk is visible to the database
server.  dbms_lob is plsql, plsql runs on the server, the server can only see
the disks it can see.

You'll need to use a different approach (such as using SQLLDR for example) or
move the images to the server.

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:624423639385
for using sqlldr, all you would need is a file of filenames to be loaded.
Review &
      Followup

      Rating: 2
Another
      Way  December 13, 2002
Reviewer:  Nuno
      Oliveira  from Porto - Portugal
I was hoping someone could provide me a diferent answer but it seems like not.

Ok, thanx anyway.

          Followup:

wait -- I see I read too fast.

...
i'm having a problem trying to upload a .tif image into a blob field from a web
page.
.......

Ok, I see you are trying to use dbms_lob.loadfromfile which is wholly
inappropriate and so would be sqlldr.

You just want file upload from a web page.

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4456872422328
the whole bit about the virtual directory -- confused me.  virtual directory =
APP SERVER thingy.  "create or replace directory" = DATABASE thingy.  The two
have *nothing whatsoever to do with eachother.

You do not need a database directory object in order to do file upload.

December 14, 2002 -- Thanks for the question regarding "Indexes ",
      version 8.1.7.3.0

      You Asked
Hi Tom,
I always thought that when a function is applied on an indexed column, that will
atomatically avoid the used of that index.
Here I have an example of a query that originally was using the index_1 on
column_a because of the hint that the query had.
After a while someone modified the query and the only change was to add a nvl
funtion on columa_a. After this modification the explain plan changed and the
query started doing a full scan of the index_1.
What I don't understand is why the database is still using index_1, shouldn't
the database have avoided using index_1 because of the nvl, regardless of the
hint in the query for that index?

index_1: on column_a

query
select /*+ index (T1 index_1 ) */
    distinct T2.column_c,
    T2.column_d
from table_1 T1,
    table_2 T2
where nvl(T1.column_a,1)=1
and t1.column_c= t2.column_c
and t2.column_x=:b1;

Execution Plan
----------------------------------------------------------
  0      SELECT STATEMENT Optimizer=CHOOSE (Cost=231510 Card=1 Bytes=30)

  1    0   SORT (UNIQUE) (Cost=231510 Card=1 Bytes=30)
  2    1     MERGE JOIN (Cost=231507 Card=1 Bytes=30)
  3    2       SORT (JOIN) (Cost=183397 Card=260163 Bytes=5203260)
  4    3         TABLE ACCESS (BY INDEX ROWID) OF 'TABLE_1' (Cost=181648
Card=260163 Bytes=5203260)

  5    4           INDEX (FULL SCAN) OF 'INDEX_1' (UNIQUE) (Cost=99416
Card=260163)

  6    2       SORT (JOIN) (Cost=48111 Card=7539667 Bytes=75396670)
  7    6         INDEX (FAST FULL SCAN) OF 'PK_TABLE_2' (UNIQUE) (Cost=3108
Card=7539667 Bytes=75396670)

Thanks

      and we said...
Ahh - the joys of hints (hints are something we should avoid, not use).  You
might want to read:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6601251003901
for the future and get rid of those hints.

In any case -- the reason the index was used was because you TOLD us "please use
this index".  so, the optimizer found a way to use it -- as asked.

Indexes can be used in many ways.  Here the index was read and every row in it
was inspected.  Normally, if the nvl wasnt there -- it would have range scanned
-- here it FULL SCANNED the index, inspecting each and every entry in it --
performing the NVL on the indexed value to determine if it should keep the row
or not.

Great example of exactly why you DON'T want to use hints -- why they should be
the last choice.

Review &
      Followup

      Rating: 4
enforcing
      histogram usage  November 27, 2002
Reviewer:
      Scott  from Chicago, IL USA
I'm in a situation where I'm trying my damn'dest to do "the right" thing, but
the cbo won't help me out here, whether that's a limitation of my own of the
optimizer I'm still not sure.  I have a reasonably simple query:

SELECT a.code,
       c.category,
       count(*)
  FROM table_a  a,
       table_b  b,
       table_c  c
 WHERE a.code = 'qwerty'
   AND b.a_id = a.id   -- pk/fk join
   AND c.b_id = b.id   -- pk/fk join
 GROUP BY
       a.code,
       c.category;

Now table_a has a couple of dozen rows and table_b has a couple hundred.
However table_c has many million rows.  A given value of a.code will pretty much
always correspond to a dozen or so rows of table_b.  However, a given row of
table_b may correspond to millions of rows of table_c or a handful or anywhere
in between.

So I want the path to use the fk index between table_b & table_c when there are
only a handful of total rows corresponding to a.code, but not to waste time with
it otherwise.  My concern is that the fact that we have table_b in between a & c
may preclude the cbo from making it's best decision.  All pk & fk columns are
indexed.  The fk index on table_c is on (b_id,category) so the query doesn't
need to even bother with the table.  Here's my failed attempt to gather the
requisite data (the optimizer never uses the table_b/table_c index):

BEGIN

  dbms_stats.gather_table_stats(
    ownname => 'ME',
    tabname => 'TABLE_A',
    method_opt => 'FOR ALL INDEXED COLUMNS',
    cascade => TRUE );

  dbms_stats.gather_table_stats(
    ownname => 'ME',
    tabname => 'TABLE_B',
    method_opt => 'FOR ALL INDEXED COLUMNS',
    cascade => TRUE );

  dbms_stats.gather_table_stats(
    ownname => 'ME',
    tabname => 'TABLE_C',
    estimate_percent => 10,
    block_sample => TRUE,
    method_opt => 'FOR COLUMNS B_ID,CATEGORY' );

END;

Scott

          Followup:

CBO or otherwise -- i fail to see how any optimizer would be able to do this.

given a code in A we get a dozen rows.
this in turn will give us a dozen squared rows in AxB (144 say)

Now, we have 144 random b.id's -- one of which might want to use an index, one
of which might not want to use an index (b.id = 1 might get a dozen rows from C,
b.id = 2 might get 1 million rows from C)

I don't see what the cardinality of the set of rows from A has to do with
anything.  Suppose it was ALWAYS 2 rows from A.  You get a random two rows from
B.  The b.id values are NOT KNOWN until the query is already in the middle of
processing.  It is far too late to decide whether to use and inex or not for C
and further -- you might feel that row 1 should use an index and row 2 should
not.  A wholly impossible situation.

I fail to see how the optimizer could even begin to do what you are thinking --
it would have to change the plan IN THE MIDDLE of the plan - for each row.

Tell you what, I give you this query:

SELECT a.code,
       c.category,
       count(*)
  FROM table_a  a,
       table_b  b,
       table_c  c
 WHERE a.code = 'asdf'
   AND b.a_id = a.id   -- pk/fk join
   AND c.b_id = b.id   -- pk/fk join
 GROUP BY
       a.code,
       c.category;

You can have any piece of information the optimizer can have -- I'll also assure
you that

o 1 row will be returned from A
o this 1 row will be matched to 2 rows in B
  (this is inside info that the CBO doesn't get)

Now, should we use the index on C when accessing it?  If so, why.  If not, why
not?

      Rating:
      5
  November 27, 2002
Reviewer:
      A reader
Thanks for answering me tom.
I understand your point, but I am still confuse with some aspects.
You said <quote> In any case -- the reason the index was used was because you
TOLD us "please use this index".  so, the optimizer found a way to use it -- as
asked.  <quote>

Let me know if I am right with the following statement:"when a function is
applied on an indexed column, that will atomatically avoid the used of that
index."

So if my statement is correct and if I take into account that hints are
suggestions (they may or may not be used by the optimizer), if I combine this
two facts shouldn't the database avoid to use the index in that case. Why did
the optimizer try to find a way to use the index when a function was applied in
the column of the index? Shouldn't be the nvl function enough reason to totally
discard the hint and avoid to use the index?

Thanks again

          Followup:

"when a function is
applied on an indexed column, that will atomatically avoid the used of that
index."

absolutely not -- false.  it would be accurate to say that

select * from t where x = 5;

can use an index range scan on an index on t(x) whereas

select * from t where x+0 = 5;

CANNOT use an index range scan on an index on t(x)

The second query can use an index fast full scan or an index full scan however
to find all of the values of X, add 0 to it and see if that equals 5 and if so
-- access the table by rowid.

Your premise that the use of a function obviates the use of an index is flawed.
It obviates using it in the manner you were thinking (index range scan).

Consider that sometimes you want the index to be used like this.  Consider:

big_table@ORA817DEV.US.ORACLE.COM> set autotrace on
big_table@ORA817DEV.US.ORACLE.COM>
big_table@ORA817DEV.US.ORACLE.COM> select /*+ full( big_table ) */ object_id,
object_name
  2    from big_table
  3   where nvl(object_id,-1) = -1;

 OBJECT_ID OBJECT_NAME
---------- ------------------------------
        -1 /1001a851_ConstantDefImpl

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=658 Card=64 Bytes=1344)
   1    0   TABLE ACCESS (FULL) OF 'BIG_TABLE' (Cost=658 Card=64 Bytes=1344)

Statistics
----------------------------------------------------------
          0  recursive calls
        117  db block gets
      22862  consistent gets
      13353  physical reads
          0  redo size
        454  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

big_table@ORA817DEV.US.ORACLE.COM> select /*+ index( big_table big_table_idx2 )
*/ object_id, object_name
  2    from big_table
  3   where nvl(object_id,-1) = -1;

 OBJECT_ID OBJECT_NAME
---------- ------------------------------
        -1 /1001a851_ConstantDefImpl

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=3498 Card=64 Bytes=1344)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'BIG_TABLE' (Cost=3498 Card=64
Bytes=1344)
   2    1     INDEX (FULL SCAN) OF 'BIG_TABLE_IDX2' (NON-UNIQUE) (Cost=3434
Card=64)

Statistics
----------------------------------------------------------
          8  recursive calls
          0  db block gets
       3442  consistent gets
       3326  physical reads
          0  redo size
        454  bytes sent via SQL*Net to client
        425  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          1  rows processed

big_table@ORA817DEV.US.ORACLE.COM>
big_table@ORA817DEV.US.ORACLE.COM> set autotrace off

See -- here by using the index as a "skinny table" and knowing that very few
rows would satisfy the request -- using the INDEX was very much prefered.  We
scanned tons less blocks and only accessed the table once

      Rating:
      5
  November 27, 2002
Reviewer:
      A reader
Thank you for clarifying my wrong perception. What I don't understand is why the
people who teach oracle always say that when you apply a function on a indexed
column the index will not be used. Saying that they contribute to the
misconception. Anyway...

Now, I run the following examples and I obtained the following:
Table_1 and table_2 are analyzed every week, index_1(t1.column_a),
index_table2(t2.column_x)

1-
select
    distinct T2.column_c,
    T2.column_d
from table_1 T1,
    table_2 T2
where T1.column_a=1234
and t1.column_c= t2.column_c
and t2.column_x=:b1;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=11 Card=1 Bytes=30)
   1    0   SORT (UNIQUE) (Cost=11 Card=1 Bytes=30)
   2    1     NESTED LOOPS (Cost=8 Card=1 Bytes=30)
   3    2       TABLE ACCESS (BY INDEX ROWID) OF 'TABLE_1' (Cost=4 Card=4
Bytes=80)

   4    3         INDEX (RANGE SCAN) OF 'INDEX_1' (NON-UNIQUE) (Cost=3 Card=4)

   5    2       TABLE ACCESS (BY INDEX ROWID) OF 'TABLE_2' (Cost=1 Card=15752284
Bytes=157522840)

   6    5         INDEX (UNIQUE SCAN) OF 'INDEX_TABLE2' (UNIQUE)

2-
But , if I add +0 to the following condition: t1.column_a+0=1234 the index is
not use and the explain plan shows a full scan, look:

select
    distinct T2.column_c,
    T2.column_d
from table_1 T1,
    table_2 T2
where T1.column_a+0=1234
and t1.column_c= t2.column_c
and t2.column_x=:b1;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=73169 Card=1 Bytes=30)

   1    0   SORT (UNIQUE) (Cost=73169 Card=1 Bytes=30)
   2    1     NESTED LOOPS (Cost=73166 Card=1 Bytes=30)
   3    2       TABLE ACCESS (FULL) OF 'TABLE_1' (Cost=73162 Card=4 Bytes=80)

   4    2       TABLE ACCESS (BY INDEX ROWID) OF 'TABLE_2' (Cost=1 Card=15752284
Bytes=157522840)

   5    4         INDEX (UNIQUE SCAN) OF 'INDEX_TABLE2' (UNIQUE)

3-
But, if I add the hint for the index, the optimizer uses the index, look

select /*+ index (T1 index_1 ) */
    distinct T2.column_c,
    T2.column_d
from table_1 T1,
    table_2 T2
where T1.column_a+0=1234
and t1.column_c= t2.column_c
and t2.column_x=:b1;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=155582 Card=1 Bytes= 30)

   1    0   SORT (UNIQUE) (Cost=155582 Card=1 Bytes=30)
   2    1     NESTED LOOPS (Cost=155580 Card=1 Bytes=30)
   3    2       TABLE ACCESS (BY INDEX ROWID) OF 'TABLE_1' (Cost=155576 Card=4
Bytes=80)

   4    3         INDEX (FULL SCAN) OF 'INDEX_1' (UNIQUE) (Cost=163763 Card=4)

   5    2       TABLE ACCESS (BY INDEX ROWID) OF 'TABLE_2' (Cost=1 Card=15752284
Bytes=157522840)

   6    5         INDEX (UNIQUE SCAN) OF 'INDEX_TABLE2' (UNIQUE)

My questions are, taking into account what you told me before,
1-shouldn't the optimizer have chosen a full scan on index_1, in the second
case,instead of doing a full scan?
2-Why do you think the optimizer chose to run a full scan on table_1 in that
case?

Thanks

          Followup:

1) no, it is a function of costs.  The cost of the full table scan was:

TABLE ACCESS (FULL) OF 'TABLE_1' (Cost=73162 Card=4 Bytes=80)

The cost of the index full scan (single block io) was

INDEX (FULL SCAN) OF 'INDEX_1' (UNIQUE) (Cost=163763 Card=4)

Tell me -- which query actually performed better?

      Rating:
      5
  November 27, 2002
Reviewer:
      A reader
Tom,

Making a comparison between the full table scan and the full index scan, the
full index scan is the one that performs better.

Thanks

      Rating:
      5
  December 06, 2002
Reviewer:
      A reader
Hi Tom,

For the previous statement
"when a function is
applied on an indexed column, that will atomatically avoid the used of that
index."

You said <quote>
absolutely not -- false.  it would be accurate to say that

select * from t where x = 5;

can use an index range scan on an index on t(x) whereas

select * from t where x+0 = 5;

CANNOT use an index range scan on an index on t(x)

<end quote>

But in  your book "Expert one-on-one " Chapter 7: Why isn't my index getting
used? you said
<quote>
case 3
For an indexed column, you query using:
select * from t where f(indexed_column) =value

and find that the index on INDEX_COLUMN is not used. this is due to the use of
the function on the column. You indexed the values of the INDEX_COLUMN - not the
value of f(INDEXED_COLUMN). The index is nullified here. You can index the
fuction if you choose to do it
<end quote>

I am little confuse with this, because this looks like contradictory. Which rule
should we follow, the one that you detailed in the case 3 in your book or your
answer in this posting.?

Thanks for all your help.

          Followup:

that does not say that index cannot be used - it says "so you are finding we are
not using your index -- why?"  well, we stopped using it for a range scan on
that query -- we found a different way to process the query and it doesn't use
your index anymore because it doesn't MAKE SENSE to use that index.

It is not that the index could not be used in some way, shape or form for that
query -- it is that the index is NOT BEING used for that query.

The index could be used -- just not in an index range scan (which is the way
most of us see indexes being used -- on many a system, there is no other use of
an index)

case #3 stands.  if you have a query:

select * from t where indexed_column = value;

and you believe it should use an index (range scan) but won't -- you probably
are doing an implicit function which is causing us to avoid using the index.
You would want to make sure that VALUE is the same datatype as indexed_column to
avoid that.

      Rating: 4
Indexes due to
      usage of functions  December 12, 2002
Reviewer:
      Lalit  from The Netherlands
Thanks for clarifying the doubt. It is going to be of great help to me.

One of my procedure is selecting data from a single table. This table is having
1,100,000 records. It is selecting 100 columns and doing a group by with 25
columns
Select a,b,c......,sum(x),sum(y),sum(z)......from table_name
group by a,b,c (25 column name). {no where condition and only one table in from
clause}

This table which is having 1,100,000 records does not have any indexes. This sql
takes a lot time for execution.
Can you suggest me some way of improving the performance of this select
statement. Should I create indexes on my table? Should I use any hints for
improving the performance.

Thanks in advance

          Followup:

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6501620260742
and up your sort_area_size considerably and watch the run time go way down.  1.1
million records isn't very much.

you don't want any indexes for such a query -- full scan is the only way to go

      Rating: 4
Indexes due to
      usage of functions   December 14, 2002
Reviewer:
      Lalit  from The Netherlands
Thanks a lot Tom.

It worked for me. When I made the sort_area_size of 100 MB, the execution time
is reduced from 75 mins to 40 mins. This is a great time gain for me,
Thanks!!!!!!!!

Do you suggest to increase the sort_area_size even more? Are there any
disadvantages of increasing sort_area_size?

For this particular case, what we are doing is, selecting from 1 table (having
1.1 million records), with 2 different processes running in parellel, doing
different group by and inserting in a table. This table is same for both the
inserts.

Process 1 -
Insert into table2
(select a,b,c...(37
columns),sum(x.usd_ex_rate),sum(y.usd_ex_rate),sum(z.usd_ex_rate) from table1
group by a,b,c...(37 columns))

Process 2 -
Insert into table2
(select a,b,c...(37
columns),sum(x.eur_ex_rate),sum(y.eur_ex_rate),sum(z.eur_ex_rate) from table1
group by a,b,c...(37 columns))

The two process running in parellel and inserting in the same table(table2), do
you have any suggestions to optimize this. Due to the increase in sort area size
now both the processes are taking 40 mins in parellel.

Thanks again for your valuable suggestions.

          Followup:

If these are batch jobs being run when the system is somewhat "otherwise not
used", feel free to analyze if upping the sort area size would help even more.

You would look to see if you are doing an SORTS TO DISK during this period.  If
you are -- then perhaps a larger sort area size would be beneficial, as long as
you have sufficient free RAM on the machine to support it.

      Rating: 3
Parallel
      Execution  December 14, 2002
Reviewer:
      Lalit  from The Netherland
Had tried to increase the sort_area_size, but there was not a big gain in time
(time reduced by 3 mins).

Sorry for asking the 2nd question in the same series. As the question was
related to the same process, I had put in here.

Can you have a look at the 2 process I had mentioned and suggest me some
solution which will be more optimized. The only difference in the two processes
is, the multiplication by usd_exchange_rate and eur_exchange rate.

Process 1 -
Insert into table2
(select a,b,c...(37
columns),sum(x.usd_ex_rate),sum(y.usd_ex_rate),sum(z.usd_ex_rate) from table1
group by a,b,c...(37 columns))

Process 2 -
Insert into table2
(select a,b,c...(37
columns),sum(x.eur_ex_rate),sum(y.eur_ex_rate),sum(z.eur_ex_rate) from table1
group by a,b,c...(37 columns))

Both these processes are running in parellel (with the increased sort_area_size
for both the session) and inserting in the same table (table2)

          Followup:

try

insert into table2
select a, b, c, ....,
       decode( rn, 1, x_usd_ex_rate, x_eur_ex_rate ),
       decode( rn, 1, y_usd_ex_rate, y_eur_ex_rate ),
       decode( rn, 1, z_usd_ex_rate, z_eur_ex_rate )
  from ( select a, b, c, .....,
              sum(x.usd_ex_rate) x_usd_ex_rate,
              sum(y.usd_ex_rate) y_usd_ex_rate,
              sum(z.usd_ex_rate) z_usd_ex_rate,
              sum(x.eur_ex_rate) x_eur_ex_rate,
              sum(y.eur_ex_rate) y_eur_ex_rate,
              sum(z.eur_ex_rate) z_eur_ex_rate
         from table1
         group by ..... ),
       ( select 1 rn from dual UNION ALL select 2 rn from dual )
/

and just do it in a single pass -- you don't need two queries.

December 14, 2002 Venkateswara Reddy -- Thanks for the question
      regarding "Informing other remoter users after stand by converting Primary
      DB", version 81.5

      You Asked
Hi Tom

How are after a long time I am posing a question.

I had been asked following question.

1. I am using Standy by in other remote place. My primary db and standby are
synchoronized. After some time my primary db crashed and machine not in the
position got bad, no backup are avaliable except my standby.
After converting my standby to primary, I need to inform other remote user over
the phone or mail or I have to leave them let them try to conect.

Supose If I am not informing them How I should give them connection to connect
to my db,

thanks

Reddy

      and we said...
some options:

use connect time failover configurations in the tnsnames.ora -- whereby you can
list hosts in order to be tried.  So, when you connect we try host1 -- host1 not
there, try host2 and so on.

Or use LDAP to configure and manage the tnsnames.ora file in a data center
instead of in files on the clients.

Or use Oracle Names to achieve the same.

Or send them an email I suppose, they'll figure out pretty quick that it doesn't
work anymore and will come asking.
Review &
      Followup

      Rating: 4
Remote
      users  December 18, 2002
Reviewer:  Reddy
       from Bangalore, India
Thanks Mr. Tom

December 14, 2002 Steve -- Thanks for the question regarding
      "Flagging rows for read only and merging data", version 9014

      You Asked
Hi tom,

I am faced with the following

A master database used to register people for multiple trade shows which exists
and is functioning at a corporate headquarters.

The client wants the functionality to be able to dump trade show specific event
information into a seperate instance that will be used on-site when the trade
show starts. Most of the data in the master instance is somehow related to
eventid which is specific for that trade show. PK's are created using sequences
on all tables.

Pulling out the data is not so problematic. I will probably build a seperate
instance with empty tables and insert as select based on the event_id criteria.

The problem is that they want the event specific data for the active trade show
to be read only in the master database at corporate headquarters. I'm not sure
exactly how to do that..a bunch of select for update statements I thought maybe?
Create new set of views each time a trade show goes live?

Also, after the trade show is finished they want to take the data from the on
site trade show database and then *merge* it back into production. If they were
not using sequences then I could see maybe using pk's to check for duplicates
and merge that way but they are using sequences as surrogate pk's. In the
simplest case what do u think of the following....
>Export the active trade show db when it is finished. It should
only contain data for that particular trade show
>Delete from event were eventid = <active trade show id> at the master
db at headquarters with the ON DELETE CASCADE option of the fk's
>Import the formerly active trade show db back into prod

I know the bulk delete is ugly but do you have any other suggestions?

tks for your opinions

steve

      and we said...
One approach is:

ops$tkyte@ORA920.US.ORACLE.COM> create table locked_event_ids( event_id number
primary key ) organization index;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create or replace package event_id_pkg
  2  as
  3     function isReadOnly( p_event_id in number ) return boolean;
  4  end;
  5  /

Package created.

ops$tkyte@ORA920.US.ORACLE.COM> create or replace package body event_id_pkg
  2  as
  3
  4  type numArray is table of number index by binary_integer;
  5  g_array  numArray;
  6
  7  function isReadOnly( p_event_id in number ) return boolean
  8  is
  9  begin
 10          return g_array.exists(p_event_id);
 11  end;
 12
 13  begin
 14          for x in ( select * from locked_event_ids )
 15          loop
 16                  g_array(x.event_id) := x.event_id;
 17          end loop;
 18  end;
 19  /

Package body created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into locked_event_ids values ( 1 );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create table events( event_id number, event_name
varchar2(25) );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into events values ( 1, 'event 1' );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into events values ( 2, 'event 2' );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create trigger events_biud_fer
  2  before insert or update or delete on events for each row
  3  begin
  4          if ( ((inserting or updating) AND event_id_pkg.isReadOnly(
:new.event_id ))
  5               or
  6                   (deleting AND event_id_pkg.isReadOnly( :old.event_id ))
  7             )
  8          then
  9                  raise_application_error( -20002, 'Data is read only' );
 10          end if;
 11  end;
 12  /

Trigger created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> update events set event_name = 'foo' where
event_id = 1;
update events set event_name = 'foo' where event_id = 1
       *
ERROR at line 1:
ORA-20002: Data is read only
ORA-06512: at "OPS$TKYTE.EVENTS_BIUD_FER", line 7
ORA-04088: error during execution of trigger 'OPS$TKYTE.EVENTS_BIUD_FER'

ops$tkyte@ORA920.US.ORACLE.COM> update events set event_name = 'foo' where
event_id = 2;

1 row updated.

ops$tkyte@ORA920.US.ORACLE.COM>

Another approach would be fine grained access control.  Here the UPDATE and
DELETE would just silently "fail" (zero rows updated, zero rows deleted) and the
insert would fail.  This is less code -- maybe more efficient then firing a
trigger for every row modified as well:

ps$tkyte@ORA920.US.ORACLE.COM> create table locked_event_ids( event_id number
primary key ) organization index;

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into locked_event_ids values ( 1 );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create table events( event_id number, event_name
varchar2(25) );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into events values ( 1, 'event 1' );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM> insert into events values ( 2, 'event 2' );

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create function my_policy( p_schema in varchar2,
p_object in varchar2 ) return varchar2
  2  as
  3  begin
  4          return 'event_id NOT IN ( select event_id from locked_event_ids )';
  5  end;
  6  /
create function my_policy( p_schema in varchar2, p_object in varchar2 ) return
varchar2
                *
ERROR at line 1:
ORA-00955: name is already used by an existing object

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> begin
  2     dbms_rls.add_policy
  3     ( object_schema   => User,
  4       object_name     => 'EVENTS',
  5       policy_name     => 'MY_POLICY',
  6       function_schema => User,
  7       policy_function => 'My_policy',
  8       statement_types => 'insert, update, delete' ,
  9       update_check    => TRUE );
 10  end;
 11  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> update events set event_name = 'foo' where
event_id = 1;

0 rows updated.

ops$tkyte@ORA920.US.ORACLE.COM> update events set event_name = 'foo' where
event_id = 2;

1 row updated.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> delete from events where event_id = 1;

0 rows deleted.

ops$tkyte@ORA920.US.ORACLE.COM> delete from events where event_id = 2;

1 row deleted.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert into events values ( 1, 'another event'
);
insert into events values ( 1, 'another event' )
            *
ERROR at line 1:
ORA-28115: policy with check option violation

ops$tkyte@ORA920.US.ORACLE.COM> insert into events values ( 2, 'another event'
);

1 row created.

ops$tkyte@ORA920.US.ORACLE.COM>

As long as the delete+insert works for you (is timely enough) it works.  Don't
see how this will work around the surrogate key issue though?  I mean, you'll
have issues with the sequences in this case as well won't you?

What they might consider doing is "assigning out" sequences.  Eg: at the main
site, create the sequences like this:

create sequence S start with 1 increment by 100;

so main creates sequences 1, 101, 201, 301, ....;

Now, you have an event -- so you tell the guys "create your sequence S start
with 2 increment by 100".  They generate 2, 102, 202, and so on... When they
bring the data back -- you select s.nextval from dual and discover it is 2002.
So, you remember that.  The next event -- you tell them "create your sequence S
start with 2002 increment by 100"

That way, you don't have any collisions and could have upto 99 events going on
at the same time (or more).

Review &
      Followup

      Rating:
      5
  December 14, 2002
Reviewer:  steve
       from dallas
tks tom,
yeah we thought of assiging sequence ranges to particular shows and were
concenred over keeping track of the sequences as time went by , however u seem
to present an elegant solution

December 14, 2002 Tom -- Thanks for the question regarding
      "Development Environment/Methodolgy", version 8.1.7

      You Asked
Tom:

Hope this gives you a bit of a break from the technical :-).  Can you give me a
general picture of what a development environment should look like, with regard
to permissions in the database?  Consider an organization with 2 data
architects, 10 application developers (web and otherwise), half a production
DBA, and a couple of QA people.  The DA's primary responsiblity is designing the
database.  We get somewhat involved in production issues, and laying out
strategies there for the long term, but most of the day to day DB activities
fall on the production DBA.  That works well.

Regarding the development environment, we have a few different database silos,
and each silo has 3 instances:  DEV, QA, and PROD.

I would categorize the developers as not having much experience with programming
with Oracle, and in some cases, SQL is even new to them.  They pretty much do
their best, and ask us for help as they go.  We try to review the SQL as they
go.

The main question is:  what about programmers' permissions in the DEV instance?
Should they have table create/alter privs there so they can move at their own
pace?  We (the data architects) are having trouble with this concept because the
programmers are not DB designers.  We want to own the design, and we want to be
the only ones creating/altering tables, even in DEV.  Because, if they create
something the wrong way, they will code to it, and then later when we object to
the design, they will claim they've already done 2 weeks of coding against it
and it's too expensive to change now.

I respect your opinion very much, and would appreciate your high level take on
this issue.

Thanks.

      and we said...
I concur, the data model is yours.  They should have appropriate grants against
it in order to develop the application and you should listen to their input (eg:
this query we developers have to run 1000 times an hour is impossible against
your schema -- maybe schema needs to change?).

It would be utter chaos for everyone to make up their own stuff -- you are
working on a single system -- the data is the core, the heart.  It must be built
in a coordinated fashion.

Developers should be able to create objects in their own schemas -- to test
with, to be able to come to you and say "hey, if it were like this ...." (i
think developers should have their own oracle instance on their desktop to play
with personally).  But they should be congnizant of the fact that their code
will only run against the "real schema".

If it is not too late, you might want to make a rule that there will be NO
"select, insert, update, deletes" allowed in the application itself -- that'll
make tuning, security, fixing the database logic tons easier as well.

Review &
      Followup

      Rating:
      4
QUESTION  December 12, 2002
Reviewer:
      PRS  from USA
I have a stored procedure. I have put DBMS_OUTPUT lines to print some
information for the debugging point of view in the stored procedure. The number
of lines with DBMS_OUTPUT is approximately 5% of the number of lines of source
code for a procedure. I have put this DBMS_OUTPUT for the debugging point of
view. This procedure exist in the production environment. So in case of any
failure I can execute this procedure by setting SET SERVEROUTPUT ON and get the
output by DBMS_OUTPUT(i.e. debug information) about the code.

My DBA has a problem of having a DBMS_OUTPUT lines of code in the production
environment. I asked him the reason. He says it is a policy not to have the
DBMS_OUTPUT lines. But he has no valid reason.

Is it wrong to have the DBMS_OUTPUT?

          Followup:

No, your DBA is being silly (i actually have much stronger words for what they
are being -- but this being a public forum and all)

You can justify for very good reasons why DBMS_OUTPUT should there.

Your DBA cannot justify squat.

Maybe your DBA thinks Oracle should remove the EVENT system we have for dumping
debug?  Maybe your DBA thinks the code for generating trace files should be
removed from the kernel?  Maybe your DBA wants to make it physically impossible
to track down issues and problems?

Maybe in order to "make them happy", you could global change
DBMS_OUTPUT.PUT_LINE to "MY_PKG.P" and create a package

create package my_pkg
as
   procedure p( p_str in varchar2 );
end;
/

create package body my_pkg
as

procedure p ( p_str in varchar2 )
is
   l_str   long := p_str;
begin
   loop
      exit when l_str is null;
      dbms_output.put_line( substr( l_str, 1, 250 ) );
      l_str := substr( l_str, 251 );
   end loop;
end;

end;
/

in test and in production:

create package my_pkg
as
   procedure p( p_str in varchar2 );
end;
/

create package body my_pkg
as

procedure p ( p_str in varchar2 )
is
begin
    NULL;
end;

end;
/

That way

a) you can debug in dev with lines > 255 even...
b) in production, your DBA is a happy camper
c) when you have a "bug" you make them put the right body back in, in order
   to get the debug back out.

      Rating: 3
The way we
      work  December 13, 2002
Reviewer:  Marc Blum
       from Aachen, Germany
In our projects we typically have 4 roles:

- Projectmanagemer/Designer: much experience, gathering of requierements,
holding contact to the customer, application design, data modeling, coordination
of all development activities

- Project-DBA: good or much experience, focuses on database technology, supports
the designers, involved in all design decisions, implements the data model,
coaches the developers, maintains all development databases, responsible for
versioning and deployment, is aware of all database related activities

- Developer: various levels of experience, development of application code
(PL/SQL, C++, VBA, Delphi etc.), works very task oriented

- Tester/QA: low experience, maybe good understanding of the requierements
(sorrily often students or part time workers, sigh)

hth

      Rating:
      5
DBMS_OUTPUT.PUT_LINE  December 13, 2002

Reviewer:  Arun Gupta  from Harrisburg, PA USA

Tom
I recently recommended that developers should comment out the
dbms_output.put_line from the code which goes into production. The reason was
simple. In our architecture, the COM+ components in middle tier call the stored
procedures and the dbms_output goes nowhere.

Though it is a good idea to have each developer run Oracle instance on their
desktops, I have found the following issues with this:
a) Who creates these instances and database objects and keeps these databases in
sync with the integrated development database?
b) Who manages these instances if something goes wrong? These databases are
operating without any backup, without any protection from file deletion.
c) Most often, developers will test the application with stored procedures on
their local instance and then forget to migrate the code to source control
mechanism. This can lead to application breaking down when deployed in
integrated development database. The developers come running to the DBA blaming
that the development database is not "configured properly".
d) The developers working in their own schema used schema_name.object_name in
queries. In the integrated developement environment, the schema_name doesn't
exist, the query fails and they again come running to the DBA.
e) Developers create test cases in their local database and then expect the DBA
to migrate these test cases to integrated development database. If sequences are
used as surrogate primary keys, this may not be possible.
f) How can we ensure that developers will follow the object naming convention
and change management procedures?

It can well be argued that the developers can have their own databases with the
premise that it will be used as scratchpad and doesn't come with any guarantees
or warranties, as development progresses, expectations rise and the local
databases sitting on the developer's desk becomes as important as the production
database!!

Just my experiences...

Thanks

          Followup:

so?  when they want to debug their code -- eg: they get the call "hey, code
broke, help" -- what do they do then?

why bother commenting it out?  it is a waste of time and energy.  Then you have
code in production that is not the same as code in dev and test.  The slightest
difference could cause issues.

Use my "my_pkg.p" approach -- then they could turn p into something that uses
utl_file instead of dbms_output even -- this stuff is way to precious to
comment out.  It has saved my butt a gazzilion times over -- even I have bugs in
my code.  without this sort of stuff -- i would NEVER be able to fix it.  I've
dedicated a rather large section of my next book to this topic and how it is so
short sighted to outlaw or remove it!!!

a) who said they had to be kept in sync?  The developers having their own
database is so that they can test ideas, play with concepts.  Development takes
place in development.  If they develop on their desktop -- they have to deploy
their changes to development for testing.

b) so? they are - as i said -- to test with, to play with.  It is not dev -- it
is a playground.  A total loss of these databases is not material to anything.

c) that is a problem in your PROCESS then, doesn't matter if they have a
database or not on their machine does it?  You have this problem with or without
a playground

d) why?  why do they come "running to you" for an obvious error on their
part????  not getting it.

e) why?????  I'm not getting it at all.  You are talking about migrating data
here -- not a test case.

f) BECAUSE THEY DO THEIR REAL WORK IN DEV which gets migrated to TEST which gets
rolled out to PRODUCTION.  the thing on their desktop -- lets them tune queries
without having to bend your arm to get the trace files.  Lets them use autotrace
without forcing you to give access to v$ tables.  Lets them test ideas without
asking you to grant them privs (eg: create any context, wanna test out this
context thing -- see how/if it works -- prove that it is the best AND THEN I'll
approach that DBA guy and see if I can bend his arm to grant this to me in DEV)

But to make them physically REMOVE Debug code -- boy -- you are way off base --
way way off base.  Your basic premise is sooooo wrong (my 20cents worth, my
experience).

      Rating: 5
Licence
      costs  December 13, 2002
Reviewer:  Paul
       from UK
I requested having my own local database to experiment on, but was denied this
due to licence cost :-(

Not really for this forum, but it might be worth checking with Oracle (Sales?)
to clarify the licencing issues before going down this route.

Paul

      Rating: 4
Great ideas and
      follow-up  December 13, 2002
Reviewer:  Eric
      Givler  from Harrisburg, PA
I think the idea of being able to trace your code is extremely important.  With
that in mind, though, is it an issue to run: alter <obj> compile debug; on
objects (procedures, packages, functions, etc) in your production instance?
This allows stepping the server code from the client-side tools like procedure
builder.

I've already put the utl_file tracing concept in place, and must say that it
works like a charm (the one from the Beginner Oracle book).

I'm a little confused on your "NO SELECT, INSERT, UPDATE, DELETE statements".
If you are using a Forms tool, and using base table blocks, this is going to
cause a lot of extra code to be written, especially in the case of complex
user-entered queries, etc. which forms handles automatically.  What's the point
of adding to the work if you have a LARGE# of developer written modules?  I
realize the options to base blocks on other things (stored procs, etc) has been
around for quite some time.

          Followup:

When I said "no insert/update/delete/select" statements -- I was (implicitly --
should have stated it) refering to hand generated code like VB, Java.

Yes, if using a tool like Powerbuilder or Forms -- having
select/insert/update/delete is virtually inescapable for practical reasons.

      Rating:
      5
  December 13, 2002
Reviewer:  Dave
       from Ohio
With regard to licensing issues, the OTN License Agreement text that you read
(and agree to) on downloading software from Technet states ...

"We grant you a nonexclusive, nontransferable limited license to use the
programs only for purposes of developing and prototyping your applications, and
not for any other purpose.

...

You may not:
·use the programs for your own internal data processing or for any commercial or
production purposes, or use the programs for any purpose except the development
and prototyping of your applications;
·use the applications you develop with the programs for any internal data
processing or commercial or production purposes without securing an appropriate
license from us;
...

etc"

... which I read to mean that you can install EE on as many desktops that want,
as long as it is solely for the purpose of development. I think you are covered
for the purposes being discussed here.

      Rating: 5
I beg to
      differ  December 14, 2002
Reviewer:  Arun
      Gupta  from Harrisburg, PA USA
======================================
But to make them physically REMOVE Debug code -- boy -- you are way off base --
way way off base.  Your basic premise is sooooo wrong
======================================
Every application has debug features. I am yet to see an application where debug
features are left turned on by default. There must be a well defined mechanism
to turn on debugging when required. How many times I have read in this forum
that every extra line of code has a performance impact associated with it.

Judging a DBA because he asked for debugging turned off (not removed) is not
justified in a forum of this standing.

As for the developers having a desktop Oracle instance and database, I have many
times read in this forum that any database used for query tuning must be as
close to production database as possible. How can this be achieved on a desktop?
Secondly, there is a real possibility of the desktop database soon becoming a
substitute for the real DEV database.

While the aim should not be to frustrate developers, a well controlled database
environment always benefits the developement process in the long run, since a
group of persons(DBA/Server Admins/Project Manager/Leads) always know what
environment settings a project is being developed under. If a developer migrates
a piece of working code from his desktop to DEV server and it breaks down, he
can spend hours trying to find what went wrong. I have seen this happen many
times. I will give real life instances personally witnessed by me.

Instance one: COM+ components. They worked very well on the desktop because the
developer had different version of dll than the server. 8 manhours were spent in
trying to figure out which dll was causing it. Result: The developers were
asked to develop and test against the server dlls only.

Instance two: Perfectly working code, tested in DEV and INTG, migrated from INTG
to Acceptance test. The transactions failed to enlist in the distributed
transaction coordinator. The difference was that we moved from Windows 2000
Advanced Server to Windows 2000 Datacenter. Oracle treats both of them as same.
I had to open a TAR with Oracle. They asked me to turn on tracing of Oracle
Service for MTS at highest level. After looking at the trace files, they
suddenly spotted that every 9th transaction was failing to enlist. They told me
to set a hidden parameter _max_transaction_branches=32. That fixed the problem.
User acceptance testing delayed by two days. Result: We moved all the databases
to Windows 2000 Datacenter.

Instance three: Developer tests the code in his schema, works fine. Forgets to
migrates to DEV, goes home (most work 6:00am to 3:00pm). The testing lead tests
the app at 4:30pm, it breaks down. Developer is called at home, he instructs the
dev lead to migrate code from local machine to DEV. Two hours lost. Result:
Developers were discouraged from developing in own schema.

All these decisions were taken by project management team. I as a DBA, had no
hand in it. However, seeing the headaches it caused, I tend to agree.

We have had our more than fair share of problems in production across multiple
projects. The one thing we have not done is use production database as debugging
platform. There are 4 pre-production platforms for this purpose. These
environments are periodically synch'ed up with production. Any code that goes
into production is kosher.

However, things vary by the size of project, technological architecture,
available resources, timelines, budgets, contractual limitations and so on.
There is nothing like a one size fits all. Thinking that forums are for sharing
a variety of experiences and opinions, I had come out with one of the approaches
that has worked for us. However, this approach is restrictive and doesn't seem
to agree with many. I apologize if I have caused any offense.

Thanks

          Followup:

dbms_output is TURNED OFF by default.  Your premise is wrong.  to make they
PHYSICALLY REMOVE IT IS WRONG.

There is overhead -- overhead something you can and should avoid.

Then there is mandatory stuff -- stuff that must be in there forever.  This
falls into this category.  You want to see some?  Just change the NO to YES in
the URL above.  I would Never in a billion years remove my debug/timing code.  I
need it CONSTANTLY.

You said (i quote)

I recently recommended that developers should comment out the
dbms_output.put_line from the code which goes into production.

you made them REMOVE IT -- it is commented out. It is not there.  They cannot
get it again without physically changing code and putting it back in and
recompiling.  You let them do that in prod when there is an issue???

I'm not judging you -- I'm saying "you are wrong to do this".  Period.

sigh -- why is everyone missing this point of the database on the desktop. You
use it to test ideas, to learn, to try things out.  You want to find out which
approach takes more latches (latches being locks, locks being serialization
devices, serialization devices being the thing that kills scalability)?  You
need a single user instance running in isolation.  It is a playground, a scratch
database.  It is not your development box, it is a playground,  It gets wiped
out -- big deal, nothing lost, just use dbca and recreate it.  See also:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6349391411093
where we discussed this playground database.

I agree a well controlled development, test, production setup is mandatory,
I think I've said that more then once here as well.  However, if the developer
wants to prove that bind variables are better then not using bind variables --
well, they need to flush the shared pool for example.  Will you let them do
that?  Oh yeah, they need to kick everyone else off during the test to make the
numbers meaningful.  Will you let them do that?  No, of course not.  So, what
can they do then?  Well, if they have that little playground instance....

And if the developer cannot migrate his code from his machine to DEV -- how pray
tell will it go from DEV to TEST magically?  Also, I never said "develop on
their desktop" -- that is what DEV is all about.  Nuff said on that topic.

Your example with the COM stuff -- that'll happen between DEV and TEST and TEST
and PROD.  Thing is -- if what you are testing is database stuff -- it won't
happen between Oracle and Oracle -- I develop on linux to deploy to solaris or
even windows all of the time.  We have the same bugs on all platforms so it
ports pretty easy.

UAT was not delayed.  UAT did what UAT was designed to do.  Catch these issues.

And with the developers not being able to have their own schema to test with --
that's a particularly bad example.  First -- the pain inflicted here was 100%
self induced -- the programmer was the entire cause.  What if they had just
written the code and not moved it into source code control or something.  Having
their own schema is not the cause here -- a person making a mistake -- yes.
Tell me -- when this same programmer who can no longer test in their own schema
AND cannot have a local database wants to compile new functionality into a
package that everyone uses -- what then?  How many hours will be wasted by:

o the programmer waiting for everyone to stop using it
o the other developers waiting for this programmer to work the kinks out -- you
know, debug this piece of code that lots of people use

All of the issues you describe happened without desktop databases -- it is all a
project management issue -- all of it policy and procedures.  I'm not saying to
develop on the desktop -- but boy, if you want to test an idea, having it there
is a nice thing.

However, we got so far off topic here -- we were talking debug code which you
are making them physically remove.  That is a bad policy period.  The Oracle
data is heavily instrumented and we can turn it on at will (hey, you know that,
Support had you do that to track down an issue!!!)

You seem to say "i want homogenous code" but you make them comment out code --
it is no longer homogenous.  Perhaps there was a side effect from calling
dbms_output that made the code work in test and dev.  Removing the call to
dbms_output introduces a bug that was not detected.  So following your methods
we must not only remove the dbms_output from prod but also from test and
therefore DEV.

True story:  Some C code.  Littered with fprintfs to a debug file (had

   if debug fprintf( .... )

all over).  Someone says "hey, code will run faster if we don't have those if's,
comment them out. don't need to test or anything, same code right..."

Well, C being C -- the physical removal of code changed some pointer locations
and exposed a nasty application related bug that wasn't exposed when the
fprint's were in the source.  Bummer, works in test -- debug shows it works --
doesn't crash, all is well in the world. But production -- doesn't work at all.
Why?  it isn't the code we developed.  It isn't the code we tested.  It is code
sans debug code.  So, not only were we handicapped in trying to figure out what
was wrong -- we had no debug we could run -- we couldn't reproduce the issue
(cause there we did have debug on).

What if the dbms_output somewhere has:

   dbms_output.put_line( 'and the answer = ' || f(x) );

well, commenting that out blindly -- just making them remove it -- could easily
materially affect the code.  What does F do?  Is it important?  Maybe they
really wanted f as a procedure, didn't care about the return value and this was
a cheap way to call it (in their mind).  Blindly removing the dbms_outputs --
well, that could change the outcome.

You haven't caused any offense, I'm still trying to convince you that leaving
that debug code in there is justified and the right thing to do.  Making them
physically remove it is a very bad idea.  If you want to outlaw a scratch
database on the desktop -- so be it.  I only see positives, no negatives.  Every
negative you came up with was pretty much policy based.  Take the COM+ dll
example.  Do you think there would have been ZERO hours of fixing if they had
the "bad dlls" from the beginning?  No -- they would have fought the DLL he%#@
for N hours on the real machine as well (been there, done that, never will I
program windows again personally).  Not a chance -- they would have spent 4
hours on the dev machine saying "why isn't this working as it should".

Anyway.

December 15, 2002 Leonardo -- Thanks for the question regarding
      "Compiling invalid Objects / execute immediate issue", version 8.0.6

      You Asked
Hi Tom, How are You?

I was looking for a simple script to compile invalid objects. I found one in the
archive, but it seemed to complex. So I tried to simplify, but no sucess.

There it is:
############################################################
--compile all invalid objects for a given schema
declare
strCmd varchar2(250);
intCmd integer;

begin

   for cInvalidTriggers in
   (
       SELECT DECODE( object_type, 'PACKAGE BODY',
              'ALTER PACKAGE ' || owner||'.'||object_name || ' COMPILE BODY;',
              'ALTER ' || object_type || ' ' || owner||'.'||object_name || '
COMPILE;' ) as cmd_txt
       FROM  sys.dba_objects a, sys.order_object_by_dependency b
       WHERE a.object_id = b.object_id(+)
             AND status = 'INVALID'
             AND owner=upper('&OWNER')
             AND object_type IN ( 'PACKAGE BODY', 'PACKAGE', 'FUNCTION',
'PROCEDURE', 'TRIGGER', 'VIEW' )
       ORDER BY dlevel desc, object_type, object_name
   )
   loop

       --getting the commando do be executed (compile the object)
       strCmd := cInvalidTriggers.cmd_txt;
       begin
          -- executing...
          execute immediate strCmd;
          exception when others then dbms_output.put_line('ERRO: ' || SqlErrM);
       end;

   end loop;

end;
############################################################

But when I run, I receive the error:
ERROR at line 22:
ORA-06550: line 22, column 19:
PLS-00103: Encountered the symbol "IMMEDIATE" when expecting one of the
following:
:= . ( @ % ;

I don't know what is going on. In fact I can't run *ANY* execute immediate (same
error).

Could you help me? Could you also draw some comments about this simplified
version of the compiling script?

Just one more: I've read, in one of your answers (classes fit better!) that each
compiled object, compiles also its dependencies. Is it valid only for 8.1.7 and
above? Because it seems not working for me!

Thanks in advance!

regards!
LZ
ps: really sorry for last time! My e-mail was misspelled!

      and we said...
execute immediate is new with 8i (new being "relative" as 8i is pretty old
itself)

create or replace procedure execute_immediate( p_sql in varchar2 )
is
    cursor_name     pls_integer default dbms_sql.open_cursor;
    ignore          pls_integer;
BEGIN
    dbms_sql.parse(cursor_name, p_sql, dbms_sql.native);
    ignore := dbms_sql.execute(cursor_name);
    dbms_sql.close_cursor(cursor_name);
END;
/

works in 8.0.

I wouldn't even bother doing this tho, not worth it.  That and you'll find that
query to be one long one (if not in 8.0, it'll be in 8i and up for sure).

You might want to use my method of:

1) pick any invalid object you haven't compiled yet
2) compile it
3) remember you tried to compile it and goto 1)

you don't need to do them in any order.

the dependency thing works in all releases.  It compiles objects IT IS DEPENDENT
on (goes "up" the chain if you will).  It does not compile objects that are
DEPENDENT on it!

consider:

ops$tkyte@ORA806.WORLD> create or replace procedure p1
  2  as
  3  begin
  4          null;
  5  end;
  6  /

Procedure created.

ops$tkyte@ORA806.WORLD> create or replace procedure p2
  2  as
  3  begin
  4          p1;
  5  end;
  6  /

Procedure created.

ops$tkyte@ORA806.WORLD> create or replace procedure p3
  2  as
  3  begin
  4          p2;
  5  end;
  6  /

Procedure created.

ops$tkyte@ORA806.WORLD>
ops$tkyte@ORA806.WORLD> alter procedure p1 compile
  2  /

Procedure altered.

so, p3 depends on p2 depends on p1... compiling p1 invalidates p2 and p3

ops$tkyte@ORA806.WORLD> @invalid
ops$tkyte@ORA806.WORLD> break on object_type skip 1
ops$tkyte@ORA806.WORLD> column status format a10
ops$tkyte@ORA806.WORLD> select object_type, object_name, status
  2  from user_objects
  3  where status = 'INVALID'
  4  order by object_type, object_name
  5  /

OBJECT_TYPE     OBJECT_NAME                    STATUS
--------------- ------------------------------ ----------
PROCEDURE       P2                             INVALID
                P3                             INVALID

ops$tkyte@ORA806.WORLD>
ops$tkyte@ORA806.WORLD> alter procedure p3 compile
  2  /

Procedure altered.

ops$tkyte@ORA806.WORLD> @invalid
ops$tkyte@ORA806.WORLD> break on object_type skip 1
ops$tkyte@ORA806.WORLD> column status format a10
ops$tkyte@ORA806.WORLD> select object_type, object_name, status
  2  from user_objects
  3  where status = 'INVALID'
  4  order by object_type, object_name
  5  /

no rows selected

but compiling p3 recompiled p2 automagically -- p3 cannot compile if p2 is
invalid, p2 cannot compile if p1 is invalid.  If all three were invalid --
compiling p3 would have validated them ALL!

ops$tkyte@ORA806.WORLD>
ops$tkyte@ORA806.WORLD> alter procedure p1 compile
  2  /

Procedure altered.

ops$tkyte@ORA806.WORLD> @invalid
ops$tkyte@ORA806.WORLD> break on object_type skip 1
ops$tkyte@ORA806.WORLD> column status format a10
ops$tkyte@ORA806.WORLD> select object_type, object_name, status
  2  from user_objects
  3  where status = 'INVALID'
  4  order by object_type, object_name
  5  /

OBJECT_TYPE     OBJECT_NAME                    STATUS
--------------- ------------------------------ ----------
PROCEDURE       P2                             INVALID
                P3                             INVALID

ops$tkyte@ORA806.WORLD>
ops$tkyte@ORA806.WORLD> alter procedure p2 compile
  2  /

Procedure altered.

ops$tkyte@ORA806.WORLD> @invalid
ops$tkyte@ORA806.WORLD> break on object_type skip 1
ops$tkyte@ORA806.WORLD> column status format a10
ops$tkyte@ORA806.WORLD> select object_type, object_name, status
  2  from user_objects
  3  where status = 'INVALID'
  4  order by object_type, object_name
  5  /

OBJECT_TYPE     OBJECT_NAME                    STATUS
--------------- ------------------------------ ----------
PROCEDURE       P3                             INVALID

ops$tkyte@ORA806.WORLD>

but compiling p2 won't touch p3 -- doesn't need to so it doesn't

Review &
      Followup

      Rating: 5
Good Solution
      and Great Explanation  December 15, 2002
Reviewer:
      Leonardo Zacche  from RJ, Brazil
That it!

A good solution (considering the resources available, eg, Oracle version) sided
by a GREAT explanation.

Thanks!
LZ

December 15, 2002 Ivan -- Thanks for the question regarding "Bitmap
      indexes and Group BY", version 8.1.6

      You Asked
Hi Tom,

I've created a bitmap indexes on DEPTNO of the EMP table

Following statement :
->create bitmap index b_emp on emp (deptno)
  storage (initial 1M next 1M pctincrease 0)
  tablespace users;

SQL> desc emp
 Name       Null?    Type
 ---------- -------- ----------------------------
 EMPNO               NUMBER(4)
 ENAME               VARCHAR2(10)
 JOB                 VARCHAR2(9)
 MGR                 NUMBER(4)
 HIREDATE            DATE
 SAL                 NUMBER(7,2)
 COMM                NUMBER(7,2)
 DEPTNO              NUMBER(2)

The table has been analyzed (so the bitmap index can be used)
-> Analyze table emp compute statistics

When executing following queries I found out that queries with a
GROUP BY clause do not automatically use this index, although
it's must faster than a full table scan.
Can you explain this ?

SQL> set autotrace on
SQL> r
  1  select count(*) from emp
  2* where deptno=40

  COUNT(*)
----------
         0

Elapsed: 00:00:00.10

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=39 Card=1
Bytes=2)
   1    0   SORT (AGGREGATE)
   2    1     BITMAP CONVERSION (COUNT)
   3    2       BITMAP INDEX (SINGLE VALUE) OF 'B_EMP'

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

SQL> select count(*),job from emp
  2  where deptno=40
  3  group by job;

no rows selected

Elapsed: 00:00:13.29

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=3687 Card=5
Bytes=45
          )

   1    0   SORT (GROUP BY) (Cost=3687 Card=5 Bytes=45)
   2    1     TABLE ACCESS (FULL) OF 'EMP' (Cost=815 Card=305835
Bytes
          =2752515)

Statistics
----------------------------------------------------------
          0  recursive calls
         21  db block gets
       5367  consistent gets
       4376  physical reads
          0  redo size
        263  bytes sent via SQL*Net to client
        313  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
          0  rows processed

SQL> select /*+ index(emp,b_emp) */  count(*),job from emp
  2   where deptno=40
  3   group by job;

no rows selected

Elapsed: 00:00:00.00

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=5622 Card=5
Bytes=45
          )

   1    0   SORT (GROUP BY) (Cost=5622 Card=5 Bytes=45)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'EMP' (Cost=2749
Card=3
          05835 Bytes=2752515)

   3    2       BITMAP CONVERSION (TO ROWIDS)
   4    3         BITMAP INDEX (SINGLE VALUE) OF 'B_EMP'

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

Thanks in advance,
Ivan Verpoort.

      and we said...
It is in the way you computed the stats.

The optimizer only knows that there are a few values of deptno
-- if you did an example like I did, selecting * from scott.emp
and then adding the table to itself -- we only have 3 deptno in
those thousands of rows.

The optimizer only knows that there are 3 values of deptno in
there -- it doesn't really know that 40 isn't even in there.
What we need to do is compute a histogram for that column in
order for the optimizer to be aware of that fact.  You would
compute histograms for columns you want this sort of selective
behavior on.  Otherwise, the optimizer felt pretty sure that 1/3
of the table would be accessed via the index and hence a full
scan was in order.

Here is the example I ran:

DOC>drop table emp;
DOC>create table emp as select * from scott.emp;
DOC>insert into emp select * from emp;
DOC>insert into emp select * from emp;
DOC>insert into emp select * from emp;
DOC>insert into emp select * from emp;
DOC>insert into emp select * from emp;
DOC>insert into emp select * from emp;
DOC>insert into emp select * from emp;
DOC>commit;
DOC>insert into emp select * from emp;
DOC>commit;
DOC>insert into emp select * from emp;
DOC>commit;
DOC>insert into emp select * from emp;
DOC>commit;
DOC>insert into emp select * from emp;
DOC>commit;
DOC>*/

ops$tkyte@DEV816> analyze table emp compute statistics;
Table analyzed.

ops$tkyte@DEV816> select count(*) from emp;

  COUNT(*)
----------
     28672

ops$tkyte@DEV816> create bitmap index b_emp on emp (deptno)
  2    storage (initial 1M next 1M pctincrease 0)
  3          tablespace users
  4  /

Index created.

ops$tkyte@DEV816> set autotrace on
ops$tkyte@DEV816> select /*+ index( emp, b_emp ) */ count(*)
from emp where deptno = 40 group by job
  2  /

no rows selected

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=192 Card=5
Bytes=45)
   1    0   SORT (GROUP BY) (Cost=192 Card=5 Bytes=45)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'EMP' (Cost=165
Card=95
          58 Bytes=86022)

   3    2       BITMAP CONVERSION (TO ROWIDS)
   4    3         BITMAP INDEX (SINGLE VALUE) OF 'B_EMP'

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

ops$tkyte@DEV816> select count(*) from emp where deptno = 40
group by job
  2  /

no rows selected

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=31 Card=5
Bytes=45)
   1    0   SORT (GROUP BY) (Cost=31 Card=5 Bytes=45)
   2    1     TABLE ACCESS (FULL) OF 'EMP' (Cost=5 Card=9558
Bytes=860
          22)

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

ops$tkyte@DEV816> analyze table emp compute statistics for
columns deptno;

Table analyzed.

ops$tkyte@DEV816> set autotrace on
ops$tkyte@DEV816> select count(*) from emp where deptno = 40
group by job
  2  /

no rows selected

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1
Bytes=9)
   1    0   SORT (GROUP BY) (Cost=3 Card=1 Bytes=9)
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'EMP' (Cost=1
Card=1 By
          tes=9)

   3    2       BITMAP CONVERSION (TO ROWIDS)
   4    3         BITMAP INDEX (SINGLE VALUE) OF 'B_EMP'

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

ops$tkyte@DEV816>

After getting the histogram -- it does what you want.  It just
needs that additional information.

Review &
      Followup

      Rating: 1
two bitmap
      indexes and group by  February 12, 2002
Reviewer:
      Jan Karaffa  from Czech Republic
Hi Tom,

ORA engine don't want to combine 2 bitmap indexes on one tabe. Let's say we have
huge (200M records) table t with the key (day,x) and value y:

create table t (day date, x number, y number);

BEGIN
 for d in 1..10 LOOP
 for i in 1..10000 LOOP
    insert into t (day,x,y) values (sysdate+d,mod(i,10),i);
 end loop; --i
 end loop; --d
END;
/

create bitmap index t_day on t (day) compute statistics;
create bitmap index t_x on t (x) compute statistics;
analyze table t compute statistics;
analyze table t compute statistics for columns DAY, X;

NOW, all is analyzed as much as possible
when I run:
   select day, x, count(*) from t group by day,x;
it goes: Full scan over table T
but, when do this:
drop index t_day;
drop index x;
create bitmap index t_z on t (day,x) compute statistics;
it goes through bitmap index, even I do: select x, count(*) from t group by x;
where x is on the second position in the index.

Why Oracle Engine doesn’t merge two bitmap indexes to get a better cost?
Or, What is the rule of thumb for bitmap index creation (specialy for star
schemas). Should I create many separate bitmap indexes and runs into the same
problem mention above, or create the only one wide bitmap index, where I put all
"attributes" of the "dimension" table?
To be clear: dimension table is for example table abc (KEY, attrib1,
...,attrib50), where attrib# has a low cardinality

          Followup:

How would using two bitmaps for select day, x, count(*) from t group by day, x;
be better?

We would have to take BOTH indexes -- "flatten them" -- join them by rowid (to
get the day/x combos) and then count.  We'd have to flatten them to join by
rowid.

Bitmaps are good when you ask questions like:

how many rows are there where x is in ( 1, 2, 3 ) and day in ( a, b, c ) or x =
10

We can combine the indexes to find those rows very fast.

      Rating: 3
low and high
      values  February 15, 2002
Reviewer:  Mikito
      Harakiri
Even without a histogram low value (10) and high value (30) are there in
statistics so that a smart optimizer would figure out that predicate value (40)
is out of range.

      Rating: 3
Bitmap indexes
      and Group BY  December 14, 2002
Reviewer:  Piotr
      Jarmuz  from Braunschweig, Germany
I did the following:

1.
create table parts(pk number, name varchar2(30), ptype number(2));

2.
begin
     for x in 1..1000000 loop
          insert into parts (pk,name,ptype)
         values (x,'name'||x,mod(abs(dbms_random.random),100));
     end loop;
     commit;
end;

3.
create bitmap index parts_type_index on parts(ptype) nologging;

4.
analyze table parts compute statistics for table for all indexes for all indexed
columns;

5.
select ptype, count(*) from parts
group by ptype;

Execution plan
---------------------------------------------------------------------
0       SELECT STATEMENT Optimizer=CHOOSE (Cost=1399 Card=100 Bytes=200)
1    0      SORT (GROUP BY) (Cost=1399 Card=100 Bytes=200)
2    1          TABLE ACCESS (FULL) OF 'PARTS' (Cost=204, Card=1000000,
Bytes=1000000)

It takes 2.5 seconds to execute that.

6.
select /*+ INDEX(parts parts_type_idx)*/ ptype, count(*) from parts
group by ptype;

Execution plan
---------------------------------------------------------------------
0       SELECT STATEMENT Optimizer=CHOOSE (Cost=276 Card=100 Bytes=200)
1    0      SORT (GROUP BY) (Cost=276 Card=100 Bytes=200)
2    1          BITMAP CONVERSION (COUNT)
3    2              BITMAP INDEX FULL SCAN OF 'PARTS_TYPE_INDEX'

It takes 40 miliseconds to execute that.

Why? What's wrong here? I reckon not the 'nologging' option.

Regards.

          Followup:

I reproduced this issue in 817 ("fixable" by setting the
optimizer_index_cost_adj downward) but in 920 -- it picked the bitmap straight
off.  setting a 10053 event trace -- i see in 817 it rejected the bitmap access
plan very early.  In 920 -- it chooses it.

So, I would say this is most likely an optimizer related issue that is fixed in
9i.  I could not find a specific problem report related to this one tho.

Adding a predicate -- such as ptype > -100 -- caused it to use the bitmap as
well so I suspect it is the strange case of the query without a predicate
ignoring the bitmap.

ops$tkyte@ORA920.US.ORACLE.COM> create table parts(pk number, name varchar2(30),
ptype number(2));

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> insert /*+ append */ into parts
  2  select rownum, 'name'||rownum, mod(rownum,100)
  3    from big_table.big_table
  4   where rownum <= 1000000;

1000000 rows created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> create bitmap index parts_type_index on
parts(ptype) nologging;

Index created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> analyze table parts compute statistics for table
for all indexes for all indexed
  2  columns;

Table analyzed.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> set autotrace on
ops$tkyte@ORA920.US.ORACLE.COM> select ptype, count(*) from parts
  2  group by ptype;

     PTYPE   COUNT(*)
---------- ----------
         0      10000
         1      10000
...
        98      10000
        99      10000

100 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=221 Card=100 Bytes=200)
   1    0   SORT (GROUP BY NOSORT) (Cost=221 Card=100 Bytes=200)
   2    1     BITMAP CONVERSION (COUNT)
   3    2       BITMAP INDEX (FULL SCAN) OF 'PARTS_TYPE_INDEX'

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        208  consistent gets
          0  physical reads
          0  redo size
       2006  bytes sent via SQL*Net to client
        565  bytes received via SQL*Net from client
          8  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        100  rows processed

      Rating: 3
Bitmap
      indexes  December 15, 2002
Reviewer:  Piotr
      Jarmuz  from Braunschweig, Germany
I reproduced your example and it picked bitmap index but it won't do with mine.

I also did tests with various table sizes using my and your data. Maybe this
will tell you more:

rows    my data    your data
-------------------------
10000     b    b
50000     b    b
80000     b    b
90000     f    b
100000    f    b
200000    f    b

b-bitmap index
f-full table scan

Those plans could even be OK (fts is not always bad), yet when I added the hint
it was an order of magnitude faster. Seems like for small tables it works for
big doesn't. Strange indeed.

But when I added dummy predicate 'where ptype>=0' (always true) it did the right
thing in all the cases although it overestimated the number of rows
(cardinality) by ~%50 in explain plan.

My original table had milion rows, 100 distinct values for ptype column, with
cardinalities in range 9697-10264. In your example it was 100 and all were equal
to 10000. So it is nearly the same. I wonder if you or anyone from the forum
could reproduce my example "exactly" (I mean add some randomness) and see if it
works in your environments.

Thank you very much.

          Followup:

I already pointed out

o works without any help in 9i
o did not in 8i

you have 8i (you don't say so, but I'm assuming that is true).  There is an
issue with bitmap indexes when there isn't a predicate.

I reproduced your issue already -- so, i'm confused?  what are you looking for??

      Rating:
      2
Version  December 15, 2002
Reviewer:
      Piotr Jarmuz  from Braunschweig, Germany
Sorry. I use 9R2 and this is why I wonder. You reproduced the case where
'mod(rownum,100)' was used to generate ptype values so you ended up with
"artificially" equalized population 100 distinct ptype each with 10000
occurences. I used random numbers. Similar but not exactly the same (quote from
your book). I reproduced your example and it works while mine randomized not.
Actually whether it picks the bitmap index or not depends on size of the table.
For small it uses it for big no. While your data no matter how big the table is
is retrieved with the help of bitmap index, ever.
So something is wrong here and I want to clear it up with your or anyone else's
help. I want to know if it is a problem of misconfiguration of my server or
maybe not-so-oficially supported OS (Linux Debian) or Oracle bug or something
else. If I knew how this randomized data behave in other environments
(hardware,OS,versions) I could do something about that (maybe). Anyway I'd be
smarter with that knowledge. I am just curious. It won't leave me in peace
unless I know it.

Kind regards.
P.S.
I am even determined to install Oracle on a windouz server to see the
difference...

          Followup:

I believe you have some setting that is making the apparent cost of the FTS
cheap enough.  Consider:

ops$tkyte@ORA920> create table parts(pk number, name varchar2(30), ptype
number(2));

Table created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> begin
  2       for x in 1..1000000 loop
  3            insert into parts (pk,name,ptype)
  4           values (x,'name'||x,mod(abs(dbms_random.random),100));
  5       end loop;
  6       commit;
  7  end;
  8  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA920>
ops$tkyte@ORA920> create bitmap index parts_type_index
  2  on parts(ptype) nologging;

Index created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> analyze table parts compute statistics
  2  for table
  3  for all indexes
  4  for all indexed columns;

Table analyzed.

ops$tkyte@ORA920>
ops$tkyte@ORA920> set autotrace on
ops$tkyte@ORA920> select ptype, count(*) from parts
  2  group by ptype;

     PTYPE   COUNT(*)
---------- ----------
         0       9935
         1       9883
         2       9993
         3       9972
         4      10104
         5       9985
         6      10049
         7      10015
         8       9975
         9       9909
        10       9950
        11      10093
        12       9958
        13       9938
        14      10091
        15      10071
        16       9904
        17       9941
        18       9966
        19      10164
        20       9998
        21      10144
        22       9884
        23       9986
        24      10008
        25       9868
        26      10019
        27      10042
        28       9976
        29      10004
        30       9959
        31       9966
        32       9923
        33       9935
        34       9941
        35      10006
        36       9971
        37       9949
        38       9963
        39       9840
        40      10085
        41       9879
        42       9934
        43      10249
        44      10170
        45      10061
        46      10147
        47      10026
        48      10188
        49      10105
        50       9770
        51       9969
        52       9989
        53      10098
        54      10144
        55       9937
        56      10092
        57      10041
        58      10244
        59       9936
        60       9999
        61      10060
        62       9875
        63      10125
        64       9855
        65       9989
        66      10091
        67       9914
        68       9780
        69      10026
        70      10092
        71       9932
        72       9900
        73      10149
        74      10048
        75       9758
        76      10110
        77       9904
        78       9881
        79       9971
        80       9938
        81       9993
        82      10124
        83       9979
        84       9988
        85      10081
        86       9885
        87      10080
        88      10065
        89      10160
        90       9849
        91       9771
        92      10031
        93      10074
        94      10004
        95       9959
        96      10008
        97      10170
        98       9966
        99      10074

100 rows selected.

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=276 Card=100 Bytes=200)
   1    0   SORT (GROUP BY NOSORT) (Cost=276 Card=100 Bytes=200)
   2    1     BITMAP CONVERSION (COUNT)
   3    2       BITMAP INDEX (FULL SCAN) OF 'PARTS_TYPE_INDEX'

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        254  consistent gets
          0  physical reads
          0  redo size
       2129  bytes sent via SQL*Net to client
        565  bytes received via SQL*Net from client
          8  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        100  rows processed

ops$tkyte@ORA920>

but when I set the multiblock read count artificially high:

ops$tkyte@ORA920> show parameter multi

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_file_multiblock_read_count        integer     16
mts_multiple_listeners               boolean     FALSE
parallel_adaptive_multi_user         boolean     FALSE

ops$tkyte@ORA920> alter session set db_file_multiblock_read_count=500;

Session altered.

ops$tkyte@ORA920> set autotrace traceonly explain
ops$tkyte@ORA920> select ptype, count(*) from parts group by ptype;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1278 Card=100 Bytes=200)
   1    0   SORT (GROUP BY) (Cost=1278 Card=100 Bytes=200)
   2    1     TABLE ACCESS (FULL) OF 'PARTS' (Cost=83 Card=1000000
Bytes=2000000)

Don't bother with the win stuff -- look at your optimizer related init.ora
settings

      Rating: 5
Right to the
      point  December 16, 2002
Reviewer:  Piotr
      Jarmuz  from Braunschweig, Germany
You're the best!
In my server was: db_file_multiblock_read_count=64
When I set it down to 16 or even 32 it did the right thing.
You just helped me get rid of
"one-of-those-strange-things-that-just-happen-without-a-reason" feeling. It's
important for me.

About win stuff it was a joke ;-)

Thank you so much.

December 16, 2002 Balaji -- Thanks for the question regarding "Help
      on Update ", version 8.1.7

      You Asked
Hi Tom,
 I am doing the following update in my table which has around 65K rows per month
and we have a retention period of 13 months. Currently when i do this update the
table has only 135 K records (2 months) and it takes more than 20 min. do to the
update.

update
me_cust_rev_details cust
set
(cust_name, db_co_id , p1g_class, p1g_class_desc , legal_status,
legal_status_desc, wwic_code, wwic_code_desc ,
bbk_client_code , ccdb_code,ccdb_group_code ,
country_of_domicile,country_of_domicile_desc, country, country_desc, cust_type,
resident_code , resident_desc, advisor_code,parent_co, cust_segment,
industry_type, cust_erf
) = (
        select
                a.customer_name, a.sub_branch_code,
                a.p1g_class, b.p1g_class_desc ,
                a.legal_status_code, c.legal_status_desc ,
                a.wwic_code,  d.wwic_desc  ,
                a.bbk_client_code ,
                a.ccdb_code , a.ccdb_group_code ,
                a.country_of_domicile, e.country_name ,
                a.country,  f.country_name,
                a.customer_type ,
                a.resident_code,
                g.resident_desc,a.advisor_code,
                h.parent_co,
                h.cust_segment, h.industry_type, h.cust_erf
        from
                me_cust_class_mappings h,
                me_cust_master a        ,
                me_br_p1g_class_codes b ,
                me_br_legal_status c    ,
                me_br_wwic_codes d      ,
                me_br_countries e       ,
                me_br_countries f       ,
                me_br_resident_codes g
        where
        a.br_code = b.br_code and a.version_date = b.version_date and
a.p1g_class = b.p1g_class_code and
        a.br_code = c.br_code and a.version_date = c.version_date and
a.legal_status_code = c.legal_status_code  and
        a.br_code = d.br_code and a.version_date = d.version_date and
a.wwic_code = to_number(d.wwic_code) and
        a.br_code = e.br_code and a.version_date = e.version_date and
a.country_of_domicile = to_number(e.country_code) and
        a.br_code = f.br_code and a.version_date = f.version_date and a.country
= to_number(f.country_code) and
        a.br_code = g.br_code and a.version_date = g.version_date and
a.resident_code = to_number(g.resident_code) and
        a.br_code = h.br_code and a.version_date = h.version_date and
a.master_number = h.cust_master and
        a.br_code = cust.br_code  and a.version_date = cust.version_date and
a.master_number  = cust.cust_master
        )
where cust.br_code = i_br_code and cust.version_date = i_version_date;

and the following is from the trc file.

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.08       0.10          0          0          0           0
Execute      1   1481.58    1510.94      25738    1666869      52694       48233
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2   1481.66    1511.04      25738    1666869      52694       48233

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 45     (recursive depth: 1)

The cpu usage is also very high.

The explain plan of the above statement is as follows

UPDATE STATEMENT Optimizer=CHOOSE (Cost=50 Card=31289 Bytes=6601979)
  UPDATE OF ME_CUST_REV_DETAILS
    PARTITION RANGE (SINGLE)
      TABLE ACCESS (FULL) OF ME_CUST_REV_DETAILS (Cost=50 Card=31289
Bytes=6601979)
  NESTED LOOPS (Cost=50 Card=5 Bytes=1595)
    NESTED LOOPS (Cost=38 Card=3 Bytes=882)
      NESTED LOOPS (Cost=30 Card=2 Bytes=538)
        NESTED LOOPS (Cost=5 Card=1 Bytes=214)
          NESTED LOOPS (Cost=4 Card=1 Bytes=180)
            NESTED LOOPS (Cost=4 Card=1 Bytes=141)
              NESTED LOOPS (Cost=4 Card=1 Bytes=106)
                TABLE ACCESS (BY GLOBAL INDEX ROWID) OF ME_CUST_MASTER (Cost=3
Card=1 Bytes=79)
                  INDEX (UNIQUE SCAN) OF PK_ME_CUST_MASTER (UNIQUE) (Cost=2
Card=1)
                PARTITION RANGE (SINGLE)
                  INDEX (UNIQUE SCAN) OF PK_ME_CUST_CLASS_MAPPINGS (UNIQUE)
(Cost=1 Card=1 Bytes=27)
              PARTITION RANGE (SINGLE)
                INDEX (UNIQUE SCAN) OF PK_ME_BR_LEGAL_STATUS (UNIQUE)
            PARTITION RANGE (SINGLE)
              INDEX (UNIQUE SCAN) OF PK_ME_BR_P1G_CLASS_CODES (UNIQUE)
          PARTITION RANGE (SINGLE)
            INDEX (RANGE SCAN) OF PK_ME_BR_RESIDENT_CODES (UNIQUE) (Cost=1
Card=4 Bytes=136)
        PARTITION RANGE (SINGLE)
          INDEX (RANGE SCAN) OF PK_ME_BR_WWIC_CODES (UNIQUE) (Cost=25 Card=1315
Bytes=72325)
      PARTITION RANGE (SINGLE)
        INDEX (RANGE SCAN) OF PK_ME_BR_COUNTRIES (UNIQUE) (Cost=4 Card=218
Bytes=5450)
    PARTITION RANGE (SINGLE)
      INDEX (RANGE SCAN) OF PK_ME_BR_COUNTRIES (UNIQUE) (Cost=4 Card=218
Bytes=5450)

When i see the execution plan , the table me_cust_rev_details goes for a FULL
table scan. We have this table as a partitioned one. How can i make this sql run
faster.

Thanks and Regards
M.Balaji

      and we said...
to full scan 135k records takes seconds not minutes.  Do not be afraid of that
full scan - that is PERFECTLY OK (go ahead -- do this:

SQL> set timing on
SQL> set arraysize 500
SQL> set autotrace traceonly
SQL> select * from me_cust_rev_details;
SQL> set autotrace off

for example:

ig_table@ORA920> set timing on
big_table@ORA920> set arraysize 500
big_table@ORA920> set autotrace traceonly
big_table@ORA920> select * from big_table where rownum <= 135000;

135000 rows selected.

Elapsed: 00:00:03.84

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=1253 Card=135000
Bytes=13635000)
   1    0   COUNT (STOPKEY)
   2    1     TABLE ACCESS (FULL) OF 'BIG_TABLE' (Cost=1253 Card=1000000
Bytes=101000000)

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       2031  consistent gets
       1640  physical reads
          0  redo size
    9253401  bytes sent via SQL*Net to client
       3458  bytes received via SQL*Net from client
        271  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
     135000  rows processed

FULL SCANS ARE NOT EVIL.  Just say that to yourself every day before going to
bed until you actually believe it!!!

Especially -- ESPECIALLY since you are going after 48,000+ rows out of 135,000.
If we used an INDEX to access that table, well, it would just go slower!!

Ok, that aside, you are doing a correlated update.  You are running that
subquery  48,233 times.

Soooo lets say that query runs in 1/100th of a second Well,

big_table@ORA920> select (1/100 * 48233) / 60 from dual;

(1/100*48233)/60
----------------
      8.03883333  (minutes)

there you go -- your query that takes about 1/100th of a second, when executed
48k times, could be a LARGE contributor!! (thats if that query even goes that
fast -- don't know)

So, here is my suggestion that will involve a TEMPORARY TABLE (yes, I do use
them sometimes ;)

Suggest you create ONCE in your database this:

create global temporary table TEMP_me_cust_rev_details on commit delete rows
as
select
       a.br_code , a.version_date,  a.master_number
       a.customer_name, a.sub_branch_code,
       a.p1g_class, b.p1g_class_desc ,
       a.legal_status_code, c.legal_status_desc ,
       a.wwic_code,  d.wwic_desc  ,
       a.bbk_client_code ,
       a.ccdb_code , a.ccdb_group_code ,
       a.country_of_domicile, e.country_name ,
       a.country,  f.country_name,
       a.customer_type ,
       a.resident_code,
       g.resident_desc,a.advisor_code,
       h.parent_co,
       h.cust_segment, h.industry_type, h.cust_erf
  from me_cust_class_mappings h,
       me_cust_master a        ,
       me_br_p1g_class_codes b ,
       me_br_legal_status c    ,
       me_br_wwic_codes d      ,
       me_br_countries e       ,
       me_br_countries f       ,
       me_br_resident_codes g
 where 1=0;

alter table TEMP_me_cust_rev_details
add constraint TEMP_ME_PK
primary key(br_code,version_date,master_number)
/

Now, your update will be a BATCH INSERT + BULK UPDATE like this:

is
begin
...
insert into TEMP_ME_CUST_REV_DETAILS
select
       a.br_code , a.version_date,  a.master_number
       a.customer_name, a.sub_branch_code,
       a.p1g_class, b.p1g_class_desc ,
       a.legal_status_code, c.legal_status_desc ,
       a.wwic_code,  d.wwic_desc  ,
       a.bbk_client_code ,
       a.ccdb_code , a.ccdb_group_code ,
       a.country_of_domicile, e.country_name ,
       a.country,  f.country_name,
       a.customer_type ,
       a.resident_code,
       g.resident_desc,a.advisor_code,
       h.parent_co,
       h.cust_segment, h.industry_type, h.cust_erf
  from me_cust_class_mappings h,
       me_cust_master a        ,
       me_br_p1g_class_codes b ,
       me_br_legal_status c    ,
       me_br_wwic_codes d      ,
       me_br_countries e       ,
       me_br_countries f       ,
       me_br_resident_codes g
 where a.br_code = b.br_code
   and a.version_date = b.version_date
   and a.p1g_class = b.p1g_class_code
   and a.br_code = c.br_code
   and a.version_date = c.version_date
   and a.legal_status_code = c.legal_status_code
   and a.br_code = d.br_code
   and a.version_date = d.version_date
   and a.wwic_code = to_number(d.wwic_code)   /* I hate when I see that, someone
didn't model this right! */
   and a.br_code = e.br_code
   and a.version_date = e.version_date
   and a.country_of_domicile = to_number(e.country_code) /* ditto */
   and a.br_code = f.br_code
   and a.version_date = f.version_date
   and a.country = to_number(f.country_code) /* all over the place */
   and a.br_code = g.br_code
   and a.version_date = g.version_date
   and a.resident_code = to_number(g.resident_code) /* not again! */
   and a.br_code = h.br_code
   and a.version_date = h.version_date
   and a.master_number = h.cust_master
   and a.br_code = I_BR_CODE
   and a.version_date = I_VERSION_DATE;

update ( select a.cust_name cust_name_1, a.db_co_id db_co_id_1, a.p1g_class
p1g_class_1, ....
                b.customer_name cust_name_2, b.sub_branch_code db_co_id_2, ....
           from me_cust_rev_details A, temp_me_cust_rev_details B
          where a.br_code = b.br_code
            and a.version_date = b.version_date
            and a.cust_master = b.master_number )
   set cust_name_1 = cust_name2, db_co_id_1 = db_co_id_2, ....;

....
end;

You may well find that doing the bulk update as opposed to 48k single little
queries -- might go faster.

One thing to test as well, how long does:

update
me_cust_rev_details cust
set
cust_name = cust_name, db_co_id = db_co_id, ... cust_erf = cust_erf
where cust.br_code = i_br_code and cust.version_date = i_version_date;

take?  That'll give you an indication of the very very fastest this could ever
run by tuning SQL.  It'll do all of the work (well, most of the work -- the
index updates won't be as bad as they are in real life where the values actually
change).
)
Review &
      Followup

      Rating: 5
Another trick
      for my bag!!  December 13, 2002
Reviewer:
      A reader  from Dallas, TX
Tom - You continue to provide the most useful Oracle information of any source
I've run across.  I know you'll eventually tire of this, but in the mean time --
thanks.

      Rating: 5
Found Some
      thing very different.  December 15, 2002
Reviewer:
      Balaji.M  from Singapore
Hi Tom,
 I need to specify some points here.
1)Since we have this table as a 13 mth data always, is it ok  if it goes for a
FULL table scan during 13th Month and Afterwards.
2)From the above Update i removed one table and updating the table
ME_CUST_REV_DETAILS seperately. After removing this table The Update for 65K
(One Month,since we go by branch and version_date -- Mth End Date), it took only
3 minutes and when i run following update it takes 19 min. I suspect this table
is a culprit and b'cos of this the previous table was taking 20 min.

update me_cust_rev_details cust set (cust.wwic_code,cust.wwic_code_desc) =
        (select a.wwic_code,b.wwic_desc
         from me_cust_master a, me_br_wwic_codes b
         where
         a.br_code = b.br_code and a.version_date = b.version_date and
a.wwic_code = to_number(b.wwic_code) and
         a.br_code = cust.br_code and a.version_date = cust.version_date and
a.master_number = cust.cust_master
        )
where cust.br_code = i_br_code and cust.version_date = i_version_date;

The TKPROF OUTPUT IS As follows. I see lot of cpu spent for the above update.

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.01          0          0          0           0
Execute      1   1189.12    1193.82      18939    1053784      50676       48233
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2   1189.13    1193.83      18939    1053784      50676       48233
Can u help me to find out the problem.

Cheers
M.Balaji

          Followup:

I suspect you are forgetting that the correlated subquery is run 65k times and
if it takes 0.02 cpu seconds per execution -- well, you do the math.

You are not finding anything different, you are reaffirming my findings -- the
correlated subquery is the main culprit here.

Do this for me ok:

alter session set sql_trace=true;
declare
  i_br_code      me_cust_rev_details.br_code%type := ....;
  i_version_date me_cust_rev_details.version_date%type := ....;
  l_wwic_code    me_cust_master.wwic_code%type;
  l_wwic_desc    me_br_wwic_codes.wwic_desc%type;
begin
   for cust in ( select * from me_cust_rev_details where br_code = i_br_code
                 and version_date = i_version_date )
   loop
       select a.wwic_code,b.wwic_desc into l_wwic_code, l_wwic_desc
         from me_cust_master a, me_br_wwic_codes b
         where a.br_code = b.br_code
           and a.version_date = b.version_date
           and a.wwic_code = to_number(b.wwic_code)
           and a.br_code = cust.br_code
           and a.version_date = cust.version_date
           and a.master_number = cust.cust_master;
   end loop;
end;
/

Now, tell us, how does that do.

December 16, 2002 ashraf -- Thanks for the question regarding
      "query", version 8.17

      You Asked
i have a table like

port          activity           activity_date       area
-----        ----------         ---------------     ------
port1         load                10-aug-02          area1
port1         charg               15-sep-02          area1
port1         sendtorepair        10-feb-02          area1

the port have a lot of activity for each port and i need to generate report like
that

port1     from load to charge        avg(days)
port1     from repair to repaired    avg(days)
.
.

port2     from load to charge        avg(days)
.
.
.

      and we said...
analytics rock - here is a method:

ops$tkyte@ORA920.US.ORACLE.COM> select * from t;

PORT       ACTIVITY             ACTIVITY_ AREA
---------- -------------------- --------- ----------
port       load                 10-AUG-02 area1
port       charge               15-SEP-02 area1
port       sendtorepair         10-FEB-03 area1
port       repaired             22-FEB-03 area1
port       load                 10-MAR-03 area1
port       charge               15-MAR-03 area1

6 rows selected.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select port, activity, nxt_activity,
activity_date, nxt_activity_date,
  2         nxt_activity_date - activity_date days_between
  3    from (
  4  select port,
  5         activity,
  6         lead(activity)
  7           over (partition by port
  8                     order by activity_date) nxt_activity,
  9         activity_date,
 10         lead(activity_date)
 11           over (partition by port
 12                     order by activity_date) nxt_activity_date
 13    from t
 14         )
 15  /

PORT       ACTIVITY        NXT_ACTIVITY         ACTIVITY_ NXT_ACTIV DAYS_BETWEEN
---------- --------------- -------------------- --------- --------- ------------
port       load            charge               10-AUG-02 15-SEP-02           36
port       charge          sendtorepair         15-SEP-02 10-FEB-03          148
port       sendtorepair    repaired             10-FEB-03 22-FEB-03           12
port       repaired        load                 22-FEB-03 10-MAR-03           16
port       load            charge               10-MAR-03 15-MAR-03            5
port       charge                               15-MAR-03

6 rows selected.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> select port, activity, nxt_activity,
  2         avg( nxt_activity_date - activity_date ) days_between
  3    from (
  4  select port,
  5         activity,
  6         lead(activity)
  7           over (partition by port
  8                     order by activity_date) nxt_activity,
  9         activity_date,
 10         lead(activity_date)
 11           over (partition by port
 12                     order by activity_date) nxt_activity_date
 13    from t
 14         )
 15   where nxt_activity is not null
 16   group by port, activity, nxt_activity
 17   order by 1, 2
 18  /

PORT       ACTIVITY             NXT_ACTIVITY         DAYS_BETWEEN
---------- -------------------- -------------------- ------------
port       charge               sendtorepair                  148
port       load                 charge                       20.5
port       repaired             load                           16
port       sendtorepair         repaired                       12

ops$tkyte@ORA920.US.ORACLE.COM>
Review &
      Followup

      Rating: 5
very vey
      useful  December 15, 2002
Reviewer:  ashraf
       from KUWAIT
i go another way by union all but about 60 lines of sql
what you say is tel me i must go to read analytics rock

thank's tom

      Rating: 4
do you here
      me  December 15, 2002
Reviewer:  ashraf
       from KUWAIT
hi tom i forget to put addation quey which is the most important for me i have
the same table with one addation column is the container unit and the activity
is for each container that is not moving for 90 day's i.e no activity for 90
day's

          Followup:

not making sense -- but, given that I've pointed out the analytic functions and
given that you can read all about them:

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96520/analysis.htm#1020
maybe you can add this functionality.

      Rating:
      4
index  December 15, 2002
Reviewer:
      A reader
what about index

          Followup:

what about them?

      Rating:
      5
index  December 15, 2002
Reviewer:
      A reader
i try
ops$tkyte@ORA920.US.ORACLE.COM> select port, activity, nxt_activity,
  2         avg( nxt_activity_date - activity_date ) days_between
  3    from (
  4  select port,
  5         activity,
  6         lead(activity)
  7           over (partition by port
  8                     order by activity_date) nxt_activity,
  9         activity_date,
 10         lead(activity_date)
 11           over (partition by port
 12                     order by activity_date) nxt_activity_date
 13    from t
 14         )
 15   where nxt_activity is not null
 16   group by port, activity, nxt_activity
 17   order by 1, 2
 18  /
so i create index on port,activity but
when i trace
it is scan full table but not using the index

          Followup:

why would that use that index?  did you want it to go MUCH slower???

Now IF:

a) one of port, activity_date, activity are NOT NULL (at least one of them) AND
b) the index was on port, activity_date, ACTIVITY (need that last column to
   have the answer retrievable from the INDEX alone -- if we have to hip hop
   to the table for each and every single index entry -- that would slow us
   down totally)

THEN

the index can and will be used.

ops$tkyte@ORA920> create table t ( port varchar2(10), activity varchar2(20),
activity_date date, area varchar2(5) );

Table created.

ops$tkyte@ORA920> create index port_idx on t(port,activity_date,activity);

Index created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> exec dbms_stats.set_table_stats( user, 'T', numrows => 100000,
numblks => 10000 );

PL/SQL procedure successfully completed.

ops$tkyte@ORA920>
ops$tkyte@ORA920> set autotrace traceonly explain
ops$tkyte@ORA920> select port, activity, nxt_activity,
  2         avg( nxt_activity_date - activity_date ) days_between
  3    from (
  4  select port,
  5         activity,
  6         lead(activity)
  7           over (partition by port
  8                     order by activity_date) nxt_activity,
  9         activity_date,
 10         lead(activity_date)
 11           over (partition by port
 12                     order by activity_date) nxt_activity_date
 13    from t
 14         )
 15   where nxt_activity is not null
 16   group by port, activity, nxt_activity
 17   order by 1, 2
 18  /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2556 Card=100000
Bytes=4900000)
   1    0   SORT (GROUP BY) (Cost=2556 Card=100000 Bytes=4900000)
   2    1     VIEW (Cost=1967 Card=100000 Bytes=4900000)
   3    2       WINDOW (SORT) (Cost=1967 Card=100000 Bytes=2800000)
   4    3         TABLE ACCESS (FULL) OF 'T' (Cost=963 Card=100000
Bytes=2800000)

ops$tkyte@ORA920>
ops$tkyte@ORA920> alter table t modify port not null;

Table altered.

ops$tkyte@ORA920> select port, activity, nxt_activity,
  2         avg( nxt_activity_date - activity_date ) days_between
  3    from (
  4  select port,
  5         activity,
  6         lead(activity)
  7           over (partition by port
  8                     order by activity_date) nxt_activity,
  9         activity_date,
 10         lead(activity_date)
 11           over (partition by port
 12                     order by activity_date) nxt_activity_date
 13    from t
 14         )
 15   where nxt_activity is not null
 16   group by port, activity, nxt_activity
 17   order by 1, 2
 18  /

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=615 Card=100000 Bytes=4900000)
   1    0   SORT (GROUP BY) (Cost=615 Card=100000 Bytes=4900000)
   2    1     VIEW (Cost=26 Card=100000 Bytes=4900000)
   3    2       WINDOW (BUFFER) (Cost=26 Card=100000 Bytes=2800000)
   4    3         INDEX (FULL SCAN) OF 'PORT_IDX' (NON-UNIQUE) (Cost=26
Card=100000 Bytes=2800000)

ops$tkyte@ORA920> set autotrace off

Also, make sure to repeat to oneself:

"indexes are not good incarnate, full table scans are not evil"

over and over and over again.  See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6749454952894
for a good example of "indexes = bad, full table scan = good"

December 16, 2002 Sushil -- Thanks for the question regarding
      "pseudo column called level", version 8.1.7

      You Asked
Hi Tom,

I have a query regarding the level column [pseudo column] used in queries with
connect by clause.
How does oracle determine the level for each row ?
A
One option would be that it stored the data in such a manner.
B
It generates the level at runtime

What I feel is that option B is being used.IF yes then I look at the generation
just like order by clause wherein the data is called in the memory first and
then ordered and displayed.
If what I think is right then in case where data is too large the sorting is
done on disk rather than memory.
What would be the case in pseudo column called level.

I have an idea of using level , no issue with that.What I am looking at is to
have a better understanding of this stuff so that my queries are optimized .

tks n regds
sushil.

      and we said...
it has to generate the level at runtime -- a row can appear anywhere in the
hierarchy.

KING could be at level one against emp:

scott@ORA920> l
  1  select level, rpad('*',2*level,'*') || ename ename
  2   from emp
  3  start with mgr is null
  4* connect by prior empno = mgr
scott@ORA920> /

     LEVEL ENAME
---------- --------------------
         1 **KING
         2 ****JONES
         3 ******SCOTT
         4 ********ADAMS
         3 ******FORD
         4 ********SMITH
         2 ****BLAKE
         3 ******ALLEN
         3 ******WARD
         3 ******MARTIN
         3 ******TURNER
         3 ******JAMES
         2 ****CLARK
         3 ******MILLER

14 rows selected.

or, if we start at the "bottom", KING might be at level 4 (or 3, or 2 or 1)

scott@ORA920> select level, rpad('*', 2*level, '*' ) || ename ename
  2   from emp
  3  start with ename = 'ADAMS'
  4  connect by prior mgr = empno;

     LEVEL ENAME
---------- --------------------
         1 **ADAMS
         2 ****SCOTT
         3 ******JONES
         4 ********KING

Review &
      Followup

      Rating: 2
one part is
      still unanswered  December 16, 2002
Reviewer:
      Sushil Vyas  from India
Hi Tom,

Tks for the reply, this has helped me be sure of the option used. I request you
to provide me info on the second part of my question where I wanted to know how
does oracle carry out this task of leveling in case the data set is too big. Are
there anything similar to sorts (disk) ?

tks n regds
sushil

          Followup:

it would be sorts to disk.  level is just assigned as rows are gotten.  If the
connect by is done via an index -- then we gets rows bit by bit -- we do not
answer the query "up front" and stash it somewhere.

It is processed EXACTLY like any other query is, no differently.

December 16, 2002 -- Thanks for the question regarding "how to
      mount 2 different databases", version 9i

      You Asked
hi tom
if I create 2 databases on a single machine sy ORCL and ORCLTST
then while starting how do I specify whic DB to start?
I want to start either one of them at a time

is there any parameter/argument?

      and we said...
depends on the operating system.

I'll assume windows -- you want to read about oradim and how to use oradim to
set the default "up/down" status of the database.  Then, you can use the
services from the control panel to start/stop them (or just net start and net
stop from the command line)

http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/win.920/a95491/create.htm#1006534

Review &
      Followup

      Rating: 4
How to start
      two db simultaneously  December 16, 2002
Reviewer:
      Lakshminarasimhan R  from Dubai
Hi Tom

How to start two databases and run parallely in the system.

I have Oracle 8.1.7 and orale9ir2 in my system.

I want to start both and talk to each other.

how to do it?

Reg
LakshmiNarasimhan R

          Followup:

start them?  just start them.  nothing special.  nothing different.  It is just
like having one database, except you have two.

December 16, 2002 M -- Thanks for the question regarding
      "DBMS_STATS.alter_schema_table_monitoring doesn't work", version 9i

      You Asked
I loged on as SYS and tried to run:

begin
sys.dbms_stats.alter_schema_table_monitoring('com2', true);
end;

but got:

PLS-00302: component 'ALTER_SCHEMA_TABLE_MONITORING' must be declared.

Why? Other GATHER procedures seem to work fine.

Thanks for any help.

      and we said...
Hmm, doc bug.

A describe on dbms_stats shows the name is actually:

ALTER_SCHEMA_TAB_MONITORING

TAB not TABLE.
Review & Followup

      Rating:
      3
dbms_stats  December 16, 2002
Reviewer:
      Venkat  from India
Tom,

When I tried dbms_stats.alter_schema_tab_monitoring,
I got the following error.

sitadmin@OASIS817>begin
sys.dbms_stats.alter_schema_tab_monitoring('SITADMIN', true);
end;
  2    3    4
  5  /
sys.dbms_stats.alter_schema_tab_monitoring('SITADMIN', true);
               *
ERROR at line 2:
ORA-06550: line 2, column 16:
PLS-00302: component 'ALTER_SCHEMA_TAB_MONITORING' must be declared
ORA-06550: line 2, column 1:
PL/SQL: Statement ignored

Can you please help out?

Regards,
Venkat

          Followup:

it be new in 9i is why....

December 16, 2002 Murali -- Thanks for the question regarding
      "Question in DYNAMIC SQL", version 8.1.7

      You Asked
Hello Scott

I have created a procedure called TRY and passing table name as the parameter (
In real scenario I will passing the table through another procedure or a CURSOR
). If I try to execute the procedure try it gives me an error.  Could you please
help me where I am doing wrong and what's the correct way of doing.  Thanks in
Advance

CREATE OR REPLACE PROCEDURE TRY (
 pi_table  IN all_tables.table_name%type  )
IS
   TYPE cv_typ IS REF CURSOR;
   cv cv_typ;
   v_invoice ap_invoices_interface_dly.invoice_num%TYPE ;
   v_vendor  ap_invoices_interface_dly.vendor_num%TYPE  ;
BEGIN
   OPEN cv FOR
      'SELECT invoice_num, vendor_id
         FROM  :l_table '  USING pi_table ;
   LOOP
      FETCH cv INTO v_invoice, v_vendor ;
      EXIT WHEN cv%NOTFOUND;
      ldpr.ln (' THE  INVOICE_NUM : = '|| v_invoice || ' THE VENDOR NUM : = ' ||
v_vendor ) ;
   END LOOP;
   CLOSE cv;
END;

SQL> exec try ('AP_INVOICES_INTERFACE');
BEGIN try ('AP_INVOICES_INTERFACE'); END;

*
ERROR at line 1:
ORA-00903: invalid table name
ORA-06512: at "INTNEW.TRY", line 9
ORA-06512: at line 1

Murali

      and we said...
You cannot bind identifiers.

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:865027340272
   OPEN cv FOR
      'SELECT invoice_num, vendor_id
         FROM  ' || pi_table;

Review &
      Followup

      Rating: 5
Name
      Change?  December 16, 2002
Reviewer:  Mark A.
      Williams  from Indianapolis, IN USA
Tom:

Changed your name to Scott have you? (See original post)  I'm not even going to
guess your password then..

- Mark

      Rating: 4
deja
      vu?  December 16, 2002
Reviewer:  Dennis
       from Missouri, USA
Isn't this the second time in the last six months that Tom was 'using' a nom de
plume??

Now at least we know who the schema was named after ;P

Dennis

      Rating: 4
ah
      ha!  December 16, 2002
Reviewer:  Dennis
       from Missouri, USA
I found it!!!

Here it is, Tom posing as "steve", who interestingly enough is the default
schema name for Erwin/ModelMart.

http://asktom.oracle.com/pls/ask/f?p=4950:8:1734678::NO::F4950_P8_DISPLAYID,F4950_P8_CRITERIA:952229840241,%7Bjava%7D
Notice how he keeps using these five letter names that start with an 'S'??

Dennis

      Rating: 4
Maybe it's
      time...  December 16, 2002
Reviewer:  robert
       from PA
Hey, Tom maybe it's time you put your foot down
before it gets worse like someone coming up with
Susan, Sarah.....

      Rating: 5
It's nearly
      Christmas ...  December 17, 2002
Reviewer:
      Paul  from UK
... so I'm in a frivolous mood.

I was told once that Scott was one of the original developers of SQL*Plus and he
had a pet cat named Tiger.

Is this true or just an urban myth?

I've got a rabbit called CHANGE_ON_INSTALL!

Paul

      Rating: 5
To
      Paul  December 17, 2002
Reviewer:  Mark A.
      Williams  from Indianapolis, IN USA
I have a book (The Difference Between God and Larry Ellison) and it states that
Bruce Scott had a daughter and her cat was named Tiger.  The book is by Mike
Wilson if you are interested.  Quite a good book actually.  And the answer to
the question posed by the book title?  "God doesn't think he's Larry Ellison."

HTH,

Mark

December 16, 2002 Lutz -- Thanks for the question regarding
      "interrupt of query execution and calling an external function", version
      oracle 8i Rel. 8.1.5

      You Asked
Hello Tom,

in my research project I would like to interrupt the query
execution process, evaluate the query outside the database
and send it back to oracle (don't ask why, it's research!).
Is it possible to interrupt the query execution process and call
an external function, whereby the parameter of the external
function is the SQL-statement itself?

For a better understanding and easier explanation
imagine the following query:

select region, month, sum(sales)
from raw_datas
where country like 'Germany' and
      year = 2000
group by region, month;

By using SQLPLUS I type the query and press return. Usually the
system executes it and presents the result on the screen.
Now I would like to interrupt the execution process and compute
the result outside the database system. The last step can be done by
calling an external function. But the external function  (e.g.
a C, C++ or Java method) needs the statement (especially the
where and group by clause), otherwise the function does not know
what to compute. Therefore, the question is pretty easy: How
can I access to the statement (e.g. reading from the system
global area)?

Thank you for helping me
and for your efforts in advance
Lutz

      and we said...
Cannot be done in that fashion.

We can however:

select * from ( stored_procedure(any_inputs_you_like) )

and the stored procedure can be in java, c, or plsql.  In that fashion, it is
possible to

select *
  from TABLE ( cast( my_function('select ... from ... where ....') as
mytableType ) )

does that interest you?
Review & Followup

      Rating: 4
interrupt a
      procedure (batch job)  December 16, 2002
Reviewer:
      A reader
Hi

I would like to know how could we interrupt a PL/SQL procedure when it´s
running.

I have a procedure which works like

declare
  l_number_loop number := 5;
  ............
begin
  ..........
  while l_number_loop <= 5
  loop
    delete ......
  end loop;
end;
/

I thought of creating a table with one column and one row with values of 0 or 1
and in each loop checking the table for that value, if 1 then exit else continue
but I would like to know if Oracle supplied PL/SQL packages DBMS_ALERT or
DBMS_PIPE can achieve my goal?

thank you!

          Followup:

Yes, either of dbms_alert or dbms_pipe could achieve your goal.  your plsql
block would register its interest in named alert for example and inside the loop
it would do a dbms_alert.waitone with an immediate timeout.  check the status to
see if you were alerted or not and break out.  similarly with pipes.  A table
would work just the same.

      Rating: 5
with those
      packages how do I interrupt?  December 16, 2002

Reviewer:  A reader
Hi again

If I use DBMS_ALERT or DBMS_PIPE how can I interrupt one process from another, I
mean if I was using the table approach I would simply open a SQLPLUS and update
the value from table from 0 to so in the next LOOP my running procedure would
stop. Can I do this with packages? Sending a message to running procedure? (I am
not quite sure how DBMS_ALERT works, DBMS_PIPE allows us to send messages
between processes...)

          Followup:

if you have my book -- read the sections on it in there.  else, check out the
supplied packages guide which describe this.

I described how to "interrupt" using alerts.   you register and then call
waitone in the loop (just as you would select * from t in the loop)

December 16, 2002 Haranadh -- Thanks for the question regarding
      "What does N/A mean in the status of an Index?", version 9.0.1

      You Asked
Hi Tom,

May be it is a very trivial question. We have created a bunch of Bitmap indexes
on a table and when we query the USER_INDEXES view it is showing the status of
the index as N/A. What does it mean? Could you please help us validate these
indexes?

Thanks,
Hari

      and we said...
is the index partitioned?

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t2 ( x int primary key,
collection_year int );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> CREATE TABLE t
  2  (
  3    x int primary key,
  4    collection_year int
  5  )
  6  PARTITION BY RANGE (COLLECTION_YEAR) (
  7    PARTITION PART_95 VALUES LESS THAN (1996) ,
  8    PARTITION PART_96 VALUES LESS THAN (1997) ,
  9    PARTITION PART_97 VALUES LESS THAN (1998) ,
 10    PARTITION PART_98 VALUES LESS THAN (1999) ,
 11    PARTITION PART_99 VALUES LESS THAN (2000) ,
 12    PARTITION PART_00 VALUES LESS THAN (2001) ,
 13    PARTITION PART_01 VALUES LESS THAN (MAXVALUE)
 14  )
 15  ;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create bitmap index t_bm_idx1 on
t(collection_year) local;

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create bitmap index t_bm_idx2 on
t2(collection_year);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select index_name, status from user_indexes
  2  where index_name like 'T_BM_IDX_';

INDEX_NAME                     STATUS
------------------------------ --------
T_BM_IDX1                      N/A
T_BM_IDX2                      VALID

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select index_name, partition_name, status
  2    from user_ind_partitions
  3   where index_name like 'T_BM_IDX_';

INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ --------
T_BM_IDX1                      PART_96                        USABLE
T_BM_IDX1                      PART_97                        USABLE
T_BM_IDX1                      PART_98                        USABLE
T_BM_IDX1                      PART_99                        USABLE
T_BM_IDX1                      PART_00                        USABLE
T_BM_IDX1                      PART_01                        USABLE
T_BM_IDX1                      PART_95                        USABLE

7 rows selected.

N/A stands for "not applicable".


Subject | Article | Last Updated (East coast time)
cursor fetch | article only | Dec 17, 2002 07:32 am
Optimization | article only | Dec 17, 2002 07:37 am
adding sysdate on the file name | article only | Dec 17, 2002 08:00 am
Using Ref cursors in Reports | article only | Dec 18, 2002 02:29 pm
Batch processes running slowly. | article only | Dec 18, 2002 02:32 pm
Retrieving date in different timezones from the database(by not using new_time function) | article only | Dec 19, 2002 07:21 am
!= and != is not bringing the correct results | article only | Dec 19, 2002 08:46 am
Primary key field datatype | article only | Dec 19, 2002 09:43 am
Oracle JVM Vendor | article only | Dec 19, 2002 10:02 am
Count(*) | article only | Dec 19, 2002 10:29 am
Circular Dependency Problem. | article only | Dec 19, 2002 11:12 am
SQL Query | article only | Dec 19, 2002 11:36 am
Index used sometimes and sometimes NOT? | article only | Dec 19, 2002 01:09 pm
Query Tuning | article only | Dec 19, 2002 01:18 pm
NOT operator performance. | article only | Dec 19, 2002 02:09 pm
Database Host name | article only | Dec 19, 2002 02:48 pm
Triggers on DBA_SOURCE | article only | Dec 19, 2002 02:58 pm
Locating character in Strings, using wild cards | article only | Dec 19, 2002 06:28 pm
latch contention | article only | Dec 20, 2002 07:28 am
PCTFREE/PCTUSED | article only | Dec 20, 2002 08:04 am
Http Server in Oracle8i Rel3 | article only | Dec 20, 2002 08:10 am
Phanton Update | article only | Dec 20, 2002 08:15 am
FGAC Query Rewrite | article only | Dec 20, 2002 09:46 am
Group By Problem | article only | Dec 20, 2002 09:59 am
Fiscal year | article only | Dec 20, 2002 10:20 am
multiplexing archive log files | article only | Dec 20, 2002 10:34 am
Rownum on fields | article only | Dec 20, 2002 11:53 am
Sql or Pl/sql approach | article only | Dec 20, 2002 03:01 pm
Sequences | article only | Dec 20, 2002 08:14 pm
Storing Images in database as BLOB Vs storing images on file system on Application Server | article only | Dec 20, 2002 08:16 pm
shared_pool_size and other memory questions | article only | Dec 20, 2002 08:18 pm
Use of Bind Variables on 8.1.6 | article only | Dec 20, 2002 08:21 pm
Analyse schemas versions | article only | Dec 21, 2002 07:46 pm
Date Problem: confusion when time goes back by one hour. | article only | Dec 22, 2002 09:10 am
Index rebuild | article only | Dec 22, 2002 09:55 am
Loop in a SQL Query | article only | Dec 22, 2002 10:02 am
Locating character in Strings, using wild cards | article only | Dec 22, 2002 12:18 pm
Explain Plan with Nested Table Bind Variable | article only | Dec 23, 2002 10:54 am
lengthy select statement | article only | Dec 23, 2002 11:14 am
Undocumented init.ora parameters | article only | Dec 23, 2002 11:15 am
Can certain characters cause database errors? | article only | Dec 23, 2002 12:58 pm
Maintaining Partitioned Tables | article only | Dec 23, 2002 01:02 pm
Index Rebuild | article only | Dec 23, 2002 01:07 pm
Partitioning Performance | article only | Dec 23, 2002 02:03 pm
Oracle Insert Question | article only | Dec 23, 2002 04:54 pm
Selective grants after creating a table or view | article only | Dec 23, 2002 05:04 pm
rollback segment writing order | article only | Dec 23, 2002 05:31 pm
Using longs in triggers | article only | Dec 23, 2002 07:18 pm
row(s) 1 - 48 of 48
December 17, 2002 Rohil -- Thanks for the question regarding "cursor fetch", version 9
You Asked
hi tom,

"When i open a cursor in Oracle, does it fetch the entire recordset into the
memory(or SGA) all in one go (i.e at a time, if there are, say, 10-20 lacs
records)?
i.e when the data is huge(lacs of records)- does it bring the entire result into
memory at once OR does it do some kind of batch-processing(process a few
records, and then bring other records)
Is any kind of paging/swapping involved in such cases?

plz clarify
awaiting ur response eagerly
thanx in advance
rohil
and we said...
No, it does not.

It fetches the data from disk as the client requests it.  It never fetches the
entire result set into some memory buffer like that.  Most queries would never
run if it did (think of that one billion row table...)
Review & Followup
Rating: 5
"fetches the data from disk as the client requests it" September 01, 2002
Reviewer: Vasaku
Tom

When we issue a query like select * form one_billion_row_table

then doesnt it mean that the client is requesting all the rows in the table?

Then how do we understand your statement

"fetches the data from disk as the client requests it"

How does the client request the data ?

Followup:
By issuing "FETCH"

The client fetches N rows at a time.  If they only fetch once and ask for 10
rows -- that'll be all the IO we do.  If they fetch twice and get 20 rows -
that'll be the IO.

If the fetch 100,000,000 times (10 rows at a time), we'll read the entire table
eventually.

Rating: 5
control fetch size September 01, 2002
Reviewer: Vasaku
Tom

How can we control the "FETCH" size . I guess each front end tool has its own
way of setting the "FETCH" size. How can we control the fetch size in SQLPLUS?

How should we determine the ideal fetch size ?

Followup:
SQL> set arraysize N

The ideal fetch size, like everything else, depends.

I personally am fond of the number "100" if the client has the ram to stash 100
rows.  Diminishing returns beyond that.

Rating: 5
Back to basics September 01, 2002
Reviewer: Vasaku
Tom

When you talk about RAM , I have a question

Once we issue a fetch , 100 rows are retuned(our array size is 100), then the
rows are kept in the RAM , and then the screen I/O is done.  ?

When you say that the performance diminishes,how can you generalize , if we have
very powerfull server with alot of RAM , cant it handle a lot greater fetch
size.?

How much RAM do you have?

Followup:
Yes, if you have an array fetch size of 100 and you issue fetch -- 100 rows will
be returned to the client and kept in memory -- then you can print them on
screen, whatever.

Diminishing returns affects virtually everything in life.  A little coffee gets
me going in the morning.  A ton of it makes my feel itchy and I get irratable.
A little salt on the veggies adds flavor -- a ton of it and it tastes terrible.
One aspirin - headache goes away, an entire bottle and I'll be in the hospital.

There are networking issues (you flood the network).  there are data structure
issues (maybe the structures are great for small amounts of data but terrible
for large amounts).  there are algorithm issues (same as data structure).  There
is the concept of keeping both ends going as well (better "flow").  The client
gets 100 rows much faster then it would get 1000 rows.  It can process the 100
rows and then ask for the next 100 -- the apparent load on both systems (client
and server) is "nice".  Server does some work, client does some work.  If you
array fetch EVERYTHING from a big table what you would see is huge load on
server and no load on client.  Then you would see no load on server and huge
load on client.

More is not better.  smooth is better.

Rating: 4
what if the query is complex? say involving multiple tables September 01, 2002
Reviewer: Rohil Bhavadasan from Ahmedabad, India
when i issue a SELECT statement which involves FETCHING millions of records from
multiple tables- does it mean that in such cases also, it does batch-processing
??(depending on the ARRAYSIZE being set).
How will Oracle analyze how to fetch the rows from different tables ? (won't it
have to store the resultset anywhere before finally fetching it?)
Do u mean to say that it fetches 1000 records in 1 I/O (if the arraysize is set
to 1000) and keeps on fetching records which satisfy the condition, until all
the records(a few million) are fetched? (however complex the cursor query is?)

Followup:
NO NO NO, a million billion times NO.

We do not have to answer any query 'up front'.  Some queries -- yes, we might
have to partially or fully (and for that we'd use temp space).  IN GENERAL --
no, a million times NO, we will not answer it up front.

We generate a plan (for example we might

scott@ORA920.US.ORACLE.COM> select * from emp, dept where
emp.deptno=dept.deptno;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   NESTED LOOPS
   2    1     TABLE ACCESS (FULL) OF 'EMP'
   3    1     TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'
   4    3       INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE)

Now, say the arraysize is 2....

Well, we'll start full scanning EMP -- we get the first row in EMP and then join
it to DEPT by DEPTNO.  Now we have one row.  We get the second row in EMP and do
the join.  Now we have two rows -- we stop and send the results back.  The
client gets them and then asks for the next 2.  We pick up in EMP where we left
off and just keep going.

No matter how complex the query is.

Rating: 5
thanx a lot!!! September 01, 2002
Reviewer: Rohil B from Ahmedabad, India
that was very nice of u, tom
now i get the whole picture of how a cursor query gets processed
thanx again for an excellent answer
bye for now

Rating: 5
Excellent !!!!! September 02, 2002
Reviewer: A reader

Rating: 4
Still some confusion!! September 05, 2002
Reviewer: Rajesh
Hi Tom,

Per Server concepts manual

"assume that a user wants to access some data in a table of a database. If the
requested information is not already in
the memory cache for the database, then it is read from the appropriate
datafiles and stored in memory."

So, If we issue a select * from one_billion_row_table, Will it still read
information(data) from the appropriate datafiles and stored in memory as per the
server concepts manual.

Followup:
they are talking about the BLOCK BUFFER CACHE (the place we look for blocks
before reading them in from disk)

It is NOT your result set that is saved anywhere. think about it -- just create
yourself a 1,000,000 row table.  then, write a plsql procedure that fetches just
the first row. The write one that fetches all of them.  Now, using tkprof --
measure the query blocks that are retrieved.  it will be self evident.

Rating: 5
Thanks for clearing my confusion!! September 06, 2002
Reviewer: Rajesh

Rating: 5
How does this work for a group by with a sort? December 11, 2002
Reviewer: Andy from Dayton, OH
Tom

I liked the example you showed and the explanation for
select * from emp, dept where emp.deptno=dept.deptno;

But how does this work for a Group by with a sort?

It seems to me that the database would have to do almost all the work up front,
it could not wait for the client to fetch the next x records.

Followup:
think "partitioning"

think "i have an index that will sort for me"

think "parallel query"

I did not say it never does build the entire result - I said it does not HAVE TO
and works very very hard NOT TO.  It also does not build it in RAM (unless is
just happens to fit in some *_area_size setting)

Rating: 3
December 12, 2002
Reviewer: A reader

Rating: 5
Oracle Document December 16, 2002
Reviewer: Ganesh Murthy from San Jose, CA USA
Can you please point us to the oracle documentation(release 8.1.7) that explains
how arraysize influences cursor fetch. I have searched the concepts manual and
the application developers guide in vain. Even though your explanation is more
than adequate, I am also simply trying to find a oracle document that I can
share with my colleagues. I found references to how a implicit/explicit cursor
works and also the reference to arraysize, but nothing where they are both
mentioned together.

Thanks,
Ganesh.

Followup:
http://otn.oracle.com/docs/products/oracle8i/doc_library/817_doc/appdev.817/a77069/05_ora.htm#36174

      Rating:
      4
  June 17, 2003
Reviewer:
      A reader  from italy

December 17, 2002 Bhavendra -- Thanks for the question regarding
      "Optimization", version Oracle 8.04

      You Asked
Hi Tom,

Please recommend me,
why the following error will be displayed?

Error ORA-17631840 while gathering statistics
Error generating AUTOTRACE report
  COUNT(*)
---------
       14

Error generating AUTOTRACE report

Execution Plan
----------------------------------------------------------
   0|    |SELECT STATEMENT Optimizer=CHOOSE
   1|   0|  SORT (AGGREGATE)
   2|   1|    NESTED LOOPS
   3|   2|      TABLE ACCESS (FULL) OF 'EMP'
   4|   2|      INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE)

Error generating AUTOTRACE STATISTICS report

Note: I run utlxplan.sql under system then I have create a
public sysnonym for plan_table and grant all on plan_table to
public then I have execute plustrce.sql under sys, afterwords
grant plustrace to public. Then I login my production user and
issued set autotrace on then select statement but above same
error occured again and again.

Thanks in advane,

Bhavendra

      and we said...
There are a couple of things that may cause this.  You should
check:

o your ARRAYSIZE setting.  There is an open issue with SQLPlus
with regards to the arraysize setting and autotrace.  Make sure
your arraysize is GREATER then 8.  Consider this small example:

SQL> set arraysize 9
SQL> select sysdate from dual;

SYSDATE
---------
15-MAY-00

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'DUAL'

Statistics
----------------------------------------------------------
[report snipped out -- it worked]

SQL> set arraysize 8
SQL> select sysdate from dual;
SP2-0612: Error generating AUTOTRACE report

SYSDATE
---------
15-MAY-00

SP2-0612: Error generating AUTOTRACE report

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'DUAL'

SP2-0612: Error generating AUTOTRACE STATISTICS report

As you can see -- having the arraysize set to 8 or less causes
this error.

o whether you are using OPS$ (OS authentication) accounts.
There is an issue using OS authenticated accounts with the
Statistics component of AUTOTRACE -- it will not work.  This is
solved in the latest release of SQLPlus for 8.1.6.  There is no
workaround prior to this other then to NOT use a OS
authenticated account for statistics tracing in SQLPlus.

o if you really have access to the 3 views the PLUSTRACE role is
to give you.  They are:

   grant select on v_$sesstat to plustrace;
   grant select on v_$statname to plustrace;
   grant select on v_$session to plustrace;

You should be able to describe:

   v$sesstat
   v$statname
   v$session

when logged in as someone with the PLUSTRACE role.  If you
cannot, the plustrce.sql script was probably NOT run as SYS
(must be SYS) or the user does not in fact have the PLUSTRACE
role currently enabled (select * from session_roles can confirm
this).

o that you did not just change your password BEFORE issuing set
autotrace on.  Consider:

SQL> connect a/c
Connected.
SQL> set autotrace on
SQL> connect a/c
Connected.
SQL> alter user a identified by a
  2  /

User altered.

SQL> set autotrace on
SP2-0619: Error while connecting
SP2-0611: Error enabling STATISTICS report

The simple act of changing out password BEFORE issuing an
autotrace makes this feature not work correctly.

Review &
      Followup

      Rating: 2
Something is
      still missing.  October 15, 2001
Reviewer:
      Daryl Biberdorf  from Plano, TX
I ran ORA_HOME/rdbms/admin/utlxplan as SYSTEM.
I created a public synonym for PLAN_TABLE as SYSTEM.
I did GRANT ALL ON PLAN_TABLE TO PUBLIC.
I ran ORA_HOME/sqlplus/admin/plustrce as SYS.
I did GRANT PLUSTRACE TO PUBLIC as SYS.
As SYSTEM, I did ALTER USER user1 DEFAULT ROLE ALL.

I logged in as user1, but enabling autotrace just doesn't work:

SQL> conn user1/abc@smtlocal
Connected.
SQL> set arraysize 10
SQL> select * from session_roles;

ROLE
--------------------------------------------------------------------------------
PLUSTRACE
CONNECT
RESOURCE

SQL> set autotrace traceonly
SQL> select * from user_tables;

32 rows selected.

Execution Plan
----------------------------------------------------------
ERROR:
ORA-01031: insufficient privileges

SP2-0612: Error generating AUTOTRACE EXPLAIN report

Statistics
----------------------------------------------------------
        207  recursive calls
          0  db block gets
       1070  consistent gets
         11  physical reads
          0  redo size
       7447  bytes sent via SQL*Net to client
        763  bytes received via SQL*Net from client
          5  SQL*Net roundtrips to/from client
          4  sorts (memory)
          0  sorts (disk)
         32  rows processed

SQL>  select * from user_tables;

32 rows selected.

SQL> desc v$sesstat
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 SID                                                NUMBER
 STATISTIC#                                         NUMBER
 VALUE                                              NUMBER

SQL> desc v$statname
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 STATISTIC#                                         NUMBER
 NAME                                               VARCHAR2(64)
 CLASS                                              NUMBER

SQL> desc v$session
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 SADDR                                              RAW(4)
 SID                                                NUMBER
 SERIAL#                                            NUMBER
 AUDSID                                             NUMBER
...

          Followup:

Thats just saying you do not have the ability to explain that query,
user_tables.

That is because user_tables is a view, a view on tables to which you have no
access, hence no ability to explain.

Its working normally -- run a query against tables you are allowed to run an
explain plan on.

      Rating: 5
what privileges
      do I have to grant ?  December 16, 2002
Reviewer:
      A reader
what specific privileges do I have to grant to other schema/role/user to let
them run explain plan. so that they can tune their queries with their tables ?

Thanks,

          Followup:

they need CREATE SESSION and access to a PLAN_TABLE is all.

      Rating: 5
I did give this
      grants  December 16, 2002
Reviewer:
      A reader
Hi tom,

  I did give them create session and they have plan_table in their respective
schema.

 case: I have created a tabele t in schema s
       I have created a view   t_v in schema s
       I gave grant select on t_v to schema s2.
       schema s2 has table t2

       the query is simple join  t_v.key = t2.key

   explain plan gives ora 01031 ?

Thanks,

          Followup:

yes, they do not have privileges to see the objects behind the views - hence,
they cannot explain them.

they would need to be able to SEE the table T behind the view T_V in order to
get the query plan (they are not allowed to know that T exists)

      Rating: 5
that's the
      point  December 16, 2002
Reviewer:
      A reader
Hi tom,

   I can not give them select or any grants on the table,
but they have to tune their queries against the our view and their applicaiton
tables.

  There are other internal groups also that are using our data services, They
use our core data and embed their data
and run the application. what is the best way to help them tune their queries ?

  also if the query performs bad we have to kill them because there are other
groups accessing the same data with another views ?

  what is the best solution for this situation ?

Thanks,

          Followup:

If in a development environment (you know, where people tune queries) you cannot
give them select -- don't know what to tell you.  seems that you should be able
to tuning.

Anyway here are ideas:

- in Oracle9i, have them use v$sql_plan.  The plan for any query executed is now
stashed in there....  they run it and the plan may be queried.  Of course,
you'll need to grant them access to the v$ tables needed!

- use sql_trace + tkprof.  sql_trace will dump the query plan as STAT records
into the trace files.  They can run tkprof and see the plans (with row counts
flowing through each step).  They will need access  to the trace files of
course.

- Suppose you could write a stored procedure, compile it as someone who IS
allowed to see the tables, have that stored procedure accept a query as input
and run the explain plan via execute immediate.  See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4456921593765
for an example

December 17, 2002 Zoliswa -- Thanks for the question regarding
      "adding sysdate on the file name", version 8.1.7

      You Asked
Hi Tom

When spooling the file on PL/SQL, i would like the file name to have sysdate
attached to the filename and it should change dynamicaly..
e.g.PM-INT-20021129_FLASHNOTIFICATIONS.TXT

Thanx
Zoliswa

      and we said...
column dt new_val X
select to_char(sysdate,'yyyymmdd') dt from dual;
spool pm_int_&X._flashnotications.txt
......
Review
      & Followup

      Rating: 5
I
      think  December 13, 2002
Reviewer:  Sikandar
      Hayat Awan  from Pindi Bhattian - Pakistan
I think that you should add time to avoid duplicate file name. You may create
two files on the same day.

      Rating: 4
adding sysdate
      on the file name  December 17, 2002
Reviewer:
      Zoliswa  from Cape Town, Republic of South Africa
I thank you very much as it worked like magic.......

      Rating: 4
adding sysdate
      on the file name  December 17, 2002
Reviewer:
      Zoliswa  from Cape Town, South Africa
HI Tom

I apologise for bothering you ...
As the answer you gave me is excellent, it does attach date to the filename, but
it also displays it(date)on the file of which i don't want it to do that,
there's already  a statement that displays date..how can i add this statement on
my query without the date being displayed on the file, but only on the
filename...

Thanking you in advance...

          Followup:

I don't know what you mean.

You run the query to get the date to START spooling (hence, you are not spooling
right now).  so, how could this date "display on the file"?

      Rating:
      3
  December 17, 2002
Reviewer:  Thomas
      Barry  from Rochester, NY
I think that when he says 'on the file', he means 'in the file'.  So perhaps
appropriate placement of spool on/spool off statements would help?

      Rating:
      5
  December 17, 2002
Reviewer:
      Zoliswa  from Cape Town, South Africa
i apologise for the misunderstanding Tom, its such that when i run the format
alone that you gave me in PL/SQL, it works fine, but when i add it on my query,
it adds the date on the text filename of which is fine because that's the way i
wanted it to be, but it also displays it at the begining of my result query as
20021217 of which i don't need it there as it has already done it's job of
adding it to the text filename...i'll try to check as to why is returning the
date on the result query......
e.g
When i run the ff query:

column dt new_val X
spool c:\test\pm_int_&X._flashnotications.txt
select to_char(sysdate,'yyyymmdd') dt from dual;
SELECT '<001>'||'PMFLASH-'|| to_char(sysdate, 'YYYYMMfmDDfm-HHSS' ) FROM DUAL;
select '<005>'||'CoCT FLASH' FROM DUAL;

Result query:

20021217
<001>PMFLASH-20021217-0440
<005>CoCT FLASH

On the Result query i don't need the first part of - 20021217, i need only the
second part <001>PMFLASH-20021217-0440,<005>CoCT FLASH        ...

Thanx
Zoliswa

      Rating:
      5
  December 17, 2002
Reviewer:
      A reader
Zoliswa,

Re-read what Tom said. Here is it again for you:

column dt new_val X
select to_char(sysdate,'yyyymmdd') dt from dual;
spool pm_int_&X._flashnotications.txt
......

Why are you putting the select statement after your spool? That should be before
you start spooling.

Tom, I just stepped-in to save you from wasting your time to repeat this silly
thing. You sure have more important stuff to do.

      Rating:
      5
  December 19, 2002
Reviewer:
      Zoliswa
i apologise for wasting your time Tom, i misread....

Reader - you really didnt have to be that harsh or rude everyone makes mistakes,
i misread, so the first statemant of re-read was OK, the rest that you mentioned
was uncalled for....

December 18, 2002 Phillip -- Thanks for the question regarding
      "Using Ref cursors in Reports", version 3.0.5.19.0

      You Asked
I produce a large number of complex reports which usually require lots of
queries and many formula/ summary columns (calculating students degree award
classification is a good example, as this varies between departments just
sufficiently to stop me using the same reports). These reports can be very
similar, and i often think that pl/sql would allow me to simplify these reports.
I think that Ref Cursor Queries would help but i am unsure as how to go about
building one.
Could you provide some examples of how to best use these queries?
Thanks in advance.

      and we said...
Article-ID:         <Note:2063557.102>
Circulation:        PUBLISHED (EXTERNAL)
Folder:             client.Dev2000.Reports.V3
Topic:              Draft Articles
Title:              CREATE PACKAGE PROCEDURE WHICH RETURNS REF CURSOR
Document-Type:      SOLUTION
Impact:             LOW
Skill-Level:        CASUAL
Server-Version:     VXX
Updated-Date:       16-AUG-1999 19:01:51
References:
Shared-Refs:
     <Problem:1015634.102> REPORT BASED ON STORED PROCEDURE WHICH RETURNS REF
CURSOR
Content-Type:       TEXT/PLAIN
Keywords:           REF; CURSOR; STORED; PROCEDURE; REPORTS;
Products:           159/SQLREP (03.00.XX);
Platforms:          GENERIC (6.0.5.29.2);

SOLUTION DESCRIPTION:
=====================

In order to use REF CURSORS, you need to be running Reports version 3.0.5.11 or
up.  You will get the REP-0065 and REP-0200 errors if you are runnning the base
version of Reports 3.0 (3.0.5.8.0).  This has been logged as Bug 571641.

SOLUTION EXPLANATION:
=====================

1. Create a table called bonus.

    Create table bonus
    (ename    varchar2(50),
          job       varchar2(20),
          sal       number,
          comm      number);

2.  Create a database package as follows:

    Create or replace package bonus_pkg as
      TYPE b_cursor is REF CURSOR RETURN bonus%ROWTYPE;
      Procedure bonus_refcur(bonus_data IN OUT b_cursor);
    End bonus_pkg;

    Create or replace package body bonus_pkg as
      Procedure bonus_refcur(bonus_data IN OUT b_cursor) as
      Begin
          Open bonus_data for select ename, job, sal, comm from bonus;
      End;
    End bonus_pkg;

3.  Create a new report. In the Data Model, create a 'Ref Cursor Query' by
    clicking on the PL/SQL icon.  Write following code in PL/SQL block:

    Function QR_1RefCurDS return bonus_pkg.b_cursor is
         refcur bonus_pkg.b_cursor;
    Begin
        bonus_pkg.bonus_refcur(refcur);
           return refcur;
    End;

3. Create the report layout by using the Report Wizard.
Review &
      Followup

      Rating: 4
knoledge
      base  November 30, 2001
Reviewer:  Vivek

Thanks!!

      Rating:
      4
  June 03, 2002
Reviewer:  Raj
      Kishore  from Nepal
I was trying it for long without any success. Now i can understand that i need
to upgrade to report version 8.0.11

Thanks

Raj

      Rating: 4
Problem
      though  December 18, 2002
Reviewer:  Jim
      Robison  from Dallas, TX
I followed the instructions, the only difference though was the I used tab as my
table, since it's so simple.

I went into the report and attached the PL/SQL as stated, but then when I went
through the wizard I experienced a problem. I am using Reports6i on an 8i
database.  The wizard found all of the column values as expected, but when it
finishes to go display, it locks up.

Do you have any suggestions?
Thanks in advance.

          Followup:

I'll have to refer you to support or to the discussion forums on otn.oracle.com
under "developer"

The above is a support note, I didn't write it up.

I've never written a "reports" report in my life ;)  sorry.

December 18, 2002 Kashif -- Thanks for the question regarding
      "Batch processes running slowly.", version 8.1.6.3

      You Asked
Hi Tom,

We have batch processes over here that run every night. They select nearly the
same or similar data over and over again, and dump them to files. The problem I
see is that owing to the same data getting selected over and over again, we
frequently run into sequential file read waits, where indexed reads are being
performed on the data files. Processes which otherwise would take 3 hours (on a
single user test database) take 7-8 hours to complete, due to the excessive
waiting.

My questions:

Is there a way to avoid these sequential file read waits? I've tried to change
the batch processes scheduled times to not overlap each other, but without much
success, there is still a lot of waiting. I've tried tuning the queries but
didn't make much progress since a lot of those queries require the tons of LIO
in order to achieve the desired results, not very restrictive predicates.

Secondly, I was wondering if there's a mechanism I can use in which I store the
data I need in memory somehow, and make that data accessible across sessions. In
this way I can perform LIOs to retrieve that data from memory, instead of having
to go to the files each time and doing PIO. You're probably thinking well,
shouldn't the data be in the cache if it's accessed over and over? Probably, but
for some reason (could be a small cache) we keep doing the PIOs. I know we can
use plsql tables and arrays to store data in memory, but since they are
restricted to one session only, it doesn't serve my purpose.

Any other ideas to get around these problems would be welcome. Thanks.

Kashif

      and we said...
read

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6749454952894
most probable cause -- you are "index happy" and "full table scan scared" --
which is wrong, dead wrong.  See that simple example there of joe programmer
being afraid of the full table scan.

LIO's are hugely expensive -- the goal is to DECREASE IO period (goto
www.hotsos.com and get the paper "so you are still using cache hit ratios")

your question is the poster child for such a problem "batch process, lots of 'db
file sequential reads' waits".  You have attempted to tune by indexing but
probably shouldn't....

Reduce the LIO's and the PIO's will take care of themselves.

Review &
      Followup

      Rating: 3
Followup
      questions.  December 16, 2002
Reviewer:
      Kashif  from Houston, TX
Thanks for the feedback. Actually, I tend to use full table scans when possible,
especially in some of our cases where the conditions are not very restrictive.
As a rule, I try to use full scans when retrieving more than 20-30 % of the
table via an index, though I always test before using full scans. Anyway, a few
follow-up questions:

I understand that indexed (sequential) file reads block other indexed reads. Do
scattered reads (in case of full scans) also block other scattered or indexed
reads?

Is a plsql table read an LIO as well? Can LIOs be reduced by using plsql tables?
That is, storing results in plsql tables and retrieving from those tables versus
issuing a select every time.

Finally, in my original question I had asked about any structures/strategies I
could use to store data in memory and then reuse in different sessions. I
realize that that is the purpose served by the buffer cache, but I was curious.

Thanks in advance.

Kashif

          Followup:

sequential reads "block" other indexed reads?  no, not in this database....

plsql table types are RAM based, stored in your UGA.  they do not incurr lio's.

Can lio's be reduced by using plsql tables?  well, that is like asking "can I
reduce lio's by not running sql" -- sure you can, but to what end?  Yes, using a
plsql "lookup table" can be useful HOWEVER -- when I see stuff like:

  ... fill a table with lookup ...
  for x in ( select * from another_table )
  loop
       something := lookup table based on a value in X
       insert into yet_another_table x
  end loop

I'm always wondering why people did just:

   insert into yet_another_table
   select * from another_table, LOOKUP_TABLE where ....

that is, don't do procedural lookups if you can do it in a query.  Don't run a
query inside of the loop -- run the query in the select.  That'll be even better
then using a plsql lookup table.  so

- looping, running a query to do a lookup = bad
- looping, using a plsql table to do a lookup = better
- no loop, just use sql = best

      Rating: 3
Reason for
      using plsql tables.  December 17, 2002
Reviewer:
      Kashif  from Houston, TX
Thanks for the info. My intention in using a plsql table is to minimize the
possibility of a sequential file read wait event, which I understand is
encountered when there is an indexed read being performed on the data files. If
I do not perform an indexed read, and instead fetch the necessary values from a
plsql table, I also minimize the possiblity of causing a sequential file read
wait as a result of the indexed read. Is this a valid theory/objective?

Kashif

          Followup:

do not try to out guess the database -- tune your sql, tune your process.

do not tune without KNOWNING what to tune.  I mean -- have you identified the
segments that as undergoing this contention?  Is it that you are in nested loops
heck and you've done it to yourself?  Is it that a couple of full table scans =
better performance for you?

In order to tune you must

o identify the cause
o determine the correct action based on the CAUSE

      Rating:
      3
Also...  December 17, 2002
Reviewer:
      Kashif  from Houston, TX
I think the reason people would opt for doing a lookup using a plsql table
instead of doing a join to the actual lookup table is to potentially reduce LIO.
Though they would incur the LIO upfront of loading the plsql table. Is that a
valid reason to use plsql lookup tables though? Thanks again.

Kashif

          Followup:

try to beat it sometime -- go for it.  If I can take your:

for x in ( select )
loop
    lookup
    insert
end

into

insert into select from join

guess who will win hands down every time.

Sure, go ahead, decrease LIO's if you can but then watch the CPU go through the
proverbial ROOF.

Use plsql lookup tables when all else fails.

      Rating: 3
I'll give it a
      shot.  December 18, 2002
Reviewer:  Kashif
       from Houston, TX
Thanks for your feedback. I think CPU is not an issue with us, we have on
average 60-70% idle time when these batch processes are running. And I remember
reading in one of your responses here that the objective should be to use as
much CPU as possible, and not to try and save it for a rainy day (I paraphrase
of course). Even though plsql tables may not be the preferred mechanism of
reducing LIOs, the SQL is simply hitting the wall, way too much LIO going on
there, especially with the lookup tables. I tried merging all the selects and
inner selects in a single view, per your recommendation of using pure SQL and
avoding context switches, and selecting from that one view via SQLPlus, but
without much improvement. Elapsed time remains more or less the same. I think
we've reached the point where all else has failed, as you mentioned. I'll try
and post results if there are any improvements. Thanks.

          Followup:

Use the CBO and analyze the objects first -- give it a try

December 19, 2002 Puneet -- Thanks for the question regarding
      "Retrieving date in different timezones from the database(by not using
      new_time function)", version 8.1.6

      You Asked
How to get the time stored in the database in different country's timezone?
All the trasaction in the database are looged in against one timezone(CST) but
users in Germany/UK/France/Australia wants to see all the times in their
timezone.
Orcale has new_time function to convert a 'time' from "this" timezone to "other"
timezone but this does not cover all the timezones in the world. German time
zone is not supported by this function.
What is the good way to convert the time stored in the database to a different
timezone that will take care of the daylight saving also(other than using
new_time function)?

Thanks in advance!!..
Puneet.

      and we said...
Well, easiest = Oracle9i where timezone support is builtin to the new TIMESTAMP
datatype.

In 8i -- you can use java to do this easily and efficiently.  I'm in "EST" (or
EDT depending on the time of year) so my example will be based on that.  I'll
load in a tiny bit of java:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace and compile java source
named "myTZ"
  2  as
  3  import java.util.*;
  4  import java.text.*;
  5  public class myTZ extends Object
  6  {
  7      static long getTimeZoneOffset(String sTzname)throws Exception
  8      {
  9          TimeZone oZone = TimeZone.getTimeZone(sTzname);
 10          return oZone.getRawOffset()/1000;
 11      }
 12
 13      public static long getServerTimeZoneOffset( String sTzname ) throws
Exception
 14      {
 15      Date d = new Date();
 16      DateFormat df2 = new SimpleDateFormat( "z" );
 17
 18          df2.setTimeZone( TimeZone.getDefault() );
 19          return getTimeZoneOffset( df2.format(d) ) - getTimeZoneOffset(
sTzname );
 20      }
 21  }
 22  /

Java created.

Now, I'll use an application context -- this will have an OFFSET that I can
add to a DATE to convert from the server TZ to any TZ I want

ops$tkyte@ORA817DEV.US.ORACLE.COM> CREATE OR REPLACE context tz using myTZ
  2  /

Context created.

and now the package to wrap the java and set the context:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package myTz
  2  as
  3      FUNCTION GetServerTimeZoneOffset( "sTzname" in varchar2 )
  4      RETURN NUMBER
  5      AS LANGUAGE JAVA
  6      NAME 'myTZ.getServerTimeZoneOffset( java.lang.String ) return long';
  7
  8      procedure init( p_client_tz in varchar2 );
  9  end;
 10  /

Package created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace package body myTz
  2  as
  3      procedure init( p_client_tz in varchar2 )
  4      is
  5      begin
  6           dbms_session.set_context( 'TZ', 'OFFSET',
getServerTimeZoneOffset(p_client_tz)/(60*60*24) );
  7      end;
  8  end;
  9  /

Package body created.

so, all your app has to do is upon connection called "myTz.init" and pass the
client timezone, here I'll test CST and PST:

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec myTz.init( 'CST' );

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter session set nls_date_format =
'dd-mon-yyyy hh24:mi:ss';

Session altered.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select sysdate, sysdate+sys_context( 'TZ',
'OFFSET' )
  2    from dual;

SYSDATE              SYSDATE+SYS_CONTEXT(
-------------------- --------------------
18-dec-2002 15:09:48 18-dec-2002 16:09:48

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec myTz.init( 'PST' );

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select sysdate, sysdate+sys_context( 'TZ',
'OFFSET' )
  2    from dual;

SYSDATE              SYSDATE+SYS_CONTEXT(
-------------------- --------------------
18-dec-2002 15:09:48 18-dec-2002 18:09:48

That shows how to convert my EST date/time into CST or PST by adding that
sys_context value to it.  Since sys_context is treated just like a bind variable
-- it'll be fast

Just make sure to convert on the LHS of equations -- eg don't

select * from t where database_column + sys_context() = to_date( '1pm', 'hham' )

Do this instead

select * from t where database_column = to_date( '1pm', 'hham')-sys_context()

instead!  (so indexes continue to function as before)

Review &
      Followup

      Rating: 5
Have a
      doubt...  December 18, 2002
Reviewer:  Puneet
      Gupta  from Minneapolis, MN USA
Thanks for such a quick response.
It worked absolutely fine.
But one thing is not clear to me.
Will it supprot all the timezones for all the country or is there any
restriction to this?
I tried different timezones and found out that if the time zone is not
recognized then it will default to GMT.So the right thing to do will be to get
the list of all the timezone which is acceped by TimeZone.getTimeZone method.

My timezone is 'CST'.

DB> exec myTz.init( 'GMT');
PL/SQL procedure successfully completed.
DB> select trunc(sys_context( 'TZ', 'OFFSET' ),2) as "Diffrence" from dual;
Diffrence
---------
     -.25
*** above is correct

DB> exec myTz.init( 'EST');
PL/SQL procedure successfully completed.
DB> select trunc(sys_context( 'TZ', 'OFFSET' ),2) as "Diffrence" from dual;
Diffrence
---------
     -.04
*** above is correct

When I wanted to get the difference with Germany I tried
MEZ(thought it would be the timezone code for Germany)
DB> exec myTz.init( 'MEZ');
PL/SQL procedure successfully completed.
DB> select trunc(sys_context( 'TZ', 'OFFSET' ),2) as "Diffrence" from dual;
Diffrence
---------
     -.25
***above is the diff with GMT and not with German time or MEZ

Then I tried a junk time zone 'XXX'
DB> exec myTz.init( 'XXX');
PL/SQL procedure successfully completed.
DB> select trunc(sys_context( 'TZ', 'OFFSET' ),2) as "Diffrence" from dual;
Diffrence
---------
     -.25
***It gave the diff with GMT (default)

Therefore, the problem for me is to find the codes supported in this method for
different timezones(with or without daylight).

Thanks,
Puneet.

          Followup:

Well, it is whatever Java supports.  (and you could obviously add some code to
make it a little more bullet proof, so it doesn't default to GMT)...

see

http://www.javaworld.com/javaworld/jw-02-1999/jw-02-internationalize.html
for some notes on this.

Also -- you might consider loading a table of your own with a list of timezones
and offsets from GMT.  Then you could just drop the java except to find the
server timezone (you want to do that for CST CDT changes) and do the rest in
plsql.  Or even add a table with start/stop dates for CST/CDT for a couple of
years and drop the java all together.

      Rating: 5
DayLight
      Savings   July 30, 2003
Reviewer:  Santhanam
       from Bangalore India
Good Example

December 19, 2002 Ram -- Thanks for the question regarding "!= and
      != is not bringing the correct results", version 9.2.0.1 and 8.7.0.0

      You Asked
here is the basic query. we want to eliminate a set of records.
the results of the query1.

  1  SELECT  rec_stat_indic, rec_stat_rsn_cd, COUNT(*)
  2  FROM mig43ppa.soldier
  3  GROUP BY rec_stat_indic, rec_stat_rsn_cd
  4* ORDER BY 1,2
SQL> /

R R   COUNT(*)
- - ----------
1 A     508883
1 D        676
1 Q        105
2 1        113
2 2       1309
2 3      13961
2 4       4036
2 6      75345
2 7      43629
2 8        231
2 9      20320

R R   COUNT(*)
- - ----------
2 T          1
2 Y      21350
3 B      25994
3 E         16

15 rows selected.

result of query2
  1  SELECT  rec_stat_indic, rec_stat_rsn_cd, COUNT(*)
  2  FROM mig43ppa.soldier
  3   WHERE   rec_stat_indic !=  '2' OR rec_stat_rsn_cd != '7'
  4  GROUP BY rec_stat_indic, rec_stat_rsn_cd
  5* ORDER BY 1,2
SQL> /

R R   COUNT(*)
- - ----------
1 A     508883
1 D        676
1 Q        105
2 1        113
2 2       1309
2 3      13961
2 4       4036
2 6      75345
2 8        231
2 9      20320
2 T          1

R R   COUNT(*)
- - ----------
2 Y      21350
3 B      25994
3 E         16

14 rows selected.

Result of query3
  1  SELECT  rec_stat_indic, rec_stat_rsn_cd, COUNT(*)
  2  FROM mig43ppa.soldier
  3   WHERE   rec_stat_indic !=  '2' AND rec_stat_rsn_cd != '7'
  4  GROUP BY rec_stat_indic, rec_stat_rsn_cd
  5* ORDER BY 1,2
SQL> /

R R   COUNT(*)
- - ----------
1 A     508883
1 D        676
1 Q        105
3 B      25994
3 E         16

the resuts of query3 are not right. It should be eliminating only
'2' and '7' records and not all '2's. It appears to me that AND and the OR
results should be switched.

for some reason I cannot understand this and why does it do this.
I played with this logic in both oracle 8.1.7.0.0 and  9.2.0.1.0 and get the
same results.

thanks
ram

      and we said...
No, it is doing the right thing.

For example:

If the box is not blue AND the box is not red, keep the box.  Now, a box cannot
be blue AND red at the same time....  so, that keeps all boxes that are not blue
and not red....

Same with

if x != 2 and y != 7

In order for that to evaluate to TRUE, both sides must be TRUE (true and false =
false).

So, if X=2, that is FALSE & <anything> = FALSE
    if Y=7, that is <anything> & FALSE = FALSE

with OR, it would be:

if x != 2 or y != 7

In order for that to evaulate to TRUE, EITHER side must be true (true or false =
true).

So, if X != 2, that is TRUE & <anything> = TRUE
    if Y != 7, that is <anything> & TRUE = TRUE

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3012348048

ops$tkyte@ORA920> select *
  2    from t
  3  /

         X          Y
---------- ----------
         2          7
         2          0
         0          7
         0          0

ops$tkyte@ORA920> select *
  2    from t
  3   where x != 2
  4     AND y != 7
  5  /

         X          Y
---------- ----------
         0          0

ops$tkyte@ORA920>

Review &
      Followup

      Rating: 5
"!= and != is
      not bringing the correct results",   December 19, 2002

Reviewer:  Ram Reddy  from Herndon, VA USA

the response was so quick and the explanation so good.
thanks
ram

December 19, 2002 -- Thanks for the question regarding "Primary key
      field datatype", version 9i

      You Asked
For all tables, I would use a single field for primary key.  Now, I'm wondering
whether the key field should be defined as NUMERIC(9,0) or VARCHAR2(9) in order
to have a better performance in searching ?

      and we said...
If the field is a NUMBER, by all means USE A NUMBER.

If the field is alpha-numeric, can contain non-numbers, then by all means use
VARCHAR.

Pick your datatype based on the values contained therein, not on any other
factor.

In your case, since you appear to have a "choice", the NUMBER datatype would be
the only sensible datatype.
Review &
      Followup

      Rating: 5
Use CODE or
      TEXT as primary key  December 19, 2002
Reviewer:
      Jeff in Miami  from Miami, Florida
Tom,

I have lots of dimension tables holding let's say Cities.

Now, we have a varchar(3) city code as primary key, such as 'MIA', with a city
Name varchar(30) say, 'Miami Beach'.

My question is why create a CODE, why would we not want to use the Name
VARCHAR(30) column as the primary key itself?

Is there any performance tradeoff, to using a VARCHAR(30) column as the primary
key rather than a VARCHAR(3) column?

Thanks,
Jeff

          Followup:

varchar2(30) is bigger -- index is larger.

varchar2(30) is bigger -- takes longer to compare string A to string B.

varchar2(30) would have to be in the fact table -- it is bigger, it adds tons of
mega/giga bytes to the table size....

      Rating: 4
Use Numeric
      Surrogate Primary Keys   December 19, 2002

Reviewer:  Sundeep  from Peoria,IL
To the previous reader:

If you have dimensions, you should use signle surrogate numeric PKs. Not only
for all the benefits Tom mentioned but also for preventing cascading updates to
primary keys. As historical data what appears to be an unchanging Primary key in
the short run (and to OLTP folks) invariable changes over a period of time.
Even city names for example are not immune from changing. A surrogate key will
prevent fact tables useing the city name from needing updates. Even Feds have
changed its coding schemes from medial procedures to diagnosis codes to Ferc
codes etc many times causing lot of heart burn for the unsuspecting.

December 19, 2002 Arun -- Thanks for the question regarding "Oracle
      JVM Vendor", version 8.1.7

      You Asked
Hi Tom,

Just a question out of curiosity, who is the Vendor for the oracle JVM in
different versions of oracle database server.

Arun

      and we said...
It is ours, we wrote it.
Review &
      Followup

      Rating: 3
Thanks a lot
      !!  December 19, 2002
Reviewer:  Arun
       from Torrance, CA USA
Thanks a lot Tom !!

December 19, 2002 Priya -- Thanks for the question regarding
      "Count(*) ", version 8.1.6

      You Asked
Hi Tom ,
Please suggest the best way to check if any rows exist for a given condition as
compared to getting the same using the count function .

Sql > select count(*) from emp where sal=5000 ;

Lets say there are 10 rows satisfying the above condition ,so the query does a
FTS and gets the count .

But my purpose is just to check and see if there is even a single row thats
satisfies the condition then return the count and exit .
Would Using rownum limit a FTS ?
eg
Sql > select count(*) from emp where sal=5000 and rownum < 2 ;

Please suggest .
Thanx as always .
Priya

      and we said...
select count(*)
  from dual
 where exists ( select NULL
                  from emp
                 where sal = 5000 );

that'll return 0 or 1.  It'll stop looking at the emp table the instant it finds
one row.

Review &
      Followup

      Rating:
      4
Count(*)  October 02, 2001
Reviewer:
      Parminder Garcha  from London, England
The use of 'exists' was certainly useful and was something I had forgotten
about, however after generating two SQL statements one using the 'Exists' and
the other doing a straight forward count as in the original question I found :-

The explain plan output showed more 'consistent gets' with the use of 'Exists'
meaning Oracle used more data blocks to process the query.  So if the Query is
used very frequently in whatever scenario does the 'Exists' mean less effiency ?
Why else would it retrieve more 'consistent gets' ?
Thanks.

          Followup:

does not seem probable to me.  You would need to supply a test case such as I am
following.

Here I create a table based on all objects.  I then find the name of the "last
object" and the name of the "first object" as they exist in this newly created
table (a row from the first block that is scanned and one from the last block).

I then run both styles of queries.  It shows that the "where exists" will stop
scanning as soon as it finds a row -- so when we search for the first row -- it
does SIGNIFICANTLY less work.  When we look for the last row -- it does about
the same amount of work (tiny bit more, it has the "overhead" of scanning DUAL
as well).

On average -- the where exists will significantly outperform the count(*).  You
would need to provide a counter case (with explain plans and all) for us to look
at.

Note that in a heavily modified database -- consistent gets will go up and down
with respect to the amount of modifications due to consistent reads to the
rollback segment.  Make sure that is not affecting your tests!

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table t as select * from all_objects;
Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> column first_oname new_val f
ops$tkyte@ORA817DEV.US.ORACLE.COM> column last_oname new_val l

ops$tkyte@ORA817DEV.US.ORACLE.COM> select object_name first_oname from t where
rownum = 1;

FIRST_ONAME
------------------------------
/1005bd30_LnkdConstant

ops$tkyte@ORA817DEV.US.ORACLE.COM> select object_name last_oname
  2   from ( select object_name
  3            from ( select object_name, rownum r from t )
  4           order by r DESC
  5            )
  6   where rownum = 1
  7  /

LAST_ONAME
------------------------------
B

ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace traceonly statistics
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*)
  2    from dual
  3   where exists ( select NULL
  4                                    from t
  5                                   where object_name = '&f' )
  6  /
old   5:                                 where object_name = '&f' )
new   5:                                 where object_name =
'/1005bd30_LnkdConstant' )

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

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*)
  2    from t
  3   where object_name = '&f'
  4  /
old   3:  where object_name = '&f'
new   3:  where object_name = '/1005bd30_LnkdConstant'

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

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*)
  2    from dual
  3   where exists ( select NULL
  4                                    from t
  5                                   where object_name = '&l' )
  6  /
old   5:                                 where object_name = '&l' )
new   5:                                 where object_name = 'B' )

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

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select count(*)
  2    from t
  3   where object_name = '&l'
  4  /
old   3:  where object_name = '&l'
new   3:  where object_name = 'B'

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

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set autotrace off
ops$tkyte@ORA817DEV.US.ORACLE.COM>

      Rating:
      4
Count(*)  October 02, 2001
Reviewer:
      Parminder Garcha  from London,England
Yes I agree, apologies for my earlier inaccuracies, putting in another test
similar to your example my results replicate yours.........conclusion 'where
exists' is the speed king....well over count(*) anyway.

      Rating:
      4
Count(*)  October 02, 2001
Reviewer:
      Parminder Garcha  from London,England
Yes I agree, apologies for my earlier inaccuracies, putting in another test
similar to your example my results replicate yours.........conclusion 'where
exists' is the speed king....well over count(*) anyway.

      Rating: 5
Sorry to tread
      old ground but....  September 24, 2002
Reviewer:
      A reader  from England
Tom, on the current thread, but i'm trying to work out given that I agree
wholeheartedly that a) the count(*) from dual... is better than a straight
check, why the following code is returning exactly the same times in execution?

SQL> desc big_table
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 A                                                  VARCHAR2(10)

SQL> select count(*) from big_table;

  COUNT(*)
----------
    100000

no indexes etc, and table not analyzed...

SQL> set timing on
SQL> ed
Wrote file afiedt.buf

  1  declare
  2    cursor c1 is select NULL from big_table where a = 'B';
  3    l1  VARCHAR2(1);
  4  begin
  5    for i in 1..1000 loop
  6      open c1;
  7      fetch c1 into l1;
  8      IF c1%FOUND THEN
  9        null;
 10      END IF;
 11      close c1;
 12    end loop;
 13* end;
SQL> /

PL/SQL procedure successfully completed.

Elapsed: 00:00:33.04

SQL> declare
  2    cursor c1 is select count(*)
  3                 from dual
  4                 WHERE EXISTS (SELECT NULL
  5                               FROM big_table
  6                               where a = 'B'
  7                               );
  8    l1  NUMBER;
  9  begin
 10    for i in 1..1000 loop
 11      open c1;
 12      fetch c1 into l1;
 13      IF c1%FOUND THEN
 14        null;
 15      END IF;
 16      close c1;
 17    end loop;
 18  end;
 19  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:33.05

It's probably something very straightforward, maybe not enough data / iterations
to show any significant increase, but I expected the second code to be quicker
than the first using you're previous comments.

Thanks (as always) for your response

          Followup:

why?  they do the SAME exact thing.

where exists -- stops after the first hit.

open
fetch
close  -- stops after the first hit

what you want to time:

SQL> declare
  2    cursor c1 is select count(*)
  3                 from dual
  4                 WHERE EXISTS (SELECT NULL
  5                               FROM big_table
  6                               where a = 'B'
  7                               );
  8    l1  NUMBER;
  9  begin
 10    for i in 1..1000 loop
 11      open c1;
 12      fetch c1 into l1;
 13      IF c1%FOUND THEN
 14        null;
 15      END IF;
 16      close c1;
 17    end loop;
 18  end;
 19  /

vs

SQL> declare

  8    l1  NUMBER;
  9  begin
 10    for i in 1..1000 loop
         select count(*) into l1
  3                 from dual
  4                 WHERE EXISTS (SELECT NULL
  5                               FROM big_table
  6                               where a = 'B'
  7                               );
 17    end loop;
 18  end;
 19  /

      Rating: 4
Great info on
      consistent gets vs. rollback segments!  September 24, 2002

Reviewer:  Robert  from Memphis, USA
Tom,

Great info!!

"...consistent gets [may] go up and down with respect to the amount of
modifications due to consistent reads to the rollback segment."

This is an important fact to remember and valuable information for analyzing our
query statistics.

Thanks,

Robert.

      Rating: 4
select
      count(*)  September 24, 2002
Reviewer:  john
      feng  from DCX.COM, Michigan, USA
I appreciate this tip to detect if there is any record regardless the record
count.

      Rating: 4
Tell me at
      least something....  December 19, 2002
Reviewer:
      Robert  from PA
Tom,
Using EMP as example with one extra column - COMPANY - that has the SAME value
"MSOFT"

EMPNO    NUMBER(4)
ENAME    VARCHAR2(10) Y
JOB      VARCHAR2(9)  Y
MGR      NUMBER(4)    Y
HIREDATE DATE         Y
SAL      NUMBER(7,2)  Y
COMM     NUMBER(7,2)  Y
DEPTNO   NUMBER(2)    Y
COMPANY  VARCHAR2(10) Y

How can I return this:
COMPANY      COUNT(*)
---------- ----------
MSOFT              0

with a query like :
SQL> select max(company), count(*) from emp where job = 'ACTOR' ;

...when there is NO such job as "ACTOR" ?

Thanks

          Followup:

It will not.

scott@ORA920> select max(ename), count(*) from emp where 1=0;

MAX(ENAME)   COUNT(*)
---------- ----------
                    0

scott@ORA920> select max(ename), count(*) from emp where job = 'NOT_A_JOB';

MAX(ENAME)   COUNT(*)
---------- ----------
                    0

scott@ORA920>

You would need to post a full example if you think otherwise...

      Rating:
      5
  February 12, 2003
Reviewer:  Helena
      Markova  from Bratislava, Slovakia

      Rating: 5
rownum is
      better  December 11, 2003
Reviewer:
      A reader
create table x as select * from dba_objects order by object_id;

select count(*) from x where object_id = 20000;
  COUNT(*)
----------
         1
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (AGGREGATE)
   2    1     TABLE ACCESS (FULL) OF 'X'
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        380  consistent gets
        240  physical reads
          0  redo size
        379  bytes sent via SQL*Net to client
        499  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
select count(*) from x where object_id = 20000 and rownum = 1;
  COUNT(*)
----------
         1
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (AGGREGATE)
   2    1     COUNT (STOPKEY)
   3    2       TABLE ACCESS (FULL) OF 'X'
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        267  consistent gets
        156  physical reads
          0  redo size
        379  bytes sent via SQL*Net to client
        499  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

select count(*) from dual where
exists (select null from x where object_id = 20000)
  COUNT(*)
----------
         1
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   SORT (AGGREGATE)
   2    1     FILTER
   3    2       TABLE ACCESS (FULL) OF 'DUAL'
   4    2       TABLE ACCESS (FULL) OF 'X'
Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        270  consistent gets
        170  physical reads
          0  redo size
        379  bytes sent via SQL*Net to client
        499  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

rownum = 1 is better than exists

December 19, 2002 Yun -- Thanks for the question regarding
      "Circular Dependency Problem.", version 8.1.7.1

      You Asked
Hi, Tom,

     How can we find Circular Dependency Problems in a schema.
     For example: we have two tables A and B. A has a FK to B
     and B has a FK to A.

Thanks.

      and we said...
why is that a problem?

EMP has a fkey to itself for example!

But anyway:

ops$tkyte@ORA920> create table a ( x int primary key, b int );

Table created.

ops$tkyte@ORA920> create table b ( x int primary key, b references a );

Table created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> alter table a add constraint a_fkey_to_b foreign key(b)
references b;

Table altered.

ops$tkyte@ORA920>
ops$tkyte@ORA920> create table t1 ( x int primary key );

Table created.

ops$tkyte@ORA920> create table t2 ( x references t1 );

Table created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> create table t ( x int primary key, y references t );

Table created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> select a.table_name, b.table_name
  2    from user_constraints a, user_constraints b
  3   where a.constraint_type = 'R'
  4     and b.constraint_name = a.r_constraint_name
  5     and a.table_name <= b.table_name
  6     and exists ( select NULL
  7                    from user_constraints c, user_constraints d
  8                                   where c.table_name = b.table_name
  9                                     and d.table_name = a.table_name
 10                                     and c.constraint_type = 'R'
 11                                     and c.r_constraint_name =
d.constraint_name )
 12  /

TABLE_NAME                     TABLE_NAME
------------------------------ ------------------------------
A                              B
T                              T

Review &
      Followup

      Rating:
      4
Excellent  December 19, 2002
Reviewer:
      Sanjay  from NJ,USA
Tom,
If most of the teachers/authors made things as simple as you do - you would have
received lot less questions. Thanks for taking pains to do this. Hopefully this
will remain a free service ....

Thanks

December 19, 2002 Vikas -- Thanks for the question regarding "SQL
      Query", version 8.0.3

      You Asked
Hi Tom,

I would appreciate if you could help me in building SQL*PLUS query.

I have a table with 3 columns

Country
State
Tag can have values V,C,N

I need the result to be shown as

Country State  Tag  Count(*)   %of Total     % of Total
                                 State        Country

US     AK      V       10        10.3%       10/1530 ~   0.65%
US     AK      C       35        36.08%      35/155  ~  22.58%
US     AK      N       52        53.60%      52/72   ~  72.22%
----------------------------------------------------
                       97        100%
----------------------------------------------------
US     CO      V     1520       91.56%     1520/1530  ~ 99.34%
US     CO      C      120        7.22%     120/155    ~ 77.41%
US     CO      N       20        1.20%     20/72      ~ 27.77%
----------------------------------------------------
                     1660        100%
----------------------------------------------------

At the end It should display, the Max & the Min Values of the Tag from all the
states depending upon the country.

Min :

US               AK         V       10        10.3%        0.65%
US               AK         C       35        36.08%      22.58%  US
  CO         N       20        1.20%       27.77%

Max :

US               CO         V       1520      91.56%      99.34%
US               CO         C        120       7.22%      77.41%
US               AK         N       52        53.60%      72.22%

Please do help me in building a SQL*QUERY for this output.

Thanks in advance.

Vikas Khanna
Vkhanna@quark.stpm.soft.net

      and we said...
Well, in 8i release 2 with the analytic functions, this would be trivial.  In
8.0.3 as you have, its a tad bit harder and definitely less performant.  We
could:

ops$tkyte@ORA817DEV.US.ORACLE.COM> break on state skip 1
ops$tkyte@ORA817DEV.US.ORACLE.COM> compute sum of cnt on state
ops$tkyte@ORA817DEV.US.ORACLE.COM> compute sum of "%of Tot State" on state
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> select country, state, tag, cnt,
  2         to_char( round( cnt/cs.cs_cnt * 100, 2 ), '990.99' ) "%of Tot
State",
  3             rpad( cnt || '/' || ct_cnt, 10 ) || '~' " ",
  4         to_char( round( cnt/ct.ct_cnt * 100, 2 ), '990.99' ) "%of Total
Country"
  5    from (select country, state, tag, count(*) cnt
  6                    from T
  7                   group by country, state, tag ) t,
  8         (select country cs_country, state cs_state, count(*) cs_cnt
  9            from t
 10           group by country, state ) cs,
 11         (select country c_country, tag c_tag, count(*) ct_cnt
 12            from t
 13           group by country, tag ) ct
 14   where t.country = cs_country
 15     and t.state = cs_state
 16     and t.country = c_country
 17     and t.tag = c_tag
 18   order by country, state, decode( tag, 'V', 1, 'C', 2, 'N', 3 )
 19  /

CO ST T        CNT %of Tot             %of Tot
-- -- - ---------- ------- ----------- -------
US AK V         10   10.31 10/1530   ~    0.65
US    C         35   36.08 35/155    ~   22.58
US    N         52   53.61 52/72     ~   72.22
   **   ---------- -------
   su           97

US CO V       1520   91.57 1520/1530 ~   99.35
US    C        120    7.23 120/155   ~   77.42
US    N         20    1.20 20/72     ~   27.78
   **   ---------- -------
   su         1660

6 rows selected.

In 8i, release 2 and up, we could:
ops$tkyte@ORA817DEV.US.ORACLE.COM> select country, state, tag, cnt,
  2     to_char( round( cnt/cs_cnt * 100, 2 ), '990.99' ) "%of Tot State",
  3     rpad( cnt || '/' || ct_cnt, 10 ) || '~' " ",
  4     to_char( round( cnt/ct_cnt * 100, 2 ), '990.99' ) "%of Total Country"
  5    from ( select distinct country, state, tag,
  6                      count(*) over ( partition by country, state, tag ) cnt,
  7                      count(*) over ( partition by country, state ) cs_cnt,
  8                      count(*) over ( partition by country, tag ) ct_cnt
  9             from t
 10             )
 11   order by country, state, decode( tag, 'V', 1, 'C', 2, 'N', 3 )
 12  /

CO ST T        CNT %of Tot             %of Tot
-- -- - ---------- ------- ----------- -------
US AK V         10   10.31 10/1530   ~    0.65
US    C         35   36.08 35/155    ~   22.58
US    N         52   53.61 52/72     ~   72.22
   **   ---------- -------
   su           97

US CO V       1520   91.57 1520/1530 ~   99.35
US    C        120    7.23 120/155   ~   77.42
US    N         20    1.20 20/72     ~   27.78
   **   ---------- -------
   su         1660

6 rows selected.

That min/max thing however is very hard and slow in 8.0 and before (pre analytic
functions).  in 8i, it would be:

ops$tkyte@ORA817DEV.US.ORACLE.COM> clear breaks
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> break on min_max skip 1
ops$tkyte@ORA817DEV.US.ORACLE.COM> select decode( cnt, min_cnt, 'MIN', 'MAX' )
min_max, country, state, tag, cnt,
  2         to_char( round( cnt/cs_cnt * 100, 2 ), '990.99' ) "%of Tot State",
  3             rpad( cnt || '/' || ct_cnt, 10 ) || '~' " ",
  4         to_char( round( cnt/ct_cnt * 100, 2 ), '990.99' ) "%of Total
Country"
  5    from ( select country, state, tag, cnt, cs_cnt, ct_cnt,
  6                      min(cnt) over ( partition by tag ) min_cnt,
  7                      max(cnt) over ( partition by tag ) max_cnt
  8             from ( select distinct country, state, tag,
  9                               count(*) over ( partition by country, state,
tag ) cnt,
 10                               count(*) over ( partition by country, state )
cs_cnt,
 11                               count(*) over ( partition by country, tag )
ct_cnt
 12                      from t
 13                      )
 14             ) a
 15   where cnt = min_cnt or cnt = max_cnt
 16   order by 1 desc, country, state, decode( tag, 'V', 1, 'C', 2, 'N', 3 )
 17  /

MIN CO ST T        CNT %of Tot             %of Tot
--- -- -- - ---------- ------- ----------- -------
MIN US AK V         10   10.31 10/1530   ~    0.65
    US AK C         35   36.08 35/155    ~   22.58
    US CO N         20    1.20 20/72     ~   27.78

MAX US AK N         52   53.61 52/72     ~   72.22
    US CO V       1520   91.57 1520/1530 ~   99.35
    US CO C        120    7.23 120/155   ~   77.42

6 rows selected.

In 8.0 and before -- the query becomes so gnarly, so inefficient as to prompt to
to say "write some procedural code, do it in plsql"....

Review &
      Followup

      Rating: 4
SUN
      HK  July 22, 2002
Reviewer:  sun chen
       from HK
Hi Tom,
      How should I code if I want to count the no of records with net_grs_amt
more than 10,000, 50,000 and 500,000? partition by pricipal, clm_ind, ac_yr_mth.

          Followup:

sum ( case when net_grs_amt > 10000 then 1 else 0 end ),
sum ( case when net_grs_amt > 50000 then 1 else 0 end )
....

      Rating: 3
How to break
      and compute ..  December 19, 2002
Reviewer:
      Reader  from USA
How do I set a break and COMPUTE if I am concatanating fields in the select
statement. I have query like
SELECT
c.FS_ORGANIZATION_NAME||'|'||
d.FI_NUMBER ||'|'||
d.FI_NAME ||'|'||
d.FI_CITY||'|'||
d.FI_STATE||'|'||
a.PPL_NUMBER ||'|'||
b.PROMOTION_LOCATION_NAME_1||'|'||
d.CD||'|'||
d.DESC||'|'||
d.RSN_CD||'|'||
d.RSN_DESC||'|'||
d.CATEGORY||'|'||
d.LOC_ID||'|'||
d.BALANCE||'|'||
d.REFERENCE_ID||'|'||
d.DT||'|'||
d.CHECK_NO||'|'||
d.CHECK_DT||'|'||
d.DYS_PAST_DUE||'|'||
d.AGED_AMT_1_5||'|'||
d.AGED_AMT_6_30||'|'||
d.AGED_AMT_31_60||'|'||
d.AGED_AMT_61_90||'|'||
d.AGED_AMT_91_180||'|'||
d.AGED_AMT_181_365 ||'|'||
d.AGED_AMT_OVER_365
From
a,
b,
field_sales_unit@CSO_PROD c,
d
WHERE
a.ppl_number=b.SAP_PLANNABLE_ACCOUNT_ID
and c.field_sales_org_id=b.field_sales_org_id
and ltrim(a.fi_number,'0')=d.fi_number
and rtrim(d.FI_NAME,' ') in (‘A’,’B’)
I tried doing

break on d.fi_name
compute sum of d.balance on d.fi_name
But it did not work

I want to break on d.FI_NAME  and Compute sum of d.BALANCE on d.FI_NAME. The
BALANCE column has a value say 100 which corresponds to one of the AGING
columns.
Also  I need to spool the results of this query into many files with different
file names say BALANCEFOR_242016 or BALANCEOFR_242013 where 242016 and 242013
are the first 6 digits/characters in the first field in the SELECT statement
i.e.C.FS_ORGANIZATION_NAME. The data looks like
242016 FOOD TEAM 4 TERR 16|5002810|AFFILIATED FOODS INC    |AMARILLO       |
242016 FOOD TEAM 4 TERR 16|5002810|AFFILIATED FOODS INC    |AMARILLO       |
242016 FOOD TEAM 4 TERR 16|5002810|AFFILIATED FOODS INC    |AMARILLO       |
242016 FOOD TEAM 4 TERR 16|5002810|AFFILIATED FOODS INC    |AMARILLO       |
242013 FOOD TEAM 4 TERR 13|5000326|AFFILIATED FOODS    |LITTLE ROCK    |
242013 FOOD TEAM 4 TERR 13|5000326|AFFILIATED FOODS    |LITTLE ROCK    |
242013 FOOD TEAM 4 TERR 13|5000326|AFFILIATED FOODS    |LITTLE ROCK    |
214001 TELESALES A-F|5000974|ALLOU DISTRIBUTORS INC    |BRENTWOOD      |NY |0|
214001 TELESALES A-F|5000974|ALLOU DISTRIBUTORS INC    |BRENTWOOD      |NY |0|
214001 TELESALES A-F|5000974|ALLOU DISTRIBUTORS INC    |BRENTWOOD      |NY |0|
214001 TELESALES A-F|5000974|ALLOU DISTRIBUTORS INC    |BRENTWOOD      |NY |0|
214001 TELESALES A-F|5000974|ALLOU DISTRIBUTORS INC    |BRENTWOOD      |NY |0|
There are other fileds which are not posted here.

Thanks for your help

          Followup:

you only have one column.  break and compute -- ain't going to work (obviously
-- how can SQLPlus which is doing the break and compute possibly break and
compute on fields it cannot see)

Also -- spooling segments of a query into separate files, well, you've gone way
beyond what a simple command line tool like sqlplus is designed to do.  You've
gone into the realm of custom coding.

You will need to use another tool -- probably a custom piece of code you develop
-- to do this.  If you search for

dump_csv

on this site, you'll see some plsql that used UTL_FILE that will get you 95% of
the way there.

December 19, 2002 Raghu -- Thanks for the question regarding "Index
      used sometimes and sometimes NOT?", version 8.1.7

      You Asked
Hi Tom,
I have an DW table with a b-tree index on a column called fin_comp_id
Now when I issue a 'DELETE FROM MY_TABLE WHERE FIN_COMP_ID = 'x' ' then the
explain plan shows that the index is being used rather unpredictably - sometimes
yes and at time, it does a FTS.
Why?
Raghu

17:48:49 FDSS_TEST_SQL>>delete from fdss_gl_daily where fin_comp_id = 'xyz' ;

0 rows deleted.

Elapsed: 00:00:00.01

Execution Plan
----------------------------------------------------------
   0      DELETE STATEMENT Optimizer=CHOOSE (Cost=8 Card=564 Bytes=733
          2)

   1    0   DELETE OF 'FDSS_GL_DAILY'
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'FDSS_GL_DAILY' (Cost=8
           Card=564 Bytes=7332)

   3    2       INDEX (RANGE SCAN) OF 'FDSS_GL_DAILY_IDX1' (NON-UNIQUE
          ) (Cost=3 Card=564)

17:49:08 FDSS_TEST_SQL>>delete from fdss_gl_daily where fin_comp_id = '311' ;

130917 rows deleted.

Elapsed: 00:00:12.06

Execution Plan
----------------------------------------------------------
   0      DELETE STATEMENT Optimizer=CHOOSE (Cost=960 Card=102488 Byte
          s=1332344)

   1    0   DELETE OF 'FDSS_GL_DAILY'
   2    1     TABLE ACCESS (BY INDEX ROWID) OF 'FDSS_GL_DAILY' (Cost=9
          60 Card=102488 Bytes=1332344)

   3    2       INDEX (RANGE SCAN) OF 'FDSS_GL_DAILY_IDX1' (NON-UNIQUE
          ) (Cost=109 Card=102488)

17:49:34 FDSS_TEST_SQL>>delete from fdss_gl_daily where fin_comp_id = '476' ;

220750 rows deleted.

Elapsed: 00:00:34.48

Execution Plan
----------------------------------------------------------
   0      DELETE STATEMENT Optimizer=CHOOSE (Cost=1014 Card=253404 Byt
          es=3294252)

   1    0   DELETE OF 'FDSS_GL_DAILY'
   2    1     TABLE ACCESS (FULL) OF 'FDSS_GL_DAILY' (Cost=1014 Card=2
          53404 Bytes=3294252)

The table has 1.2M recs and the fin_comp_ids of 311 and 476 are the ones with
the most records. Is there a threshold of the % of rows that a DML will affect
when the index is jettisoned and a FTP done?

      and we said...
Yes, the optimizer is doing EXACTLY what is it supposed to be doing.

You have histograms on this table.

It understands the distribution of data.

Your data is skewed -- it will use an index when an index is appropriate, it'll
use a full table scan when that is more appropriate.

It is working perfectly.
Review & Followup

      Rating:
      5
  December 19, 2002
Reviewer:
      A reader
Hi Tom

I heard that histogram is obsolite after oracle 8i.

Is it true?

Thanks,

          Followup:

patently FALSE.

      Rating: 5
Only the
      explain plan  December 19, 2002
Reviewer:
      Raghu  from Calif, USA
Thanks Tom. That confirms what I guessed at. Also, when I do a "set autotr trace
explain" and issue a statement, should it not just give me the explain plan
without executing the sql itself? In my case it went ahead and deleted the rows
(which took more time). What's the exact command for just getting the explain
plan?

          Followup:

set autotrace traceonly explain

will just explain a SELECT, since actually executing the select won't have any
effect (you see no rows, you see no stats).

but for DELETE (or insert/update/merge) it runs the command because NOT
executing it does have a side effect.

To just see the plan:

scott@ORA920> delete from plan_table;

0 rows deleted.

scott@ORA920> explain plan for delete from emp;

Explained.

scott@ORA920> @?/rdbms/admin/utlxpls

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------
--------------------------------------------------

--------------------------------------------------------------------
| Id  | Operation            |  Name       | Rows  | Bytes | Cost  |
--------------------------------------------------------------------
|   0 | DELETE STATEMENT     |             |       |       |       |
|   1 |  DELETE              | EMP         |       |       |       |
|   2 |   TABLE ACCESS FULL  | EMP         |       |       |       |
--------------------------------------------------------------------

Note: rule based optimization

10 rows selected.

scott@ORA920> delete from plan_table;

3 rows deleted.

December 19, 2002 -- Thanks for the question regarding "Query
      Tuning", version 8.1.7

      You Asked
Hi Tom,

How can we write a better query from the performance point of view. We are using
this query many times (search screen)in our application and needs your
recommendations. Do you recommend this query to be in a stored procedure and pin
it in shared pool as we are using this many times?

I have the following tables create with the following script.

ANSER table
------------
CREATE TABLE ANSER_TABLE_MB (
  EVENT_ID        CHAR (5)      NOT NULL,
  EVENT_SUBID     CHAR (1)      NOT NULL,
  USER_ID         CHAR (8)      NOT NULL,
  ENQ_NO          CHAR (2)      NOT NULL,
  ANS_NO          CHAR (2),
  ANS_FREE        VARCHAR2 (4000),
  ADD_DATE        DATE,
  UPD_DATE        DATE,
  OPTION_NO       CHAR (2)      DEFAULT '0' NOT NULL,
  TRANSACTION_ID  NUMBER (10),
  CONSTRAINT ANSER_TABLE_PIDX
  PRIMARY KEY ( EVENT_ID, EVENT_SUBID, USER_ID, ENQ_NO, OPTION_NO )
    USING INDEX
     TABLESPACE IDX PCTFREE 10
     STORAGE ( INITIAL 65536 ))
   TABLESPACE DATA
   PCTFREE 10
   PCTUSED 40
   INITRANS 1
   MAXTRANS 255
  STORAGE (
   INITIAL 65536
   MINEXTENTS 1
   MAXEXTENTS 2147483645
   FREELISTS 1 FREELIST GROUPS 1 )
   NOCACHE;

CREATE INDEX CCC.ANSER_TABLE_IDX3 ON
  "CCC".ANSER_TABLE_MB(USER_ID, EVENT_ID, ENQ_NO)
  TABLESPACE IDX PCTFREE 10  STORAGE(INITIAL 65536 )
;

CONTACT table
-------------

CREATE TABLE CCC_CONTACTS_MB (
  TRANSACTION_ID                  NUMBER (10)   NOT NULL,
  CONTACT_ID                      NUMBER (10),
  NAME_PREFIX                     VARCHAR2 (1000),
  PRE_FIRST_NAME                  VARCHAR2 (1000),
  FIRST_NAME                      VARCHAR2 (1000),
  FIRST_NAME_KANA                 VARCHAR2 (1000),
  POST_FIRST_NAME                 VARCHAR2 (1000),
  MIDDLE_NAME                     VARCHAR2 (1000),
  PRE_LAST_NAME                   VARCHAR2 (1000),
  LAST_NAME                       VARCHAR2 (1000),
  LAST_NAME_KANA                  VARCHAR2 (1000),
  POST_LAST_NAME                  VARCHAR2 (1000),
  NAME_SUFFIX                     VARCHAR2 (1000),
  HONOURS                         VARCHAR2 (1000),
  JOB_TITLE                       VARCHAR2 (1000),
  JOB_TITLE_CLASS                 VARCHAR2 (1000),
  JOB_LEVEL                       VARCHAR2 (1000),
  COMPANY_NAME                    VARCHAR2 (1000),
  DEPARTMENT                      VARCHAR2 (1000),
  GENDER                          VARCHAR2 (255),
  AGE_GROUP                       VARCHAR2 (255),
  AGE                             VARCHAR2 (30),
  AGE_RECORDED_DT                 VARCHAR2 (60),
  PREFERRED_LANGUAGE              VARCHAR2 (255),
  EDUCATIONAL_QUALIFICATIONS      VARCHAR2 (255),
  PREFERRED_COMMUNICATION_METHOD  VARCHAR2 (255),
  PREFERRED_CALL_TIME             VARCHAR2 (255),
  BUSINESS_RELATION_STATUS        VARCHAR2 (255),
  COMPETITOR_FLAG                 VARCHAR2 (30),
  WEB_BUYER_FLAG                  VARCHAR2 (30),
  SRC_CUSTOMER_ID                 VARCHAR2 (1000),
  SRC_ADDRESS_ID                  VARCHAR2 (1000),
  LANGUAGE_ID                     NUMBER,
  CONTACT_STATUS                  VARCHAR2 (255),
  SOURCE_SYSTEM                   VARCHAR2 (255),
  SOURCE_CONTACT_ID               VARCHAR2 (1000),
  RECORD_STATUS                   VARCHAR2 (255),
  TRANSACTION_DT                  DATE,
  START_DT                        DATE,
  END_DT                          DATE,
  CREATION_DT                     DATE          NOT NULL,
  USER_CREATED                    VARCHAR2 (255)  NOT NULL,
  MODIFICATION_DT                 DATE,
  USER_MODIFIED                   VARCHAR2 (255),
  ORIG_COMPANY_NAME               VARCHAR2 (1000),
  DEPARTMENT_2                    VARCHAR2 (1000),
  NUM_OF_EMPLOYEES                VARCHAR2 (255),
  INDUSTRY                        VARCHAR2 (255),
  PREFECTURE_CODE                 VARCHAR2 (1000),
  BRANCH_OFFICE                   VARCHAR2 (1000),
  PRIMARY KEY ( TRANSACTION_ID )
    USING INDEX
     TABLESPACE DATA PCTFREE 10
     STORAGE ( INITIAL 65536 ))
   TABLESPACE DATA
   PCTFREE 10
   PCTUSED 40
   INITRANS 1
   MAXTRANS 255
  STORAGE (
   INITIAL 65536
   MINEXTENTS 1
   MAXEXTENTS 2147483645
   FREELISTS 1 FREELIST GROUPS 1 )
   NOCACHE;

CREATE INDEX OPS$CCCADM.CCC_CONT_TNID_MB ON
  "OPS$CCCADM".CCC_CONTACTS_MB(CONTACT_ID)
  TABLESPACE IDX PCTFREE 10  STORAGE(INITIAL 65536 )
;

CREATE INDEX OPS$CCCADM.CCC_CONT_SRC_CNID_MB ON
  "OPS$CCCADM".CCC_CONTACTS_MB(SOURCE_CONTACT_ID)
  TABLESPACE IDX PCTFREE 10  STORAGE(INITIAL 65536 )
;

ADDRESS table
--------------
CREATE TABLE CCC_P_ADDRS_MB (
  TRANSACTION_ID             NUMBER (10)   NOT NULL,
  CONTACT_POSTAL_ADDRESS_ID  NUMBER (10)   NOT NULL,
  ADDRESS_LOCATION_TYPE      VARCHAR2 (255)  DEFAULT 'UNKNOWN',
  ADDRESS_LINE_1             VARCHAR2 (2000),
  ADDRESS_LINE_2             VARCHAR2 (2000),
  ADDRESS_LINE_3             VARCHAR2 (2000),
  ADDRESS_LINE_4             VARCHAR2 (2000),
  CITY                       VARCHAR2 (2000),
  COUNTY                     VARCHAR2 (2000),
  STATE                      VARCHAR2 (2000),
  COUNTRY                    VARCHAR2 (2000),
  COUNTRY_CD                 VARCHAR2 (2000),
  ZIP_POSTAL_CD              VARCHAR2 (255),
  DEPARTMENT                 VARCHAR2 (255),
  MAIL_STOP                  VARCHAR2 (255),
  BUILDING_NUM               VARCHAR2 (255),
  HOUSE_NUM                  VARCHAR2 (255),
  ROOM_NUM                   VARCHAR2 (255),
  PULL_DATE                  VARCHAR2 (255),
  ADDRESS_UPDATED_DT         DATE,
  PRIMARY_ADDRESS_FLAG       VARCHAR2 (30),
  ADDRESS_STATUS             VARCHAR2 (255)  DEFAULT 'VALID',
  START_DT                   DATE,
  END_DT                     DATE,
  CREATION_DT                DATE          NOT NULL,
  USER_CREATED               VARCHAR2 (255)  NOT NULL,
  MODIFICATION_DT            DATE,
  USER_MODIFIED              VARCHAR2 (255))
   TABLESPACE DATA
   PCTFREE 10
   PCTUSED 40
   INITRANS 1
   MAXTRANS 255
  STORAGE (
   INITIAL 65536
   MINEXTENTS 1
   MAXEXTENTS 2147483645
   FREELISTS 1 FREELIST GROUPS 1 )
   NOCACHE;

CREATE INDEX OPS$CCCADM.CCC_PADD_TRNID_MB ON
  "OPS$CCCADM".CCC_P_ADDRS_MB(TRANSACTION_ID)
  TABLESPACE IDX PCTFREE 10  STORAGE(INITIAL 65536 )
;

PREF table
----------
CREATE TABLE CCC_PREFERENCES_MB (
  TRANSACTION_ID         NUMBER (10)   NOT NULL,
  CONTACT_PREFERENCE_ID  NUMBER (10)   NOT NULL,
  PREFERENCE_TYPE        NUMBER (10),
  PREFERENCE_FLAG        VARCHAR2 (1),
  START_DT               DATE,
  END_DT                 DATE,
  CREATION_DT            DATE          NOT NULL,
  USER_CREATED           VARCHAR2 (30)  NOT NULL,
  MODIFICATION_DT        DATE,
  USER_MODIFIED          VARCHAR2 (30),
  TRANSACTION_DT         DATE)
   TABLESPACE DATA
   PCTFREE 10
   PCTUSED 40
   INITRANS 1
   MAXTRANS 255
  STORAGE (
   INITIAL 65536
   MINEXTENTS 1
   MAXEXTENTS 2147483645
   FREELISTS 1 FREELIST GROUPS 1 )
   NOCACHE;

CREATE INDEX OPS$CCCADM.CCC_PREF_TRANID_MB ON
  "OPS$CCCADM".CCC_PREFERENCES_MB(TRANSACTION_ID)
  TABLESPACE DATA PCTFREE 10  STORAGE(INITIAL 65536 )
;

CREATE INDEX OPS$CCCADM.CCC_PREF_PREFID_MB ON
  "OPS$CCCADM".CCC_PREFERENCES_MB(CONTACT_PREFERENCE_ID)
  TABLESPACE DATA PCTFREE 10  STORAGE(INITIAL 65536 )
;

EADDRS table
------------

CREATE TABLE CCC_E_ADDRS_MB (
  TRANSACTION_ID          NUMBER (10)   NOT NULL,
  CONTACT_EADDRESS_ID     NUMBER (10)   NOT NULL,
  EADDRESS_TYPE           VARCHAR2 (255),
  EADDRESS_LOCATION_TYPE  VARCHAR2 (255)  DEFAULT 'UNKNOWN',
  PHONE_COUNTRY_CD        VARCHAR2 (255),
  PHONE_AREA_CD           VARCHAR2 (255),
  PHONE_LOCAL_PREFIX      VARCHAR2 (255),
  PHONE_LOCAL_NUM         VARCHAR2 (2000),
  PHONE_EXTENSION_NUM     VARCHAR2 (255),
  EADDRESS                VARCHAR2 (2000),
  EADDRESS_STATUS         VARCHAR2 (255)  DEFAULT 'VALID',
  TIMEZONE_CD             VARCHAR2 (255),
  EADDRESS_UPDATED_DT     DATE,
  PRIMARY_EADDRESS_FLAG   VARCHAR2 (30),
  START_DT                DATE,
  END_DT                  DATE,
  CREATION_DT             DATE          NOT NULL,
  USER_CREATED            VARCHAR2 (255)  NOT NULL,
  MODIFICATION_DT         DATE,
  USER_MODIFIED           VARCHAR2 (255))
   TABLESPACE DATA
   PCTFREE 10
   PCTUSED 40
   INITRANS 1
   MAXTRANS 255
  STORAGE (
   INITIAL 65536
   MINEXTENTS 1
   MAXEXTENTS 2147483645
   FREELISTS 1 FREELIST GROUPS 1 )
   NOCACHE;

CREATE INDEX OPS$CCCADM.CCC_EADD_TN_ID_PAFLG_TY_MB ON
  "OPS$CCCADM".CCC_E_ADDRS_MB(TRANSACTION_ID, PRIMARY_EADDRESS_FLAG,
EADDRESS_TYPE)
  TABLESPACE DATA PCTFREE 10  STORAGE(INITIAL 65536 )
;

CREATE INDEX OPS$CCCADM.CCC_ETLESTG_TRNID_LOCAL ON
  "OPS$CCCADM".CCC_E_ADDRS_MB(TRANSACTION_ID)
  TABLESPACE DATA PCTFREE 10  STORAGE(INITIAL 65536 )
;

The Query goes like this:

SELECT      DISTINCT(ANSER.TRANSACTION_ID)
    , CONTACT.FIRST_NAME
    , CONTACT.FIRST_NAME_KANA
    , CONTACT.LAST_NAME
    , CONTACT.LAST_NAME_KANA
    , CONTACT.JOB_TITLE
    , CONTACT.DEPARTMENT
    , CONTACT.DEPARTMENT_2
    , CONTACT.ORIG_COMPANY_NAME
    , CONTACT.BRANCH_OFFICE
    , CONTACT.INDUSTRY
    , CONTACT.BUSINESS_RELATION_STATUS
    , PREF.PREFERENCE_FLAG
    , CONTACT.NUM_OF_EMPLOYEES
    , ADDRESS.ADDRESS_LOCATION_TYPE
    , ADDRESS.ADDRESS_LINE_1
    , ADDRESS.ADDRESS_LINE_2
    , ADDRESS.ADDRESS_LINE_3
    , ADDRESS.ZIP_POSTAL_CD
    , CONTACT.MODIFICATION_DT
    , MAX(DECODE(EADDRESS_TYPE, 'EMAIL', EADDRESS, NULL)) "EMAIL"
    , MAX(DECODE(EADDRESS_TYPE, 'FAX', EADDRESS, NULL)) "FAX"
    , MAX(DECODE(EADDRESS_TYPE, 'PHONE', EADDRESS, NULL)) "PHONE"
FROM     ANSER_TABLE_MB ANSER
    , CCC_CONTACTS_MB CONTACT
    , CCC_P_ADDRS_MB ADDRESS
    , CCC_PREFERENCES_MB PREF
    , CCC_E_ADDRS_MB EADDRS
WHERE    EVENT_ID = '00156'
AND    ANSER.TRANSACTION_ID = CONTACT.TRANSACTION_ID
AND    CONTACT.TRANSACTION_ID = ADDRESS.TRANSACTION_ID (+)
AND    CONTACT.TRANSACTION_ID = PREF.TRANSACTION_ID (+)
AND    ( PREF.PREFERENCE_TYPE = 4 OR PREF.PREFERENCE_TYPE IS NULL )
AND    EADDRS.TRANSACTION_ID = CONTACT.TRANSACTION_ID
GROUP BY ANSER.TRANSACTION_ID
    , CONTACT.FIRST_NAME
    , CONTACT.FIRST_NAME_KANA
    , CONTACT.LAST_NAME
    , CONTACT.LAST_NAME_KANA
    , CONTACT.JOB_TITLE
    , CONTACT.DEPARTMENT
    , CONTACT.DEPARTMENT_2
    , CONTACT.ORIG_COMPANY_NAME
    , CONTACT.BRANCH_OFFICE
    , CONTACT.INDUSTRY
    , CONTACT.BUSINESS_RELATION_STATUS
    , PREF.PREFERENCE_FLAG
    , CONTACT.NUM_OF_EMPLOYEES
    , ADDRESS.ADDRESS_LOCATION_TYPE
    , ADDRESS.ADDRESS_LINE_1
    , ADDRESS.ADDRESS_LINE_2
    , ADDRESS.ADDRESS_LINE_3
    , ADDRESS.ZIP_POSTAL_CD
    , CONTACT.MODIFICATION_DT
ORDER BY CONTACT.LAST_NAME ASC

Please recommend any changes in the query as well as tables storage structures,
or recommendations of adding/deleting indexes.

Regards,
Vijaya Chander V.S

      and we said...
I recommend everything to be in stored procedures, but that is me, my way of
doing it.

Pinning is not needed.  If you use it -- it'll be there in the cache.  The cache
is an LRU -- least recently use -- gets flushed.  If you use it, you won't be
seeing it get flushed.

Hopefully you do understand that:

SELECT      DISTINCT(ANSER.TRANSACTION_ID)
    ,

is the same as:

SELECT      DISTINCT ANSER.TRANSACTION_ID
    ,

and that distinct is applied to the ENTIRE ROW, distinct is not a function on
that column (as your syntax would lead me to believe that you think it is)

And further, since you are grouping by everything, the distinct is in fact
*redundant*, eg: not necessary, the results are already distincted, group by
will assure that.

Review &
      Followup

      Rating: 4
thank
      you  October 06, 2002
Reviewer:  Vijaya Chander
      V.S  from Bangalore , India
Hi Tom,

Thank you for your answer.Do you think that those outer joins hamper the
performance. Can we split that query into multiple queries in  a stored
procedure so that it helps as many end users use this resultset.can you please
suggest how to split this query into multiple queries and join their individual
into a single resultset. we tried this but failed where we joined multiple
resultsets into single result set. we need an expert advice like you.

          Followup:

If the outer join is NECESSARY, it is NECESSARY.  If it is not, it should not be
there.

Outer joins are not about performance, they are about getting the right answer!
It would not matter if it ran 100 times slower with the outer join -- if the
outer join is mandatory, well, you NEED it.

If the outer join is not needed, by all means remove it.

Breaking a query into tiny tidbits is a sqlserver approach -- for when the
database cannot handle the work all by itself.  It is almost always the slower
approach in Oracle.

It is however what I would term a "nasty" query.  The need to outer join, the
need to group by in order to transpose (indicates a data model built for purity
and genericity -- not for speed and high performance), using " ... = 4 or is
null" can be ugly as well (the fact that NULL entrys are not made in indices and
that OR's are just plain ugly for optimizers.  But since you are outer joining
to PREF anyway, maybe it doesn't matter)....

      Rating: 5
How are null
      values handled in outer-join?  December 18, 2002

Reviewer:  Sam  from San Mateo, CA
Tom,

I have the following test query (a hash outer join is much more efficient, but I
am trying to understand what happens when outer-joining in a nested loop):
SELECT /*+ use_nl(a b c) leading(a) */ a.object_id, b.val_col1, c.val_col1
from sto_outer_test a, sto_outer_test2 b, sto_outer_test2 c
where a.fk_col1 = b.pk_col1 (+)
and a.fk_col2 = c.pk_col1 (+)

call     count  ...  disk      query    current  rows
------- ------  ...  --------- -------- -------- -------
parse        1  ...          0        0        0       0
Execute      1  ...          0        0        0       0
Fetch       51  ...          0   300294        4   50000
------- ------  ...  --------- -------- -------- -------
total       53  ...          0   300294        4   50000

Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   GOAL: CHOOSE
  50000   NESTED LOOPS (OUTER)
  50000    NESTED LOOPS (OUTER)
  50000     TABLE ACCESS   GOAL: ANALYZED (FULL) OF 'STO_OUTER_TEST'
    100     TABLE ACCESS   GOAL: ANALYZED (FULL) OF 'STO_OUTER_TEST2'
    100    TABLE ACCESS   GOAL: ANALYZED (FULL) OF 'STO_OUTER_TEST2'

Only 100 rows in STO_OUTER_TEST has a value in both fk_col1 and fk_col2.  The
other rows have null's in both columns.

STO_OUTER_TEST2 has only 1 row.

My question is in the nested loop, on seeing a value of null for a.fk_col1, does
Oracle still need to scan b and c?  Or does it skip this scan since it knows it
won't find a match?  If so, why is the number of LIO's so high?
(the same query using a hash outer join requires < 500 disk + buffer gets only)

Is there any advantage to removing the outer-join in the SQL and using some post
query pl/sql logic and only fetch values from sto_outer_test2 if a.fk_col1 or
a.fk_col2 is not null?

The script used to create the tables:
drop table sto_outer_test;

create table sto_outer_test as
select object_id, object_name from all_objects
where rownum <= 50000;

alter table sto_outer_test add (fk_col1 varchar2(10), fk_col2 varchar2(10));

update sto_outer_test set fk_col1 = 'A', fk_col2 = 'A' where rownum <= 100;
commit;

drop table sto_outer_test2;

create table sto_outer_test2 (pk_col1 VARCHAR2(10) not null, val_col1
VARCHAR2(10));

insert into sto_outer_test2 values ('A', 'A');

commit;

analyze table sto_outer_test compute statistics;
analyze table sto_outer_test2 compute statistics;

          Followup:

Using 9iR2 (9202) tkprof, we can see the secondary table is read.  I modified
the example so that ALL of the fk columns were null (removed the update).  The
tkprof shows:

SELECT /*+ use_nl(a b c) leading(a) */ a.object_id, b.val_col1, c.val_col1
from sto_outer_test a, sto_outer_test2 b, sto_outer_test2 c
where a.fk_col1 = b.pk_col1 (+)
and a.fk_col2 = c.pk_col1 (+)

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch     1960      2.15       2.09          0     413443          0       29382
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total     1962      2.16       2.10          0     413443          0       29382

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 110

Rows     Row Source Operation
-------  ---------------------------------------------------
  29382  NESTED LOOPS OUTER (cr=413443 r=0 w=0 time=1964309 us)
  29382   NESTED LOOPS OUTER (cr=207769 r=0 w=0 time=1111172 us)
  29382    TABLE ACCESS FULL STO_OUTER_TEST (cr=2095 r=0 w=0 time=140313 us)
      0    TABLE ACCESS FULL STO_OUTER_TEST2 (cr=205674 r=0 w=0 time=815819 us)
      0   TABLE ACCESS FULL STO_OUTER_TEST2 (cr=205674 r=0 w=0 time=699652 us)

the cr= is consistent gets for that object.  There were 205,674 consistent reads
against that table

But -- that aside -- don't try to out guess the SQL.  Remove the hint, let it do
the work.  It would be the rare, totally degenerate case that you would be able
to beat by doing it yourself.  Let SQL do SQL.  You do procedural logic only
when you need to (eg: application stuff).

      Rating:
      4
Question  December 19, 2002
Reviewer:
      Tony Tailor  from UK
>>I recommend everything to be in stored procedures, but >>that is me, my way of
 doing it.

Tom what do you mean by this ?

          Followup:

they asked:

Do you recommend this query to be in a stored procedure...

and I simply said "yes, i like everything in stored procedures".  That is the
way I do it.  I try not to put any SQL (select/insert/update/delete/merge) in
the application itself if I am coding the application (4gls like forms and
powerbuilder -- totally different set of rules apply).

If you bury the query in a EJB that is called from a servlet that is invoked by
a browser on an app server farm and someone says "the query is slow" -- I find
it inifinitely easier to suck out the one procedure -- fix the query -- put it
back in, as opposed to tracking down where in the bean/servlet/etc etc etc the
query is -- finding a java programmer to recode the java -- compile it -- deploy
it to N app servers -- etc.

but, that is me, my way of doing it.

      Rating: 4
Null values in
      outer-join  December 19, 2002
Reviewer:  Sam
       from San Mateo, CA
Tom,

> But -- that aside -- don't try to out guess the SQL.
> Remove the hint, let it do the work.  It would be the
> rare, totally degenerate case that you would be able to
> beat by doing it yourself.  Let SQL do SQL.  You do
> procedural logic only when you need to (eg: application
> stuff).

Thanks for the answer.  The real query we use has a lot more tables involved,
and the optimizer is choosing to use a nested loop from the driving table.

Since we know for almost all customers most of the columns involved in the
outer-join have null values, someone proposed removing the outer-joins (there
are about 5 outer-joins) and instead do a select in the post-query trigger (we
are using Forms) if the outer-join column value is not null. The results from
tkprof does look a lot better since we removed all the unnecessary reads on the
outer-join tables (that you've demonstrated in your example).  I'm still not
sure if this is a good idea since you always recommend doing as much as possible
in SQL.  However, you also said it's very important to remove unnecessary LIO.

What's your opinion?

          Followup:

try this instead:

select ......,
       decode( FK_COL, NULL, NULL,
                    (select col from other_table where pk = FK_COL ) )
  from t

eg:

scott@ORA920> update emp set deptno = NULL where mod(empno,2) = 1;

4 rows updated.

instead of:

scott@ORA920>
scott@ORA920> select ename, dname
  2    from emp, dept
  3   where emp.deptno = dept.deptno(+)
  4   order by 1
  5  /

ENAME      DNAME
---------- --------------
ADAMS      RESEARCH
ALLEN
BLAKE      SALES
CLARK      ACCOUNTING
FORD       RESEARCH
JAMES      SALES
JONES      RESEARCH
KING
MARTIN     SALES
MILLER     ACCOUNTING
SCOTT      RESEARCH
SMITH
TURNER     SALES
WARD

14 rows selected.

code

scott@ORA920> select ename, decode( deptno, null, null, (select dname from dept
where deptno = EMP.DEPTNO) ) dname
  2    from emp
  3   order by 1
  4  /

ENAME      DNAME
---------- --------------
ADAMS      RESEARCH
ALLEN
BLAKE      SALES
CLARK      ACCOUNTING
FORD       RESEARCH
JAMES      SALES
JONES      RESEARCH
KING
MARTIN     SALES
MILLER     ACCOUNTING
SCOTT      RESEARCH
SMITH
TURNER     SALES
WARD

14 rows selected.

December 19, 2002 Kashif -- Thanks for the question regarding "NOT
      operator performance.", version 8.1.6.3

      You Asked
Hi Tom,

Simple question: Are there any performance implications in using the NOT
operator? E.g.

select
...
from
...
where not (x.id in (1,2,3,4)
           and x.name in ('ABC')
           and hire_date > sysdate)
...

I've read in the articles of some famous authors to avoid this operator at all
costs (no pun intended). Thanks.

Kashif

      and we said...
I despise ROT (rules of thumb).

where x = 5;

that can use an index nicely right -- it can use an index on X and do a range
scan to find rows with x=5.

where x <> 5  or where NOT(x=5)

that CANNOT use an index nicely -- it cannot range scan nicely.  It is also
going to get most of the data (where as x=5 wouldn't -- typically)

However -- and big however -- so what?  If your question is "find me the rows
where x is not 5" what the heck else are you going to do?  Short of NOT ASKING
the question that is!

Use it.

Just understand what happens when you do.

Also -- ask the famous authors which of the following queries is "better" (and
why)

create table t1 as select * from all_objects;
alter table t1 add constraint t1_pk primary key(object_id);

analyze table t1 compute statistics for table
for all indexes for all indexed columns;

ops$tkyte@ORA920> select * from t1 where object_id < 55;

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=42 Card=47 Bytes=4512)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T1' (Cost=42 Card=47 Bytes=4512)
   2    1     INDEX (RANGE SCAN) OF 'T1_PK' (UNIQUE) (Cost=2 Card=47)

ops$tkyte@ORA920> select * from t1 where NOT (object_id >= 55);

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=42 Card=47 Bytes=4512)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T1' (Cost=42 Card=47 Bytes=4512)
   2    1     INDEX (RANGE SCAN) OF 'T1_PK' (UNIQUE) (Cost=2 Card=47)

December 19, 2002 Jitender -- Thanks for the question regarding
      "Database Host name", version 8.1.7

      You Asked
Hi Tom,

I am aware of the following two ways to get database host machine name .
1) v$instance view but that is accessible to DBA and requires select privilges
to be given to developers.
2) UTL_INADDR but this requires java stuff to be installed in the database .

Is there any other way to get database host machine name without using above
methods ?

Thanks

Jitender

      and we said...
that is about it -- short of writing a C extproc. Guess the DBA's could write a
ON STARTUP trigger that would copy the v$instance data to a "real" table for you
as well.

create table server_hostname as select host_name from v$instance;
grant select on server_hostname to public;

create or replace trigger server_hostname_trigger
after startup on database
begin
    delete from server_hostname;
    insert into server_hostname select host_name from v$instance;
end;
/

for example, then that table would have the hostname in it and all could see it.
Review &
      Followup

      Rating:
      4
  December 20, 2002
Reviewer:
      Jitender
Thanks

December 19, 2002 Adam -- Thanks for the question regarding
      "Triggers on DBA_SOURCE", version 9.1.2

      You Asked
Quick question if you don't mind..

I keep an audit of when PL/SQL code was changed in the database (We have an
amazing Mr Nobody who compiles/drops code). We have lines within the code which
contains the CVS version and check in date and time etc.  I already audit this
information by looping around DBA_SOURCE once a day, and note this info and the
compile time etc.

I however want to run this procedure whenever dba_source is modified.  I have
been advised that adding a trigger to DBA_SOURCE would be wrong and is just
something which you shouldn't do (Touching a system table).

I personlly don't see the harm so, is it bad practice and if so why?

Have a good Christmas, and many thanks.

      and we said...
(9.1.2 is not a version ;)

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6020239969856
for a solution to this problem.

Now, as for putting triggers on SYS owned things, the very first thing that
would happen is:

  1  create or replace trigger x
  2  instead of insert on dba_source
  3  begin
  4  null;
  5* end;
sys@ORA817DEV.US.ORACLE.COM> /
create or replace trigger x
                          *
ERROR at line 1:
ORA-04089: cannot create triggers on objects owned by SYS

it is 100% forbidden.

The second issue would be that we don't INSERT into dba_source (or update or
delete it).  It is a view.  It is the base tables we work on - but then you are
back to the 4089 error ;)

DDL or DML against the data dictionary is among the set of things that are
always really bad ideas.

If you ever feel tempted to touch anything owned by SYS or SYSTEM stop, don't,
do something else

December 19, 2002 Adolph -- Thanks for the question regarding
      "Locating character in Strings, using wild cards", version 8.1.7

      You Asked
Hi Tom,
I have a string for example '45794G6H', and I want to do a select to find out if
any character e.g 'G'. or 'H' is found in the string.  I am not interested in
the position.
How can I do this ?
Thanks,
   Adolph

      and we said...
where S <> translate( S, :bv, rpad( '*', length(:bv), '*' ) )

just turn all of the G's and H's into *'s compare

you could use

where str like '%G%' or str like '%H%'

but the translate trick lets you write a single query with binds

Review &
      Followup

      Rating: 3
Alternate
      method  December 19, 2002
Reviewer:  pk
       from alaska USA
Why can't we use instr function to find out whether the variable exists ?

select decode(sign(instr('45794G6H','G') - 0),1,'Variable is present','Variable
not present')  result
from dual
/
PK>/

RESULT
-------------------
Variable is present

select decode(sign(instr('45794G6H','P') - 0),1,'Variable is present','Variable
not present')  result
from dual
/
PK>/

RESULT
--------------------
Variable not present

          Followup:

sure, now -- do it for G and H at the same time and make it so that it works for
ABC as well as GH as well as X (eg: varying numbers of characters...)

December 20, 2002 -- Thanks for the question regarding "latch
      contention", version 8.1.6

      You Asked
Hi Tom,

I'm trying to identify the source of some sleeps. I did the
following query with these results:

SELECT addr, latch#, gets, misses, sleeps FROM v$latch_children
WHERE sleeps>2000 ORDER BY sleeps desc;

 ADDR        LATCH#      GETS    MISSES    SLEEPS
 -------- --------- --------- --------- ---------
 63448620        66 1.112E+09   4724658      6573
 634350F4        66 609065631   1957482      2327
 633B87C8        66 335239357    255728      2003

Then I used the address of the first row in the following query
and got back no rows selected.

SELECT a.segment_name , a.segment_type , a.owner , a.tablespace_name
FROM sys.dba_extents a,
(SELECT dbarfil,dbablk,class,state FROM x$bh WHERE hladdr='63448620') b
WHERE a.file_id = b.dbarfil
AND b.DBABLK BETWEEN block_id and block_id + blocks -1;

no rows selected

What am I doing wrong to identify the object? The address from
v$latch_children doesn't appear in x$bh?

Thanks

      and we said...
That block can be lllllooooonnnnnggggg gone by now (from x$bh).  Is the subquery
returning anything.

If you look at your numbers, you are actually doing excellent.  I would not lose
1 second of sleep over this -- you do not have any serious contention issues
here -- compare the GETS to SLEEPS.

You have 335 MILLION gets and 2000 sleeps.  that means that

0.0005% of your gets were affected.  Hmmmmmm.

2000 by itself sounds big, until you put it next to 335 MILLION.

Look for sleeps as a percentage of gets instead of a raw number like 2000.
Review &
      Followup

      Rating: 2
Seem to have a
      similar problem...  December 17, 2002
Reviewer:
      Mike  from UK
Hi Tom,

we seem to have a similar problem, were trying to benchmark our software, and as
such simulate 50 users busily using the software via PL/SQL scripts.

we've done some tests, and while everything is fine and dandy with a single user
the more we add the worse it gets... we've got to 50 concurrent users now and
things arew getting a little slooooooooow.

we've stat's packed it and found some very worrying things, I searched through
your book (v good btw) and we're looking into some stuff but I still have some
questions...

Our top 2 wait events are quite distressing...

~~~~~~~~~~~~~~~~~                                             Wait     % Total
Event                                               Waits  Time (cs)   Wt Time
-------------------------------------------- ------------ ------------ -------
latch free                                        804,573      566,778   43.82
library cache pin                                  15,044      469,781   36.32

The latch free we are fairly happy is due to some very hot index leaf blocks,
we're going to reverse key those to see if that helps the problem.

the other on the libary cache pin is a bit more distrubing.  the stats pack
report also shows an alarming CPU to wall clock%...

Instance Efficiency Percentages (Target 100%)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            Buffer Nowait %:   99.96       Redo NoWait %:   99.22
            Buffer  Hit   %:   99.96    In-memory Sort %:  100.00
            Library Hit   %:  100.01        Soft Parse %:   99.99
         Execute to Parse %:   95.82         Latch Hit %:   98.95
Parse CPU to Parse Elapsd %:    6.88     % Non-Parse CPU:  100.00

but the others seem quite "nice"

the following also jumped out at me as a good thing,

Statistic                                    Total   per Second    per Trans
--------------------------------- ---------------- ------------ ------------
parse count (hard)                               4          0.0          0.0
parse count (total)                         48,489         69.5         13.8

The bit that then confussed the hell out of me was the Latch sleep breakdown
which showed

                                Get                                  Spin &
Latch Name                    Requests         Misses      Sleeps Sleeps 1->4
-------------------------- -------------- ----------- ----------- ------------
library cache                   4,872,597     389,100     745,712 91090/21933/
                                                                  175257/10082
                                                                  0/0
cache buffers chains           33,991,454      61,916      44,479 31982/22648/
                                                                  3899/3387/0
cache buffers lru chain         5,799,637      32,872       5,635 27605/4935/2
                                                                  96/36/0
undo global data                1,386,214       7,996       1,834 6311/1542/13
                                                                  7/6/0

Which says to me after reading page 491 of your book around 491 times that not
only does a libary cache latch request have a 1 in 10 chance of missing, but if
it does it will probably end up sleeping twice, as most missed laches get
serverd on the 3rd attempt!

eurgh!!  where have we gone wrong?

Thanks in advance for any pointers...

Mike.

          Followup:

the execute to parse is

round( 100 * ( 1- PARSES/EXECUTES), 2 )

for that to be very near 100, your parses must be very very near your executes.

What is your application written in?

How many/what kind CPU's do you have?

Why are you parsing and executing statements only ONCE in your application --
you never reuse any SQL?

How is your benchmark setup -- any think times (realistics think times)?

      Rating: 3
Answers to
      Questions  December 19, 2002
Reviewer:  Mike
      Jones  from UK
Hi, Thanks for the response!

The application is pure PL/SQL.

It's running on a 4CPU (550 MHZ) HP box, which for clarity is running 32bit
Oracle 8.1.7.3 (with some memory patches) on 64 bit HP-UX (historic reasons for
the 32/64 mismatch)

I'm alarmed that we are not reusing any SQL, I can say that most of the SQL in
the application is implicit cursors, using either a ROWID in the where or doing
a bulk collect.  I assume this is the reason?  if so it would seem logical to
convert these to explicit cursors, however I also remember reading a comment
from you that you NEVER use explicit cursors, so how do you get around the
problem?

The stat's pack report I pasted parts of are not realistic in terms of think
times, this was intentional, but also mis-guided.

We have moved on now, to having far more test data in the system, so we are only
working on a small percentage of the total amount of data in the system (but
still as much actual data) and introducing realistic wait times.

This seems like an additional question to me, If you would like I will wait
until I can post a proper Question.

Thanks as always,

Mike.

          Followup:

implicit cursors are reused transparently.

one would need to know HOW the test was conducted -- did you

o login
o run a procedure
o logout
o goto 1

or

o login
o run a procedure
o goto 2 for a couple of runs
o logout

if you did the first case, you might expect what we see.  If you did the second,
we would not.

Make sure your benchmark mirrors reality (or it is just a waste of time/energy
and effort)

working on a subset is another "bad idea" unless it is a vertical subset (and
the user base is also a vertical subset).  Eg, only 100 out of the 500 tables
(vertical) but totally full..

      Rating: 5
really
      confussed....  December 19, 2002
Reviewer:
      A reader
Hi, thanks for the follow ups...

I'll explain it in detail, as what we are seeing seems to go against what you
are saying.

the application in question is a Workflow system, so...

A) user completes an action,
B) the system works out what they have to do next and tells them
C) go to A

on the scenario we tested we set up 50 "plans" that contained in total 250
actions that the user had to do.

we used 1 script which we passed in a parameter which was the plan that the
script would work on, we then ran the script 50 times (concurrently) with each
script given a different plan to work on.

the script goes

a) find an action to do
b) do it (which involves the system working out what they should do next)
c) go back to a

when it finds no more actions to do the script ends and the session is closed.
To mimic the end user, after each action is done a commit is issued by the
script.

the call to complete the action and work out the results, is quite intensive,
this is a package call, we are confident that we are using bind vairables
throughout.

I have two very confussed DBA's, one of them commented, that although the
benchmark did not mirror reality in that it replayed the transactions quicker
than a user would, he would not expect this to have any impact on whether a soft
parse was required or not, and that we clearly don't understand something that
we really all should.

As a side note on the data, we thought that having 50 plans in the sytem and
working on them all was not fair, as in reality, a production system would have
over 60K plans and we would have at most 50 users working through 50 of them. so
we have changed the test data to reflect this, the number tables worked on is
always the same.

As ever thanks for sticking with this.

Mike.

          Followup:

does this process do a ton of dynamic sql?

      Rating: 5
Dynamic SQL
      usage  December 19, 2002
Reviewer:  Mike
      Jones
Define ton!  It does a lot, yes, each call to the system to "work out the
consequences" could result on average in 100 dynamic SQL statements, however
they would all be very similar, and would use bind variables.  I would say we
would have only 3 or 4 distint ones in terms of the actual SQL that would appear
in V$SQL.

99% of this is via execute immidiate as well, only a small amount (perhaps 3 per
session) use dbms_sql.

I know that execute immidiate's cause a soft parse, I think I can see where you
are going with this.  In short we need to reduce this.  Would using DBMS_SQL
over execute immediate address this?  my memory's hunch is that it would as we
take control of the cursors at that point.

Thanks again, I think I owe you a drink!

Mike.

          Followup:

that is your soft parse then -- every native dyanmic sql call is by definition a
soft parse.

In order to reduce some of the latching -- set session_cached_cursors to say 100
and try again.

If the statement is to be executed many times during the course of the session
-- dbms_sql would be better -- then you are able to

o parse ONCE
o execute over and over and over

      Rating:
      5
Reader  December 19, 2002
Reviewer:
      A reader
May be it is just an oversight
"
the execute to parse is

round( 100 * ( 1- PARSES/EXECUTES), 2 )

for that to be very near 100, your parses must be very very near your executes.

"

For a value of 95.82 in the statspack,
parse/execute = .0418 or 4 parse to 100 executes. Is this
not an acceptable statistics

          Followup:

whoops -- you are right ;(  got that backwards.

1 parse, 25 executes is pretty good -- but, in a wicked twist of fate -- turns
out they are doing lots of dynamic sql (native dynamic) which does increase
their parses and if it can be reworked to avoid that....

thanks for the followup.

December 20, 2002 Kailash -- Thanks for the question regarding
      "PCTFREE/PCTUSED", version 8.x

      You Asked
Can a block on freelist be used to update existing rows in the block. This is
because most of the books I read say that the freelist blocks are used for
performing new inserts and do not talk about updates to existing rows.
Can the free space in a block on freelist be consumed by updates to existing
rows or can it only be used for inserts?
In other words, how does Oracle handle updates to rows in blocks that are on
free list. It would be very helpful if you can explain with an example.

      and we said...
You haven't read my book "Expert One on One Oracle" then!

...
An object may have more then one FREELIST. If you anticipate heavy insert or
update activity on an object by many concurrent users, configuring more then
one FREELIST can make a major positive impact on performance (at the cost of
possible additional storage).
.....

If we update a row and that update causes the row to migrate or chain (meaning,
we need another block), we goto the freelists first.  Tons of examples in the
book in the chapter on tables where I cover HWM's, pctfree/pctused, chaining,
migrating and so on.
Review & Followup

      Rating:
      2
  December 19, 2002
Reviewer:
      A reader
My doubt is
1. Can a block's free space be used for updates to existing rows even if the
block is on free list and even before reaching PCTFREE limit or are those blocks
be used only for inserts?

          Followup:

yes it can.  It is only if the update causes the row to migrate or chain that a
new block from the freelist will be requested.

an update can fill the block all of the way up.

      Rating:
      5
  December 19, 2002
Reviewer:  Dave
       from Ohio
I've been in the habit of setting pctfree=0 for data warehouse fact tables to
get maximum rows per block (no updates on existing rows).

Recently I was advised that pctfree=1 would be better, as it guarantees that
Oracle has the space that it needs for "transaction entries". Needless to say I
have read the Concepts Guide, where mention is made that ...

"A transaction entry is required in a block for each INSERT, UPDATE,
DELETE,andSELECT...FOR UPDATE statement accessing one or more rows in the block.
The space required for transaction entries is operating system dependent;
however, transaction entries in most operating systems require approximately 23
bytes."

Since I expect none of these types of transaction to occur I'm inclined to stick
with pctfree=0, but I'm curious as to a couple of points.

i) Are these transaction entries used for locking purposes, or what?
ii) What would happen if Oracle did not have the required bytes available for
transaction entries, or is this space always reserved so the advice on pctfree=1
is a misinterpretation of the concepts guide?

          Followup:

i) they are used to manage what transactions are modifying what rows on the
block -- yes, they are used for locking purposes.

ii) there is always space reserved, you control how much via the initrans
setting on the table which in 8i and before defaulted to 1 for tables, 2 for
indexes and in 9i and up is 2 for both.

So, on your table, pctfree=0

December 20, 2002 Omar -- Thanks for the question regarding "Http
      Server in Oracle8i Rel3", version 8.1.6

      You Asked
Hi Tom,
  Your efforts are highly appreciated for helping the Oracle Community.
   Q1. I need to know what is the difference between the Apache http server
which comes with Oracle8i Rel3, and the Oracle iAS 9i product, do they serve the
same thing or what.

   Q2. Can I partition a table after its creation without recreate it.

thanks in advance

      and we said...
q1)
There is iAS -- a standalone application server.

In Oracle8i release3, there is an http server *in the database*.  I believe that
is what you mean.  With 8.1.7 I can store a jsp, servlet, plsql web page --
whatever, in the database and use http straight into the database to retrieve
it.  A java webserver is embedded.

iAS is a fully featured app server outside of the database.

q2) yes and no.  Technically -- i'd say "yes you can" but functionally you
cannot.  If what you want to do is take an existing table and make that the
first partition of a partitioned table -- yes you can.  You would construct the
empty partitioned table and then exchange a partition of that empty table with
the existing full table.  The full table would become the first full partition
of the new table.

If on the other hand you want to physically PARTITION an existing table -- that
will obviously require a rebuild as the rows that are next to each other right
now must be placed into their appropriate partition -- the table must be
rewritten.

Review &
      Followup

      Rating: 4
HTTP
      Listener  April 23, 2001
Reviewer:  Phanikumar
      Bhogaraju  from Chicago, IL USA
Thank you. I learnt something new today!

      Rating: 5
PL-SQL code in
      html format  December 20, 2002
Reviewer:
      atul  from India
Sir,

I'm running 8i on red-hat linux 7.1..
I want to write simple HTML script which uses PL/SQL to access oracle..
I saw apache directory under $ORACLE_HOME..
so i assume that 8i typical installation loads apache web server...
I started web server by giving command..
httpdsctl start..
It gives message that server is started..

what next i have to do??besides writing code???
Am i on correct track???
Plz help me ...

Thanks.
atul

          Followup:

see

http://download-west.oracle.com/docs/cd/A97329_01/mix.902/q20102/toc.htm
the docs for apache and mod_plsql are there.  you want to use mod_plsql

      Rating: 5
PL-SQL code in
      html format  December 20, 2002
Reviewer:
      atul  from India
Sir,

I'm running 8i on red-hat linux 7.1..
I want to write simple HTML script which uses PL/SQL to access oracle..
I saw apache directory under $ORACLE_HOME..
so i assume that 8i typical installation loads apache web server...
I started web server by giving command..
httpdsctl start..
It gives message that server is started..

also how can i know port no of web server??
what next i have to do??besides writing code???
Am i on correct track???
Plz help me ...

Thanks.
atul

December 20, 2002 Carlos -- Thanks for the question regarding
      "Phanton Update", version 7.3.4

      You Asked
Hi Tom:

Where have the fallowin problem: In Developer2000 2.1 Forms 5.0.6.8 and Oracle
7.3.4, we insert a record in table INVENTORY_DETAILS and have de next trigger :

create or replace trigger "UPDATE_ORDERS" BEFORE INSERT ON INVENTORY_DETAILS
REFERENCING NEW AS NEW OLD AS OLD FOR EACH ROW
BEGIN
   IF :NEW.COD_MOVIMIENTO = 50 THEN
      ACTUALIZA_ORDEN(:NEW.CODIGO_COMPANIA,
                      :NEW.CODIGO_SUCURSAL,
                      :NEW.CODIGO_LOCALIDAD,
                      :NEW.COD_MOVIMIENTO,
                      :NEW.NUMERO_DOCUMENTO,
                      :NEW.MARCA,
                      :NEW.MODELO,
                      ABS(:NEW.CANTIDAD));
   END IF;
END;

The procedure is ...

CREATE OR REPLACE Procedure actualiza_orden(p_compania
INVENTORY_DETAILS.codigo_compania%Type,
                                            p_sucursal
INVENTORY_DETAILS.codigo_sucursal%Type,
                                            p_localidad
INVENTORY_DETAILS.codigo_localidad%Type,
                                            p_movimiento
INVENTORY_DETAILS.cod_movimiento%Type,
                                            p_numero
INVENTORY_DETAILS.numero_documento%Type,
                                            p_marca
INVENTORY_DETAILS.marca%Type,
                                            p_modelo
INVENTORY_DETAILS.modelo%Type,
                                            p_cantidad
INVENTORY_DETAILS.cantidad%Type) IS
   w_rownum       rowid;
   w_despachada   orders.numero_conduce%Type;
   w_pedida       orders.numero_conduce%Type;
   w_estado       orders.estado_orden%Type;
   w_factura      orders.numero_factura%Type;
   w_orden        orders.numero_orden%Type;
Begin
   select
        factura,
        orden_despacho
   into w_factura,
        w_orden
   from inventory_header
   where codigo_compania = p_compania
   and codigo_sucursal   = p_sucursal
   and codigo_localidad  = p_localidad
   and cod_movimiento    = p_movimiento
   and numero_documento  = p_numero;

   select
        rowid,
        nvl(numero_conduce,0) despachada,
        cantidad pedida
   into w_rownum,
        w_despachada,
        w_pedida
   from orders
   where codigo_compania = p_compania
   and codigo_sucursal   = codigo_sucursal
   and numero_orden      = w_orden
   and marca             = p_marca
   and modelo            = p_modelo
   and tipo_venta        = tipo_venta
   and numero_factura    = w_factura
   and codigo_sucursal_salida  = p_sucursal
   and codigo_localidad_salida = p_localidad
   and nvl(estado_orden,'P') = 'P';

   w_despachada := w_despachada + p_cantidad;

   if w_despachada = w_pedida then
      w_estado := 'C';
   else
      w_estado := 'P';
   end if;

   update orders
   set numero_conduce = w_despachada,
       estado_orden   = w_estado
   where rowid = w_rownum;
End;

My problem is that some times the fields in orders get not updated by this
procedure but the record in my inventory_details table is inserted any way.
What we tried to do is update the orders table any time ship items to the
customer.  Are we ok. Is there a better way to do this...

Thanks in advance

Carlos Marquez

      and we said...
You have a slight flaw in your transactional logic here.

Consider what happens when two people insert a row at ABOUT the same time with
the same company information.

The trigger fires.

They both read the orders/factura tables -- getting your "aggregate" counts...
the counts they read DO NOT INCLUDE EACHOTHERS UPDATES (the uncommitted changes
are not visible).

They both update the row -- one blocks for a moment, the other finishes and the
blocked one *overwrites* the data.

What you *need* to do is -- select the row in the orders table that you want to
update FOR UPDATE and THEN run the other queries.  That way -- the sessions will
serialize on the row you want to update, one will get it the other not.  The one
that gets it will read the subordinate data -- update the row and eventually
commit - that is when the other transaction will unblock -- read the data,
update and commit.

That is the cause of your "phantom" update -- you have a serious data integrity
issue here.  You need to force a little serialization on the ORDERS table.
Review &
      Followup

      Rating: 5
Null Values in
      Fields...  December 20, 2002
Reviewer:  Carlos
      Marquez  from Dominican Republic
Thanks for You fast response.  I think it could be other situation here.
Looking at the store procedure estado_orden I think that the fields (
estado_orden, numero_conduce ) fails to get updated, in the case You pointed out
the code is execute and one of then lost the data, but the fields get update, in
my case the fields remain in null state in the ORDERS table and the record in
the  INVENTORY_DETAILS is inserted any way.  It looks like...

1. Insert Row in INVENTORY_DETAILS
2. ROW LEVEL Trigger UPDATE_ORDERS is fire
3. Procedure ACTUALIZA_ORDEN is called
  3.1 The INVENTORY_HEADER data is read
  3.2 The ORDERS Data is read
  3.3 Try to update the ORDERS table
  4.4 Update fails...
4. Row is inserted
5. Transaction is commited

Carlos Marquez...

          Followup:

if 4.4 is true -- you would get an error, which would fail the trigger, which
would fail the insert.

If either of the select intos failed -- same thing.

Hence, 4.4 cannot be true.  The rowid is coming straight from the prior select
into against orders, the row exists.

The data itself you select, that might be NULL, but that would be a different
problem all together.

You do have the concurrency issue to which I talk of -- that is clear.

Add a

if ( sql%rowcount = 0 )
then
   raise_application_error( -20001, 'Update failed ' || <meaningful data> );
end if;

after your update to see that it always updates a single row.

The transactional logic here is very flawed in a multi-user environment.  It
needs to be changed.

December 20, 2002 Marc -- Thanks for the question regarding "FGAC
      Query Rewrite", version 8.1.7

      You Asked
Hi Tom,

the 8i and 9i documentation says about how FGAC is implemented:

<quote>
In this case, you might create a function that adds the following predicate:

 Cust_no = (SELECT Custno FROM Customers WHERE Custname =
            SYS_CONTEXT ('userenv','session_user'))

A user enters the statement:

SELECT * FROM Orders_tab

The Oracle server calls the function you created to implement the security
policy.

The function dynamically modifies the user's statement to read:

SELECT * FROM Orders_tab WHERE Custno = (
   SELECT Custno FROM Customers
       WHERE Custname = SYS_CONTEXT('userenv', 'session_user'))
</quote>

According to your book and your answers in asktom, the query should be modified
to:

SELECT * FROM
(
SELECT *
  FROM Orders_tab
 WHERE
Cust_no = (SELECT Custno FROM Customers
           WHERE Custname = SYS_CONTEXT('userenv','session_user'))
)

Could you clarify this? Thank you very much in advance

      and we said...
They are being a fast and loose -- conceptually, they are correct -- technically
-- not.

Suppose the query was

select * from orders_tab where x = 5 or y = 2

you cannot blindly just add "and cust_no = ..." to the end of that.  The inline
view is a more correct conceptual way to view it.  that query would become:

select * from
  ( select * from orders where cust_no = ..... )
 where x = 5 or y = 2;

In reality - NEITHER is 100% accurate as the query rewrite is done against
internal data structures, we don't really rewrite the SQL text and then parse it
again.  We parse it, augment the parse tree (rewrite) and then optimize.

It is more accurate to use the inline view analogy as it covers the situations
like above where you have pre-existing predicates and you cannot just "glue
additional constraints" onto.
Review &
      Followup

      Rating: 4
Thanx,
      interesting insight into internals  December 21, 2002

Reviewer:  Marc Blum  from Aachen, Germany

December 20, 2002 Andrew -- Thanks for the question regarding
      "Group By Problem", version 9i

      You Asked
Hi Tom,

When I run the following SQL :

SELECT

DECODE(SUBSTR(f017.pend_crn,1,1), 'L', 'LOTHIAN '||chr(38)||' BORDERS', 'S',
'STRATHCLYDE', 'D', 'DUMF. '||chr(38)||' GALL', 'G', 'GRAMPIAN','C', 'CENTRAL',
'F', 'FIFE', 'T', 'TAYSIDE', 'N', 'NORTHERN', '', 'SPACES', 'OTHER') FORCE,

SUM(DECODE(SUBSTR(f017.pend_crn,1,1), 'L', 1, 'S', 1, 'D', 1, 'C', 1, 'G', 1,
'F', 1, 'T', 1, 'N', 1, '', 1, 1)) RESULTS

FROM f017
WHERE UPPER(f017.bail_details_ind) <> 'B'
AND UPPER(f017.pend_crn_inds) LIKE '%F%'
GROUP BY DECODE(SUBSTR(f017.pend_crn,1,1), 'L', 'LOTHIAN '||chr(38)||' BORDERS',
'S', 'STRATHCLYDE', 'D', 'DUMF. '||chr(38)||' GALL', 'G', 'GRAMPIAN','C',
'CENTRAL', 'F', 'FIFE', 'T', 'TAYSIDE', 'N', 'NORTHERN', '', 'SPACES', 'OTHER');

I get the following results :-

FORCE                             RESULTS
-----------------               ------------
CENTRAL                             3
DUMF. & GALL                       9
FIFE                                       5
GRAMPIAN                              6
LOTHIAN & BORDERS                  6
NORTHERN                              5
OTHER                                   9
STRATHCLYDE                            166
TAYSIDE                               7

This is nearly correct apart from the 'SPACES' results of 0 is not appearing in
the result set.  If any of the results for the other forces were equal to 0 then
they would also not appear.

How would you change the query so that the complete result set was shown
including where results equal 0?

Kind Regards

Andrew.

      and we said...
well, are there any rows where the substr( f017.pend_crn,1, 1) is NULL?

if not, SPACES won't be there.

You could take the above and outer join it.  Say your query above is "Q", then:

set define off    <<<=== better then using chr(38)

select a.force, nvl( b.results, 0 ) results
  from ( select 'LOTHIAN & BORDERS' force from dual union all
         select 'STRATHCLYDE' from dual union all
         select 'DUMF. & GALL' from dual union all
         select 'GRAMPIAN' from dual union all
         select 'CENTRAL' from dual union all
         select 'FIFE' from dual union all
         select 'TAYSIDE' from dual union all
         select 'NORTHERN' from dual union all
         select 'SPACES' from dual union all
         select 'OTHER'from dual ) A,
        ( Q ) b
 where a.force = b.force (+)
/

it'll work like this:

scott@ORA817DEV.US.ORACLE.COM> select a.deptno, nvl( b.cnt, 0 ) cnt
  2    from ( select 10 deptno from dual union all
  3                   select 20 from dual union all
  4                   select 30 from dual union all
  5                   select 40 from dual ) a,
  6             ( select deptno, count(*) cnt from emp group by deptno ) b
  7   where a.deptno = b.deptno(+)
  8  /

    DEPTNO        CNT
---------- ----------
        10          3
        20          5
        30          6
        40          0

Review &
      Followup

      Rating:
      4
  January 06, 2003
Reviewer:  Andrew
      Ford  from Glasgow, Scotland, UK
Very good feedback.  Seems to bring back the correct results.

December 20, 2002 -- Thanks for the question regarding "Fiscal
      year", version 8.1.7

      You Asked
 Hi Tom

I am a newbie to Oracle and my Task are to...

1.I have to write a query to retrive the date in the present fiscal
year(2002-2003), preivious fiscal year ( 2001-2002) and the year before
that(2000-2001)My fiscal year starts on the 1th of July and ends on june 30th.

2. I have to identify which quater these date will be in that perticular fiscal
year.

Thank you in Advance.

Happy Holiday's

      and we said...
see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6744650194752
using the same technique we can:

ops$tkyte@ORA817DEV.US.ORACLE.COM> select dt,
  2         to_char(dt,'yyyy')+trunc((to_char(dt,'Q')-0.1)/2) fy,
  3         mod(to_char(dt,'Q')+1,4)+1
  4    from (select add_months(trunc(sysdate,'y'), rownum-1 ) dt
  5            from all_objects where rownum <= 12 )
  6  /

DT                FY MOD(TO_CHAR(DT,'Q')+1,4)+1
--------- ---------- --------------------------
01-JAN-02       2002                          3
01-FEB-02       2002                          3
01-MAR-02       2002                          3
01-APR-02       2002                          4
01-MAY-02       2002                          4
01-JUN-02       2002                          4
01-JUL-02       2003                          1
01-AUG-02       2003                          1
01-SEP-02       2003                          1
01-OCT-02       2003                          2
01-NOV-02       2003                          2
01-DEC-02       2003                          2

12 rows selected.

December 20, 2002 -- Thanks for the question regarding
      "multiplexing archive log files", version 8.1.7

      You Asked
I'm working on my project of multiplexing archive log files and have run into a
problem with one of the two methods which are used to accomplish this task.

Method 2 works:

LOG_ARCHIVE_DEST = '/disk1/arc'
LOG_ARCHIVE_DUPLEX_DEST ='/disk2/arc'

But method 1 returns an error when I try to startup the database:

LOG_ARCHIVE_DEST_1 ='LOCATION = /disk1/arc'
LOG_ARCHIVE_DEST_2 = 'LOCATION = /disk2/arc'

The error message is : 'ORA-00439: feature not enabled: Managed Standby'

Any ideas??

Thanks,
Frank Koskotas

      and we said...
Using LOG_ARCHIVE_DEST_n requires the Enterprise Edition of Oracle as it is
specifically designed for the managed standby capability and that is only
available in EE.

You must have SE.  Only the first option is available for you.
Review &
      Followup

      Rating:
      4
  August 28, 2001
Reviewer:
      A reader

      Rating:
      4
  May 07, 2002
Reviewer:
      A reader
Hi,

The database is running in ARCHIVELOG mode in a Windows NT server.
The value for log_archive_dest_1 is a folder in the same server. As a precaution
I wanted to
specify one more location for archived logfiles in a different system.
So, I created a mapped network drive (G:\) for a folder Archivelogs in a
different system.
Then in the Init.ora file if I give

log_archive_dest_2 = "Location=G:\test"

This fails, stating that Oracle cannot translate the destination.
I tried by giving the name of the system too "\\sys002\archivelogs\test". This
also failed with the
same error message.

Is it possible to configure log_archive_dest_2 to a different system?

Thanks.
Vijay.

          Followup:

It is possible but not highly recommended (too many inter-system dependencies)

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:241814624807
it is the same issue.

      Rating:
      4
LOG_ARCHIVE_DEST_2  December 18, 2002

Reviewer:  Will  from Austin
I have made the LOG_ARCHIVE_DEST_2='location=\\other_machine\folder\' work in NT
by doing the following in test:

o  Using LOG_ARCHIVE_DEST_1 instead of LOG_ARCHIVE_DEST
o  Setting the logon user of the instance and listener services to a domain
account that is also a local admin

It seems to do what I want, which is:

o  Put the archived log in both places
o  Continue with an error in the alert log if either destination goes bad  (both
DESTs are optional)
o  Stop everything if both destinations go bad

Unlike what I am used to from past experience, when I make a destination good
again (i.e. make space), logs do not automatically get written there again.  In
V_$ARCHIVE_DEST, the "fixed" destination still showed a status of ERROR.  It was
not until I reissued, alter system set log_archive_dest_2 =
'location=\\other_machine\folder\'  that subsequent logs got written there.  Is
this expected behavior?  Is there a better way of resetting the error status of
the destination?  I am working in 8.1.6.

This feature seems to meet our needs pretty well and allows us to bring into the
database some functionality that is currently done outside.  Further, the alert
log output would trigger notifications (pages, emails, etc.) for log archive
problems as it does for other problems.

Your statement:

"It is possible but not highly recommended (too many inter-system dependencies)"

does not give me a warm and fuzzy feeling about using this feature in
production.  Can you provide some examples of inter-system dependencies,
especially those that would be a factor even when we are writing the logs
locally as well?

What if you're writing logs to another server for a standby database?

Thanks Tom.

          Followup:

The inter-system dependency is actually presented in your case here.

the remote system goes down.
we stop copying.  there are archives NOT on the other system
you fix problem.

it is up to you to MANUALLY fix the missing archives (hence the fact that you
need to manually FIX the database to make it start moving logs again)

      Rating: 3
Same problem
      with local dest  December 18, 2002
Reviewer:
      Will  from Austin
I changed LOG_ARCHIVE_DEST_2 from a remote machine to a folder on the local
machine and experienced the same problem:  I have to kick it to make it work
again after DEST_2 goes from bad to good.

I guess I'm not quite seeing how this is an inter-system thing.

Also, isn't having to fix a broken secondary log location better than not having
one at all?

My external routine for copying the logs would be in the same boat (and so would
I) if the remote machine goes down.

Still not sure why it's better to use the external method instead of the
database.

Thanks in advance.

          Followup:

You want to have to manually kick -- you have to sync them up anyway (eg: if
this remote archival location is to be of any use, after a failure, you need to
manually intervene in order to get the missed logs over there -- else, if it
"just fixed itself" and started archiving again, well, you would be missing
archives there.  You would have a false sense of security).

The systems are dependent on eachother, they need to work together.

Yes, having a secondary location is better then none at all -- but if it is
sometimes up, sometimes down -- maybe not.  Rather have something always there
(like my tape drive)...

I don't know what this "external method" is.

      Rating: 5
getting your
      point  December 20, 2002
Reviewer:  Will
       from Austin
Given that I have seen this:

...
Fri Dec 20 05:03:39 2002
Thread 1 advanced to log sequence 91
  Current log# 2 seq# 91 mem# 0: D:\REDO_CTL\TPAC\REDO2B.LOG
  Current log# 2 seq# 91 mem# 1: F:\REDO_CTL\TPAC\REDO2A.LOG
Fri Dec 20 05:03:39 2002
ARC0: Beginning to archive log# 1 seq# 90
ARC0: Completed archiving log# 1 seq# 90
*****  NO LINES DELETED FROM HERE *****
Dump file D:\Oracle\Ora81\RDBMS\trace\tpac\tpacALRT.LOG
Fri Dec 20 06:12:05 2002
ORACLE V8.1.6.1.0 - Production vsnsta=0
vsnsql=e vsnxtr=3
Windows NT Version 4.0 Service Pack 6, CPU type 586
Starting up ORACLE RDBMS Version: 8.1.6.1.0.
...

two mornings in a row since I started the testing described above, I think I'll
continue to use the external method which is just a simple batch file like so:

:repeat
title Archive files copying now
xcopy <LOG_ARCHIVE_DEST_1>\*.* \\other_machine\folder /d /v >>
d:\sql\archivecopy.log
title Archive waiting to copy
sleep 1800
goto repeat

Any ideas on why the database apparently crashed (quietly) and restarted?  I am
99.999% percent sure that no person restarted the database at 06:12:05 this
morning.

          Followup:

the database would not restart automagically -- someone or something did that.
The service would not just kick in.

did someone reboot the server?

If you look in the event log, (event log, not alert), you should see sysdba
connections being made (auditing -- mandatory auditing that cannot be turned
off).  do you see any around the time of this mystery -- if so, i would suspect
shutdown abort, startup.

      Rating:
      5
disregard  December 20, 2002
Reviewer:
      Will  from Austin
Apparently the box blue-screened and restarted.

          Followup:

windows, always up (well, always getting up anyway...)

December 20, 2002 Jim -- Thanks for the question regarding "Rownum
      on fields", version 8.1.6

      You Asked
Say I had a database of all people in US and their address.  I want to query for
1 SMITH for each state.  Even though there are millions of Smiths, I want one
per state (don't care which one).  I would get 50 rows (no D.C.) and 50
different Smiths.

Thanks.

      and we said...
I would probably do this:

select state, ( select first_name || ' ' || last_name || ....
                  from big_table
                 where last_name = 'SMITH'
                   and state = little_table.state
                   and rownum = 1 )
  from little_table
/

where little table just has the states in it.  It'll be the fastest way given an
index on state and last_name
Review &
      Followup

      Rating: 5
Rownum on
      fields  December 19, 2002
Reviewer:  Barry
       from Bellevue, Nebraska
This is great & simple too.  2 questions: #1 Do the values in the select
statement for the "Big Table" query have to be concatenated, or can you have
multiple columns?  #2 What if I want to bring back 3 Smiths, etc per state
instead of just one (and rownum <=3)? I tried that and it gave me an Oracle 1427
error, single row subquery returns more than one row.  We have lots of
situations where we need to pull several rows per region/divsion/agent/etc.
Thanks for the good info.

          Followup:

1) concatenated.  you can (and I have) substr them back out.  eg:

Rownum on fields  December 19, 2002
Reviewer:  Barry  from Bellevue, Nebraska

This is great & simple too.  2 questions: #1 Do the values in the select
statement for the "Big Table" query have to be concatenated, or can you have
multiple columns?  #2 What if I want to bring back 3 Smiths, etc per state
instead of just one (and rownum <=3)? I tried that and it gave me an Oracle 1427

error, single row subquery returns more than one row.  We have lots of
situations where we need to pull several rows per region/divsion/agent/etc.
Thanks for the good info.

select state,
       rtrim(substr( data, 1, 30 )) first_name,
       rtrim(substr( data, 31, 30)) last_name,
       ....
  from (
select state, ( select rpad(first_name,30) || rpad(last_name,30) || ....
                  from big_table
                 where last_name = 'SMITH'
                   and state = little_table.state
                   and rownum = 1 ) DATA
  from little_table
       )
/

just beware of nulls in the rpad, you may have to NVL some of the values...

2) that could be done with a nested table/varray or cursor.  Consider:

ops$tkyte@ORA920> select username, cursor( select object_name, object_type,
status
  2                             from all_objects
  3                            where owner = all_users.username
  4                              and rownum <= 3 )
  5    from all_users
  6   order by username
  7  /

USERNAME                       CURSOR(SELECTOBJECT_
------------------------------ --------------------
A                              CURSOR STATEMENT : 2

CURSOR STATEMENT : 2

OBJECT_NAME                    OBJECT_TYPE        STATUS
------------------------------ ------------------ -------
T1                             TABLE              VALID
T1_IDX1                        INDEX              VALID
T1_IDX2                        INDEX              VALID

ANONYMOUS                      CURSOR STATEMENT : 2

CURSOR STATEMENT : 2

no rows selected

USERNAME                       CURSOR(SELECTOBJECT_
------------------------------ --------------------
B                              CURSOR STATEMENT : 2

CURSOR STATEMENT : 2

OBJECT_NAME                    OBJECT_TYPE        STATUS
------------------------------ ------------------ -------
SYS_C003305                    INDEX              VALID
SYS_C003306                    INDEX              VALID
T1                             TABLE              VALID

It can return a result set that way for each row.

      Rating:
      4
  December 20, 2002
Reviewer:  Rochak
      Agrawal  from India
it is realy simple and good information..
Thanks

      Rating: 4
Select in a
      select  December 20, 2002
Reviewer:
      A reader
This will help in many other things as well.  Thank You

      Rating: 4
Different
      Oracle version.  December 20, 2002
Reviewer:
      Kashif  from Houston, TX
Hi Tom,

I'm currently working with 8.1.6.3, and I would look to use similar SQL, i.e.
select (select), inside plsql. Any workarounds? Also, can the cursor expression
be used in my version of Oracle? Thanks in advance.

Kashif

          Followup:

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3027089372477
cursor() is new with 8.0 and above.

      Rating:
      5
excellent  May 30, 2003
Reviewer:
      A reader

December 20, 2002 Tony -- Thanks for the question regarding "Sql or
      Pl/sql approach", version 8.1.6

      You Asked
Tom,
There are cases in which SQL and PL/SQL both can do the job, but with more
complexity in SQL then in PL/SQL. Examples could be DECODE/CASE statement in SQL
 VS nested IF statements in PL/SQL. Some people are so comfortable in SQL with
NO experience in PL/SQL start to think on SQL way, and other group start talking
of maintenance/enhancement through PL/SQL way with their level of comforts in
PL/SQL. What are your suggestions in terms of performance or otherwise?
Thanks
Tony

      and we said...
My mantra:

if you can do it in a single sql statement -- do it.

if not, try a little plsql

if that doesn't cut it, add a tiny bit of java to help it out

if that doesn't work (too slow, too whatever), a c based extproc works nicely.

Hence, if I can do it in SQL, I do it in SQL.  PLSQL is always the second
choice.
Review
      & Followup

      Rating:
      4
Thanks  September 23, 2002
Reviewer:
      Tony
Thanks for your views and quick response.

      Rating: 3
move embedded
      sqls to sp/functions (v8.1.5)  December 05, 2002

Reviewer:  Ram  from India
in a c/s app (powerbuilder/oracle) we have some processes which read a .txt file
(n number of records). each record could be an Add(A)/Delete(D) or Update (U).
each record potentially affects multiple tables ie A/D/U to x number of tables.
i find that all the sqls to do this has been embedded within PB. so there will
be x number of trips per record (totalling x & n trips) between the client and
server. and some processes run for about 6 hrs !

1)won't a proc/function that does this in the db be more efficient ? <your
opinion about this would be most helpful.>
2) its quite difficult to maintain code that is a total mix of pb code and sqls.
3) how to determine the approximate benefit in performance after moving the code
to plsql. now i know the current time taken is about 6 hours. but how to 'guess'
that after putting the code in modules of sp's/functions the process will take x
time ?

i am sure it will be a better approach but my being 'sure' doesnt help me. i
need to show & prove that it will run so much times faster. and for that i need
to develop it.
any idea how to pre-judge it ?

thank you.

          Followup:

1) i would think so.  I would have PB load the records into a scratch (global
temporary table) as fast as it can and then just run a stored procedure to do
the modifications IN BULK -- no row at a time processing, mass updates, inserts
and deletes.  three statement/table MAX here.

2) agreed. especially since that CS PB app is heading on its way out and now you
have the job of ripping it apart to discover what it does.  wouldn't it be nice
if it was just in the database?  waiting and ready to be reused?  programming
paradigms change like fashion -- every spring...  Data, well, it pretty much
stays the same.

3) you cannot.  It can be determined by a process known as benchmarking.

You can set up a simple simulation.  You don't have to fully develop it.  Just
show that moving the change records into the database, running 3 statements
instead of hundreds/thousands is much faster.  Done.

      Rating: 4
plsql - ok. but
      bulks ?  December 05, 2002
Reviewer:  Ram
       from India
ok. almost what i was thinking of. (more than half way through your book + this
site and i guess i have started thinking on the right lines :))

but i doubt whether i will be able to use the BULK feature. reason: then input
values (for each column) needs to be validated, sometimes cross checking against
the db. moreover, inserting/updating/deleting from n tables for 1 particular
record makes up a transaction unit.

"ripping apart the code to see what it does"...hmmm.
you might find this hard to believe ( or maybe you have seen much "better"
coding ) but in one place i actually saw code like this:
if variable > 0 then
    if variable = 0 then (=> whatever can cause this to happen is beyond my
understanding ! )
        .....
    end if ;
end if ;

          Followup:

cross checking against the DB -- sounds suspiciously like a foreign key to me?

validated -- check constraints are really handy for that

then you can use bulk features.  Also, no reason you cannot

o load plsql table
o validate data
o bulk insert

is there?

      Rating: 5
programming
      apporach  December 20, 2002
Reviewer:
      A reader
Hi tom,

  I wrote a stored procedure as follows...

create or replace sp
( p_message out varchar2,p_status out varchar2)
as

var1 varchar2(20);
var2 number;
var3 varchar2(30);
begin
      select id_val into var2 from t;

      begin
            select id_name int var1 from t2
            where id_val = var2;

      exception
            when no_data_found then
            -- dbms_output.put_line('we need to insert...');
             RAISE;
            when too_many_rows then
            -- dbms_output.put_line('we need to insert...');
                 null;
             when others then
             -- dbms_output.put_line('trap the error ...');
                 p_message := sqlerrm;
                 p_status := 'F';
              RAISE;
       end;
     /****************************/
      begin
         select username into var3 from user;
          raise too_many_rows;
      exception
         when no_data_found then
         --dbms_output.put_line('insert again..');
              RAISE;
         when too_many_rows
         -- dbms_output.put_line('row already exists');
              null;
         when others then
          -- dbms_output.put_line('trap the error ...');
              p_message := sqlerrm;
               p_status := 'F';
            RAISE;
      end;

exception
         when no_data_found then
              my_pkg.insert_t(var1,var2,p_status);
         when others then
       -- dbms_output.put_line('trap the error ...');
              p_status := 'F';
    insert into error_table(err.nextval,p_status,p_message);

end sp;

/*****************************************************/

  do you think the way this script is written is not good ?
is the script writer uses the exception as the " label " ??

 like "go to " statements in c or other languages ?

 if it is bad what would be the right approach ?

Thanks,

          Followup:

I think it is non-sensical -- i don't see the purpose.  I don't see the point of
the raise too_many_rows with a NULL exception block.

I think a when others that is not followed by a RAISE is almost always a bug (in
your case, it is a bug -- I hate "send back a status" coding -  just let the
exception propagate up to a level where someone can deal with it.)

If you want to find out whether you should insert or not, just:

  begin
      insert ....
  exception
      when dup_val_on_index then
          null;
           /* didn't need to insert */
  end;

      Rating: 5
about the null
      in the exception  December 20, 2002
Reviewer:
      A reader
Tom,

   I have audit trail implemented on the table t and users
so when atleast one row exists we need to update(but because of audit trail we
insert )
 so in the above procedure whereever I am raising too_many_rows
 it is update ( I just worte null; to illustrate)

 but my question is the use of the exeception in above
procedure is like label ? like go to stmt ?

          Followup:

i didn't like it -- sorry if that wasn't clear.

      Rating: 4
words of
      wisdom  February 12, 2003
Reviewer:  brian
      e.  from boston
this is exactly what our dba said.
it is, however, not what the instructor of the oracle class i'm taking said.  i
think this is because they said the problem was impossible to solve using plain
sql, and it was not, and that made him cranky.

December 20, 2002 Venkat -- Thanks for the question regarding
      "Sequences", version 8.0.6

      You Asked
Hi Tom,
   What you suggest is the best way to generate system generated numbers. As
Oracle Sequences are not gap free, can you please suggets an alternative.

Thanks..

Prasad

      and we said...
I would recommend ignoring the fact that they are not gap free.

Period. (i've said that lots here, many many discussions on it.  No one has ever
presented a good reason for gap free numbers beyond "it's always been that way")

Review &
      Followup

      Rating:
      3
Sequences  June 22, 2002
Reviewer:
      Venkatesa Prasad  from India
Tom,
   Suppose I cannot use sequences for my PK generator, then what is the best way
to generate.

   Well, I cannot use sequences for basically 3 reasons.

  1. My numbers have to be generated financial year-wise.
      - Then I have to create sequences 1 for each financial year. isn't it?

  2. I have a master table which tells what are the various voucher types and
when user selects a given voucher type in the transcation, system has to
generate a number for that type and that too it should start afresh for every
month. I cannot create sequences before itself, as I dont know how many voucher
types will get added each month.

   3. Third is related to gaps. Suppose in the front end (we user Developer 2000
- Forms 4.5), the user saves the data but leaves the session uncommitted (I mean
'POST' is issued). Then sequence is ost without the data getting committed. I
mean the saved data is rolled back. Then how to avoid gaps.

Can you clarify, please.

Venkatesa Prasad

          Followup:

1) why -- why not just know that "1-100323" are fy1.  100324-2431223 are fy2 and
so on.  Since you "reset" the numbers for each year, this obviously isn't a
primary key by itself -- the pk must be "FY + ID" (two columns).  This id is
just to keep it unique within the year.

2) why again?  why?

3) well, if you use a gap free approach -- you know what'll happen in this case?
 NO ONE ELSE IN YOUR ENTIRE SYSTEM WILL BE ABLE TO WORK, that is what.  Why?
cause this session with uncommitted data will have the id generating table
locked.

Sigh, I hesitate to show you this, but here goes.  Gap free, non-concurrent,
highly NON-scalable:

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table ids ( name varchar2(30), id
number );

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function get_nextval(
p_name in varchar2 ) return number
  2  as
  3     l_id number;
  4  begin
  5     update ids set id = id+1 where name = upper(p_name)
  6     returning id into l_id;
  7
  8     if ( sql%rowcount = 0 )
  9     then
 10            raise_application_error( -20001, 'No such id name ' || p_name );
 11     end if;
 12     return l_id;
 13  end;
 14  /

Function created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_output.put_line( get_nextval( 'foo'
) )
BEGIN dbms_output.put_line( get_nextval( 'foo' ) ); END;

*
ERROR at line 1:
ORA-20001: No such id name foo
ORA-06512: at "OPS$TKYTE.GET_NEXTVAL", line 10
ORA-06512: at line 1

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> insert into ids values ( 'FOO', 0 );

1 row created.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_output.put_line( get_nextval( 'foo'
) )
1

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec dbms_output.put_line( get_nextval( 'foo'
) )
2

PL/SQL procedure successfully completed.

Now, when you want to reset, just update ids set id = 0 where name = 'whatever'.

Yuck.

      Rating:
      5
sequnces  November 12, 2002
Reviewer:
      mo
Tom:

I have a production instance that started to skip sequnce numbers.  The first 15
records created were fine and created with number 1000 - 1015 but the nafter oct
1 I statrted noticing I got 1020, 1043 and 1060.

I am only doing a select on the sequence.next val in my save procedure.  First I
select ito into a variable and the ni use the variable in my insert statement.
The reason I use a variable instead of seq.nextval is that I insert into
multiple tables with same sequnce numbers.

What do you think may be happening that causing this?  This is a web application
using stored procedures.

          Followup:

There is no such thing as a sequence "skipping numbers".  In order for a
sequence to "skip numbers" it would have to first assure gap free number
sequences and they explicitly DO NOT.

Sounds like your shared pool is way too small and the sequence is getting aged
out (not used enough).

But -- I would just *NOT WORRY ABOUT IT AT ALL* (unless your shared pool is
really too small, fix that of course but don't worry about the sequence)

a sequence is used to generate a primary key/unique value. Nothing more, nothing
less.  any assumptions about "gap free" or "lost numbers" is in vain.

      Rating:
      5
sequence  November 12, 2002
Reviewer:
      mo
TOm:

<Sounds like your shared pool is way too small and the sequence is getting aged
out (not used enough).  >

Can you elaborate on how can a sequence get aged out if it is not used.  How
does it increment if you are nor running (select seq.nextval from dual).

2. The primary key is used by client as a reference number. It is really awkward
to have then refer to PO 1000 and the npo 1050. It makes you think that there
are 50 po's in the system when in fact there are only 2.

Thank you,

          Followup:

things in the shared pool (where sequences are cached) are aged out using an LRU
(least recently used). Hence, if you select s.nextval and then don't touch S for
a while and we need space in the shared pool -- out goes S and out goes any
cached values for S.

2) no it isn't.  why is it?  count(*) tells them how many po's are in the
system.  A po number doesn't, cannot.

Think about this.  Its the days before computers.  Everything is on paper.  You
have a stack of numbered PO's in a tablet.  The current number is 1 on the top.
You fill out PO #1 and put it in the OUT box.  You bump your coffee onto this
tablet spilling it.  Whoops.  The coffee soaks the next 20 sheets which you tear
off and through away.  You are on PO 21 -- are you confused?  Do you think there
are 20 po's in your out box?  No -- you see there is one PO there.

Getting back to today. If you've told your users to look at the PO number to
figure out how many there are -- they've been mislead.  There is a fact about
sequences -- an UNDENIABLE, UNESCAPABLE fact -- they are not gap free, will
never be gap free, they will have gaps!.  No matter WHAT you do, you cannot
look at the sequence number (your po number) and make any conclusions about the
count of PO's in your system -- none.

      Rating: 5
How does
      sequences avoid serialization?  December 18, 2002

Reviewer:  Sam  from San Mateo, CA
Tom,

As usual, your answers are most helpful.

I read this in the Oracle9i Application Developers Guide:
"Sequences eliminate serialization and improve the concurrency of your
application".  Is this really true?  Or do they just mean "sequences minimizes
serialization because it's native in Oracle and is really fast especially when
the numbers are cached"?  What gets locked when I do my_sequence_s.nextval?

Also, I saw this statement in my trace file:
update seq$ set increment$=:2,minvalue=:3,maxvalue=:4,cycle#=:5,order$=:6,
  cache=:7,highwater=:8,audit$=:9
where
 obj#=:1

Could you tell me what operation caused this SQL?

          Followup:

there is a lightweight latch used to serialize access for a split instant.  100%
de-serialization, no, 99.99% -- yes.

The alternative is to serialize entire TRANSACTIONS...

that is recursive SQL, everytime you advance the sequence in the SGA beyond the
cache size, we need to update a database table in a recursive (autonomous)
transaction

      Rating: 3
Strong
      reason  December 19, 2002
Reviewer:  Tony
       from India
  Tom, You say that gap free numbers are not required at all. I hope you will
agree with the following case:
  Generated numbers should have built-in intelligence so that end users will
easily identify the counts and other business related thing. For example,
prefixing purchase order number with vendor code will defintely help the end
users to know the vendor by merely seeing the PO number. Only numbers (PO
number, Request number, voucher number...etc) with lot of gaps will confuse the
users. There are valid reasons , as above, to have gap free numbers. We can't
ignore the importance of gap free numbers just becoz Oracle doesn't have the
feature.

          Followup:

I disagree totally, 100%

Your argument will be "well, they used to use paper and the paper was
sequentially numbered.  they relied on that number".

My counter argument will be "well, I just spilled my coffee on your tablet of
paper destroying the top 20 pieces of paper.  I put them in the garbage.  Did
the business shutdown?  No, of course not".

We void checks all of the time.  Same thing.

If your end users rely on "counts" embedded in a number, they are getting BAD
information.  Period.  There are cancelled orders.  There will be undone
transactions.  The counts will be *wrong*.

Knowing the vendor by looking at the "po" number -- trivial.  the PO number is
obviously a compound field on a printed report -- eg:

select vendor_id || '-' || primary_key PO_NUMBER, ....
  from table

If gaps confuse users -- it is only because you told them there was some implied
meaning to it.  There is no other reason -- cause the old paper based system
sure couldn't have taught them this.

      Rating:
      5
  December 19, 2002
Reviewer:  Jeff
      Larimore  from Miami, Florida
Tom,

Is Oracle considering creating a column type of IDENTITY whereby Oracle will
automatically assign a unique value to a column?

This would create a sequence tied to the column automatically and would not
require the user to define a sequence for the column, and then create an insert
trigger to call it, which you I know you recommend against.

I've seen this in other DBMS servers and have found it extremely user-friendly,
self-documenting and convienient.

Thank you,
Jeff

          Followup:

I will submit this to the people that can make it happen...

      Rating: 5
That nasty word
      "embedded"  December 19, 2002
Reviewer:  Connor
      McDonald  from UK
The problem with "informational" columns, for example, the PO number telling you
how many there are, is that it breaks a lot of the rules for relational
databases.  Scalar columns represent an attribute (singular not plural).

There is nothing worse than being forced to work on a system where column P
contains "02X3751" because it was

- create in the year '02'
- is of type 'X'
- is the 3751'st recording of such
Ugh!!  It doesn't take long before your start seeing hideous queries like:

where substr(P,4,4) = '3751'

and voila! Welcome to function-based-index heaven...

What's wrong with a year, type, and occurrence column ?

          Followup:

exactly -- and then a view to synthesize these "human friendly" things

select vendor_id || '-' || primary_key PO_NUMBER, ....
  from table

people do seem to have an irrational fear of views for some reason....

store the data in its component pieces, glue them together however you see fit
on OUTPUT.

      Rating:
      4
  December 19, 2002
Reviewer:  Tianhua
      Wu  from Atlanta, GA
I agree in most case the gap should not be a problem at all.  However, in some
country (e.g. Italy), you can not have the gap by law (if I understood
correctly).

          Followup:

what did they do when the coffee got spilt?

what do they do when a customer cancels an order?

what would be the purpose of such a law?  I mean -- what is to stop me from just
creating a dummy order for $0 or just to go back and put the orders in out of
sequence.

I would just create a view:

select a.*, rownum LAW_ABIDING_STUFF from t;

I guess.... no gaps there and just as meaningful!

      Rating: 3
Sequence
      generation  December 19, 2002
Reviewer:  pk
       from alaska
For sequence generation why don't you do this :-
PK>create table temp1(id number(12));

Table created.

Elapsed: 00:00:00.51
PK>select * from temp1;

no rows selected

Elapsed: 00:00:00.10
PK>select nvl(max(id),0) + 1 seq_num from temp1;

   SEQ_NUM
----------
         1

Elapsed: 00:00:00.31
PK>

          Followup:

can you spell "serialization"

I can, you just did in SQL ;)

read:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3379873654938
I *hate* that method more than most anything you can do (well, not as much as
not using bind variables -- but a very very close second)

      Rating: 5
Try this
      then...  December 19, 2002
Reviewer:  Connor
      McDonald  from UK
session 1:
  select max(...)+1

session 2:
  select max(...)+1

Ooops....data corruption.

      Rating:
      3
  December 20, 2002
Reviewer:  Dave
       from Ohio
Tianhua's point is an important one, and the legal requirement to avoid gaps in
sequences is a real one in many countries.

Commonly it applies to tax-related "documents" such as invoices -- if a tax
authority finds a missing value in a sequence of invoices it raises a red flag
to them that there is something fishy going on (although I have to add that even
if there were no apparant rationale for the law, it has to be complied with if
only because a company will be prosecuted and fined for not being in compliance.
They will then sue their software vendor.)  For all the scenarios of spilt
coffee, cancelled orders, dummy orders etc. there is a legally sanctioned
resolution.

The view-method would be legally unacceptable as the invoice number must not be
subject to change.

However that doesn't mean that you have to cripple the performance of your
application by serializing the generation of invoices, for example. Consider
whether you could PK the invoice with a regular (performant) methodology, then
update the records later on as a batch process, such as ...

update invoices set invoice_number=inv_seq.next_val;

... or ...

select max(invoice_number) from invoices into l_max_inv_number;
update invoices set invoice_number=l_max_inv_number+rownum
where invoice_number is null;

You can put a unique constraint on the nullable invoice_number field, and maybe
a function-based index also to give fast identification of the null invoice
number rows.

Tom, I'm not familiar with Oracle Applications, but perhaps you could get
someone from the Applications team to address how they deal with requirements
like this -- I bet that they are familiar with the scenario, and have a way of
coping with it.

          Followup:

But my point seems to be getting totally missed.

anyway.

you next_val -- not gap free. 100% not gap fere.

Your select max(), not very concurrent (eg: leads to duplicates out the
wahzoo)...

They do it like you would with your checkbook.  It is called a "void".  fill in
dummy records.  Assign the numbers when you close the books, not when filling
them.

Tell me -- what happened when someone spilt coffee on the invoice forms???

      Rating:
      4
  December 20, 2002
Reviewer:  Tianhua
      Wu  from Atlanta, GA
select a.*, rownum LAW_ABIDING_STUFF from t;
...

Unfortunately, the rownum will be different for the same record in different
queries.

The law is law, there are not too much we can do about it.  And it indeed makes
life much difficult.  On the other hand, it may generate new positions.  :)

      Rating:
      5
Hmmmm  December 20, 2002
Reviewer:
      George Johnson  from UK
I totally accept the gap-free argument, I can't see what the problem is, if
numbers are skipped due to cancellations, employee incompetance (split coffee),
then surely simply keeping an official record that missed numbers are "voided"
should satisfy TAX officials, but I guess my real question would be, what on
earth is a "whazoo"? (I'm from the UK you see.)

Tks in adv

          Followup:

I guess I spelled it wrong ;)

Main Entry: wa·zoo
Pronunciation: (")wä-'zü
Function: noun
Etymology: origin unknown
Date: 1983
- up the wazoo also out the wazoo : in excess <we've got lawyers up the wazoo --
Steven Bochco>

http://www.m-w.com/cgi-bin/dictionary?va=wazoo

      Rating:
      3
  December 20, 2002
Reviewer:  Dave
       from Ohio
I understand your point, which is that gap-free sequences are logically
meaningless and highly non-concurrent, no? However the point that I was helping
to make is even more simple --  it is a legal requirement in many cases to have
gap-free sequences. Full-stop, that's it. We may not like it, it may not be
logical, it may not be performant, but it is the law. What software house or
development team can afford to have a policy that legal requirements can be
ignored?

It was my intention that the methods I suggested should allow concurrency in
generating the new records by delaying the attempt to generate gap-free
sequences until after the insert. The methods were to be implemented as a
post-creation batch job (say DBMS_JOB every few minutes) which would not have to
compete with other sessions for the invoice_number sequence, or would not have
to use a sequence at all. In either case it would be a single update process.

When someone spilt coffee on the form, you kept the damaged form to show the
authorities. If an invoice is generated incorrectly it is kept, marked as void,
and regenerated under a new number.

          Followup:

The "gap free" numbers can (and should be) assigned after the fact, during a
bulk process, the closing of the books.  I did not realize that is what you were
also proposing.  I see the next_val in there -- that'll never work.  sequences
by definition promise NOT to be gap free.

Ok, someone drops the cigarrette on the form...
The form was left in the rain, totally obliterating it...
The forms blew away in a wind storm...
<any one of 1,000 other scenarios>

      Rating: 3
Legal
      reasons  December 20, 2002
Reviewer:  Tony

Tom, in our country also, gap-free invoice, voucher numbers are mandatory in
legal terms. Even if we happen to generate wrong voucher, it can be cancelled.
But the cancelled vouchers should be in the system during auditing. If the
voucher is hand written one , we can atleast try to convince the auditors to
some extent saying that its lost. As we use computers, we can't convince the
auditors. we can't say that performance/concurrency is the reason for not having
gap-free numbers.

          Followup:

assign the numbers in a batch process AFTER the fact.

does it matter if the number is assigned right now, this instant.  Or tonight at
midnight.

(funny, the fact that computer wise we can assign these numbers *after the fact*
makes them *ever less meaningless*, they are useless for any purpose -- except
to consume disk space of course)

      Rating: 5
Federal
      Regulations and Sequences  December 20, 2002

Reviewer:  Pedro Diaz  from Dominican Republic

Well, here I go...
In my country You have to match the pre-printed document number with the
computer generated number, it is restricted by laws to have a gap in any case.
To make things more dificult EVERY branch office need its own sequence and it
most be GAP FREE.

We tray many ways but no one gets it done.  We have:

A. SELECT MAX( NVL( invoice_num, 0) ) +1
    FROM invoices
      WHERE branch_office = :branch_num

B. SELECT Last_invoice + 1
    FROM branch_sequences
    Where branch_office = :branch_num

C. The Trigger / Package Approach:
create or replace package body invoice_sequence_pkg is

   type sequence_type is table of invoices.invoice_num%type
                          index by binary_integer;

   type branch_office_type is table of invoices.branch_num%type
                          index by binary_integer;

   sequence_table  sequence_type;

   branch_table    branch_office_type;

   CURSOR last_sequence is
      SELECT A.branch_num, NVL( max( A.invoice_num ), 0 ) last_invoice
        FROM invoices A
        WHERE invoice_date >='01-Jul-01'
      GROUP BY a.branch_num;

   procedure Init_tables is
   begin

         actual_sequence    := 0;

      sequence_table.delete;

         branch_table.delete;

   end;

   procedure Sequence_Branch_Office is
   begin
     FOR branch_sequence IN last_sequence LOOP
        sequence_table( branch_sequence.branch_num ) :=
branch_sequence.last_invoice;
     END LOOP;
   end Sequence_Branch_Office;

   function netx_sequence( branch_num_in in invoices.branch_num%type) return
invoices.invoice_num%type is
   begin
      return ( NVL ( sequence_table( cbranch_num_in ), 0 ) + 1 );
   end;

end invoice_sequence_pkg;

Calling it from Statement lavel and Row Level triggers in invoice table.

Its one of this correct, helpfull, or does exists any better way to do it.

          Followup:

short of moving?

you should do it like this:

  UPDATE branch_sequences
     set last_invoice = last_invoice+1
   where branch_offset = :branch_num
  RETURNING last_invoice into :invoice_number;

  if (sql%rowcount = 0)
  then
      do the insert of 1
  end if;

you want to use the UPDATE, not a select, in order to impose serialization and
avoid artificial "dups" that will cause transactions to fail.

      Rating: 5
Sequences are
      just that Sequential  December 20, 2002
Reviewer:
      Jeff Larimore  from Sunny Miami Florida
Great topic!

It seems clear that there will be a requirement for a geometric sequence
generator (no gaps) in some app somewhere.

Now if gaps or serialization are not important, then simply a unique random
number would do.

1.  Sequence - generates a series of numbers in order. -Oracle has this, its
called a Sequence.

2.  Geometric sequence - generates a series of numbers whereby each term differs
from previous term by same amount, ie 1,2,3 or 10,20,30., no gaps  - We need
this from Oracle.

Tom,
I tried once to put something like seq.nextval in the default value of a column,
hoping that this would fool Oracle into automatically grabbing the next value of
the sequence if none was specified for the column on insert.

I believe it didn't like that, but felt this was a smooth way of creating the
equivalent of an IDENTITY column.

Have you got a way to make that work?

Thanks,
Jeff

          Followup:

you use a trigger + sequence to accomplish that.

"geometric sequence" as you call it -- you already got it:

create table gseqs ( name varchar2(30), val int );

create or replace function gseq_nextval( p_name in varchar2 ) return number
as
   l_val number;
begin
   update gseqs
      set val = val+1
    where name = p_name
   returning val into l_val;

   if ( sql%rowcount = 0 )
   then
        raise_application_error( -20001, 'No such sequence exists' );
   end if;
   return l_val;
end;

December 20, 2002 Vinod -- Thanks for the question regarding
      "Storing Images in database as BLOB Vs storing images on file system on
      Application Server", version 8.1.6

      You Asked
Hi Tom
We are developing a web based application . We need to  store images
which will be accessed by this application . We are wondering what is
the performance difference (and also pros and cons )between storing images in
database as blobs vs storing them on web server (appliation server) as files.
When this systen will go in production we will have 30,000 images each one of
50K size.

Thanks.
Vinod Ladda.

      and we said...
Pro's

o single source of data.  You back up your database,you backed up your webserver
in effect.

o point in time recovery of a single source -- no need to worry about syncing up
a file system and a database

o "flat" but scalable storage -- having 30k, 300k, 300million, 3billion records
in a table is just fine.  30k of images in a single directory is NOT advisiable
at all (no more then a couple of hundred would be recommended).  you would have
to figure out HOW to hierarchically distribute the images into many directories.

o browser will cache images from the database just like an image from the file
system

Con's

o a database connection to get an image, if you use connection pooling, this is
ok if you are using a connetion pool.

o it will take longer to retrieve the image from the database then from the
filesystem.

What I do --

I use a mixture.  For very heavily accessed images (those on a home page) i keep
the "real" copy in the database but also store it on the file system.  I
reference the file system version in the pages, knowing that If I lose it, need
to restore from a backup, whatever -- I have it in the database.  For less
frequently accessed images (or anything BIG, pdfs, docs, etc) -- i keep them in
the database only.
Review & Followup

      Rating: 3
Storing images
      in database  May 04, 2001
Reviewer:  Vinod
      Ladda  from DALLAS,TX, USA
Thanks Tom. It is useful information.

      Rating: 5
cache vs
      nocache for lob storage  December 20, 2002

Reviewer:  Sam  from San Mateo, CA
Tom,

We have a shopping catalog application and we store the item images in the
database as blobs with the NOCACHE  option specified.  Currently we only display
the item image when a user drills down to see the details of an item.  The
search results page does not show any item images.  Since many users never
drills down to the item details page, the images are not fetched that
frequently.

We are developing a new feature where we will show thumbnail images on the
search results page.  This will result in the images being fetched much more
frequently.

We are thinking about switching to CACHE READ for the image lob column (our lobs
are updated very infrequently), but we are a little concerned over what that
would do to the data buffer hit ratios and the impact on the rest of the
application.  Assuming 20-30% of the catalog is accessed much more frequently
than the rest, do you think it's a good idea to use CACHE READ?  Please also let
us know if there are any differences between 8.1.7 and 9i or 9iR2 related to lob
retrieval.

Longer term we will start caching some image files on the file system, but for
now we need to figure out the most appropriate cache setting.

          Followup:

well, there is the WEB CACHE as well -- which could help dramatically (see the
9iAS docs for more details)....

If you access the images frequently -- well, just like rows of data -- would you
not want it cached in the buffer cache?  Why would a thumbnail (pretty small,
less then 16k typically) be any different then a block or two of frequently
accessed data?

December 20, 2002 Brian -- Thanks for the question regarding
      "shared_pool_size and other memory questions", version 8.1.6

      You Asked
----
System: 8.1.6, 1GbRAM, 3 CPUs
----

When I

SELECT * FROM V$PARAMETER

I see (among others)

...
shared_pool_size 12000000
shared_pool_reserved_size 600000
...

When I

select nvl(pool,'buffer cache'),count(*),sum(bytes)
  from v$sgastat
  group by pool
 order by pool

I get

NVL(POOL,'BUFFERCACHE')    COUNT(*)    SUM(BYTES)
java pool    1    20000768
shared pool    32    13997564
buffer cache    3    20616176

1. Does the fact that 13997564 is greater than 12000000 :

A. Mean that the shared_pool_size parameter is too small?
B. Mean that unnecessary swapping is occuring?

2. Should the reserved size be greater than 5%?

3. The " select sum(bytes) from v$sgastat" is 54614508:

A. Is 54Mb rather small for a 1GbRAM machine?
B. Does this mean that 1Gb minus 54Mb is "unused"?

      and we said...
1) the correct answer is C -- none of the above.  The shared pool is made up of
MANY components.  When you specify the shared_pool_size in the SGA, you are only
setting part of it (the library cache mostly).  Other parameters affect the size
of the shared pool as well -- we keep many data structures in there.

2) no, in fact, in 8.0 and up with the addition of the large pool, the reserved
size can be ZERO if you like.  the shared pool reserved size set aside part of
the sga for large allocations (those done by MTS for example).  In 8.0 and up,
they should take place in the large pool and the reserved size isn't as
relevant.  In fact you might find that in a well tuned system you are setting
aside 5% of your shared pool for memory allocations that will NEVER HAPPEN.

3) I would say a 54m SGA is on the small side, yes.
Review &
      Followup

      Rating: 4
Shouldn't sum
      of bytes in v$sga and v$sgastat match though ?  October 16,
      2001
Reviewer:  Sameer  from NJ
I can understand why stat in v$sgastat is not exactly equal to shared_pool_size
in init.ora but shouldn't sum(value) in v$sga match with sum(bytes) in v$sgastat
?

I know my SGA is 1.2G (confirmed by v$sga and ipcs). Somehow the value of 5.5G
reported by v$sgastat (due to abnormal 4.2G for shared pool miscellaneous) is
incorrect. Not sure why though.

This is what I see:

SQL> select * from v$sga;

NAME                      VALUE
-------------------- ----------
Fixed Size                69616
Variable Size         367816704
Database Buffers      838860800
Redo Buffers              90112

SQL> select sum(value) from v$sga;

SUM(VALUE)
----------
1206837232

  1  select nvl(pool,'<unnamed pool>') pool,  name, bytes from v$sgastat
  2* order by 1, 3 desc
SQL> /

POOL           NAME                            BYTES
-------------- -------------------------- ----------
<unnamed pool> db_block_buffers            838860800
<unnamed pool> fixed_sga                       69616
<unnamed pool> log_buffer                      66560
java pool      free memory                  17772544
java pool      memory in use                 2228224
shared pool    miscellaneous              4292546012
shared pool    sql area                    165410724
shared pool    library cache                92752548
shared pool    free memory                  44493116
shared pool    db_block_buffers             27852800
shared pool    dictionary cache              5134736

POOL           NAME                            BYTES
-------------- -------------------------- ----------
shared pool    ktprhtnew3                    4313176
shared pool    db_block_hash_buckets         3539016
shared pool    KQLS heap                     3047488
shared pool    PL/SQL MPCODE                 1709988
shared pool    PL/SQL DIANA                   977796
shared pool    event statistics per sess      390080
shared pool    sessions                       242880
shared pool    KGFF heap                       79244
shared pool    PX msg pool                     64496
shared pool    table columns                   62612
shared pool    joxlod: in ehe                  40636

POOL           NAME                            BYTES
-------------- -------------------------- ----------
shared pool    type object de                  34160
shared pool    table definiti                  17852
shared pool    trigger defini                   9180
shared pool    PX subheap                       7024
shared pool    KGK heap                         6612
shared pool    pl/sql source                    4800
shared pool    view columns d                   2160
shared pool    PLS non-lib hp                   2096
shared pool    trigger inform                   1780
shared pool    temporary tabl                   1692
shared pool    fixed allocation callback         960

POOL           NAME                            BYTES
-------------- -------------------------- ----------
shared pool    errors                            792
shared pool    joxs heap init                    432

35 rows selected.

SQL> select sum(bytes) from v$sgastat;

SUM(BYTES)
----------
5501744632

          Followup:

That number is suspiciously close to 2^32 = 4294967296.

Looks like an issue, yes.  Please contact support (i don't see anything filed
against it specifically in the probably database)

      Rating: 5
shared
      pool  December 20, 2002
Reviewer:  mo

Tom:

1. What is the difference between sharred pool and block buffer cache? in your
book you say the first one is used for cursors and stored procedures. DOes it
mean for QUERY only while the block buffer cache is for insert and update?

2. How do you size these things? How do you determine the right size? In your
book you say you do not want it too big or too small? can you figure out from
queries how much cache memory they need?

3.  X$bh does not exist in oracle?

SQL> select tch from x$bh;
select tch from x$bh
                *
ERROR at line 1:
ORA-00942: table or view does not exist

          Followup:

1) shared pool - code
   buffer cache - data

2) 9i will automate this for you to a large degree with the buffer cache
advisories.  Before then, it was tuning with statspack.

3) v$bh is the one you want to use.  x$bh -- is sys.x$bh which you may well not
have access to.

December 20, 2002 Thiruvaiyaru -- Thanks for the question regarding
      "Use of Bind Variables on 8.1.6", version 8.1.6

      You Asked
Tom,

What is the use of Bind Variables on 8.1.6?
How can the usage of this improve the performance
of the Database and also how do I implement
bind variables on the database ie. Should I need to add
a parameter on the init.ora to implement this ?

Please Explain.

Thanks in Advance
Sai

      and we said...
Bind variables are in all releases of the database.  they are a
MAJOR way to increase performance, reduce memory needs and free
up tons of CPU time.

Consider a system that queries employees by empno.  You could
generate 10's of thousands of queries like:

select * from emp where empno = 1;
select * from emp where empno = 2;
...
select * from emp where empno = N;

Each query is unique -- never before seen -- because the values
are hard coded into it.  Each needs to be parsed, security
checked, optimized and so on.  In many cases -- the time to
parse/validate/optimizer the query is an order of MAGNITUDE
greater then the time to actually execute the query.  You can
find your self spending 90% of your runtime parsing queries --
not executing them!

Using bind variables - a good system would code the query as:

select * from emp where empno = :x;

Then, everyone would use the same query plan -- saving the
parsing, the optimizing and so on (its done once and saved in
the shared pool).  Now, we all execute the query but use a bind
variable to set the empno before we open the cursor.

Now, in 8.1.6, they added an "auto binder" feature whereby the
kernel will automagically rewrite the queries with literals in
them to use bind variables.  That is, the system would see the
query select * from emp where empno = 1 and rewrite that with a
bind variable in place of x and bind for you.

I am of mixed feelings on this feature.  While it is better then
NOT using bind variables -- it is a far cry from using bind
variables.  It is not as bad as not using them, not as good as
using them.  The system still must do a ton of work to rewrite
the query -- work you could *easily* do yourself (and your code
will execute faster if you do).

To test this out, you can set the session or system parameter

cursor_sharing = force

That can be set in the init.ora file or set via an alter session
statement.
Review & Followup

      Rating: 5
Good ideas for
      scalable databases  April 05, 2001
Reviewer:
      Kristy  from Centreville, VA USA

      Rating: 5
Hard coded
      values and bind variables   November 01, 2001

Reviewer:  Nag
Can you give me a small example from which the perils of using hard coded values
can be demonstrated.
and similarly if you can show how the with the same example how the performance
improves using bind variables, that would be great.

          Followup:

Nag -- you have my book don't you (if you don't, you of all people should given
the number of questions you ask).  It's the common, recurring theme throughtout
all 1,297 pages.

Read page 27 of my book for the first example.

      Rating: 3
Your
      Book  November 02, 2001
Reviewer:  Ak
       from India
Hi tom,
   I am Not getting your book in india,and i remember
Telling You that you are talking with WROX in the regard.
Can you update me on this ...??

Thanks,

Regards
   Ak

          Followup:

The book is in india -- people have bought it.  You should be able to go into a
book store with the ISBN and they can order it (its shipping and available in
india for sure)

      Rating: 5
binds vs
      no-binds  November 02, 2001
Reviewer:
      Connor  from Deepest Darkest Scotland
For the previous poster:

Try this:

No-bind
-------
set timing on
declare
  x number;
begin
  for i in 1 .. 50000 loop
   execute immediate
     'select 1 from dual where dummy = '||i
     into x;
  end loop;
end;

with-bind
-------
set timing on
declare
  x number;
begin
  for i in 1 .. 50000 loop
    select 1 into x from dual where dummy = i;
  end loop;
end;

and see which is quicker.  This is a SINGLE-USER test.  Now try firing up three
SQLPlus sessions and run three concurrent tests... Things get much much worse...

      Rating: 5
binds vs
      no-binds  November 02, 2001
Reviewer:
      Connor  from Deepest Darkest Scotland
Oops (a fix added) For the previous poster:

Try this:

No-bind
-------
set timing on
declare
  x number;
begin
  for i in 1 .. 50000 loop
   execute immediate
     'select count(*) from dual where dummy = '||i
     into x;
  end loop;
end;

with-bind
-------
set timing on
declare
  x number;
begin
  for i in 1 .. 50000 loop
    select count(*) into x from dual where dummy = i;
  end loop;
end;

      Rating: 5
Bind
      variables  November 02, 2001
Reviewer:  an
       from DE
it's what i am looking for.  thank you!

      Rating: 3
ugly hack that
      will die eventually  November 02, 2001
Reviewer:
      Mikito Harakiri
They are just extra level of comlexity for application writer. Consider this:

SQL>explain plan for select 1 from dual where TRUNC(:1)=Trunc(SYSDATE);
explain plan for select 1 from dual where TRUNC(:1)=Trunc(SYSDATE)
                                                   *
ERROR at line 1:
ORA-00932: inconsistent datatypes

However:

SQLREPO@DB1>explain plan for select 1 from dual where 'A'=Trunc(SYSDATE);

Explained.

Literal substitution should take care of all the performance problems bind
variables were introduced for.

          Followup:

Won't die, won't go away.

Bind variables are the way to go.

You want to do the above?  This is easy:

ops$tkyte@ORA717DEV.US.ORACLE.COM> variable dt varchar2(25)
ops$tkyte@ORA717DEV.US.ORACLE.COM> set autotrace traceonly
ops$tkyte@ORA717DEV.US.ORACLE.COM> select 1 from dual where trunc(to_date(:n)) =
trunc(sysdate);

no rows selected

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   FILTER
   2    1     TABLE ACCESS (FULL) OF 'DUAL'

(tkprof would be even better).  Explain plan is sort of weak and I personally
*never ever* use it.  Not for anything.

Anyway, the reason it won't "go away" is that you want:

select * from t where x = :x;

to be optimized "generically" -- assume some normal value for :x and optimze.
the queries:

select * from t where x = 1;
select * from t where x = 2;

on the other hand, will each be optimized differently if you have stats that
include column stats.  One might use an index, the other not.  They are very
different queries.

You see, bind variables allow us to parse, optimize, etc etc etc -- ONCE.
Literals -- they are the way to make us do it specifically for that query.  I
guess once CPU's reach the speed of light maybe this won't be as necessary....
But we are very very very far from that now.

Bind variables -- not hard and you need to use them.  Don't use them and you
will fail.

      Rating: 3
cursor
      sharing  November 02, 2001
Reviewer:  Mikito
      Harakiri
By literal substitution I meant substituting literals with binds automatically
by server available in version 9. Wouldn't this feature render explicit binds
obsolete?

          Followup:

That is a feature of Oracle8i release 2 (for a couple of years now) version 816.

It is called "cursor_sharing=force".

It exists NOW however -- it is a crutch you can use until you do it properly.
It gets you partially there, it does NOT get you all you can get.

For example, people who do NOT use bind variables do code like this:

   Statement stmt = conn.createStatement();
   for( int i = 0; i < 100; i++ )
   {
       stmt.execute( "insert into t values ( " + i + ")" );
   }

Ok, so each time through the loop that does a HARD parse of the statement.  The
cursor sharing = force will turn that into a sharable cursor so that the first
time through it does a hard parse and then 99 soft parses.

The ONLY proper way to write that is:

    PreparedStatement ps =
      conn.prepareStatement ("insert into t values ( ? )" );

    for( int i = 0; i < 100; i++ )
    {
        ps.setInt( 1, s );
        ps.execute();
    }

Now that does 1 parse and 100 executes. The soft parse, while much better then a
hard parse, is still incredibly inefficient as compared to "not parsing at all".
 An autobinding feature does not, cannot, will not remove the soft parse you
must do. The soft parse -- latches the library cache just like the hard parse --
limiting scalability, decreasing response time and wasting resources.

There are other nasty side effects to "auto binding" (i have a complete
discussion on this in chapter 10 in my book).  I personally hesitate to tell
people about this particular "feature" as it is NOT the correct solution.  It
gets them over the hump and then they ignore the problem and go back to their
old ways.  There is only one way to do this -- the right way.

      Rating: 4
Should be
      DUMMY=VARCHAR2  December 28, 2001
Reviewer:
      A reader
HI, Cornor,

I think maybe it should be dummy=TO_CHAR(i), otherwise
it can not work. Mine DB is 8.1.6

Take care in the Deepest Darkest Scotland

      Rating: 5
bind
      variables  December 20, 2002
Reviewer:  mo

Tom:

1.  DO the use of bind variables only works in queries.  Does it apply also for
insert or update statements?

2.  If I have a cursor like this in a web app Am I usin bind vriable or not"

FOr x in (select * from table where col=i_lname)
LOOP
..
END LOOP;

or would you re-write it like

FOR x in (select * from table where col=:lname)
LOOP
..
END LOOp;

Thank you,

          Followup:

1) DML, all DML.  DML = select, insert, update, delete, merge.

2) plsql autobinds.  if I_LNAME is a variable, it is a bind variable in plsql.
In Pro*C, it would be where col = :lname, jdbc/odbc "where col = ?", in plsql,
just where col = plsql_variable....

      Rating:
      5
bind  December 21, 2002
Reviewer:
      mo
TOm:

as a followup actually i_lname is the input parameter to the stored procedure.

create or replace proc2
(  i_lname   IN VARCHAR2 DEFAULT NULL )
as

FOR x in (select * from table where col1=i_lname)
LOOP
..
END LOOP;

Since this is a parameter, i gues it will be treated as a bind variable.
Correct?

2.  Can you see the SQL statements (hard/soft parsed) in shared pool for
specific session or for all system users?

Thank you,

December 21, 2002 MUHAMMAD -- Thanks for the question regarding
      "Analyse schemas versions", version 8.1.6

      You Asked
Hello Tom, here are my questions.

Q1:In your practical approach for a heavy OLTP system with a batch that runs in
the night and a full db backup using rman that runs after the batch but before
the day begins for online. What do you recommend in terms of analyse database.
There are several analyse
versions available. In this particular case what analyse you would recommend and
the syntax?
At this point, what I am doing is:
1. At the end of day activity i,e around 7:00 pm, I analyze full database with
execute dbms.....utility,,,_anallyse_database('ESTIMATE');
Then after the batch and rman backup is over I do the same think again at around
next morning 7:00 am.
These analyse take time. I like to know is there a better way to speed up or am
I analysing too much?
There are versions like sample 50 or sample 5% etc, Should I use them and what
benifit i would get. Also can you explain different analyse commands and their
effects
For example i am confused, if i say analyze table t1 estimate statistics, does
it mean all its indexes are also auto analysed or do i have to say with indexed
columsn? Also whan you analyse a shceman does all indexes get analysed too?
If i create or rebuild an index, I found that even if i dont analyse that just
crated index, optimiser still uses it, so what does it mean,
I thought analyse is for cost based optimiser and you must anbalyse objects
before oracle optimiser uses them.
The rule is that if you have a bulk of update you should analyse.
But sometimes you never are sure when to analuse and what kind of variations yo
use. There are std palkacges in 8i that you can customise analyse too.
So in my case instead of findign out what objects changed i just analysed the
whole db, but it takes time. what is tthe best practise and does it affect
online transactions to an extent that analysing objects can cause trans to
fail????

Moreover what is it with analysing sys schema. Does analyse database analysis it
too? Also if instead of full db analyse what if i just analyse at schema level
only the application schemas. So if i adopt this policy when do i anayse the sys
and others?

I have experienced s targne effect too. There was a database which was slow and
found out that oracle was taking time in recursove calls to its sys objects . I
just analyse sys shcema with delete options and wow the perf increases tripple.
i DONT KNOW WHY....

Lot of talks, just simple advise with yoru exp the std syntax of analyse that
you sue for a heavy oltp at ehat times and what versions

Q2: I have found lot of executions on select count(*) from customer where code =
:b1 lying in my sga.  Confirmed that there are plsql programs where programmer
uses this statement a  lot just to see if a customer exists and then follow down
the procedure further ,.
The code column is primay key index so no doubt the statement uses index scan.
Is there any better way than above that the progeammer can substitute ?

Q3: Likewise i found from v$sqlarea that there are lots of i/o on the following
type of objects:
begin cis_caller ( :input_fields , :output_fields ) ; END ;
begin txd_oltp . txd_process ( :insrcid , :indestid , :inmti , :inflds , :indata
, :outmti , :outflds , :outdata ) ; END ;

Now these are procedures that prpgrammr wrote and every trsn that comes from
tuxedo calls these procedure in databasw and then these procedures decides what
to do next.
BuT how come the v$sqlarea shows high i/o on procedures???????
Ok maybe these are called sveral times, does that means my lib cache is not
casing them or small enough but i checked my sharedpool is large enough...
Should I pin these, would doing so reduce the above i/o?????

Last one: I have read the benifits of multiple buffer pools.
So far i amusign only the default pool for my heavy oltp database.
my sga is 1.8 gb abd by db buffers cache is 900 MB.
My hit ratio is 89% and is decreasing but very slowly.
Now its very diffuclt to implement the multi buffer concept becuase it needs
accuracy of knowing and calculting size of your objects.
But atlease I know this.
I know for certain the objects that are large tables like log tables
and it dept users query (not the front end users but the developers)
this table in case there is a problem in online trans,
wgen they query they write heavy queries and we cant index all columsn of these
tables. these tables store a lot of data line months.
So these queries are producing full table scans,
If i create multiple pools how can i setup the size of diff pools
becuase i only have this knowlege i do not know my small accessed tables. I mean
i like to make sure that these long querues are in recycle and rest of the
things stay same as they are now and at same time i dnt end up with warse db
buffers by alocating different size
pools?

Thanks tom for the patience to read and reply.
cheers

      and we said...
1) don't use dbms_utility.

Use dbms_stats.
Do it by schema and only do the schemas you want.
Use monitoring so as to only get the schemas that need it.

see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:5792247321358

do NOT analyze the sys schema in 9i and before.  do not use dbms_utility, it
quite "over does it"

read

http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76992/stats.htm#13547
to get an understanding of the how/what/why/when and wheres of gathering
statistics.

2) if the programming needs to know the customer exists, that is one way.  I
cannot answer fully without knowing more.  For example if the reason they want
to know is so they can either UPDATE existing or INSERT new, I would just:

   update customer set ....
   if ( sql%rowcount = 0 )
   then
        insert new....
   end if;

instead of:

   select count(*) into l_cnt where code = :n
   if ( l_cnt = 0 )
   then
       insert
   else
       update
   end if

but it depends on what they are doing.

q3) you are seeing the sum of all logical and physical io's performed by all of
the SQL these procedures execute (and the sql's of any procedures they in turn
call as well).  It is a total for all work done by that top level call.

PINNING will not have any effect on IO whatsoever, NO, zippo -- zero.

q4) I hate cache hit ratios.  See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6749454952894
for why -- I have an AWESOME cache hit ratio there -- but too bad it is caused
by some really truly bad SQL!

Your "bad" cache hit may will be an indicator your system is truly tuned!  A
better cache hit might indicate you have tuning to do.

So, do you have an identifiable, measurable, real performance issue?  If so,
look at your top wait events -- not these ratios -- to see what you need to look
at.

Review &
      Followup

      Rating:
      4
analyse....  December 21, 2002
Reviewer:
      shd
Just one clarification,
why is it not important in 8i or 9i as you mentioed to
not to analyse full database or not to use dbms_utility
and also why you mentioned not to analyse sys schema??
(by the way i will turn to dbms_stats tomorrow)
So in my case since am doig all of these, should
i delte stats for sys schema?? as i wil be only doign apps stats calucaltio but
what would happen to alreay danalysed sys schema??

Also in using dbms stats should  i salyse schema or chema with indexes
separatly? and what %age should i analyse?
5 or 10 or default?

thanks

          Followup:

because the recursive sql issued by sys is only tested under RBO and doing an
analyze on those objects can through the database into a tizzy.

get rid of stats on sys -- you've seen the side effects already.

do the schema with indexes -- method opt of "for all indexed columns" and
cascade set the TRUE is a good starting place.

Unless the objects are huge -- compute is my preferred method.  Estimate kicks
in when your objects are too big to analyze in the window you would like to
analyze in.

      Rating:
      5
Thanks...  December 22, 2002
Reviewer:
      shd  from ksa
Thanks indeed, your clarification makes sense.
Thats what we look for to get info based onyour practical exp.
Thanks once again.

December 22, 2002 Peter -- Thanks for the question regarding "Date
      Problem: confusion when time goes back by one hour.", version 8.1.7

      You Asked
Hello Tom
We have the following problem concerning the hour change.

From an external application we are given date with a UTC time
and from that we convert it into oracle with the to_date function
which applies DST.

For certain calculations we need to use the original time value. This is not a
problem when time goes forward (in spring) but in the fall
because time goes backwards, effectively the same hour is recorded twice.
So how can we know if a record was taken before or after the hour change. Do we
need an extra field to flag whether we are in summer time?

      and we said...
Well, the real answer is -- Oracle9i with the TIMESTAMP WITH TIMEZONE.

Else, you'll have to save the TZ in a column with the data itself to tell.  You
can get the timezone via C or Java, see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:452820143983

Review &
      Followup

      Rating: 3
Getting all the
      details of a date   December 21, 2002
Reviewer:
      Peter Jones  from Switzerland
Thankyou for your reply.
So with Oracle9 and TIMESTAMP will I be able to
know if a date was inserted with DST or not?
E.G.
If I insert a value and its timestamp into table t before the hour change (say
at 01:30),
insert into t values (my_value,sysdate)
and then do the same after the hour goes back (again at 01:30) will I be able to
know which values were taken before and after the change?

          Followup:

if you create the column as timestamp with timezone, yes.  the timezone is part
of the datatype.

      Rating: 3
Date Problem
      Multi Languate  December 22, 2002
Reviewer:
      Jignesh  from India
Hi Tom,

As per our clients requirement using third-party software, we were able to save
data in backend using two languages i.e. Thai and English at a same time.  But
while retrieving from Forms6i, we found that the Font should be MS-Sans-Serif
for thai or else it will show junc characters.  We have set all Nls_Language
setting for getting Thai Language.  We have also faced the same problem with
Reports and resolved by setting Ms-Sans-Serif.  But in case of date, wherever
the format is 'dd-mon-rrrr', it is giving error at runtime as Invalid Month.  if
we change it to dd/mm/rrrr, it works fine.  i.e. if the month is MON-RRRR and
even if we have used it in conditions like to_char, it gives error, is there any
special settings to be made to resolve this problem or we have to go and change
all date format to dd/mm/rrrr?               Thanks in advance.

          Followup:

Ms-Sans-Serif is a "font", it doesn't have anything to do with the underlying
issue.  It is 100% a display thing.

Not sure what your issue is, but it is most likely Windows.  Do this:

select dump( :month_string ), dump( to_char( sysdate, 'MON' ) ) from dual;

in your form and print out the results.  See what the difference between what
you are trying to send to the server as a month in :month_string vs what the
server wants to see in a month as generated by the to_char are.

December 22, 2002 -- Thanks for the question regarding "Index
      rebuild", version 8.1.7.0.0

      You Asked
Hi,
When we rebuild the index, where we can get the info from the dictionary ...
like the date when last rebuilt or how do we ensure if the index is really get
rebuilt!!!!

Jitendra

      and we said...
if the command succeeds, you know the index was rebuilt.  I hope you don't
rebuild indexes very often or on a schedule.  It is very very very rare to ever
have to rebuild an index -- bitmap indexes being the rare exception to this
rule.

See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6601312252730
for my opinion of rebuilding indexes on a recurring basis.

so, if your goal is to write a procedure to rebuild every index that hasn't been
rebuilt in N days/weeks/months/whatever STOP, stop right now.  Don't do it.

you can look at LAST_DDL_TIME in user_objects where .... but you probably need
to be on 8174 as there was a patch to get this updated for index rebuilds.

December 22, 2002 Bhavesh -- Thanks for the question regarding
      "Loop in a SQL Query", version 9i

      You Asked
I would like to achieve the following. If my parameters are Valid From =
10/12/2002 and Valid To=15/12/2002, a 'SQL Query' should give me output from
DUAL table or otherwise, as follows

o/p
==========
10/12/2002
11/12/2002
12/12/2002
13/12/2002
14/12/2002
15/12/2002

Please note it has to be a SQL Query, not PL/SQL.
Basically I want to loop through all the days(like a dynamic cursor) entered by
the user and use the output for another Query. I shall be using this in a Java
Server Side code.

Thanks

      and we said...
In this example (sysdate-10) is your from date and sysdate is your to date.  You
would use bind variables and supply these values at runtime of course.

ops$tkyte@ORA920> create or replace type dateCollection as table of date
  2  /

Type created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> create or replace function all_dates( p_start in date, p_stop
in date )
  2  return dateCollection
  3  PIPELINED
  4  as
  5  begin
  6          for i in 0 .. (p_stop-p_start)
  7          loop
  8                  pipe row( p_start+i );
  9          end loop;
 10          return;
 11  end;
 12  /

Function created.

the java routines will use this query then:

ops$tkyte@ORA920>
ops$tkyte@ORA920> select *
  2    from TABLE( all_dates( sysdate-10, sysdate ) )
  3  /

COLUMN_VA
---------
12-DEC-02
13-DEC-02
14-DEC-02
15-DEC-02
16-DEC-02
17-DEC-02
18-DEC-02
19-DEC-02
20-DEC-02
21-DEC-02
22-DEC-02

11 rows selected.

Is the way to do that.

You can also:

ops$tkyte@ORA920> select (sysdate-10)+r
  2    from ( select rownum-1 r
  3             from all_objects
  4                    where rownum <= (sysdate)-(sysdate-10)+1
  5             )
  6  /

(SYSDATE-
---------
12-DEC-02
13-DEC-02
14-DEC-02
15-DEC-02
16-DEC-02
17-DEC-02
18-DEC-02
19-DEC-02
20-DEC-02
21-DEC-02
22-DEC-02

11 rows selected.

assuming all_objects is big enough (if not, create a big enough table elsewhere
and use that)

Review &
      Followup

      Rating: 4
Loop in a SQL
      Query  December 22, 2002
Reviewer:  Bhavesh
      Gandhi  from India, Bombay
Thanks a lot for the solution.

Parellely I also reached the second solution as suggested here.

I am really happy that this website replies promptly. This is the second time I
have asked a question and I have received a solution on both occasions.

December 22, 2002 Adolph -- Thanks for the question regarding
      "Locating character in Strings, using wild cards", version 8.1.7

      You Asked
Tom,
   Thanks for your answer.
The issue with my problem is that I don't know beforehand if this string
contains any character or Letters.

Take this example from this table.

Product   Order#     Catlg#
-------   ------     ---------
SHOES     5672       7639-357E7         E
SHIRTS    5682       67A34-T8392        A T

Now somebody inadvertently keyed in characters in the catlg# column. The issue
here is to scan the Catlg# column and pick out any letter that was added in the
catlg# and display them. I will be creating a view and will display this
characters in another column.

Thanks,
 Adolph

      and we said...
turn the numbers into nothing..  turn everything you don't care about into 0 and
then replace 0 with nothing:

ops$tkyte@ORA920> select replace( translate( '67A34-T8392', '0123456789-',
'00000000000' ), '0', '' )
  2  from dual;

RE
--
AT

Review &
      Followup

      Rating:
      5
  December 22, 2002
Reviewer:
      A reader
Thanks, Mr.Tom
You made my day.

December 23, 2002 Phillip -- Thanks for the question regarding
      "Explain Plan with Nested Table Bind Variable", version 8.1.7

      You Asked
Before I forget:  Oracle 8.1.7 on a Sequent

In my area we use a _lot_ of stored procedures.  One of my administrative tasks
is to extract out the SQL used in those procedures, create Explain Plans from
them, and then file the results.    This is so that when I get complaints 6
months down the road that the query suddenly doesn't work I can compare current
plans and statictics with something that existed back when it was supposedly
Great Stuff.

What I have been doing is to create Sql*plus scripts where I (1) define bind
variables for all the parameters and pl/sql variables used in the SQL I am
testing, and (2) run the SQL through autotrace.  I prefer to do this over using
tkprof because it is generally less work.

Well, I have run into a problem.  I have a stored procedure with a nested table
as one of its parameters.  What the SQL needs is a predicate in its WHERE clause
of the form
      column1 IN ('value1','value2', 'value3' .....)
where what is in paraenthesis is passed as a parameter.  Since
      colum1 IN parameter
will not work, I use
      column1 IN (SELECT * FROM TABLE(CAST (:ptbl_GroupList as pdoGroupType)))
      where pdoGroupType is an appropriately defined nested table.

The problem of course is that my beautiful scheme fails, because sql*plus has no
concept of a nested table bind variable.

So, my question is, do I need to use tkprof to get an accurate explain plan in
this case, or can I substitue a literal string in my IN clause?
(I know that in general, substituting literals for bind variables in this sort
of analysis generates misleading results).

      and we said...
Oh, to be on 9i -- with the v$sql_plan table -- where your ENTIRE (flawed)
process becomes a simple:

insert into plan_audit_trail
select sysdate, a.* from v$sql_plan a;

I say flawed -- cause a simple environmental change in the users session will
change the plan (eg: a bigger sort area size for example)....

Also, unless you parse as the connected user, as the parsing schema, I don't see
how this works either (select * from t -- well, when I run that, it is x.t, when
you run it is is y.t)  but anyway...

Why don't you just do this instead -- using EXPLAIN PLAN -- no bind variable
declarations needed...

Here are the tables you might use to store this stuff:

ops$tkyte@ORA817DEV.US.ORACLE.COM> drop sequence ep_seq;

Sequence dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create sequence ep_seq;

Sequence created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table ep_table_dtl;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table ep_table_dtl (
  2      statement_id    number,
  3      timestamp       date,
  4      remarks         varchar2(80),
  5      operation       varchar2(30),
  6      options         varchar2(30),
  7      object_node     varchar2(128),
  8      object_owner    varchar2(30),
  9      object_name     varchar2(30),
 10      object_instance numeric,
 11      object_type     varchar2(30),
 12      optimizer       varchar2(255),
 13      search_columns  number,
 14      id      numeric,
 15      parent_id   numeric,
 16      position    numeric,
 17      cost        numeric,
 18      cardinality numeric,
 19      bytes       numeric,
 20      other_tag       varchar2(255),
 21      partition_start varchar2(255),
 22          partition_stop  varchar2(255),
 23          partition_id    numeric,
 24      other       long,
 25      distribution    varchar2(30));

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create index ep_table_dtl_idx on
ep_table_dtl(statement_id);

Index created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table ep_table;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table ep_table
  2  ( id int primary key,
  3    dt date,
  4    parse_user varchar2(30),
  5    sql_text varchar2(4000),
  6    sql_text_all clob,
  7    error  varchar2(4000)
  8  )
  9  /

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> alter table ep_table_dtl add constraint
ep_table_dtl_fk
  2  foreign key(statement_id) references ep_table(id)
  3  on delete cascade;

Table altered.

sort of a master/detail with queries pointing to plans...

Then this procedure will whip through v$sqltext_with_newlines and get all of the
DML and try to parse it.  I'll use set current schema to make it so the queries
are parsed using the "correct" userid -- but -- bear in mind that there might be
more then one parsing userid!  You might modify this to do the explain plan once
for each possible parsing user.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace procedure get_all_plans
  2  authid current_user
  3  as
  4      l_date            date := sysdate;
  5      l_last_address    v$sqltext_with_newlines.address%type := '00';
  6      l_query           long;
  7
  8      procedure save_plan
  9      is
 10          l_id   number;
 11          l_clob clob;
 12          l_error varchar2(4000);
 13          l_username all_users.username%type;
 14      begin
 15          if ( l_query is null ) then return; end if;
 16
 17          select nvl( max(username), user )
 18            into l_username
 19            from all_users
 20           where user_id = (select parsing_schema_id
 21                          from v$sql
 22                         where address = l_last_address
 23                           and rownum = 1 ); /* guess at the parsing schemaid
*/
 24
 25          insert into ep_table( id, dt, parse_user, sql_text, sql_text_all )
 26          values ( ep_seq.nextval, l_date, l_username,
substr(l_query,1,4000), empty_clob() )
 27          returning id, sql_text_all into l_id, l_clob;
 28
 29          dbms_lob.writeAppend( l_clob, length(l_query), l_query );
 30
 31
 32          begin
 33              execute immediate 'alter session set current_schema = ' ||
l_username;
 34              execute immediate
 35              'explain plan
 36                   set statement_id = ''' || l_id || '''
 37                  into ep_table_dtl
 38                   for ' || l_query;
 39              execute immediate 'alter session set current_schema = ' ||
user;
 40          exception
 41              when others then
 42                  execute immediate 'alter session set current_schema = ' ||
user;
 43                  l_error := substr( sqlerrm, 1, 4000 );
 44                  update ep_table set error = l_error where id = l_id;
 45                  dbms_output.put_line( substr( l_query, 1, 80 ) || '...' );
 46                  dbms_output.put_line( substr( l_error, 1, 250 ) );
 47                  dbms_output.put_line( '----------------------------' );
 48          end;
 49      end save_plan;
 50
 51  begin
 52      for x in ( select *
 53                   from v$sqltext_with_newlines
 54                  where command_type in ( 2 /* inserts */,
 55                                          3 /* selects */,
 56                                          6 /* update */,
 57                                          7 /* delete */ )
 58                  order by address, hash_value, piece )
 59      loop
 60          if ( l_last_address <> x.address )
 61          then
 62              save_plan;
 63              l_last_address := x.address;
 64              l_query := null;
 65          end if;
 66          l_query := l_query || x.sql_text;
 67      end loop;
 68      save_plan;
 69  end;
 70  /

Procedure created.

and now all you need to do is run this:

ops$tkyte@ORA817DEV.US.ORACLE.COM> exec get_all_plans

since it was an authid current user procedure, ROLES will be in place so
running as a DBA with select any table should get you decent results.

It prints out any query in error, you can review them to see if you care or not
and grant

Review &
      Followup

      Rating: 5
Well,
      Duh!!!  December 23, 2002
Reviewer:  Phil
      Singer  from Dearborn, MI USA
Why am I not on 9i?  It sounds too much like a Dilbert strip.

Why don't I do this instead?, Well, now that you have made this obvious, I am
going to do it instead.  Thanks very much

December 23, 2002 -- Thanks for the question regarding "lengthy
      select statement", version 8.1.7

      You Asked
Hi Tom,

I have a pretty length select statement...Something like this:

(select ....from <table1> where ....
 union all
 select ....from <table2> where ....
 union all
 select(select ....from <table1> where ....)
  /
  (select ....from <table2> where ....)
  *100
  from dual

In other words my third union need to get the percentage of
first one over the second. Is there any way to simplify that?

Thank You

HERE IS MY EXAMPLE:

PACKAGE BODY PKG_RPT_ACC_RESET_SUMMARY
IS
  PROCEDURE USP_RPT_ACC_RESET_SUMMARY
        ( p_startdate IN VARCHAR2,
          p_enddate IN VARCHAR2,
          p_chain IN VARCHAR2,
          p_regionname IN VARCHAR2,
          p_questionnaire IN VARCHAR2,
         p_resultset OUT rc_resultset)

  IS
  v_output  rc_resultset;
  BEGIN
       OPEN v_output FOR
    '

    select concat(RPAD(''Calls Scheduled'',20),count(call.callid)) "NUMBER"
        from call,questionnaire,outlet,mapregionoutlet,region,chain
        where call.outletid=outlet.outletid
        and   outlet.outletid=mapregionoutlet.outletid
        and   mapregionoutlet.regionid=region.regionid
        and   call.questionnaireid=questionnaire.questionnaireid
        and   chain.chainid=outlet.chainid
        and   chain.name=:p_chain
        and   region.name like ''%'' || :p_regionname || ''%''
        and   call.duedate between to_date(:p_startdate,''mm/dd/yyyy'') and
to_date(:p_enddate,''mm/dd/yyyy'')
        and   questionnaire.name = :p_questionnaire

     UNION ALL
        select concat(RPAD(''Calls Actual'',20),count(call.callid))
        from call,questionnaire,outlet,mapregionoutlet,region,chain
        where call.outletid=outlet.outletid
        and   outlet.outletid=mapregionoutlet.outletid
        and   mapregionoutlet.regionid=region.regionid
        and   call.questionnaireid=questionnaire.questionnaireid
        and   chain.chainid=outlet.chainid
        and   chain.name=:p_chain
        and   region.name like ''%'' || :p_regionname || ''%''
        and   call.datecompleted between to_date(:p_startdate,''mm/dd/yyyy'')
and to_date(:p_enddate,''mm/dd/yyyy'')
        and   questionnaire.name = :p_questionnaire

     UNION ALL

        SELECT
        concat(RPAD(''% Actual to Plan'',20),ROUND((select count(call.callid)
        from call,questionnaire,outlet,mapregionoutlet,region,chain
        where call.outletid=outlet.outletid
        and   outlet.outletid=mapregionoutlet.outletid
        and   mapregionoutlet.regionid=region.regionid
        and   call.questionnaireid=questionnaire.questionnaireid
        and   chain.chainid=outlet.chainid
        and   chain.name=:p_chain
        and   region.name like ''%'' || :p_regionname || ''%''
        and   call.datecompleted between to_date(:p_startdate,''mm/dd/yyyy'')
and to_date(:p_enddate,''mm/dd/yyyy'')
        and   questionnaire.name = :p_questionnaire)/
        (select count(call.callid)
        from call,questionnaire,outlet,mapregionoutlet,region,chain
        where call.outletid=outlet.outletid
        and   outlet.outletid=mapregionoutlet.outletid
        and   mapregionoutlet.regionid=region.regionid
        and   call.questionnaireid=questionnaire.questionnaireid
        and   chain.chainid=outlet.chainid
        and   chain.name=:p_chain
        and   region.name like ''%'' || :p_regionname || ''%''
        and call.duedate between to_date(:p_startdate,''mm/dd/yyyy'') and
to_date(:p_enddate,''mm/dd/yyyy'')
        and   questionnaire.name = :p_questionnaire)*100))
        FROM DUAL

    'using
            p_chain,p_regionname,p_startdate,p_enddate,p_questionnaire,
            p_chain,p_regionname,p_startdate,p_enddate,p_questionnaire,
            p_chain,p_regionname,p_startdate,p_enddate,p_questionnaire,
            p_chain,p_regionname,p_startdate,p_enddate,p_questionnaire;
p_resultset := v_output;

   END;
END;

      and we said...
I believe we can do this in a single pass.  Basically, you want to return 3
rows:

row 1: calls scheduled
row 2: calls actual
row 3: calls actual/calls_scheduled

What we'll do is ONE query to get us a single row with two columns, calls
scheduled and calls actual.

Then, we can divide to get the third value easily.

Then, we cartesian product this one row with 3 rows from dual and we get our 3
rows of output -- and we only made one pass on the query itself... Try (i did
not actually RUN this as I don't have your tables but I believe it to be pretty
close) this query. I sum up the CASE of the date -- to count the number of
records for each -- then using inline views just keep processing the results to
get what I want:

open v_output for
'
select decode( rn, 1, c1, 2, c2, c3 ) "NUMBER"
  from (
select rpad( ''Calls Scheduled'', 20 ) || calls_scheduled c1,
       rpad( ''Calls Actual'',    20 ) || calls_actual    c2,
       rpad( ''% Actual to Plan'',20 ) ||
               decode( calls_scheduled, 0, ''****'',
                            round( calls_actual/calls_scheduled*100, 2 ) ) c3
  from (
select sum( case when (call.duedate between :p_startdate and :p_enddate)
            then 1
            else 0
            end ) calls_scheduled,
       sum( case when (call.datecompleted between :p_startdate and :p_enddate)
            then 1
            else 0
            end ) calls_completed
  from call,
       questionnaire,
       outlet,
       mapregionoutlet,
       region,
       chain
 where call.outletid=outlet.outletid
   and outlet.outletid=mapregionoutlet.outletid
   and mapregionoutlet.regionid=region.regionid
   and call.questionnaireid=questionnaire.questionnaireid
   and chain.chainid=outlet.chainid
   and chain.name=:p_chain
   and region.name like :p_regionname
   and questionnaire.name = :p_questionnaire
   and (call.duedate between :p_startdate and :p_enddate
        or
        call.datecompleted between :p_startdate and :p_enddate)
       ),
   ( select 1 rn from dual
     union all
     select 2 rn from dual
     union all
     select 3 rn from dual )
     )
' using to_date( p_start_date, 'mm/dd/yyyy' ),
        to_date( p_end_date, 'mm/dd/yyyy' ),
        to_date( p_start_date, 'mm/dd/yyyy' ),
        to_date( p_end_date, 'mm/dd/yyyy' ),
        p_chain,
        '%' || p_regionName || '%',
        p_questionnaire,
        to_date( p_start_date, 'mm/dd/yyyy' ),
        to_date( p_end_date, 'mm/dd/yyyy' ),
        to_date( p_start_date, 'mm/dd/yyyy' ),
        to_date( p_end_date, 'mm/dd/yyyy' );

Review &
      Followup

      Rating:
      4
  December 16, 2002
Reviewer:
      A reader
THANK YOU VERY MUCH TOM

      Rating:
      4
  December 18, 2002
Reviewer:
      A reader
Tom,
In the query you showed me you selected call act. and call scheduled from only
one region (where region.name like ''%'' || :p_regionname || ''%'')...What if I
want to select call act. and call sched. where region.level1=9 along with call
act. and call sched where region.level1=10
along with call act. and call sched where region.level1=125.
How should I proceed? Separate query with union since to all of them we have to
apply different where clause in regard to region level or else?

Thank You very much

          Followup:

Is it that you want 9 rows back (3 for each "region") or 3 rows back across all
regions.

      Rating:
      4
  December 20, 2002
Reviewer:
      A reader
Tom,

Thanks very much for you quick response. I did how you suggested and everythin
is working perfect...
Following is another question pertaining to the same quiry:

select  sum(case
            when (question.questiontext='Enter Display Type' and
answer.answerinteger=1
                  )
            then 1
            else 0
            end) "System XL",
        sum(case
            when (question.questiontext='Enter Display Type' and
answer.answerinteger=2
                 )
            then 1
            else 0
            end) "Vista",
        sum(case
            when (question.questiontext='Enter Display Type' and
answer.answerinteger=3
                  )
            then 1
            else 0
            end) "Other",
        sum(case
            when (question.questiontext='Enter Total Time Spent in the Store, in
Hours'
                  )
            then answer.answerinteger
            else 0
            end) "(Included) SPP Hrs.",
        avg(case
            when (question.questiontext='Enter Display Width in Feet'
                  )
            then answer.answerinteger
            else 0
            end) "(Included) SPP Hrs."
FROM   outlet,
       call,
       question,
       answer,
       region,
       mapregionoutlet,
       chain,
       project
WHERE  outlet.outletid=call.outletid
and    call.callid=answer.callid
and    answer.questionid=question.questionid
and    outlet.outletid=mapregionoutlet.outletid
and    mapregionoutlet.regionid=region.regionid
and    outlet.chainid=chain.chainid
and    call.projectid=project.projectid
and    call.duedate between to_date('1/1/2001','mm/dd/yyyy') and
to_date('12/31/2001','mm/dd/yyyy')
and    chain.chainid=132
and    project.name in ('Secondary Maybelline Reset','Maybelline Reset')
and    region.level1=9

For the first 3 cases I applied condition of project.name in ('Secondary
Maybelline Reset','Maybelline Reset')
However, for the last case I don't want to include anything where project.name
in 'Maybelline Reset', only where project name in 'Secondary Maybelline Reset'.

I've tried to put that filter inside of each case, however it runs forever, so I
had to cancel the operation.

Thanks again

          Followup:

avg(case
    when (question.questiontext='Enter Display Width in Feet' and
           project.name = 'Secondary Maybelline Reset'
          )
    then answer.answerinteger
    else NULL
     end) "(Included) SPP Hrs."

add the predicate in the case, return NULL, not 0, and it won't be averaged in.

      Rating:
      5
  December 20, 2002
Reviewer:
      A reader
Thanks a lot, everything works.
How can I make following select statement work for many different regions, which
is the first inner select statement I need to repeat 5 times just to change
region.level1 to region.level2=10,then region.level2=120 and so on. If I just
concatenate them all the select statement will be humangous. Would it be better
and possible for me to write function to return let's say 10 columns based on
the region.level

          Followup:

why not

and region.level1 in ( .... )
group by region.level1

and add region.level1 to the select list.  get a row/region?

      Rating:
      5
  December 20, 2002
Reviewer:
      A reader
I forgot to include select statement:

select decode(rn,1,c1,2,c2,3,c3,4,c4,5,c5,6,c6,7,c7,c8) "NATIONAL"
from

(select
        sum(case
            when (question.questiontext='Enter Display Width in Feet' and
project.name = 'Maybelline Reset' and    region.level1=9)
            then answer.answerinteger
            else NULL
            end)  c1,
        sum(case
            when (question.questiontext='Enter Display Type' and
answer.answerinteger=1 and project.name = 'Maybelline Reset' and
region.level1=9)
            then 1
            else 0
            end) c2,
        sum(case
            when (question.questiontext='Enter Display Type' and
answer.answerinteger=2 and project.name = 'Maybelline Reset' and
region.level1=9)
            then 1
            else 0
            end) c3,
        sum(case
            when (question.questiontext='Enter Display Type' and
answer.answerinteger=3 and project.name = 'Maybelline Reset' and
region.level1=9)
            then 1
            else 0
            end) c4,
        sum(case
            when (question.questiontext='Enter Total Time Spent in the Store, in
Hours' and project.name = 'Maybelline Reset' and    region.level1=9)
            then answer.answerinteger
            else NULL
            end) c5,
         sum(case
            when (question.questiontext='Enter Total Time Spent in the Store, in
Hours' and project.name = 'Maybelline Reset' and    region.level1=9)
            then answer.answerinteger
            else NULL
            end)+
         sum(case
            when (question.questiontext='Enter Total Time Spent in the Store, in
Hours' and project.name = 'Secondary Maybelline Reset' and    region.level1=9)
            then answer.answerinteger
            else NULL
            end)  c6,
         to_char(to_number(avg(case
            when (question.questiontext='Enter Total Time Spent in the Store, in
Hours'and project.name in ('Maybelline Reset') and    region.level1=9)
            then answer.answerinteger
            else 0
            end)),'9.99')c7,
         to_char(to_number(avg(case
            when (question.questiontext='Enter Total Time Spent in the Store, in
Hours'and project.name in ('Maybelline Reset','Secondary Maybelline Reset') and
  region.level1=9)
            then answer.answerinteger
            else 0
            end)),'9.99') c8
FROM   outlet,
       call,
       question,
       answer,
       region,
       mapregionoutlet,
       chain,
       project
WHERE  outlet.outletid=call.outletid
and    call.callid=answer.callid
and    answer.questionid=question.questionid
and    outlet.outletid=mapregionoutlet.outletid
and    mapregionoutlet.regionid=region.regionid
and    outlet.chainid=chain.chainid
and    call.projectid=project.projectid
and    call.duedate between to_date('1/1/2001','mm/dd/yyyy') and
to_date('12/31/2001','mm/dd/yyyy')
and    chain.chainid=132
and    project.name in ('Secondary Maybelline Reset','Maybelline Reset')),
( select 1 rn from dual
             union all
             select 2 rn from dual
             union all
             select 3 rn from dual
             union all
             select 4 rn from dual
             union all
             select 5 rn from dual
             union all
             select 6 rn from dual
             union all
             select 7 rn from dual
             union all
             select 8 rn from dual
             )
--and    region.level1=9

      Rating:
      5
  December 20, 2002
Reviewer:
      A reader
I am sorry Tom but I did not get it...
How the function would look like with following select statement and return
something like that:

Heading -->c1  c2  c3  c4
Value   -->12  23  32  423

    select
        sum(case
            when (question.questiontext='Enter Display Width in Feet' and
project.name = 'Maybelline Reset' and    region.level1=9)
            then answer.answerinteger
            else NULL
            end)  c1,
        sum(case
            when (question.questiontext='Enter Display Type' and
answer.answerinteger=1 and project.name = 'Maybelline Reset' and
region.level1=9)
            then 1
            else 0
            end) c2,
        sum(case
            when (question.questiontext='Enter Display Type' and
answer.answerinteger=2 and project.name = 'Maybelline Reset' and
region.level1=9)
            then 1
            else 0
            end) c3,
        sum(case
            when (question.questiontext='Enter Display Type' and
answer.answerinteger=3 and project.name = 'Maybelline Reset' and
region.level1=9)
            then 1
            else 0
            end) c4,
        sum(case
            when (question.questiontext='Enter Total Time Spent in the Store, in
Hours' and project.name = 'Maybelline Reset' and    region.level1=9)
            then answer.answerinteger
            else NULL
            end) c5,
         sum(case
            when (question.questiontext='Enter Total Time Spent in the Store, in
Hours' and project.name = 'Maybelline Reset' and    region.level1=9)
            then answer.answerinteger
            else NULL
            end)+
         sum(case
            when (question.questiontext='Enter Total Time Spent in the Store, in
Hours' and project.name = 'Secondary Maybelline Reset' and    region.level1=9)
            then answer.answerinteger
            else NULL
            end)  c6,
         to_char(to_number(avg(case
            when (question.questiontext='Enter Total Time Spent in the Store, in
Hours'and project.name in ('Maybelline Reset') and    region.level1=9)
            then answer.answerinteger
            else 0
            end)),'9.99')c7,
         to_char(to_number(avg(case
            when (question.questiontext='Enter Total Time Spent in the Store, in
Hours'and project.name in ('Maybelline Reset','Secondary Maybelline Reset') and
  region.level1=9)
            then answer.answerinteger
            else 0
            end)),'9.99') c8
FROM   outlet,
       call,
       question,
       answer,
       region,
       mapregionoutlet,
       chain,
       project
WHERE  outlet.outletid=call.outletid
and    call.callid=answer.callid
and    answer.questionid=question.questionid
and    outlet.outletid=mapregionoutlet.outletid
and    mapregionoutlet.regionid=region.regionid
and    outlet.chainid=chain.chainid
and    call.projectid=project.projectid
and    call.duedate between to_date('1/1/2001','mm/dd/yyyy') and
to_date('12/31/2001','mm/dd/yyyy')
and    chain.chainid=132
and    project.name in ('Secondary Maybelline Reset','Maybelline Reset')

          Followup:

    select  region.level1,
        sum(case
            when (question.questiontext='Enter Display Width in Feet' and
project.name = 'Maybelline Reset' and    region.level1=9)
            then answer.answerinteger
            else NULL
            end)  c1,
        sum(case
            when (question.questiontext='Enter Display Type' and
answer.answerinteger=1 and project.name = 'Maybelline Reset' and
region.level1=9)
            then 1
            else 0
            end) c2,
        sum(case
            when (question.questiontext='Enter Display Type' and
answer.answerinteger=2 and project.name = 'Maybelline Reset' and
region.level1=9)
            then 1
            else 0
            end) c3,
        sum(case
            when (question.questiontext='Enter Display Type' and
answer.answerinteger=3 and project.name = 'Maybelline Reset' and
region.level1=9)
            then 1
            else 0
            end) c4,
        sum(case
            when (question.questiontext='Enter Total Time Spent in the Store, in

Hours' and project.name = 'Maybelline Reset' and    region.level1=9)
            then answer.answerinteger
            else NULL
            end) c5,
         sum(case
            when (question.questiontext='Enter Total Time Spent in the Store, in

Hours' and project.name = 'Maybelline Reset' and    region.level1=9)
            then answer.answerinteger
            else NULL
            end)+
         sum(case
            when (question.questiontext='Enter Total Time Spent in the Store, in

Hours' and project.name = 'Secondary Maybelline Reset' and    region.level1=9)
            then answer.answerinteger
            else NULL
            end)  c6,
         to_char(to_number(avg(case
            when (question.questiontext='Enter Total Time Spent in the Store, in

Hours'and project.name in ('Maybelline Reset') and    region.level1=9)
            then answer.answerinteger
            else 0
            end)),'9.99')c7,
         to_char(to_number(avg(case
            when (question.questiontext='Enter Total Time Spent in the Store, in

Hours'and project.name in ('Maybelline Reset','Secondary Maybelline Reset') and

  region.level1=9)
            then answer.answerinteger
            else 0
            end)),'9.99') c8
FROM   outlet,
       call,
       question,
       answer,
       region,
       mapregionoutlet,
       chain,
       project
WHERE  outlet.outletid=call.outletid
and    call.callid=answer.callid
and    answer.questionid=question.questionid
and    outlet.outletid=mapregionoutlet.outletid
and    mapregionoutlet.regionid=region.regionid
and    outlet.chainid=chain.chainid
and    call.projectid=project.projectid
and    call.duedate between to_date('1/1/2001','mm/dd/yyyy') and
to_date('12/31/2001','mm/dd/yyyy')
and    chain.chainid=132
and    project.name in ('Secondary Maybelline Reset','Maybelline Reset')
and    region.level1 in ( 9, 10, 11, ... )
group by region.level1

you would get a row/region.

      Rating: 5
Are you some
      kind of machine ?  December 20, 2002
Reviewer:
      C
Tom, I really don't know... Are you really human ???

Sometimes I just don't believe you ALONE (I know other experts step in some rare
times) can answer such long, complex and -- many times -- dull and boring
questions.

How do you make it ?

It just doesn't make sense... In addition to spending soooo muuuuuch time on
this site (working Saturdays and Sundays too), you must keep up-to-date with the
Oracle DB, you are a VP for government, education and health, you write lengthy
and awesome books, gives lectures, etc, etc, etc.

IT JUST DOESN'T MAKE SENSE...

Do you get any leisure at all ? What do you do with your spare time ?

Either you don't exist or you are some kind of godlike creature.

Hasn't Oracle just made you up ?

      Rating:
      5
  December 20, 2002
Reviewer:
      A reader
Sorry if I did not specify it more clear, but the first row should be based on
pridicate region.level1=9 and going forward the predicate should be based on
region.level2 in (10,120,245).How to do it? With the following select statement
I got 3 requeired with level 2, but did not get first one with level 1

    select
        sum(case
            when (question.questiontext='Enter Display Width in Feet' and
project.name = 'Maybelline Reset' and    region.level1=9)
            then answer.answerinteger
            else NULL
            end)  c1,
        sum(case
            when (question.questiontext='Enter Display Type' and
answer.answerinteger=1 and project.name = 'Maybelline Reset' and
region.level1=9)
            then 1
            else 0
            end) c2,
        sum(case
            when (question.questiontext='Enter Display Type' and
answer.answerinteger=2 and project.name = 'Maybelline Reset' and
region.level1=9)
            then 1
            else 0
            end) c3,
        sum(case
            when (question.questiontext='Enter Display Type' and
answer.answerinteger=3 and project.name = 'Maybelline Reset' and
region.level1=9)
            then 1
            else 0
            end) c4,
        sum(case
            when (question.questiontext='Enter Total Time Spent in the Store, in
Hours' and project.name = 'Maybelline Reset' and    region.level1=9)
            then answer.answerinteger
            else NULL
            end) c5,
         sum(case
            when (question.questiontext='Enter Total Time Spent in the Store, in
Hours' and project.name = 'Maybelline Reset' and    region.level1=9)
            then answer.answerinteger
            else NULL
            end)+
         sum(case
            when (question.questiontext='Enter Total Time Spent in the Store, in
Hours' and project.name = 'Secondary Maybelline Reset' and    region.level1=9)
            then answer.answerinteger
            else NULL
            end)  c6,
         to_char(to_number(avg(case
            when (question.questiontext='Enter Total Time Spent in the Store, in
Hours'and project.name in ('Maybelline Reset') and    region.level1=9)
            then answer.answerinteger
            else 0
            end)),'9.99')c7,
         to_char(to_number(avg(case
            when (question.questiontext='Enter Total Time Spent in the Store, in
Hours'and project.name in ('Maybelline Reset','Secondary Maybelline Reset') and
  region.level1=9)
            then answer.answerinteger
            else 0
            end)),'9.99') c8
FROM   outlet,
       call,
       question,
       answer,
       region,
       mapregionoutlet,
       chain,
       project
WHERE  outlet.outletid=call.outletid
and    call.callid=answer.callid
and    answer.questionid=question.questionid
and    outlet.outletid=mapregionoutlet.outletid
and    mapregionoutlet.regionid=region.regionid
and    outlet.chainid=chain.chainid
and    call.projectid=project.projectid
and    call.duedate between to_date('1/1/2001','mm/dd/yyyy') and
to_date('12/31/2001','mm/dd/yyyy')
and    chain.chainid=132
and    project.name in ('Secondary Maybelline Reset','Maybelline Reset')
and    region.level1 in (0,9)
and    region.level2 in ( 0,9, 10, 120,245 )
group by region.level2,region.level1

          Followup:

if I understand, I think this is it:

Select
        sum(case
            when (question.questiontext='Enter Display Width in Feet' and
project.name = 'Maybelline Reset' and region.level1 in ( 9, 10 ) )
            then answer.answerinteger
            else NULL
            end)  c1_lvl1,
        sum(case
            when (question.questiontext='Enter Display Width in Feet' and
project.name = 'Maybelline Reset' and region.level2 in ( 10, 120, 245 ) )
            then answer.answerinteger
            else NULL
            end)  c1_lvl2,
         .....
FROM   outlet,
       call,
       question,
       answer,
       region,
       mapregionoutlet,
       chain,
       project
WHERE  outlet.outletid=call.outletid
and    call.callid=answer.callid
and    answer.questionid=question.questionid
and    outlet.outletid=mapregionoutlet.outletid
and    mapregionoutlet.regionid=region.regionid
and    outlet.chainid=chain.chainid
and    call.projectid=project.projectid
and    call.duedate between to_date('1/1/2001','mm/dd/yyyy') and
to_date('12/31/2001','mm/dd/yyyy')
and    chain.chainid=132
and    project.name in ('Secondary Maybelline Reset','Maybelline Reset')
and    ( region.level1 in (0,9) or region.level2 in ( 0,9, 10, 120,245 ) )

I think you need an "or" and then use the in again in the case statement.
that'll give you a single row with all of the numbers.

      Rating:
      4
  December 20, 2002
Reviewer:
      A reader
Does not work...
I need a function which would return only one record...
However, predicate in that function should vary. On one occasion I need to put
in predicate 'where region.level1=9'
on another occasion I need to put in predicate 'where region.level2=10'.How can
I accomplish that?

          Followup:

native dynamic sql.

..
is
  type rc is ref cursor;
  l_cursor  rc;
  ..
begin
  open l_cursor for your_query || your_predicate using your_bind variables...

      Rating:
      4
  December 23, 2002
Reviewer:
      A reader
Tom,

What am I doing wrong? The following function does not work...And would it be
possibile to pass 'v_where' value as a parameter as well?

FUNCTION F_ACC_RESET_ACC_DTL_COSM(p_region_level INTEGER)

  RETURN  PKG_RPT_ACC_RESET_ACC_DTL_COSM.v_refcursor
 IS
 v_where VARCHAR2(100) ;

l_cursor PKG_RPT_ACC_RESET_ACC_DTL_COSM.v_refcursor;
BEGIN
v_where:='and region.level1';
     open l_cursor for 'select
        sum(case
            when (question.questiontext=''Enter Display Width in Feet'' and
project.name = ''Maybelline Reset'' )
            then answer.answerinteger
            else NULL
            end)  c1,
        sum(case
            when (question.questiontext=''Enter Display Type'' and
answer.answerinteger=1 and project.name = ''Maybelline Reset'' )
            then 1
            else 0
            end) c2,
        sum(case
            when (question.questiontext=''Enter Display Type'' and
answer.answerinteger=2 and project.name = ''Maybelline Reset'')
            then 1
            else 0
            end) c3,
        sum(case
            when (question.questiontext=''Enter Display Type'' and
answer.answerinteger=3 and project.name = ''Maybelline Reset'')
            then 1
            else 0
            end) c4,
        sum(case
            when (question.questiontext=''Enter Total Time Spent in the Store,
in Hours'' and project.name = ''Maybelline Reset'')
            then answer.answerinteger
            else NULL
            end) c5,
         sum(case
            when (question.questiontext=''Enter Total Time Spent in the Store,
in Hours'' and project.name = ''Maybelline Reset'')
            then answer.answerinteger
            else NULL
            end)+
         sum(case
            when (question.questiontext=''Enter Total Time Spent in the Store,
in Hours'' and project.name = ''Secondary Maybelline Reset'')
            then answer.answerinteger
            else NULL
            end)  c6,
         to_char(to_number(avg(case
            when (question.questiontext=''Enter Total Time Spent in the Store,
in Hours''and project.name in (''Maybelline Reset'') )
            then answer.answerinteger
            else 0
            end)),''9.99'')c7,
         to_char(to_number(avg(case
            when (question.questiontext=''Enter Total Time Spent in the Store,
in Hours''and project.name in (''Maybelline Reset'',''Secondary Maybelline
Reset'') )
            then answer.answerinteger
            else 0
            end)),''9.99'') c8
FROM   outlet,
       call,
       question,
       answer,
       region,
       mapregionoutlet,
       chain,
       project
WHERE  outlet.outletid=call.outletid
and    call.callid=answer.callid
and    answer.questionid=question.questionid
and    outlet.outletid=mapregionoutlet.outletid
and    mapregionoutlet.regionid=region.regionid
and    outlet.chainid=chain.chainid
and    call.projectid=project.projectid
and    call.duedate between to_date(''1/1/2001'',''mm/dd/yyyy'') and
to_date(''12/31/2001'',''mm/dd/yyyy'')
and    chain.chainid=132
and    project.name in (''Secondary Maybelline Reset'',''Maybelline Reset'')  '
|| v_where =':p_region_level'
--and    region.level1=:p_region_level'
using p_region_level;
RETURN l_cursor ;

END;

          Followup:

v_where:='and region.level1';
....

and    project.name in (''Secondary Maybelline Reset'',''Maybelline Reset'')  '
|| v_where =':p_region_level'
          ^^^^ should be         || v_where || ' = :p_region_level'
using p_region_level;
RETURN l_cursor ;

December 23, 2002 Andre -- Thanks for the question regarding
      "Undocumented init.ora parameters", version 8.1.6

      You Asked
I´ve recently been aware of the undocumented init.ora parameters
-- those begining with "_" (underline). There is list of them in
a book available on the market. In which situation could (or
should) we, mortal DBAs, use them ? Do you remeber any specific
situation where it can be necessary to take advantage of them ?
In what aspects do they differ from the documented ones ? Can we
regularily use them in any init.ora file ? Can you give me an
example ?

Thanks !

      and we said...
In my development database, i use only one --
_TRACE_FILES_PUBLIC.  That makes trace files readable by all,
not just the DBA group.  In my real database, I use none.  On my
development database I want my developers (well, i demand it
actually) to use sql_trace+timed_statistics and tkprof
frequently -- hence they need to be able to read the trace
files.

In general -- you will only use these undocumented parameters at
the request of Oracle Support.  The use of them can be damaging
to a database and their implementation can and will change from
release to release.

Review &
      Followup

      Rating: 3
oracle
      support  April 28, 2002
Reviewer:  liz
       from fl
helllo tom
i need more underestanding of the folowing

In general -- you will only use these undocumented parameters at the request of
Oracle Support.  The use of them can be damaging to a database and their
implementation can and will change from release to release.

          Followup:

Hmmm, I don't know how to make it more clear.

Basically I'm saying:

DO NOT use undocumented parameters that begin with underscores.

If you do, you may damage your database.

Many of these parameters cause side effects that if you are not aware of them,
could seriously damage your database.

Until a parameter is documented, it is unsafe.  Use at your own risk.

      Rating:
      5
parameters  December 23, 2002
Reviewer:
      mo
Tom:

Does "Select * from v$parameter" give you all the
parameters defined for that database?  Are those the same defined in the
init.ora paramete file?

2.  how you query the session parameters? any v$ for that?

Thank you,

          Followup:

yes, maybe.

many of the session parameters modify your view of the v$parameter table.
change your sort_area_size for example and see.

December 23, 2002 Tracy -- Thanks for the question regarding "Can
      certain characters cause database errors?", version 9.2

      You Asked
Hi Tom...

I was asked to verify a statement made by our software vendor in a section of
their "Data Standards" document. The statement, in effect, says that the
characters , (comma) # (pound sign) * (asterisk) and % (percent sign) should
never be used in any data input situations because... "they cause ORACLE
database errors".

I found this a bit hard to believe, thinking that the application that is
running may have a problem with these characters, but that the database would
certainly not have any problems. I have created a table with a varchar2(30)
column and have successfully inserted and selected rows with these characters in
them. Before I respond to my boss, I thought I'd check with you to see if you
were aware of any situations where characters like these could cause trouble.
Thanks for your help!!

      and we said...
they are full of hooey.

only the applicaiton would have an issue with this.  For example, there is an
application called SQLPlus.  It has "issues" with &

ops$tkyte@ORA920> insert into t values ( 'A&P' );
Enter value for p:
old   1: insert into t values ( 'A&P' )
new   1: insert into t values ( 'A' )

1 row created.

but that application, knowing people would want to do this, gave us a
command:

ops$tkyte@ORA920> set define off
ops$tkyte@ORA920> insert into t values ( 'A&P' );

1 row created.

ops$tkyte@ORA920> select * from t;

X
-----
A
A&P

ops$tkyte@ORA920>

so, it is your application vendor that has a fear of ",#*%" -- not us.

Review &
      Followup

      Rating: 5
Can certain
      characters cause database errors?  December 23, 2002

Reviewer:  Tracy Adams  from Orem, UT USA
Thanks Tom, I appreciate your response. Now I can go to my boss with your answer
as well as my own testing results. Thanks again!!

      Rating: 3
Application
      generated by Oracle Designer ?  December 25, 2002

Reviewer:  Rui Brito  from Portugal
Hi
I use Oracle Designer and the forms generated have a hard-coded, created by
Oracle, that permits execute query with a few treacks. If we put the generated
form in Enter-Query, and type "#is null" in a item, and Execute-Query, the form
append the Where Clause with something like "... COLUMN is null", where the
COLUMN is the table column correspondent to form/item. Like this one, exists
many more features:
<, >, ...
So, if your application vendor use Designer (for example), they have this
limitation and provided by ORACLE !!!
Why limitation ? If a column value start with #, the query don´t work, because
the value # is replaced by the column name.

I hope that my approach helps to see this "limitations" like a "applications
rules" and not like a restrictions.
I appreciate very much Tom´s answers, and understand that the database is a
repository to almost everything, and applications can change this great rule.

Thanks and Merry Christmas
Rui Brito

December 23, 2002 KN -- Thanks for the question regarding
      "Maintaining Partitioned Tables", version 8.1.7.4

      You Asked
Hi Tom,

I need to build a table that will hold read-only data for up to 2 months. The
table will have a load (via a perl script run half hourly) of 3 million new rows
a day.  Queries will be using the date col in the table for data elimination,
hence partitioning is the way to go.

Using monthly our daily partitions seem impractical, partitioning data by week
seems sensible, as I can easily drop a partition older than 9 weeks old (to
ensure I hold 2 months data).

My question to you is what do you suggest as the best way to maintain this
table.  Should I create 9 partitions and have some code to cycle through these
and truncate the oldest partition to make room for new data.  Or is it better to
have partitions stamped with creation date and have the maintenance code drop/
create partitions based on that timestamp.

Virtually all my DBA experience has been with OLTP databases (where speed is the
essence), now have discovered the brave new world of datawarehouses, which has
thrown up some interesting problems

Any examples will be greatly appreciated

Many Thanks

      and we said...
I would have a procedure run weekly/whatever to add a new "high end" partition
for new data.  I would drop the oldest partition.  It is called a sliding
window.  See

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:928294572145
for an example.  there I used a partition "the_rest", that has the nice
attribute that if the job that is to add the new partition doesn't run for
whatever reason, we still collect the data - I'll just have to split that
partition.

Perl would probably be among the total dog slowest methods I could imagine to
bulk load the database.  SQLLDR would be my tool of choice in 8i -- external
tables + SQL would be my choice in 9i.  Perl -- would not be on the radar screen
even.

Review &
      Followup

      Rating: 5
Maintaining
      Partitioned Tables  December 23, 2002
Reviewer:
      A reader
Great.  Has given me the confidence I need to work with Partitioned tables

December 23, 2002 Ghazanfar -- Thanks for the question regarding
      "Index Rebuild", version 8.1.6

      You Asked
Very lucky to get opportunity to place a question...
Thanks in Advance ( the actual way )
Our ERP is based on CS-3, Oracle 8i is in backend.
We have serious issue of database performanance. I am of the thought this is due
to Indexing. Our database size is almost 30Gb.
Kindly suggest the method of Rebuild the indexes, and if I want to create new
indexes,which types of tables should I hit, and what the index types I should
chose. Please describe with detail so that I don't feel any problem conceptually
and Practically. This is OLTP type system, and certain tables are transacted
more than frequently at the sunrise..
I have another confustion, please describle difference between Instance and
Database......

If my question is very simple, don't mind, I am very new in this field.

Regards,
Ghazan.........

      and we said...
Uh oh.

"we are going slow"
"don't know why"
"theorize -- must be indexes, cause everyone knows that indexes = fast, so if we
are going slow, indexes must be broken"

do you see the flaw in this logic?

First of all -- in a 3rd party application you cannot be making the choice as
to what kind or even what fields should be indexed.  That is 100% the job and
responsibility of the 3rd party application vendor.  You cannot add indexes to
their schema.  You cannot remove indexes. You cannot change the type of indexes.
 You cannot TOUCH their stuff -- they and they alone can.

Second of all -- rebuilding the indexes would most likely be a waste of time,
energy and effort.  What you need to do FIRST is figure out WHY you are
performing slowly.  What is the cause.  Then, you fix the causes. You do not go
blindly into the dark and just change things.  Read this article:

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6601312252730
that'll show you a case whereby rebuilding the indexes was like setting
"slow=for_sure" for the first couple of days afterwards...

Use tools like statspack, tkprof, systems level stuff (no os, no advice -- sar,
vmstat, top on Unix, whatever gui du-jour you like on windows).  Find out the
root cause.

Statspack -- a 15 minute window -- would be what I start with perhaps.  See if
there is some low hanging fruit that can be fixed.  And by all means get your
application vendor involved

December 23, 2002 Samwise -- Thanks for the question regarding
      "Partitioning Performance", version 8.1.7

      You Asked
Tom,

We have a large table which is (range) partitioned on the primary key. This
primary key is also partitioned (locally/prefixed).
Queries running against this table, which are doing range scans on the primary
key, are running slower than they were before the table was partitioned.
Can you give some possible explainations of this?

Thank You, Tom.

      and we said...
because you didn't test it ....

happens every time, someone says 'hey, partitioning is like setting fast=true,
lets go for it'.  then, they find it runs slower.....

Think about it -- before you had a single index, a single data structure.  You
did queries like:

  select * from t where pk between A and B;

one index, simple.

Now, you have Umpteen indexes -- where pk between A and B might have to hit 2,
3, 10, 30, N indexes in order to get the answer.  You made it HARDER for us to
get the result sets you frequently ask for -- not EASIER.

Partitioning is not like setting "fast=true".  Partitioning is a tool that after
testing your theories, really truly understanding the physics behind it -- you
can use to make administration easier, queries run as fast or faster then
before.  Improperly used -- like anything else -- it'll be more like setting
"slow=definitely".

Sounds like you might want a globally partitioned primary key index to get back
to ONE index to scan -- either by having one global index partition or by having
larger ranges so as to reduce the number of indexes to scan.

Review &
      Followup

      Rating: 4
Good info...
      but  December 23, 2002
Reviewer:  Samwise
       from USA
Tom,

The index scans I am referring to are not large ones... They are 'small' scans
that occurs because sometimes we don't have the entire primary key... maybe we
just have the 1st or just the 1st and 2nd segments of the index. In other words,
it is almost a 'direct hit'. It may have to scan thru 100 or so records at most.
That being the case, I would have thought it would just be dealing with a single
partition(?).

Thanks.

          Followup:

Do you have a working example -- eg: here is the tkprof from the table BEFORE
partitioning, here is the tkprof afterwards showing the difference.

Or at least the create table/constraint/index statements and a tkprof showing
bad performance right now?

December 23, 2002 -- Thanks for the question regarding "Oracle
      Insert Question", version 8.1.7

      You Asked
I am trying to bulk insert into a oracle table through Coldfusion from one
datasource (Access) to another (Oracle). I know in SQL server you can separate
each new insert statement with a ";".  But this is giving me an error in
Oracle8i. Ex:

SQL Server:
 insert into table (column1,column2) values(1,2);
 insert into table (column1,column2) values(1,2);
 insert into table (column1,column2) values(1,2);

Oracle:
 insert into table (column1,column2) values(1,2);
 insert into table (column1,column2) values(1,2);

Is there hope?

      and we said...
ahhh -- don't do that!

You want (eg: MUST, HAVE TO) use bind variables.  Don't build literal sql
statements and execute them.

Yes, you could do what you are doing -- but I'm not going to tell you how -- it
would be the worst thing you could do.

Use generic connectivity and do an INSERT INTO oracleTable select * from
"sqlserver_table"

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4406709207206
has the pointer to the docs on setting this up.

Review &
      Followup

      Rating: 1
Oracle Insert
      Statement  December 23, 2002
Reviewer:  Grover
      Fields  from Orlando, FL USA
Not what I needed

          Followup:

It is what you want, it is what you need.

Doing it your way would be the single worst mistake you could make in your
career working with Oracle.

I only give "good" answers -- I don't tell you how to do it the *bad*, *wrong*
way.

      Rating: 4
bind
      variables  December 23, 2002
Reviewer:  josh

why are you using cold fusion?

Another method of doing this, that I have used successfully with cold fusion is
to create an oracle stored procedure that utilizes bind variables for the values
and call it from the cold fusion once you have retrieved the data from access.

Tom's method is indeed the fastest.

      Rating: 4
Oracle
      Insert  December 23, 2002
Reviewer:  Grover
      Fields  from Orlando, FL USA
This is a web based application and Coldfusion interacts with the access
database via an ODBC link.  Tom knows his stuff, I am sure it is the correct
method however, When migrating (DATA ONLY) from Access to Oracle it is a process
of two or three steps.
  1.  Build the tables
  2.  Query the access database and insert the results into Oracle.
  3.  Build the indexes, and dump the logs.

I have completed step 1.  The reason why this makes it so tough is this, with
SQL server insert statements, I can make one call to the database with all of
the data being outputted on the fly.  And it works fine.

<cfquery datasource="oracle">
 <Cfoutput query="output">
   insert into table(column1) values(#output.value#);
 </cfoutput>
</cfquery>

With Oracle on the other hand, I am TRYING to prevent from making more than one
database call to the server.

Again, this is a web based application that needs to be in the simplest form for
the consumer.

Ex:
http://www.clickfix.com/revorg/clickfix/migration/
UserID: admin
Password: demo

Click on link #1

This all a consumer has to do.  Enter there "NEW" datasource name.  Then all of
the content from the access database gets entered into the new database.

Thanks

          Followup:

I'll take your last paragraph and flesh is out some:

..
This all a consumer has to do.  Enter there "NEW" datasource name.  Then all of
the content from the access database gets entered into the new database.  And
the side effect we've just accomplished is to totally flood their shared pool
with literal SQL statements that actually take LONGER to parse then to actually
do the insert -- and we've bumped out all of their good SQL that they would like
to reuse by cannot.  In short, we've really killed their system and any good DBA
would outlaw our tool against their databases.
.....

Oracle is not SQLServer
SQLServer is not Oracle

They have no concept of a shared pool, we do.  What you propose to do would be a
very bad thing indeed in Oracle!

In Oracle -- it will be infinitely preferred for you to prepare a single insert
statement and bind the values to it over and over -- executing it many times
with a single parse.  Forget the round trips for a minute (you are concentrating
on trying to tune in one area when there is another infinitely more important
area).  Do this with bind variables.  Really -- do this with bind variables.  Do
it a row at a time.  Do not build tons of literal SQL like that and execute it.

      Rating: 5
Bind
      variables  December 23, 2002
Reviewer:
      Elche
You could also write an access macro to dump the contents of the tables out to
flat file and invoke insert/append from an external table if you are using 9i.

You should look into using oracle stored procedures called from coldFusion for
this.

you could write one stored procedure using bind variables that you invoke from a
loop in cold fusion.

If you don't make use of bind variables your app is going to be crazy slow.

      Rating: 5
Insert In
      Oracle  December 23, 2002
Reviewer:  Grover
      Fields  from Orlando, Fl USA
Thanks, that's all I needed to know.

Keep up the good work.

      Rating:
      5
ColdFusion  December 24, 2002
Reviewer:
      Andrew Gilfrin  from Dover, UK
If you are after information on Coldfusion with Oracle, I know that there is
somebody who reguarly uses Coldfusion with Oracle on Quest's Pipelines

http://www.quest-pipelines.com/pipelines/dba/index.asp
I wont mention names but I'm sure if you post a question in the PL/SQL forums
you would get an answer. Not that Toms answer is not correct.

December 23, 2002 Joe -- Thanks for the question regarding
      "Selective grants after creating a table or view ", version 8.1.7

      You Asked
Tom:

I work with in a PeopleSoft environment with over 12,000 tables/views. For
security purposes we have 25+ roles each having different levels of access. We
maintain the relationships between the different roles and their level of access
with several tables which allows us to have a view that can be queried. This
view (role_sql_text_vw) has two fields (recname, sql_text). What we currently do
after creating a table or view is query role_sql_text_vw  for the appropriate
statements. For example if we just created the table PS_JOB we would manually
issue the following SQL:

select sql_text from role_sql_text_vw where recname = 'PS_JOB';

This will return anywhere from 2 to 100 rows depending on what table or view is
queried. The rows look like this:

GRANT UPDATE ON SYSADM.PS_JOB TO PS_HR_READ;

We currently spool these lines to a file and then execute that file.

I would like to accomplish the following:

After creating a table or view have a trigger or ?? query ROLE_SQL_TEXT_VW and
automatically execute the 2 to 100 grant statements found in ROLE_SQL_TEXT_VW
for that object.

Any suggestions on how to accomplish this would be appreciated.

      and we said...
see

http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:646423863863

December 23, 2002 Frank -- Thanks for the question regarding
      "rollback segment writing order", version 8.1.7

      You Asked
Hi, Tom
Happy holiday!

I did a test with regard to the rollback segment's extent usage.

In the system i have only 1 rollback segment online rbs01 other than the system
rbs. And I assigned the transactions to use the rbs01 explicitly(set transaction
use rollback segment...).

First i had a session update something which made ext0 full. Then i had another
session update something which made ext1-ext18 full, and then committed it.
Finally, i had another session update something to make ext19 full and required
more extent to continue the transaction.

Rollback segment : rbs01 has 20 extents, and their status
ext0  active
ext1  commited
ext2  commited
...
...
...
ext19 current

I've found that the oracle generates a new extent or report error if
maxextents=20 when the current extent (ext19) is full and the very NEXT extent
(ext0) is still active. I also noticed that ext1-ext18 are all commited and free
to be overwritten, but oracle just doesnt use them.
Q1. Why doesnt oracle write the rollback data to other extents in stead of being
blocked by the right NEXT active one?
Q2. If this is true, has it been improved in the 9i's UNDO management, could u
show it by example?
Thank you!

      and we said...
q1) because they are by design a CIRCULAR buffer.
q2) it is the same, they are a circular buffer.

It is not a matter of "improving" -- if you managed the data in a "heap" as you
propose (which could be done), it would add tremendous overhead.  The simplicity
of the circular buffer is one of its best performance attributes.  Think about
it -- who is to say that there isn't a byte of rollback data needed in ext15 for
that active transaction?  It would take lots more mgmt to make that happen.

If you search for

"circular buffer"

you'll find quite a few discussions on this very topic.
December 23, 2002 alan -- Thanks for the question regarding "Using
      longs in triggers", version 8.1.7.4.5

      You Asked
Hi,

I have a third party application that has a few tables that contain long
columns.  I want to create an delete-before-row trigger to audit deletions from
these tables.  When a row is deleted I want to copy the row to an audit table.

The problem is that longs are not supported in triggers (or at least in the :OLD
record that I am picking up the row values from).

Is there a work around for this?  I thought about inserting all the data except
the long first and then fetching the row again and updating the long columns but
that seems yucky.

It would be sufficient to have a varchar2(2000) in the target audit table and
just copy up to this length.  That would cover 99% of the cases and if some data
got truncated we could live with that.

Thanks in advance for your help!!!

Alan

      and we said...
Here is one approach -- the autonomous transaction allows us to read the row as
it existed BEFORE your transaction wacked it.  See

http://asktom.oracle.com/~tkyte/autonomous/index.html
for more info on that feature.

ops$tkyte@ORA817DEV.US.ORACLE.COM> copy from ops$tkyte/xxx@ora817dev create foo
using select * from dba_views

Array fetch/bind size is 15. (arraysize is 15)
Will commit after every 10 array binds. (copycommit is 10)
Maximum long size is 80. (long is 80)
Table FOO created.

   1314 rows selected from ops$tkyte@ora817dev.
   1314 rows inserted into FOO.
   1314 rows committed into FOO at DEFAULT HOST connection.

that is my demo table - upon which we'll create a trigger...

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table audit_table;

Table dropped.

ops$tkyte@ORA817DEV.US.ORACLE.COM> create table audit_table
  2  as
  3  select owner, view_name, text_length, rpad('*',4000,'*') text,
  4         type_text_length, type_text, oid_text_length, oid_text,
  5         view_type_owner, view_type
  6    from foo
  7   where 1=0;

Table created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace function get_long_text(
p_rowid in rowid ) return varchar2
  2  is
  3      PRAGMA AUTONOMOUS_TRANSACTION;
  4          l_text long;
  5  begin
  6          select text into l_text
  7            from foo
  8           where rowid = p_rowid;
  9
 10          return substr( l_text, 1, 4000 );
 11  end;
 12  /

Function created.

that function will read the long for us.  If we have longs >32k, this will not
work, but rather you would have to use DBMS_SQL which allows you to piecewise
fetch a long instead...

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create or replace trigger foo_trigger
  2  after delete on foo for each row
  3  begin
  4      insert into audit_table
  5      ( owner, view_name, text_length, text,
  6        type_text_length, type_text, oid_text_length, oid_text,
  7        view_type_owner, view_type )
  8          values
  9      ( :old.owner, :old.view_name, :old.text_length, get_long_text(
:old.rowid ),
 10        :old.type_text_length, :old.type_text, :old.oid_text_length,
:old.oid_text,
 11        :old.view_type_owner, :old.view_type );
 12  end;
 13  /

Trigger created.

ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> set long 80
ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from audit_table;

no rows selected

ops$tkyte@ORA817DEV.US.ORACLE.COM> delete from foo where text_length = 15999;

1 row deleted.

ops$tkyte@ORA817DEV.US.ORACLE.COM> select * from audit_table;

OWNER                          VIEW_NAME                      TEXT_LENGTH
------------------------------ ------------------------------ -----------
TEXT
---------------------------------------------------------------------------------
--------------------------------------------------
TYPE_TEXT_LENGTH
----------------
TYPE_TEXT
---------------------------------------------------------------------------------
--------------------------------------------------
OID_TEXT_LENGTH
---------------
OID_TEXT
---------------------------------------------------------------------------------
--------------------------------------------------
VIEW_TYPE_OWNER                VIEW_TYPE
------------------------------ ------------------------------
SYS                            STRADDLING_TS_OBJECTS                15999
(select t.obj#, t.ts#, l.lobj#, l.ts#,'Base table and lob object not fully c

and there you go...

Review &
      Followup

      Rating: 5
Great Answer -
      But, just out of curiosity ...  December 21, 2002

Reviewer:  Alan Rosenthal  from San Diego
Hi,

Thanks for the workaround.

Just out of curiosity what is in the :OLD.LONGCOLUMN variable in the record?  Is
it the long value from the deleted record?  Undefined?  Junk?

Thanks again,

Alan

          Followup:

junk

      Rating: 5
Great, but why
      the Autonomous Transaction?  December 23, 2002

Reviewer:  alan rosenthal  from san diego
Hi Tom,

Your suggestion worked fine.  Thanks!

But, if I may ask, what is the purpose of the AUTONOMOUS TRANSACTION Pragma in
your code example?  I read through the article you provided but am still not
sure why that is there.

Thanks again,

Alan

          Followup:

take it out and see what happens.

You need to read some data you deleted.  You need to do this in a row trigger.
well, that'll not happen due to the mutating table constraint (also, the data
unfortunately doesn't really exist anymore!)

So, we use a separate transaction which cannot see the delete yet. Using the
built in multi-versioning that Oracle always does, that autonomous transaction
sees the table BEFORE the delete.

Hence -- it avoids the mutating table and it can still see the data.

Now, everyone -- don't get all gung ho and thing "woo-hoo, the mutating table
issue goes away".  This is the 1% of the time that this feature is useful for
resolving the mutating table error.  The other 99 times out of 100 *it does not
apply*...


Subject | Article | Last Updated (East coast time)
Grouping with analytics | article only | Dec 24, 2002 08:25 am
right-hand index | article only | Dec 24, 2002 08:28 am
Functions | article only | Dec 24, 2002 10:05 am
When is advisable to reinstall listener. | article only | Dec 24, 2002 10:11 am
Executing a DDL-statement > 32K in PL/SQL | article only | Dec 24, 2002 10:15 am
Regarding upgradation | article only | Dec 24, 2002 11:58 am
While experimenting with DUAL | article only | Dec 24, 2002 01:40 pm
N to M paging | article only | Dec 24, 2002 05:54 pm
Preventing ARC from Jamming DB | article only | Dec 24, 2002 07:53 pm
access and writing to non-oracle databases | article only | Dec 25, 2002 08:38 am
'like' operator in decode function | article only | Dec 25, 2002 08:54 am
pinnign objects | article only | Dec 25, 2002 04:08 pm
high watermark | article only | Dec 26, 2002 09:46 am
Rename primary key | article only | Dec 26, 2002 11:22 am
RAID 5 | article only | Dec 26, 2002 11:23 am
database | article only | Dec 26, 2002 11:25 am
unique key vs unique index | article only | Dec 26, 2002 11:59 am
Dedicated Vs Shared server connections. | article only | Dec 26, 2002 02:34 pm
Select Min(column) v/s using index and stopkey (rownum) | article only | Dec 26, 2002 05:26 pm
CBO | article only | Dec 27, 2002 09:16 am
Doing it in SQL or in PL/SQL | article only | Dec 27, 2002 09:34 am
STATSPACK | article only | Dec 27, 2002 11:14 am
How to convert Hex to decimal? | article only | Dec 27, 2002 11:28 am
How to sepreate milion of records to number of files | article only | Dec 27, 2002 03:32 pm
how to create test as a copy of prod, but much smaller | article only | Dec 27, 2002 03:51 pm
How to show the contents of an object table | article only | Dec 27, 2002 05:25 pm
Pessimistic Locking via Web | article only | Dec 27, 2002 05:27 pm
indexing question | article only | Dec 28, 2002 10:03 am
Summary Tables | article only | Dec 28, 2002 12:00 pm
v$session_longops rubbish data | article only | Dec 28, 2002 01:50 pm
Tracing packages in SQL*Plus | article only | Dec 28, 2002 03:54 pm
PL/SQL Tuning | article only | Dec 29, 2002 09:43 am
long query | article only | Dec 29, 2002 09:59 am
NOT != or <>, disable index usage. | article only | Dec 29, 2002 06:14 pm
Sql Query | article only | Dec 30, 2002 08:00 am
Does a Bitmap index in OLTP make long slowdowns? | article only | Dec 30, 2002 08:24 am
Rename a column | article only | Dec 30, 2002 12:53 pm
Exp from oracle/Linux & imp on oracle/NT | article only | Dec 30, 2002 12:54 pm
Deadlock detection internals | article only | Dec 30, 2002 02:52 pm
Duplicate check constraints | article only | Dec 30, 2002 02:58 pm
Relational database - why the name | article only | Dec 30, 2002 03:27 pm
8i to 8.0.5 - Subqueries | article only | Dec 30, 2002 04:31 pm
row(s) 1 - 42 of 42
December 24, 2002 Mishka -- Thanks for the question regarding "Grouping with analytics", version 9.0
You Asked
Hi, Tom.

I've got a SQL puzzler that I haven't been able to work out on my own and I was
hoping you could give me an assist.  Here's the underlying table:

my_tab(
 zip_code    VARCHAR2(5),
 avg_income  NUMBER);

The avg_income field contains a number between 1 and 59999. I'm trying to find
the number of zip_codes for a given range of avg_income.  The output I'm trying
for would look something like this:

zip_count   income_range
152         1-5000
438         5001-10000
1455        10001-15000
5365        15001-20000

Thanks.

Mishka
and we said...
does not need analytics.  Simple division or CASE will do.  Consider:

ops$tkyte@ORA920> select avg_income,
  2         floor( (avg_income-0.000001)/5000 ),
  3         case when avg_income between 1 and 5000 then '1-5000'
  4              when avg_income between 5001 and 10000 then '5001-10000'
  5              when avg_income between 10001 and 15000 then '10001-15000'
  6              when avg_income between 15001 and 20000 then '15001-20000'
  7              else 'above 20000'
  8         end income_range
  9    from t
 10  /

AVG_INCOME FLOOR((AVG_INCOME-0.000001)/5000) INCOME_RANG
---------- --------------------------------- -----------
         1                                 0 1-5000
      5000                                 0 1-5000
      5001                                 1 5001-10000
     10000                                 1 5001-10000
     10001                                 2 10001-15000
     15000                                 2 10001-15000
     15001                                 3 15001-20000
     20000                                 3 15001-20000
     20001                                 4 above 20000

9 rows selected.

So, you can use either of:

ops$tkyte@ORA920>
ops$tkyte@ORA920> select count(distinct zip_code), income_range
  2    from (
  3  select zip_code,
  4         floor( (avg_income-0.000001)/5000 ) grp,
  5         case when avg_income between 1 and 5000 then '1-5000'
  6              when avg_income between 5001 and 10000 then '5001-10000'
  7              when avg_income between 10001 and 15000 then '10001-15000'
  8              when avg_income between 15001 and 20000 then '15001-20000'
  9              else 'above 20000'
 10         end income_range
 11    from t
 12         )
 13   group by income_range
 14  /

COUNT(DISTINCTZIP_CODE) INCOME_RANG
----------------------- -----------
                      1 1-5000
                      1 10001-15000
                      1 15001-20000
                      1 5001-10000
                      1 above 20000

ops$tkyte@ORA920> select count(distinct zip_code), grp
  2    from (
  3  select zip_code,
  4         floor( (avg_income-0.000001)/5000 ) grp,
  5         case when avg_income between 1 and 5000 then '1-5000'
  6              when avg_income between 5001 and 10000 then '5001-10000'
  7              when avg_income between 10001 and 15000 then '10001-15000'
  8              when avg_income between 15001 and 20000 then '15001-20000'
  9              else 'above 20000'
 10         end income_range
 11    from t
 12         )
 13   group by grp
 14  /

COUNT(DISTINCTZIP_CODE)        GRP
----------------------- ----------
                      1          0
                      1          1
                      1          2
                      1          3
                      1          4
Review & Followup
Rating: 5
FLOOR pls explain December 23, 2002
Reviewer: A reader
Can u please explain floor( (avg_income-0.000001)/5000 )
why do u choose 0.000001 ?

Followup:
figured avg_income was dollars and cents (2 decimal places).  So i picked a
number smaller then 0.01 so as to not materially affect the result.  In this
fashion 5000, when having that small number subtracted from it and divided by
5000 -- returns 0, so the range is inclusive instead of exclusive.

Rating: 5
Good pointer December 23, 2002
Reviewer: Mishka McCowan from Bethesda, MD
Thanks for showing me how to solve the problem and for indirectly showing me
something else: Just because I'm unfamiliar with an aspect of Oracle (in this
case analytics) doesn't mean it holds the answer to the problem at hand.

Thanks again and have a great holiday.

December 24, 2002 Winston -- Thanks for the question regarding "right-hand index", version 8.1.7.3
You Asked
Hi Tom,

On the document "Oracle8i Designing and Tuning for Performance", it says
"Scalability Limitations Within the Application
In most of this CPU tuning discussion, we assume you can achieve linear
scalability, but this is never actually the case. How flat or nonlinear the
scalability is indicates how far away from optimal performance your system is.
Problems in your application might be adversely affecting scalability. Examples
of this include too
many indexes, right-hand index problems, too much data in the blocks, or not
properly partitioning the data. These types of contention problems waste CPU
cycles and prevent the application from attaining linear scalability."

1. Can you elaborate on 'right-hand index problem'? What is it? How to detect it
and how to avoid it?

2. My understanding is that "Two much data in the blocks" could possibly save
I/O. Can you give me an example in which case it could need more CPU time.

Thanks and hava a wonderful Christmas!
and we said...
1) it is a rare issue in very high end systems.  It is inserting into a table
that has an index on a column that is always increasing (eg: a column populated
by a sequence).  All of the values goto the "right hand side".

reverse key indexes may be useful in such a case.  It is *very rare* to need to
go that far.

2) update t set column = column || column;

on a table with full blocks will cause massive row migration as the data no
longer fits on a block.

insert into t (y ) select x from another_t

where there is an index on y and the index is "fully packed" will cause us to
split blocks all over the place.
Review & Followup
Rating: 4
December 23, 2002
Reviewer: A reader
In the case of "Two much data in the blocks" it could affect the performance of
insert/update (many cpu-intensive operations behind the scene to maintain the
index), but it will likely improve the performance of query.

Rating: 4
December 23, 2002
Reviewer: A reader

Rating: 4
what's the rare condition? December 23, 2002
Reviewer: kumar from India
Tom,
You have mentioned in your reply, "It is *very rare* to need to go that far".
Can u elaborate what is that *rare" case condition ? we have similar condition
in our db and so far not faced any major performance issue.

Followup:
exactly -- that is why it is rare.  It would happen in extremely high end
systems, typically in a clustered environment (lots of block sharing across
nodes).

Rating: 3
What's the rare condition? December 23, 2002
Reviewer: kumar from India
Tom,
In your reply for right-hand indexes you have mentioned, "It is *very rare* to
need to go that far". Can u elaborate what is that *rare* case condition ? we
have similar indexes in our db but so far not faced any major issue.Just wanted
to be aware of that.

Have a great christmas !!!

December 24, 2002 kam -- Thanks for the question regarding "Functions", version 8.1.7
You Asked
Sir
   You Have a Function f1 which is used to insert into a Temporary Table.Can you
use that function in a Select Statement

ex:- select f1 from dual;

Thanks...
and we said...
If it is coded as an autonomous transaction, yes.

See
http://asktom.oracle.com/~tkyte/autonomous/index.html
i have an example of doing exactly that there.
December 24, 2002 Juan -- Thanks for the question regarding "When is advisable to reinstall listener.", version 9.2
You Asked
Hi Tom,

Merry Chrismas o<:)>

In the real world did you see the need for reinstalling the LISTENER service,
and how you know you need it.

Recently I had a problem connecting with the database after installing Oracle
Server, only after reinstalling the listener this problem solved, this was not a
problem of configuration files.

Is there a flag that tells you, you need to reinstall it.

I tell this because I have a problem after applying patch 9.2.0.2,
and one user told me she had a similar problem , and she resolved it
reinstalling it´s listener.

But I don't like the technique DO THIS, TO SEE IF SOMETHING HAPPENS, I would
like to know if there is something in the debugging that flags you the need for
reinstalling the LISTENER.

Thanks in advance.
and we said...
Never.

It was a problem of the configuration files or you got into DLL hell (thanks
bill).  But since you do not give me any information like error messages,
symptons, etc -- I cannot say.

I've applied the 9.2.0.2 patch without issue -- I've only done it on linux
though, cannot vouch for windows.

If you follow the upgrade procedures as documented, you should never have to
reinstall anything.
Review & Followup
Rating: 3
December 24, 2002
Reviewer: A reader
Thanks Tom

December 24, 2002 Vincent -- Thanks for the question regarding "Executing a DDL-statement > 32K in PL/SQL", version 9.0.2
You Asked
In a database procedure I have a DDL-statement stored in a CLOB. The length of
this DDL-statement is more than 32K. Therefore I can not use EXECUTE IMMEDIATE.
Do you know any way to execute the DDL-statement?
and we said...
wow, what DDL statement do you have that would be bigger then that.  Thats
incredible.  Anyway.
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1023031268045
talks about how you can use dbms_sql.parse to parse a plsql table type that
contains the DDL statement in pieces (as if you typed it in line by line in
sqlplus).  It can deal with statements >32k.
December 24, 2002 Om -- Thanks for the question regarding "Regarding upgradation", version 8.1.7
You Asked
Dear Tom,

We had the following internal error on one of our databases.

Subject: Re : ORA-00600: internal error code, arguments: [16365], [3156571152],
[1], [10], [], [], [], []

and Oracle said

"This error is a half-duplex violation

Could be caused by bug 738982 which is fixed in the 8.0.5.2 patchset so I would
recommend that you apply this in any event."

Question
========

We are planning to upgrade our oracle database to 8.1.7 for
the following two reasons -

1) To resolve the patch set issue.

2) To make use of features available in 8.1.7.

If we just upgrade the database to 8.1.7 will it serve the purpose.

Best regards,
Om
and we said...
I see no relationship between that bug# and this internal error.  This internal
error is caused by issues on the client side, not the server side.  It is
generally caused by improper client code (eg: multi-threaded code that is not
well behaved).  PHP is known to cause this - and PHP fixed it.

But you give me no real information to go on -- so I cannot really comment
further.

Make sure to upgrade to 8174, not 8170 -- as 8174 is the ONLY v8 code that is
supported.
December 24, 2002 raju -- Thanks for the question regarding "While experimenting with DUAL", version 9.2.0.1
You Asked
Hi Tom,

I tried experimenting with some of the SQL's to do with DUAL table which was
posted on this site. I wanted to 'select from dual' after doing 'alter database
close' but couldn't do so.

There were no session connected to the database except for SYS, but the command
failed. Any ideas where I am going wrong.

SYS@ORA92:MYCOMP> conn sys/change_on_install@ora92 as sysdba
Connected.
SYS@ORA92:MYCOMP> alter database close;
alter database close
*
ERROR at line 1:
ORA-01093: ALTER DATABASE CLOSE only permitted with no sessions connected

So, just to make sure that there was really nobody connected I wanted to
shutdown and restart the database.The shutdown was successful but the startup
failed.

SYS@ORA92:MYCOMP>
SYS@ORA92:MYCOMP> shutdown
Database closed.
Database dismounted.
ORACLE instance shut down.
SYS@ORA92:MYCOMP> startup
ORACLE instance started.
ORA-12516: TNS:listener could not find available handler with matching protocol
stack
SYS@ORA92:MYCOMP>

I am using 9.2.0.1 database on WindowsNT.

Thanks
and we said...
alter database close will fail for many reasons -- job queue processes,
dbms_job, AQ, many background processes.  Generally:

sys@ORA920> alter database close;
alter database close
*
ERROR at line 1:
ORA-01093: ALTER DATABASE CLOSE only permitted with no sessions connected

sys@ORA920> alter system set job_queue_processes = 0;
System altered.

sys@ORA920> alter system set aq_tm_processes=0;
System altered.

sys@ORA920> alter database close;
Database altered.

will do it.

As for the second one, haven't seen that on myself.  I cannot reproduce.  But
not knowing your tns and shared server setup, I would guess you want to start
looking there.

sys@ORA920> shutdown
ORA-01109: database not open

Database dismounted.
ORACLE instance shut down.
sys@ORA920> startup
ORACLE instance started.

Total System Global Area  303108296 bytes
Fixed Size                   450760 bytes
Variable Size             134217728 bytes
Database Buffers          167772160 bytes
Redo Buffers                 667648 bytes
Database mounted.
Database opened.
sys@ORA920>

I did find a support note on this simply by searching for ora-12516 startup on
metalink:

Article-ID:         <Note:209410.1>
Alias:              SOL:OSS28215
Circulation:        PUBLISHED (EXTERNAL)
Folder:             network.TNSListener

    * symptom: Listener errors on startup
    * symptom: IIOP configured in the Listener.ora file
    * symptom: TNS-12516 / ORA-12516: TNS:listener could not find available
handler with matching protocol stack
* cause: LSNRCTL uses the first address listed in the LISTENER.ORA file to
communicate with the listener process. It is unable to use GIOP for this.

fix:

Add an IPC address as the first address listed in the ADDRESS_LIST section of
the LISTENER.ORA file.

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1))
    )
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.10)(PORT = 1521))
    )
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.10)(PORT = 2481))
      (PROTOCOL_STACK =
        (PRESENTATION = GIOP)
        (SESSION = RAW)
      )
    )
  )
Review & Followup
Rating: 5
Thank December 24, 2002
Reviewer: raju from london
Tom,

You are a star....Thanks ...I got things working by following your comments.

Rating: 5
Something more on Startup.. December 24, 2002
Reviewer: raju from london
Hi Tom,

Why starting the WindowsNT service always starts the database ? The reason I ask
is ... I tried shutting down the database by connecting as SYS, but the service
was still running. So I stopped the service and then wanted to startup the
database by connecting as SYS. This is what happened when I did that ...

D:\>sqlplus "sys/sys@ora92 as sysdba"

SQL*Plus: Release 9.2.0.1.0 - Production on Sun Sep 9 18:15:21 2001

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

ERROR:
ORA-12500: TNS:listener failed to start a dedicated server process

So I had to start the service which also started the database. Is it OK to start
the database via services ?

Thanks

Followup:
You need to have the service running -- the database may or may not be started
-- but the SERVICE must be running.

In order to set the autostart status of the database -- see the documentation in
the window nt admin guide for oradim.  this tool will set that.

Yes, it is OK to start/stop the database via the services.  back when I used
windows -- it is what I did on my testing machines.

December 24, 2002 Winston -- Thanks for the question regarding "N to M paging", version 8.1.7
You Asked
Hi Tom,

I have browsed your web site about rownum and paging N to M

 select a.c1, a.c2, a.c3, a.c4
      from ( select a.* , rownum r
               from ( select t1.*, t2.* from t1, t2
                      where t1.c1=t2.c1
                      order by t1.c1, t2.c2 desc,t1.c3 ) a
              where rownum <= :M
               )
     where r >= :N
    /

I understand by reading your website and by doing the TKPROF myself that if T1,
T2 contains large volume of data, we'd better have index for the columns in the
order by clause. And I understand the huge performance difference between
'no-index could be used' case and 'index could be used' case. If index could be
used, much less I/O , much faster

My question is how can I make sure that we always have the right index in place
no what what the result set will be ordered by. For example if it's order by
columns from different tables joined together?
and we need to allow the web users to pick up any columns display to them? If
they click on a column title once...then order the result set by that column in
ascending order... click again, order by desc..

Thanks for your time and have a wonderful Christmas!
Winston
and we said...
"If index could be used, much less I/O , much faster"

that is only true sometimes!!!  just keep that in mind.  In general, to get the
first rows -- an index path could be the fastest path.  To get the 'middle' of a
huge result set -- probably not, to get the end of it, most likely not.  See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6749454952894
for a good example of that.

In order to get the best overall performance from this, you might consider:

select a.c1, a.c2, a.c3, a.c4
      from ( select /*+ FIRST_ROWS */ a.* , rownum r
               from ( select t1.*, t2.* from t1, t2
                      where t1.c1=t2.c1
                      order by t1.c1, t2.c2 desc,t1.c3 ) a
              where rownum <= :M
               )
     where r >= :N
/

Here, since you have a multi-table join, there will be no chance of an index
returning the data sorted -- but by using top-n processing as you are here, you
do it as efficiently as possible.
Review & Followup
Rating: 5
Thank you December 24, 2002
Reviewer: A reader
In my case, using index and not using index, there is huge difference.

Case 1. No index

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch       11     44.74      68.11      27300    1313488          8         100
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       13     44.74      68.11      27300    1313488          8         100

Misses in library cache during parse: 0
Optimizer goal: FIRST_ROWS
Parsing user id: 55     (recursive depth: 1)

Rows     Row Source Operation
-------  ---------------------------------------------------
    100  VIEW
    100   COUNT STOPKEY
    100    VIEW
    100     SORT ORDER BY STOPKEY
 303844      FILTER
 303844       HASH JOIN OUTER
 303844        HASH JOIN OUTER
 303844         NESTED LOOPS
 193026          NESTED LOOPS
 155135           TABLE ACCESS BY INDEX ROWID T1
 155135            INDEX RANGE SCAN (object id 5545)
 348159           TABLE ACCESS BY INDEX ROWID T2
 348159            INDEX RANGE SCAN (object id 5560)
 303844          TABLE ACCESS BY INDEX ROWID T3
 496869           INDEX RANGE SCAN (object id 5488)
     49         TABLE ACCESS FULL T4
     54        TABLE ACCESS FULL T5

Note: I have another hint  /*+ FULL(T4) FULL(T5) */ in addition to first_rows,
otherwise we cannot get 49, 54.

********************************************************************************
Case 2 No index
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.01       0.00          0          0          0           0
Fetch       11      0.03       0.03          0        696          0         100
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       13      0.04       0.03          0        696          0         100

Misses in library cache during parse: 0
Optimizer goal: FIRST_ROWS
Parsing user id: 55     (recursive depth: 1)

Rows     Row Source Operation
-------  ---------------------------------------------------
    100  VIEW
    100   COUNT STOPKEY
    100    VIEW
    100     FILTER
    100      NESTED LOOPS OUTER
    100       NESTED LOOPS OUTER
    100        NESTED LOOPS
     59         NESTED LOOPS
     52          TABLE ACCESS BY INDEX ROWID T1
     52           INDEX RANGE SCAN DESCENDING (object id 5545)
    110          TABLE ACCESS BY INDEX ROWID T2
    110           INDEX RANGE SCAN (object id 5560)
    158         TABLE ACCESS BY INDEX ROWID T3
    158          INDEX RANGE SCAN (object id 5488)
    100        TABLE ACCESS BY INDEX ROWID T4
    199         INDEX UNIQUE SCAN (object id 5541)
    100       TABLE ACCESS BY INDEX ROWID T5
    199        INDEX UNIQUE SCAN (object id 5521)

********************************************************************************

Do you have more suggestions for "no indexe" case? The I/O is just so much. I am
trying to benchmark clustering T1 and T2 now. I would like to know what other
options you recommend me to try out

Thanks

Followup:
Don't get me wrong -- for these types of queries, using the index is most likely
the right approach.

It is the blanket statement "indexes = fast" that was I clarifying.

To get the first 100 rows -- this is the right approach.

Rating: 4
December 24, 2002
Reviewer: A reader
Correction to the above posting.

Case 1: "order by" columns doesn't allow using of index to filter at very ealier
stage

Case2: "order by" columns make it possible to filter out at early stage

December 24, 2002 Jay -- Thanks for the question regarding "Preventing ARC from Jamming DB", version 9
You Asked
Greetings Tom!
Here's my problem:

I want to ensure that my archive log location never shuts me down, hence the
attached script. However, theoretically, couldn't tar backup an archived file
that ARC hadn't finished filling -- i.e., the directory's capacity would pass
the specified threshold whereupon tar etc. would execute. Then, during this
time, a log switch to the directory would also occur; tar would then backup this
file as well even though ARC hadn't finished filling it up. The archived log
would be removed, and I would be left with a bad file? In any event, is this
approach the right way to go??

(I know people often just OS bkup the Arc location every few days,
but that doesn't prevent the possibility of a large unexpected job
creating greater than expected redo and jamming the db. Also, it
leaves this Achilles heel unguarded.)

Finally, I chose tar because RMAN will not execute from the server (RH72) itself
(despite the presence of the executable, and the fact that it runs from
clients). Do you have any idea why??

Finally, as far as Linux executables, is tar this best way to go?

Thanks a zillion
Jay Hart

df -k | grep /var > dfk.result
archive_filesystem=`awk -F" "  '{ print $6 }' dfk.result`
archive_capacity=`awk -F" "    '{ print $5 }' dfk.result`

if [[ $archive_capacity > 90% ]]
then
    echo "Filesystem ${archive_filesystem} is ${archive_capacity} filled"
    # rman -- RMAN DOES NOT EXECUTE ON THE LINUX SERVER DESPITE THE EXECUTABLE!!
      find /var/opt/oracle/archive/* -mtime +2 | tar cvzf
/usr/oracle/tar_archive /var/opt/oracle/archive
      find /var/opt/oracle/archive/* -mtime +2 -exec rm -f {} \;

fi
and we said...
as for:

...
Finally, I chose tar because RMAN will not execute from the server (RH72) itself

(despite the presence of the executable, and the fact that it runs from
clients). Do you have any idea why??
......

no clue since you give me nothing to go on!  No errors, no info, nothing...  So,
I'll pass on that.

As for the other thing.  Here is a script I've used in the past (you might have
to modify for different releases of Oracle as the v$log tables change over
time).  It was designed to compress archives AFTER they were archived.  Hence, I
ask the database "what files are done" and only compress them when they are
finished.  You could do the same for backup.  What you might do is (instead of
gzip'ing them) is a) COPY them to the alternate location and then b) rename them
so the subsequent ls's dont find them again

#!/bin/ksh

set -xv
export ORACLE_SID=ora8i
export ORACLE_HOME=/export/home/oracle8i
export PATH=$PATH:$ORACLE_HOME/bin:/opt/gnu/bin
export DUPLEX_LOC=/arch2/oradata/ora8i/arch

echo $ORACLE_HOME
echo $ORACLE_SID

echo set heading off > /tmp/$$.sql
echo set feedback off >> /tmp/$$.sql
echo set linesize 200 >> /tmp/$$.sql
echo set trimspool on >> /tmp/$$.sql
echo column cmd format a70 >> /tmp/$$.sql
echo spool /tmp/$$.sh >> /tmp/$$.sql
echo select "'/opt/gnu/bin/gzip -v ' || name ||' ; cp -p '||name||'.gz
$DUPLEX_LOC ' " >> /tmp/$$.sql
echo 'from sys.v_$archived_log' >> /tmp/$$.sql
echo 'where name in(' >> /tmp/$$.sql
ls /arch1/oradata/ora8i/arch/*.log | sed "s/^/'/;s/"'$'"/',/" >> /tmp/$$.sql
echo 'NULL ) and sequence# not in ' >> /tmp/$$.sql
echo '( select sequence# from v$log where archived = '"'NO'"')' >> /tmp/$$.sql
echo '/' >> /tmp/$$.sql
echo spool off >> /tmp/$$.sql
echo exit >> /tmp/$$.sql

sqlplus / @/tmp/$$.sql

sh /tmp/$$.sh

rm /tmp/$$.*
Review & Followup
Rating: 5
EXTREMELY HELPFUL December 24, 2002
Reviewer: Jay Hart
1. Tom -- are you a computer? You are sooooooo quick. MUCH appreciated! I
couldn't have asked for more -- I searched the net overtime.

2. As to the RMAN problem -- there's nothing to include/send. No error msg, no
Alert log entry, zilch. It just doesn't respond when accessed at the db. The
only thing I can report is that the command line skips to a new, unmarked line
(i.e. no shell) and that's it. However, RMAN is completely operative/responsive
from clients. I know its a weird one, and I've given up on it awile ago, but is
we're on the issue -- do you have any ideas?? The db and os operate flawlessly
(except for RMAN).

RH7.2/9.0.1

Followup:
I would have to refer you to support for #2 -- you see, as far as anything is
concerned -- there is not any difference between a client and server here... not
sure what to tell you.

Rating: 5
b/t/w -- a hint December 24, 2002
Reviewer: Jay Hart
by the way, if i intentionally insert
 oemapp (no argument)
at the kde shell prompt, it'l respond with

[oracle@RRlin bin]$ oemapp
Usage: oemapp <application name>
The installed applications are: txtmgr, worksheet, ocmcli, dbastudio, console,
esmsrv, opm, cpta, oemutil, topsess, dataguard, ocm, lmviewer, cp, sdoadvisor,
esm, pm
[oracle@RRlin bin]$

-- no RMAN!!

But -- isn't RMAN installed by default (esp. w/enterprise ed.)??!
Why wouldn't it be installed on the db? How can i add it?

Followup:
rman is rman -- it isn't oem.

I think I figured out why rman isn't working for you.

$ which rman
$ man rman

PolyglotMan(1)                                                  PolyglotMan(1)

NAME
       PolyglotMan,  rman - reverse compile man pages from formatted form to a
       number of source formats
.....

you aren't running our rman!!  if you just run rman, it's probably running the
wrong thing and that just sort of sits there -- reading from stdin!

Rating: 5
How did you know that????? December 24, 2002
Reviewer: Jay Hart
Incredible -- really. You are absolutely right -- PolyglotMan, who would have
known?!

As for oem -- I let that one off way too soon, and regreted
it the minute i did.

Followup:
I ran rman myself and hit the same issue ;)

then I noticed that rman help=y didn't produce an Oracle type response....

Simple "man rman" after that...

Rating: 5
Less seriously December 24, 2002
Reviewer: Jay Hart
Revealing though process - thanks for sharing it.
HOWEVER, even though you '... ran rman and hit the same issue ;) '
doesn't mean in any way that we're anywhere near the same league, because (among
others), you '...noticed that rman help=y didn't produce an Oracle type
response...Simple "man rman" after that...'

(Red)Hat's off to you! :)

n.b. will this be a book ftnote / oracle update??

Followup:
Look at that, it is already a support note ;)

Article-ID:         <Note:101050.1>
Circulation:        PUBLISHED (EXTERNAL)
Folder:             server.Recovery.RMAN
Topic:              Known Problems
Title:              RMAN Error on Linux - rman: can't pen rcvcat target or it
                    Just Hangs
Document-Type:      PROBLEM
Impact:             MEDIUM
Skill-Level:        NOVICE
Server-Version:     08.00.0X to 08.01.0X
Updated-Date:       08-FEB-2002 15:24:54
References:
Attachments:        NONE
Content-Type:       TEXT/PLAIN
Keywords:           HANG; LINUX; RMAN;
Products:           5/RMAN;
Platforms:          46;

Problem Description
-------------------

You are running RMAN from the command line and the prompt will not return
-or-
You receive a error similar to one of the following:
% rman: can't open rcvcat
-or-
% rman: can't open target
-or-
% rman: can't open nocatalog

Solution Description
--------------------

Ensure that correct RMAN executable is being run by typing

$which rman

The output will most likely show '/usr/bin/rman'

Execute RMAN by typing in the following:

$ORACLE_HOME/bin/rman

Another solution is to add $ORACLE_HOME/bin before /usr/bin
in the PATH environment variable

Explanation
-----------

Under a default REDHAT LINUX 6.1 and 6.2 install, a KDE program called rman (a
file
viewing / editing program) is installed under /usr/bin.  If this is listed
prior to $ORACLE_HOME/bin in the users PATH environment then it is
called instead of Oracle RMAN.

Rating: 5
Geeeze December 24, 2002
Reviewer: Jay Hart
Yup, well done
Come to think of it, if this hails from 6.1 ...

December 25, 2002 Eben -- Thanks for the question regarding "access and writing to non-oracle databases", version oracle 8
You Asked
I need to load data from Oracle into my user's ACT! database.  I
can probably do this with visual basic or some such but I'd
rather do it in oracle - preferably in sql plus (but forms or
reports in Developer 2000 would be ok).  The act file is a flat
file in dbf format.

Can you point me to any resources that would help me learn how
to open the dbf file with an sql session?

Added version number:    8.0.3.0.1
and we said...
If you have odbc access to it, we can.  Since DBF is odbc
accessible I will assume you have odbc access to it.  Here is a
note that shows how to copy data from Oracle to ODBC.

PURPOSE
-------

This document is a description on how to use ODBC from within
SQLPlus.  The example given is for FoxPro ODBC.  It also gives
some info on the COPY command to copy data from one DB to
another.

SCOPE & APPLICATION
-------------------

For everyone that wants to use ODBC, and that wants to be able
to use the COPY command. This document might be handy for people
that want to be able to interchange data from
another DB than Oracle into an Oracle DB, or vice versa.

USING ODBC AND THE COPY COMMAND
-------------------------------

-)Prerequisites:
* Make sure that there is a working ODBC DSN, this can best be
verified with ODBC test.
Remember: for Foxpro specific that the structure of the DB is
different from Oracle,
This means when doing the ODBCtest the SQL you enter is a bit
different from Oracle,
use as table name the file (dbf flat file).  A username/password
is not needed.
* You need to have OCA (Open Client Adapter) installed.
For the tar an Oracle8 v8.0.6 and OCA v6.0.5.3.0 from Oracle
Lite v4.0.0.2 was used.
When the OCA is not installed you might get an ORA-22 (invalid
session id; access denied)
error.

-)How to do it:
Here is what you can type in SQLPlus:
COPY FROM system1/manager1@odbc:dsnname TO
system/manager@v805.world CREATE foxprotable
(col1,col2,col3) USING SELECT * FROM foxpro.dbf;
The system1/manager1 is a default login, it is not used bij
Foxpro, but you have to put
something there.

-)Things to take into account:
* Make sure you exclude the rownumber column that is in the
Foxpro tables, when you add that
column to the list of columns to be copied you might get errors.
* You can always use a tool like MS Excel to get more info about
the conents of the Foxpro
DBF files.
* You might get a warning 'Server not....' this is normal, you
also get this message when
using the Oracle ODBC to login to the Oracle DB.
Review & Followup
Rating: 5
ODBC test works but not sqlplus January 07, 2002
Reviewer: DV from India
Dear Tom,

ODBC 32bit test works for me, but SQL*Plus is giving ORA-03121: no interface
driver connected - function not performed
error.
What can be wrong.
Regards

Rating: 2
What about an AlphaServer January 14, 2002
Reviewer: Pranav from CT, USA
Is it possible to get data from a DB2 database to Oracle on a VMS Machine.

Rating: 3
ora-03121 August 22, 2002
Reviewer: Mick Barlow from Lancs, England
I too, receive an 'ora-03121: no interface driver connected - function not
performed' and i cant work out why. Please help?

Followup:
are you actually using the sqlplus that comes with developer/2000
(forms/reports).

If not, this interface driver doesn't exist, only developer shipped it.

Rating: 5
INTERSOLV is not available on the 6i CD? December 25, 2002
Reviewer: Sikandar Hayat Awan from Pindi Bhattian - Pakistan
Dear TOM,
Please guide me as I am trying to install INTERSOLV drivers from Forms 6i but
unable to find there. In the developer 2000 CD these were available. I got info
from Metalink that drivers are available on 6i CD but when I try to install
(custom) unable to find there.
Please guide me in this regard so I will be able to install INTERSOLV drivers.
Thanks

Followup:
Just grab the ODBC drivers from the vendor that makes the ODBC source you want
to connect to -- they all have them.  goto their website and download them.

Rating: 4
SQLPLUS ODBC MS ACCESS April 09, 2003
Reviewer: A reader from Netherlands
This helped to access ms access database with sqlplus.

Rating: 3
ora-03121 April 26, 2003
Reviewer: Madhuri Bondre from India
hi

even I am getting the same error ora-03121.

I am using sqlplus.exe which is shipped along with developer 6i.
what could be reason for this error ?

how to solve
please help

December 25, 2002 Pranesh -- Thanks for the question regarding "'like' operator in decode function", version 8.1.5
You Asked
Hi.

i want to know whether we can use 'LIKE' operator in  DECODE function. if
yes..kindly let me know some examples to use .

i want to write a query like this :

if <column> like '%1%' then <function> .....is it possible ?

thnx in advance

Pranesh i s
and we said...
No, but you can easily use instr for that:

decode( nvl(instr( column, '1' ),0), 0, 'not there', 'there' )

In 816 and up, there is a CASE statement that accomplishes this nicely:

select case when column like '%1%' then ....
            else ...
        end
  from ....
Review & Followup
Rating: 5
Substr function for like operator December 26, 2002
Reviewer: Ashiq from Trichy,Tamil Nadu,India
We can   substr function for most like operators in CASE statement.For example

case when ename like 'EMP%' then 'Yes' else 'No' end

same thing we can accomplish thru substr function
  decode(substr(ename,1,3),'EMP','Yes','No') .

 We can do all the functionality through decode itself  without using CASE
statement.

December 25, 2002 Shdeeb -- Thanks for the question regarding "pinnign objects", version 816..3
You Asked
1. I have seen some high executions and i/os on some plsql packages.
I am not caching any procedure in my sharedpool so far. So far my shared pool
cache rations are very good neat to 100%. However these rations may not be
realistsic. So what should one look for contention on shared pool in stats pack
results?
I have se aside aroung 1/2 GN for shared pool. what queries shall i run to see
if my sharedpool is utilised in full or is there any space. This is so that if
there is a space i can pin these plsql packages or if its too free space i can
even shrink shared pool.
Also when pinnign packahes shall i pin the whole package
or one proc inside a package? Cna i pin fucntions too?
What do you recommedn here , does pinning program objects (func, proc, seq)
cause any perf degradation?
What stand oackages whould any one pin in all case?

2. I also like to pin some most commonn used small tables in sga.
But i have one default pool? what shoudl i do, should alter table cache help???
or rather i create multiple pools. With regard to multiple polls , dont you
think thats a headache? becuase you based al these pools (keep, recyel, def) to
have some values based on yoru queries that you run against obejcts space etc.
But to be hoenst you may decide on some tables to be in keep pool byt those
objects may increase in rows sometimes vbery fast, and you calulcation say after
3 months may not hold true. so you have to agains add more buffers and bounce
db... Is having multiple pools for oltp nature dn really makes a BIG diff in
perf?????

3. some says you can even cache objects on sga by alter table cache, and then
runnign select count(*) from table, is these true?? for 8i????

4. Do you know how to pin whole sga in nt in memory? does it worth doign this if
yo have ram?

thanks
and we said...
1) do not pin anything.  The shared pool does the work for you.  pinning was a
short term solution to a problem which the large_pool solved entirely.  pinning
is a waste of your time.

If something is executed frequently, it'll not be aged out.

use statspack to monitor your shared pool usage/utilization.

2) why?  do you have a scientific reason to do so?

besides, it is physically impossible to pin a table.  Cannot be done.  search
this site for

"alter table cache"

for discussions on what this actually does.  It does NOT pin a table in the sga
at all.  If you use the data, the data will be cached.  If you do not use the
data it will not be cached - that is the function of a cache.  It'll take care
of itself.

There are only three pools -- not too much of a headache - but it is the last
step in the tuning process, very last.

3) nope, never been true -- never.

4) nope, don't know if "lock_sga" works on that platform but in general -- it is
a very BAD thing to do.  you want the OS to have the flexibility to MANAGE
MEMORY -- that is its job.
Review & Followup
Rating: 5
agree with you 100% December 25, 2002
Reviewer: shdeedb from ksa
I fully agree with you and I appreciate youre replies because these are with our
exp.
Just one think  i like to ask:
you said large pool has colpletly solved pinning problem
how??
I am not using large pool becuase i though it is used by rman and under mts.
While i am on deciated server config.
So how come large pool will help in pinning?
and if if it does, what hall iset it too? initially?
Likewise there is another parameter java pool size whose def is 20mb , i set it
to 5mb also becuase we are not usng java database services. is this right thing
to do?
thanks

Followup:
You only needed to pin things when we used the shared pool as the ONLY pool and
you used features like MTS.

Under MTS, we would allocate memory for the UGA from the shared pool AND memory
for packages/cursors etc.

packages / cursors etc should be managed using a LRU.
UGA memory should be managed like a heap (eg: allocate -- and free explicitly)

but the shared pool only did LRU memory management -- hence UGA requests which
could be aged out (the session logged out) were not because they were "fresher".
  Hence the shared pool would get fragmented like crazy - things you didn't want
to age out would, things you wanted to age out wouldn't.  That and UGA memory
requests are "big", package/cursor/etc requests are small.

So, by dividing that memory up -- the problem that pinning was introduced to
help alleviate -- went away.

If you do not use PQ, rman, MTS -- you need no large pool and you never needed
pinning!

You can set the java pool really small (like 0m on later releases, 1m on older
ones) if not using java.

Rating: 5
thanks a lot December 25, 2002
Reviewer: shdeedb from ksa
Thanks indeed for your explanation.
I have asked you another questionin anohjer thread this evening, i think i
missed checking "email notification" so i can i get the reply from yoru
website.....

I wanted to add somethign on it.
 A transaction is sent to database at say 12:01:21 sec.
and got reply back from database at 12:13:56. which is 10 fold longer than it
normay takes.
However when I see the archived log via log minor, i see that the same
transaction starts (operation = start) as show in log at 12:03:55 sec and
completed (commit) at 12:03:56.
So what was the db doing bewtween 12:01:21 to 12:03:52.
Does that mean the db does a lot before anythign gets to redo log buffer?? and
then to redo log files????
I have a redo log buffer as of 5 mb and i fail to see it is hhavign a flusign
issue.
Please do not reply to this if you got my earliar email.
Cheers

Followup:
my question to you would be what is up with the elapsed time from 12:01:21 to
12:03:55??

Obviously there is more here then meets the eye, not understanding how your
system works, I cannot really answer.

When you see the commit record in the log, the stuff needed for redo for that
transaction was already written, so no -- it is not a 10 minute wait on the redo
log buffer.

Rating: 4
clarification December 25, 2002
Reviewer: shdeeb from ksa
Yes but you see (i hope you have read my other email where i have posted the log
miner entries and if not i can post em here).
The start of trnsaction and the insert into a log table goes around 12:01:21
then nothing in redo log, until
time 12:02:52 when i see a delete statement (sgainst the insert which i did
earliar) indicating a rollback.
This rollback is becuase there is a time out set at the client side for some
90secs. So why the trsna did not cont after it has inserted into the log table
like always
From aix i can see a lots of cpu waiting on i/o.
I think prob some check point occurs and db was so busy that nothign is accepted
in to redo buffer during that 90 secs. What do you think??

By the way how come youre are working on x-mas as for here its a normal day in
middle east.
Cheers
Merry xmas to you

December 26, 2002 deniz -- Thanks for the question regarding "high watermark", version 8.1.7.4
You Asked
Hello,

How can I find the segment which has highest watermark on a datafile and does
"alter table move" command drop the high watermark
so i can shrink the datafile.

Regards,
and we said...
ops$tkyte@ORA920> select file_id, max( to_char( block_id, 'fm0000000000' ) || '
' ||
  2         owner || '.' || segment_name || '.' ||  segment_type )
  3    from dba_extents
  4   where file_id = 1
  5   group by file_id
  6  /

   FILE_ID
----------
MAX(TO_CHAR(BLOCK_ID,'FM0000000000')||''||OWNER||'.'||SEGMENT_NAME||'.'||SEGMENT_
TYPE)
---------------------------------------------------------------------------------
--------------------------------------------------
         1
0000073217 A.T2_IDX1.INDEX

Elapsed: 00:00:03.46
ops$tkyte@ORA920> drop index a.t2_idx1;

Index dropped.

Elapsed: 00:00:00.31
ops$tkyte@ORA920> select file_id, max( to_char( block_id, 'fm0000000000' ) || '
' ||
  2  owner || '.' || segment_name || '.' ||  segment_type )
  3  from dba_extents
  4   where file_id = 1
  5  group by file_id
  6  /

   FILE_ID
----------
MAX(TO_CHAR(BLOCK_ID,'FM0000000000')||''||OWNER||'.'||SEGMENT_NAME||'.'||SEGMENT_
TYPE)
---------------------------------------------------------------------------------
--------------------------------------------------
         1
0000073209 A.T2.TABLE

Elapsed: 00:00:03.35
December 26, 2002 Sean -- Thanks for the question regarding "Rename primary key", version 817
You Asked
Hi Tom,

I inherited a database application with a lot of primary keys created with sys
name, such as SYS_C002383.  I want to change the name of primary key to the more
meaningful one, such as dept_deptno_pk.

What I did is:
Alter table dept
Drop primary key cascade;

But by doing this, I also need to recreate all related foreign keys.  It is a
daunting task since there are total more than one thousand  foreign keys.
Besides, I may miss some foreign keys if I have to manually recreate all of
them.

The question:
Are there any other ways to change the name of primary key?

Best,

Sean
and we said...
In 9iR1 and before -- you are doing the only thing that could be done for now.

I do have a script, cons.sql, which will make it so you don't lose those fkeys.
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:399218963817

In 9iR2 it is as easy as:

ops$tkyte@ORA920> create table t ( x int primary key );

Table created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> column constraint_name new_val c
ops$tkyte@ORA920> select constraint_name
  2    from user_constraints
  3   where table_name = 'T'
  4     and constraint_type = 'P'
  5  /

CONSTRAINT_NAME
------------------------------
SYS_C005198

ops$tkyte@ORA920>
ops$tkyte@ORA920> alter table t rename constraint &c to t_pk
  2  /
old   1: alter table t rename constraint &c to t_pk
new   1: alter table t rename constraint SYS_C005198 to t_pk

Table altered.

ops$tkyte@ORA920> select constraint_name
  2    from user_constraints
  3   where table_name = 'T'
  4     and constraint_type = 'P'
  5  /

CONSTRAINT_NAME
------------------------------
T_PK
Review & Followup
Rating: 5
names for other constraints December 25, 2002
Reviewer: Will from Texas
Do you prefer to give all constraints meaningful names, even not nulls or other
checks?

Followup:
not null -- i wouldn't name them, all others sure.

Rating: 5
Naming Convention December 25, 2002
Reviewer: Sri from India
Tom,

  What is the most acceptable naming convention for constraints.  For example if
empno is the primary key for emp table, I generally name the constraint as
pk_emp_empno.  But I have found some books following conventions like
emp_empno_pk etc.  Which is the most accepted one and why?

Sri

Followup:
Don't know -- they are "personal",  ask 5 people and you'll probably get 3 or 4
answers.

I seem to use

table_pk
t1_fk_t2
column_unique

myself.

December 26, 2002 Syed Jaffar -- Thanks for the question regarding "RAID 5", version 8.1.7
You Asked
Hi Tom,

Yesterday I have visited one of my friend's office and I had a loot at their
database.  They are using RAID 5 technology, Server is Compaq Proliant with 9
physical hard disk.  Those 9 physical hard disk were made logically into two
disks, i.e. c and d drive.  Oracle has installed in the D drive and ofcourse all
the datafiles have been installed in the same drive.
1.  Is there any use doing multiplexing of control file and redo log members on
drive D?
2.  Is the DATABASE may have disk i/o contention? as they have installed all the
files on the same disk?
The very bad thing is they are running database in noarchive mode. Can you
please clear me all these things. They have 2gb of physical ram and the SGA is
1077MB, that includes, 700 of Data Buffer, 300 Shared Pool majorly.

thanks in advance
and we said...
1) yes, you can get corruption of a control or redo log file and if you do --
bamm, it is gone.

Also, raid 5 is not perfect.  Lose two disks and bamm -- data gone.

2) but they are on many physical disks.  raid 5 is striping.  If I had my way
however, the temp files, redo, and control files wouldn't be on raid 5 unless
their write transaction level is way small -- trivially small.

By not running in archivelog mode -- they are assuring themselves that one day
they WILL lose their database -- not might, will.  100% sure on that.
Review & Followup
Rating: 5
Optimal Configuration December 25, 2002
Reviewer: Sri from India
Tom,

  Thank you for those excellent answers.  Can you please advice, for a medium
business organisation, what all things to take care when we buy a server and
install Oracle.  I mean, the server configuration and the installation details.

Sri

Followup:
don't know as if there is a single answer.  I personally would be looking for
the best box I could buy that doesn't come with windoze -- rather runs linux --
for the server.  Other then that, doesn't matter.

December 26, 2002 ankit -- Thanks for the question regarding "database", version 8.1.7
You Asked
why standby database is not created with export/import
and we said...
because standby databases are physical bit by bit copies and exp/imp is a
LOGICAL copy, not a physical bit by bit copy.

besides -- why would you want to take the SLOWEST method on earth (exp/imp),
instead of the fastest (datafile copy)...
Review & Followup
Rating: 2
standby database December 25, 2002
Reviewer: ankit from India
thats fine that exp/imp is logical but whats its going to have impact on the
standby.

Followup:
Umm, physical standby works by apply redo logs.

redo logs have things like "change 5 bytes on block 55 file 200 to 'abcde'"

If you use an IMP, block 55 of file 200 is DIFFERENT from block 55 of file 200
on the source system.  Hence you cannot apply the redo logs to it.

Therefore you need a PHYSICAL backup.

Still cannot figure out why you would even consider using the dog slowest
method...  Sort of funny "i want to know why I cannot do it as slow as
possible"...

December 26, 2002 Nagaraju -- Thanks for the question regarding "unique key vs unique index", version 9.2
You Asked
Tom,

Merry Chrismas eve

1.Can you tell whatz the difference between unique key and unique index

I see, on creating unique key unique index gets created.
on creating unique index unique key doesn't get created but uniqueness is still
checked.

so, i don't understand why is this?

2.I heard from somebody that uniqueness thru indexes is better, is that right?
why?

thanks in advance.

Happy holidays.
and we said...
1) see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:4941517587762
it is not true that creating a unique key will always create a unique index.

o it can use an existing unique index
o it can use an existing NON-unique index
o the index it creates when it creates one could be non-unique (deferrable
constraints)
o the index it creates when it creates one could be unique

2) 100% totally false.  disagree totally.  get them to provide their arguments
to the contrary and I'll gladly debate them.
Review & Followup
Rating: 4
Enforcing Unique Constraints without indexes? December 24, 2002
Reviewer: Dwayne Croteau from Calgary, AB
Merry Christmas Tom,

How do you enforce unique constraints without indexes?

Dwayne

Followup:
you cannot?  I gave the four situations above

o use existing unique
o use existing non-unique
o create unique
o create non-unique

depending on the circumstances...

Rating: 3
Enforcing Unique Constraints December 25, 2002
Reviewer: Dwayne Croteau from Calgary, AB
Tom,

I interpreted Nagaraju's second question as "I heard from somebody that
ENFORCING uniqueness thru indexes is better..."

Perhaps your interpretation of Nagaraju's question was "I heard from somebody
that UNIQUE INDEXES are better..."

Could you please elaborate?

Thanks

Dwayne

Followup:
There are two choices:

a) I use a unique index in order to enforce uniqueness.
b) I use a unique constraint in order to enforce uniqueness.

b) is superior.

A) may be a SIDE EFFECT of B) -- but maybe not.  In any case, using a unique
constraint when something is to be unique is the correct, proper and best
practice.

Using a unique index -- not.

Rating: 4
Original idea December 26, 2002
Reviewer: A reader
Hi,
What is the original idea of UNIQUE INDEX besides
the PK things involved?

THanks

Followup:
well, in v6.0 and before -- primary keys didn't do anything ;)

also, they still have a place.  Suppose you wanted a column X to be unique when
status = 'ACTIVE'.  If status <> 'ACTIVE', X doesn't have to be unique.  We'll,
you could write tons of buggy code or just:

create unique index i on t( decode( status, 'ACTIVE', x, NULL ) );

that'll do it -- try doing that with a constraint....

December 26, 2002 Ramasamy -- Thanks for the question regarding "Dedicated Vs Shared server connections.", version 8.1.7
You Asked
Tom,

Colud you tell me How do i find out whether the
database is running in dedicated server mode or Shared server mode?.

I am expecting something like this?.
For example i'll query like this to find the logging mode.

select log_mode from v$database

LOG_MODE
------------
NOARCHIVELOG

I know the rest of the short cuts to find it through looking
at background processes (ps -ef).

Thanks,
Ramasamy
and we said...
a database doesn't "run" in either mode.

It can accept connections of both kinds.

SQL> show parameter mts

will show it MTS has been configured

$ lsnrctl status

will show is the listener is setup to accept MTS...

but utlimately -- it is upto the client connection.  They can specify
server=shared, server=dedicated in their tns connection entries.

If you would like to find out what SESSIONS are using,

select username, status from v$session

status in ( shared, none ) = active/inactive MTS connection
status in ( active, inactive ) = active/inactive dedciated server connection
Review & Followup
Rating: 3
December 26, 2002
Reviewer: Ramasamy from Charlotte , NC
select server from v$session.

server column on v$session can tell
whether it is dedicated or shared.

Thanks.
Ramasamy

December 26, 2002 Devang -- Thanks for the question regarding "Select Min(column) v/s using index and stopkey (rownum)", version 8.1.7
You Asked
This is a conceptual question.

I am trying to tune a sql and was given a suggestion but I am not sure about the
result be same all the time.

Background:
Table "tab1" with around 100K+ records and PK is "col1" (Number).

Question:
Will the output of following two sql be identical all the times

SELECT MIN(col1)
FROM tab1
WHERE status_flag=:bind_var1
AND col1>=:bind_var2

SELECT col1
FROM tab1
WHERE status_flag=:bind_var1
AND col1 >= :bind_var2
AND rownum = 1
ORDER BY 1

Explain plan respectively are

SELECT STATEMENT Optimizer=CHOOSE (Cost=19 Card=1 Bytes=6)
SORT (AGGREGATE)
TABLE ACCESS (BY INDEX ROWID) OF tab1 (Cost=19 Card=5175 Bytes=31050)
INDEX (RANGE SCAN) OF pk_tab1 (UNIQUE) (Cost=4 Card=5175)

SELECT STATEMENT Optimizer=CHOOSE (Cost=19 Card=5175 Bytes=31050)
COUNT
FILTER
TABLE ACCESS (BY INDEX ROWID) OF tab1 (Cost=19 Card=5175 Bytes=31050)
INDEX (RANGE SCAN) OF pk_tab1 (UNIQUE) (Cost=4 Card=5175)

I am aware that first sql will never raise no_data_found exception.

Thanks
and we said...
the second query returns garbage.

It gets one *random* row from the table -- sorts it (the one row)....  It is
relying on the optimizer to choose the index on the primary key.  It is not any
faster then the min would be -- just less stable, prone to producing the wrong
answer, totally non-intuitive as to its actual question.

In short, I wouldn't do it.  The presence or lack of an index should not have
ANY bearing on the answer. And here it does.  Tomorrow -- a new index, a
different plan -- wrong answer comes out.  Consider:

ops$tkyte@ORA817DEV> create table emp as select * from scott.emp order by empno
desc;

Table created.

ops$tkyte@ORA817DEV>
ops$tkyte@ORA817DEV> variable bind_var1 varchar2(30);
ops$tkyte@ORA817DEV> variable bind_var2 number
ops$tkyte@ORA817DEV>
ops$tkyte@ORA817DEV> exec :bind_var1 := 'CLERK'; :bind_var2 := 7788;

PL/SQL procedure successfully completed.

ops$tkyte@ORA817DEV>
ops$tkyte@ORA817DEV> select min(empno)
  2    from emp
  3   where job = :bind_var1
  4     and empno >= :bind_var2;

MIN(EMPNO)
----------
      7876

ops$tkyte@ORA817DEV>
ops$tkyte@ORA817DEV>
ops$tkyte@ORA817DEV> select empno
  2    from emp
  3   where job = :bind_var1
  4     and empno >= :bind_var2
  5     and rownum = 1
  6   order by 1;

     EMPNO
----------
      7934

ops$tkyte@ORA817DEV>
ops$tkyte@ORA817DEV> select *
  2    from (
  3  select empno
  4    from emp
  5   where job = :bind_var1
  6     and empno >= :bind_var2
  7   order by 1
  8     )
  9   where rownum = 1
 10  /

     EMPNO
----------
      7876

so, you need to go back to the person who suggested the rownum and remind them
that ROWNUM is assigned AFTER the row is fetched and the rest of the predicate
is evaluated but BEFORE any ordering or aggregation -- so they get a row, sort
it and return it.  You need to sort, get a row, return!  That is what the second
query does.

In some cases, the second query may be more performant then the min query --
especially in this "two key" case -- where the index cannot be used for the
min/max itself.

also, you can solve that little "it doesn't always return a row" problem like
this:

ops$tkyte@ORA817DEV> select min(empno)
  2    from (
  3  select empno
  4    from emp
  5   where job = :bind_var1
  6     and empno >= :bind_var2
  7   order by 1
  8     )
  9   where rownum = 1
 10  /
December 27, 2002 Venkatesa -- Thanks for the question regarding "CBO", version 8.0.6
You Asked
Hi Tom,
   To put it simple, if CBO is the default optimizing mechanism and it
frequently needs analyzing of objects, why don't Oracle internally do the
analyze without DBA/ Developer doing it?

Sorry, if it is too silly.

And thanks in advance.

Venkatesa Prasad
and we said...
Because you would get mad at us if we did it during your peak processing time
right?

We give you the tools to automate it 100% at your convienence.  DBMS_STATS with
the gather stale option combined with table monitoring and dbms_job let you
schedule these things to take place when and if it is convienent for you.
Review & Followup
Rating: 5
Could you please give us an Example on that 100% Automation - Tom...would be Great July 22, 2002
Reviewer: Pasko
Hi Tom,

Could you please break this down to pieces for us :

DBMS_STATS + gather stale option + table monitoring +  dbms_job

On how to do the scheduled Analysis .

Best regards

Followup:
dbms_job = supplied package to run procedures on a schedule (like CRON in unix)

table monitoring = feature of the database that watches modifications to tables
and tells us automagically when they need to be analyzed

gather_stale_option = feature of the dbms_stats package that only gathers stats
on tables that are marked stale according the to TABLE MONITORING feature

dbms_stats = plsql package to automate the gathering of stats.

So, using dbms_job you could schedule a call to dbms_stats for 3am every morning
to gather statistics only on those tables that have changed enough since the
last stats gather to warrant it.

Rating: 5
Can execute dbms_utility.analyze_schema('Schema_name','COMPUTE') satisfy the CBO requirment? July 22, 2002
Reviewer: A Reader from US
Hi Tom,
We scheduled execute dbms_utility.analyze_schema('Schema_name','COMPUTE')weekly
and rin it at night.  Can
dbms_utility.analyze_schema('Schema_name','COMPUTE')satisfy the CBO reqirement?
Thank you in advance.

Followup:
dbms_utility.analyze_schema should only be used in 73.  In 80 and up --
DBMS_STATS is the appropriate tool to use and dbms_utility.analyze_schema is
only for backwards compatibility.

Rating: 4
dbms_stats.gather_database_stats gathers sys user statistics in 8i July 22, 2002
Reviewer: Sharon Zheng from California, USA
Tom,
since there is a bug in 8i (bug was said to be fixed in 9i1),
dbms_stats.gather_database_stats will collect statistics for sys schema. My
college is strongly against any statistics on data dictionary tables. Either we
delete the statistics on sys user or only collect schema users', other than SYS
user, statistics. Could you explain what are the cons of collecting statistics
on objects owned by SYS?
thanks.

Followup:
Dont collect stats on sys owned tables until probably 10i.

I wouldn't use dbms_stats.gather_database_stats in general , schema stats seems
to be a much "nicer" way to do it (and lets me run more then one at a time)

Rating: 4
December 27, 2002
Reviewer: MinMin from GA, USA
I have cought an artical in the Metalink, Troubleshooting Oracle Apps
Performance Issues:
"..
keep in mind that when an object has its CBO stats refreshed, all references to
that object in the shared pool or library cache become invalid, requiring a new
hard
parse for each SQL statement referencing the object. Therefore, in order to
avoid temporarily affecting the performance of all active users in a production
environment,
you must refrain from gathering CBO stats during times of intensive system load.
"
Could you please elobrate that? Does it imply that the shared pool got flushed
as result of the stats gathered?

Followup:
the shared pool -- no.

"for each SQL statement referencing the object" yes.

statements that access the object with new stats, new indexes, new whatevers
must be repared, hence the are invalidated so the next execute will hard parse
them again -- to take advantage of the new stuff.

Rating: 4
December 27, 2002
Reviewer: MinMin from GA, USA
I have cought an artical in the Metalink, Troubleshooting Oracle Apps
Performance Issues:
"..
keep in mind that when an object has its CBO stats refreshed, all references to
that object in the shared pool or library cache become invalid, requiring a new
hard
parse for each SQL statement referencing the object. Therefore, in order to
avoid temporarily affecting the performance of all active users in a production
environment,
you must refrain from gathering CBO stats during times of intensive system load.
"
Could you please elobrate that? Does it imply that the shared pool got flushed
as result of the stats gathered?

Rating: 4
To MinMin December 27, 2002
Reviewer: C
It means your shared pool objects become invalid because of the stats gathering.
Every new SQL and PL/SQL will have to be hard parsed again. In very, very heavy
systems, that might have performance implications.

December 27, 2002 -- Thanks for the question regarding "Doing it in SQL or in PL/SQL", version 8.1.7
You Asked
Happy holidays Tom, Thanks for a great site.

I have a requirement for a project I'm working on. Basically I'm going to do a
Package, for this and other processing, to move data into some tables. It goes
something like this:

Let's say I have an account table with
account_number,
account_owner.
Then there's the transaction table that has
account_number,
account_owner,
trans_amount ,
trans_date

Let's suppose that both account_number and account_owner are varchar2, that
trans_amount it's number  and trans_date it's a date, that the
amount_1..amount_3 are numbers and trans_date1.. trans_date3 are dates.

The table that I want to put the data in has the
account_number,
account_owner,
amount_1,
amount_2,
amount_3,
trans_date1,
trans_date2,
trans_date3

Basically amount_1.. to amount_3 will have the amount for the first, second and
if it has 3 or more transactions then amount_3 will have a sum of the rest of
transaction(3,4,5...) and trans_date1 .. trans_date3 will have the corresponding
transactions dates(the trans_date3 will have the last date a transaction was
made in the case of 3 or more transactions).

I know it can be done in PL/SQL(bulk collecting will be the best), but it's
there a way to do it with SQL and an insert select? If it was 9i R2 and not
8.1.7  any difference ?

Thanks in advanced
and we said...
Sure, this will work in 8iR2 (816) and up with analytics.  I'll do it step by
step so you can see what I'm doing.  start with test data:

ops$tkyte@ORA920.US.ORACLE.COM> create table t( account_number varchar2(2),
account_owner varchar2(2), trans_amount number, trans_date date );

Table created.

ops$tkyte@ORA920.US.ORACLE.COM>
ops$tkyte@ORA920.US.ORACLE.COM> begin
  2      for i in 1 .. 10
  3      loop
  4          insert into t values ( 'x', 'y', i, sysdate-i );
  5      end loop;
  6      insert into t
  7      select 'a', 'b', trans_amount*10, trans_date-rownum
  8        from t;
  9  end;
 10  /

PL/SQL procedure successfully completed.

first, we'll use analytics to assign each row a row number within
account_number after sorting by trans date.

ops$tkyte@ORA920.US.ORACLE.COM> select account_number,
  2         account_owner,
  3         trans_amount,
  4         trans_date,
  5         row_number()
  6            over ( partition by account_number
  7                   order by trans_date DESC nulls last ) rn
  8    from t
  9   order by 1, 4 desc
 10  /

AC AC TRANS_AMOUNT TRANS_DAT         RN
-- -- ------------ --------- ----------
a  b            10 24-DEC-02          1
a  b            20 22-DEC-02          2
a  b            30 20-DEC-02          3
a  b            40 18-DEC-02          4
a  b            50 16-DEC-02          5
a  b            60 14-DEC-02          6
a  b            70 12-DEC-02          7
a  b            80 10-DEC-02          8
a  b            90 08-DEC-02          9
a  b           100 06-DEC-02         10
x  y             1 25-DEC-02          1
x  y             2 24-DEC-02          2
x  y             3 23-DEC-02          3
x  y             4 22-DEC-02          4
x  y             5 21-DEC-02          5
x  y             6 20-DEC-02          6
x  y             7 19-DEC-02          7
x  y             8 18-DEC-02          8
x  y             9 17-DEC-02          9
x  y            10 16-DEC-02         10

20 rows selected.

Now, we can begin to transpose -- we'll pick off amt1, amt2 and then leave the
rest for amt3 using decode.  We'll get the trans dates as well this way:

ops$tkyte@ORA920.US.ORACLE.COM> select account_number,
  2         account_owner,
  3         decode( rn, 1, trans_amount, null ) amt1,
  4         decode( rn, 2, trans_amount, null ) amt2,
  5         decode( rn, 1, to_number(null), 2, to_number(null), trans_amount )
amt3,
  6         decode( rn, 1, trans_date, null ) td1,
  7         decode( rn, 2, trans_date, null ) td2,
  8         decode( rn, 3, trans_date, null ) td3
  9    from (
 10  select account_number,
 11         account_owner,
 12         trans_amount,
 13         trans_date,
 14         row_number()
 15            over ( partition by account_number
 16                   order by trans_date DESC nulls last ) rn
 17    from t
 18         )
 19  /

AC AC       AMT1       AMT2       AMT3 TD1       TD2       TD3
-- -- ---------- ---------- ---------- --------- --------- ---------
a  b          10                       24-DEC-02
a  b                     20                      22-DEC-02
a  b                                30                     20-DEC-02
a  b                                40
a  b                                50
a  b                                60
a  b                                70
a  b                                80
a  b                                90
a  b                               100
x  y           1                       25-DEC-02
x  y                      2                      24-DEC-02
x  y                                 3                     23-DEC-02
x  y                                 4
x  y                                 5
x  y                                 6
x  y                                 7
x  y                                 8
x  y                                 9
x  y                                10

20 rows selected.

and now use SUM() to aggregate the amounts and MAX() to pick off the non-null
dates and that'll squish out all of the redundant rows:

ops$tkyte@ORA920.US.ORACLE.COM> select account_number,
  2         account_owner,
  3         sum(decode( rn, 1, trans_amount, null )) amt1,
  4         sum(decode( rn, 2, trans_amount, null )) amt2,
  5         sum(decode( rn, 1, to_number(null), 2, to_number(null), trans_amount
)) amt3,
  6         max(decode( rn, 1, trans_date, null )) td1,
  7         max(decode( rn, 2, trans_date, null )) td2,
  8         max(decode( rn, 3, trans_date, null )) td3
  9    from (
 10  select account_number,
 11         account_owner,
 12         trans_amount,
 13         trans_date,
 14         row_number()
 15            over ( partition by account_number
 16                   order by trans_date DESC nulls last ) rn
 17    from t
 18         )
 19   group by account_number, account_owner
 20  /

AC AC       AMT1       AMT2       AMT3 TD1       TD2       TD3
-- -- ---------- ---------- ---------- --------- --------- ---------
a  b          10         20        520 24-DEC-02 22-DEC-02 20-DEC-02
x  y           1          2         52 25-DEC-02 24-DEC-02 23-DEC-02

ta-dah.  analytics rock
Review & Followup
Rating: 5
Excellent Answer December 26, 2002
Reviewer: A reader
Thanks Tom. That was exactly what I was looking for.

Analytics Rocks

Rating: 4
Can I have the output like this December 27, 2002
Reviewer: Siba from Delhi
The Following is a TREE view of the EMP Table.

SELECT Empno,Ename,Sal,LEVEL
FROM EMP
START WITH Empno In (7839,1234)
CONNECT BY mgr = PRIOR empno;

     EMPNO ENAME             SAL      LEVEL
---------- ---------- ---------- ----------
      7839 KING             5000          1
      7566 JONES            2975          2
      7788 SCOTT            3000          3
      7876 ADAMS            1100          4
      7902 FORD             3000          3
      7369 SMITH             800          4
      7698 BLAKE            2850          2
      7499 ALLEN            1600          3
      7521 WARD             1250          3
      7654 MARTIN           1250          3
      7844 TURNER           1500          3

     EMPNO ENAME             SAL      LEVEL
---------- ---------- ---------- ----------
      7900 JAMES             950          3
      7782 CLARK            2450          2
      7934 MILLER           1300          3
      1234 SHAUN            5000          1
      1235 MARK             4000          2
      1240 KELLY            2000          2

I require the output to be like the following. The above TREE has 2 Parent
levels (LEVEL=1). I want the numbering
for each TREE as follows.

     EMPNO ENAME             SAL      LEVEL     ROWNUM
---------- ---------- ---------- ---------- ----------
      7839 KING             5000          1          1
      7566 JONES            2975          2          2
      7788 SCOTT            3000          3          3
      7876 ADAMS            1100          4          4
      7902 FORD             3000          3          5
      7369 SMITH             800          4          6
      7698 BLAKE            2850          2          7
      7499 ALLEN            1600          3          8
      7521 WARD             1250          3          9
      7654 MARTIN           1250          3         10
      7844 TURNER           1500          3         11

     EMPNO ENAME             SAL      LEVEL     ROWNUM
---------- ---------- ---------- ---------- ----------
      7900 JAMES             950          3         12
      7782 CLARK            2450          2         13
      7934 MILLER           1300          3         14
      1234 SHAUN            5000          1          1
      1235 MARK             4000          2          2
      1240 KELLY            2000          2          3

Please suggest how to achieve this.
Siba

Followup:
not without destroying the hierarchy.  we would have to use analytics and that
implies "sort" -- "sort" implies "destroy the structure".

Well, I can see a way -- but with the exception of really small hierarchies, it
may be painfully slow.  You can try it:

scott@ORA817DEV> select empno, ename, sal, lvl,
  2         row_number() over ( partition by my_root order by rn ) rownumber
  3    from ( SELECT rpad( '*', 2*level, '*' ) || Empno empno,
  4                  Ename,Sal,LEVEL lvl,
  5                  substr(
  6                     (select max( to_char(level,'fm0000') || empno)
  7                        from emp emp2
  8                       where empno in ( 7698, 7566 )
  9                       start with emp2.empno = emp.empno
 10                       connect by prior mgr = empno ), 5 ) my_root,
 11                  rownum rn
 12             FROM EMP
 13            START WITH empno in ( 7698, 7566 )
 14          CONNECT BY mgr = PRIOR empno
 15        )
 16   order by rn
 17  /

EMPNO           ENAME             SAL        LVL  ROWNUMBER
--------------- ---------- ---------- ---------- ----------
**7566          JONES            2975          1          1
****7788        SCOTT            3000          2          2
******7876      ADAMS            1100          3          3
****7902        FORD             3000          2          4
******7369      SMITH             800          3          5
**7698          BLAKE            2850          1          1
****7499        ALLEN            1600          2          2
****7521        WARD             1250          2          3
****7654        MARTIN           1250          2          4
****7844        TURNER           1500          2          5
****7900        JAMES             950          2          6

11 rows selected.

that'll work in 815 and up -- in 9i, it can be better (more efficient) via
sys_connect_by_path:

scott@ORA920> select empno, ename, sal, lvl,
  2         row_number() over ( partition by my_root order by rn ) rownumber
  3    from ( SELECT rpad( '*', 2*level, '*' ) || Empno empno,
  4                  Ename,Sal,LEVEL lvl,
  5                  substr( sys_connect_by_path( to_char(empno,'fm0000'), '.'
), 2, 4 ) my_root,
  6                  rownum rn
  7             FROM EMP
  8            START WITH empno in ( 7698, 7566 )
  9          CONNECT BY mgr = PRIOR empno
 10        )
 11   order by rn
 12  /

EMPNO           ENAME             SAL        LVL  ROWNUMBER
--------------- ---------- ---------- ---------- ----------
**7566          JONES            2975          1          1
****7788        SCOTT            3000          2          2
******7876      ADAMS            1100          3          3
****7902        FORD             3000          2          4
******7369      SMITH             800          3          5
**7698          BLAKE            2850          1          1
****7499        ALLEN            1600          2          2
****7521        WARD             1250          2          3
****7654        MARTIN           1250          2          4
****7844        TURNER           1500          2          5
****7900        JAMES             950          2          6

11 rows selected.

December 27, 2002 Venkatesh -- Thanks for the question regarding "STATSPACK ", version 8.1.6
You Asked
Hello Tom ,
          I am using STATPACK to gather Database Performance data on a database
that caters to more than one Web Application .At the moment its just one
application schema that I am interested in collecting the performance data for .
 Since connection pooling mechanism is used , how could I possibly use Statspack
to gather "SESSION" specific data , as the number of Database Connections made
by the application may vary from time to time .Is there a way to gather the
statistics for all the sessions that may have been created by the application at
any given point of time ?  Querying the DB manually for all the sessions may
prove to be cumbersome as well as incorrect as connections  are dynamically
created and dropped .Moreever I would like to schedule the job .
Hope I have been able to elucidate my concern .
Thanx and Regards
Venki
and we said...
You cannot do that.  It would not make sense even to do that.

You either tune a system as a whole or you do not tune.  Statspack is for tuning
at the database level.  That is what you need, that is what you want.

You would use AUDITING (which your application should be doing) so you can track
things like number of page views, who is the most active user, when are your
peaks and valleys and so on.   It is what I do -- Every page viewed on asktom
generates an insert into an audit trail -- every one.

see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6690299776039
for more details on this approach.
December 27, 2002 Guoqiang -- Thanks for the question regarding "How to convert Hex to decimal?", version 8.1.7
You Asked
Tom,

  Does Oracle have a function or easy way to convert hex to decimal or decimal
to Hex ?
  Thanks !

           Guoqiang.
and we said...
select to_number( 'AA', 'xx' ) from dual;

hex to decimal

select to_char( 111, 'xxxx' ) from dual

decimal to hex
Review & Followup
Rating: 4
More December 27, 2002
Reviewer: A reader
Hi,

What about all Oct, Bin, hex and dec functions and
convert to each other? or where is the docu should we read.

Thanks

Followup:
more more more.... I hear that all of the time....
http://asktom.oracle.com/~tkyte/hexdec/index.html

Rating: 3
Excellent December 27, 2002
Reviewer: A reader

Rating: 5
Thanks a lot! December 27, 2002
Reviewer: Guoqiang from China
Oh,Oh,Oh,Tom,

So easy.

And,Does have the same way to covert decimal to bin or oct?

Rating: 5
December 27, 2002
Reviewer: Guoqiang
Sorry for not refresh to see others response.

Thanks!

December 27, 2002 Mohammed -- Thanks for the question regarding "How to sepreate milion of records to number of files", version 8i
You Asked
Hi Tom :

         How can i Separate million of record, to number of days in each month,
I want to spool each result of each day to one file for example in the following
SQl . I want the result as following
file 1 is name like XXX01032002.LST
file 2 is name like xxx02032002.LST
. . .  .  .  .  . . xxx03032002.LST
and so on till
File 31 is name like xxx31032002.LST

Select to_date(call_date,'dd-mon-yy hh24:mi:ss') DAYES,count(rownum)
SAB_PTC_CALLS_REC,sum(duration) DURATION_SEC,
       sum(ceil(duration/60)) Duration_MIN
from   calls
Where  A_type like '0'
       and b_type like '1'
       and ((to_number(substr( b_number,1,2)) between 01 and 09) or
(to_number(substr( b_number,1,2)) like '58'))
       and call_date between to_date('01-MARCh-02 00:00:00' ,'dd-mon-yy
hh24:mi:ss')
       and to_Date('31-MARCH-02 23:59:59', 'dd-mon-yy hh24:mi:ss')
group by  to_date(call_date, 'dd-mon-yy hh24:mi:ss');

My data is so big so I need to separate is as I mentioned above

If thire is a solution for this pleas help as soon as possible.
and we said...
You'll have to write a program.  If you are interested, you can use my
"array_flat" pro*c program.  It dumps a query to disk.  Hence, you could write:

array_flat user/pw "select .... and call_date between
to_date('01-mar-2002','dd-mon-yyyy') and to_date( '01-mar-2002
23:59:50','dd-mon-yyyy hh24:mi:ss') and ......" > xx01032002.lst

array_flat user/pw "select .... and call_date between
to_date('02-mar-2002','dd-mon-yyyy') and to_date( '02-mar-2002
23:59:50','dd-mon-yyyy hh24:mi:ss') and ......" > xx02032002.lst

Or, you could simply modify the C code yourself to do this.  Search this site
for array_flat for the code.

Alternatively, we could "spool" the data if that is sufficient using SQL to
write SQL.  For example, this query -- if you capture its output - creates a
script for you (note: never use LIKE unless you have to, use equals):

ops$tkyte@ORA817DEV.US.ORACLE.COM> select 'spool xx' || to_char(
trunc(sysdate,'month')+r, 'ddmmyyyy' ) || '
  2          select to_date( call_date, ''dd-mon-yy hh24:mi:ss'' ) days,
count(*), ....
  3            from calls
  4           where a_type = ''O''
  5             and b_type = ''1''
  6             and to_number(substr(b_number,1,2)) in ( 1,2,3,4,5,6,7,8,9,58 )
  7             and call_date between trunc(sysdate,''month'')+' || r || ' and
  8                   trunc(sysdate,''month'')+' || round((r+1-1/24/60),5) || '
  9           group by to_date( call_date, ''dd-mon-yy hh24:mi:ss'' )' ||
 10                   chr(10) || '/' || chr(10) || 'spool off'
 11    from ( select rownum-1 r
 12             from all_objects
 13            where rownum <= to_char( last_day(sysdate), 'dd' )
 14           )
 15  /

'SPOOLXX'||TO_CHAR(TRUNC(SYSDATE,'MONTH')+R,'DDMMYYYY')||'SELECTTO_DATE(CALL_DATE
,''DD-MON-YYHH24:MI:SS'')DAYS,COUNT(*),....FROMCAL
---------------------------------------------------------------------------------
--------------------------------------------------
spool xx01032002
        select to_date( call_date, 'dd-mon-yy hh24:mi:ss' ) days, count(*), ....
          from calls
         where a_type = 'O'
           and b_type = '1'
           and to_number(substr(b_number,1,2)) in ( 1,2,3,4,5,6,7,8,9,58 )
           and call_date between trunc(sysdate,'month')+0 and
                                 trunc(sysdate,'month')+.99931
         group by to_date( call_date, 'dd-mon-yy hh24:mi:ss' )
/
spool off

spool xx02032002
        select to_date( call_date, 'dd-mon-yy hh24:mi:ss' ) days, count(*), ....
          from calls
         where a_type = 'O'
           and b_type = '1'
           and to_number(substr(b_number,1,2)) in ( 1,2,3,4,5,6,7,8,9,58 )
           and call_date between trunc(sysdate,'month')+1 and
                                 trunc(sysdate,'month')+1.99931
         group by to_date( call_date, 'dd-mon-yy hh24:mi:ss' )
/
spool off

........
Review & Followup
Rating: 2
How to do this with SPOOL December 27, 2002
Reviewer: Alan from USA
Tom,
Thanks for the wonderful site. Regaular reader first time poster - I learnt
Oracle reading this site and technet. I have a requirement where my query for
sales(joining three tables) generates 10,000,000 records. I want to spool this
data to 50 different pipe(|) delimited files and the condition is that I want
data for a each territory to be in a separate file. For example
TERRITORY 1234 Eastern US - File name 1234SALES.csv
TERRITORY 4321 Central US - Fiel name 4321SALES.csv
The territory information is in a separate table ( TERRITORY) which has three
fields
TERRITORY    NOT NULL VARCHAR2(5)
TERRITORY_NAME    VARCHAR2(50)
FS_LEVEL    NOT NULL CHAR(1)

How can I do it. I searched this site for a similar example but could not find
it.
I am a beginner and will be obliged if you would help me.

Thanks

Followup:
you won't be doing it with spool, spool just spools to a constant file, no way
to change it midstream.  PLSQL can do this, here is some psuedo code:

procedure dump
is
   l_last_filename varchar2(255) := 'no files yet';
   l_file utl_file.file_handle;
begin
   /* assuming you name after "po" */
   for x in ( select * from t order by PO_NUMBER )
   loop
       if ( x.po_number <> l_last_filename )
       then
           if ( utl_file.is_open( l_file ) )
           then
                utl_file.close( l_file );
           end if;
           l_file := utl_file.fopen( x.po_number );
           l_last_filename := x.po_number;
       end if;
       utl_file.put_line( x.c1 || '|' || x.c2 || ..... );
    end loop;
    if ( utl_file.is_open( l_file ) )
    then
       utl_file.fclose( l_file )
    end if;
end;

December 27, 2002 Eugene -- Thanks for the question regarding "how to create test as a copy of prod, but much smaller", version 8.1.7
You Asked
Hi Tom,
We have about 1000 tables in our environment. Some of the tables have 20 mill
rows and some have 10 rows. Our test server not as big and powerfull as
production one. We need to be able to create test environment as a much smaller
version of prod. I cannot go by created_date (which each table has) because many
tables are "master" tables and were created on day one.
What is the 'safe' way to do it?
Thanks for your help,
Eugene
and we said...
It should not matter that the test server isn't as "big and powerful" when it
comes to amounts of data.  Unless you test with real volumes - the tests aren't
testing the right stuff.  It'll run slower, but it'll run (and just think, if
you get it running fast enough on that small box -- what'll do on the big one).
Anyway.

I am not personally aware of any 3rd party tools.  I know 9iR2 OEM has a data
subsetter (not 817's oem)

See
http://download-west.oracle.com/docs/cd/B10501_01/em.920/a96679/overview.htm#1024552
for info on that new feature.

I'll "publish" this question so others can see it and if they know of some
tool....
Review & Followup
Rating: 3
wrote my own December 27, 2002
Reviewer: Paul from the subtropics
I saw a product called databee out on the web.  There used to be a product
called datashark that I think is defunct.

Much less to the point, out of sheer frustration I wrote my own schema/data
subsetter.  It aint' pretty, but it works.  To use it,  you define the source
and target schemas, the set of objects to be copied,  some storage parameters,
and if you want, subqueries and/or predicates to be applied to the various
data-subsetting queries.  You can enter a subquery, associate it with a column
name, and the program can automatically associate the subquery with any table in
your object set that has an index on that column name.  You can enter an object
name, then expand the object set based on RI and/or dependencies.  The program
generates code to create the  objects in the target schema, and generates code
to pipe the data subset to the target database.  If the table contains longs, it
uses sqlplus copy, otherwise it uses our inhouse OCI data unloader piped to
sqlldr via named pipes, which across our network is as I recall 50% faster than
sqlplus copy.  It's far from perfect (doesn't do types or partitioned tables ),
but it sure saves a lot of work.  I might cut it over to use dbms_metadata if I
ever get time and when we ever switch to 9i.

I guess the point is that I have never regretted spending the time writing this
program.  Having the ability to quickly clone a schema/data subset is
invaluable.  There are never enough production clones to go around, and besides
you never need the whole database - often not even a whole schema - only part of
it to develop.   Our largest effort to date involved creating a test schema with
a data subset containing about 200 million rows, however I often use it to
create very small test schemas so we can test in isolation.

Back to the thread - re how to identify tables that aren't master tables, which
haven't "been there from day one" -   are you saying that you want to create and
populate a subset schema and then periodically incrementally refresh it with new
or changed objects and data?

December 27, 2002 Wengli -- Thanks for the question regarding "How to show the contents of an object table", version 8.1.7
You Asked
Greetings,

1. Could you show me in simple select statement how to display the contents of
an object table. For example, I have the following:

   SQL > CREATE TYPE Course AS OBJECT (
     2    course_no NUMBER(4),
     3    title     VARCHAR2(35),
     4    credits    NUMBER(1));

   SQL > CREATE TYPE CourseList AS TABLE OF Course;

   SQL > CREATE TABLE department (
     2   name     VARCHAR2(20),
     3   director VARCHAR2(20),
     4   office   VARCHAR2(20),
     5   courses  CourseList)
     6   NESTED TABLE courses STORE AS courses_tab;

I want to display the COURSES in such a way that it will look like it came from
a database table:

COURSE_NO     TITLE                        CREDITS
---------     -------------------------    -------
1000          'General Psychology'          5
2100          'Experimental Psychology'     4
1002          'Expository Writing'          3

2. Also, I wanted to know how I can write a view using an OBJECT table.

Thanks.
and we said...
ops$tkyte@ORA920> insert into department values
  2  ( 'x', 'y', 'z', courseList( course(1000, 'Gen Psyc', 5 ),
  3                               course(2100, 'Exp Psyc', 4 ),
  4                               course(1002, 'Expos. Writing', 3 ) ) );

1 row created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> insert into department values
  2  ( 'a', 'b', 'c', courseList( course(9100, 'xxGen Psyc', 5 ),
  3                               course(9210, 'xxExp Psyc', 4 ),
  4                               course(9100, 'xxExpos. Writ', 3 ) ) );

1 row created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> select course_no, title, credits
  2    from department, TABLE(department.courses)
  3  /

 COURSE_NO TITLE                                  CREDITS
---------- ----------------------------------- ----------
      1000 Gen Psyc                                     5
      2100 Exp Psyc                                     4
      1002 Expos. Writing                               3
      9100 xxGen Psyc                                   5
      9210 xxExp Psyc                                   4

      9100 xxExpos. Writ                                3

6 rows selected.

And I don't understand the last part -- if you can write the SQL query -- you
just add "create view viewname as" in front of it??

But -- you know what -- if you find yourself needing to treat this nested table
as a real table like this -- It ISN'T a good candidate for a nested table!!!!!
Review & Followup
Rating: 3
How to show the contents of an object table December 27, 2002
Reviewer: Wengli from California, USA
Thanks for your response.

I was just wondering why you said that my sample table is not a good candidate
for a nested table? Because I got those examples from an Oracle 8 documentation
(I know :=), I'm quite left behind). Also, could you please tell me, if there is
any performance difference between the REAL database table and a nested table?

Thanks again.

Followup:
If you want to perpertually view these nested tables as if they were a single
flat table -- putting them into a nested table makes it harder, slower and less
natural.

Do what is natural.

If you have my book -- "Expert one on one Oracle" -- I go into the nitty gritty
details behind this object relational stuff, describe when you might want to use
it or not.

Yes, there can be a difference between them -- especially if you constantly want
to treat them as a "real table"

December 27, 2002 Meyer -- Thanks for the question regarding "Pessimistic Locking via Web", version 9IAS 8.16
You Asked
I am reading your book, and just read about pessimistic locking to make sure
users do not get vanishing updates (by being the second session to update a
non-locked row).

I am wondering if/how you normally address this via mod/pls-sql applications.
Specifically a simple web form that allows updates. If this is answered in a
later chapter, pls excuse this question.

I do not need a specific coded example (but would be fine)...but looking for
your insight. I have seen ticketmast give 10 minute locks on tickets just before
you purchase...and I can think of ways to accomplish that would be non-simple
(do not want to say complicated :)...but wondering if there is a simplified or
best way. What/how do you suggest.

Thanks,
Meyer
and we said...
I wrote the owa_opt_lock package for that:
http://technet.oracle.com/docs/products/ias/doc_library/90200doc_otn/web.902/a90101/pslock.htm#1005550
Review & Followup
Rating: 4
Pessimistic October 08, 2002
Reviewer: Meyer from Baltimore, MD
Sorry if my ? was not clear.

I am familiar with that package and it is great (I did not know you were the
author).

BUT, I think owa_opt_lock only works for optimistic locking...

Interested in your ideas to implement pessimistic locking in that environment. I
am thinking of adding a timestamp column to the table(s) and update the stamps
when I would normally (select the row for update)...and have my initial
selection Query only select where timestamp meets (timestamp=null) or (timestamp
> sysdate - 10 minutes). Wondering if you know a better way or have any
suggestions.

Followup:
It is optimistic locking -- that is how you HAVE to do it in a stateless web
environment (stateless from the db perspective anyway)

pessimistic locking involves actually locking the row -- and since we don't
maintain locks across pages -- we cannot do it.

what you describe with timestamps is more akin to a method of doing optimistic
locking, NOT pessimistic!

Rating: 4
Pessimistic October 08, 2002
Reviewer: Meyer from Baltimore, MD
Sorry if my ? was not clear.

I am familiar with that package and it is great (I did not know you were the
author).

BUT, I think owa_opt_lock only works for optimistic locking...

Interested in your ideas to implement pessimistic locking in that environment. I
am thinking of adding a timestamp column to the table(s) and update the stamps
when I would normally (select the row for update)...and have my initial
selection Query only select where timestamp meets (timestamp=null) or (timestamp
> sysdate + 10 minutes). Wondering if you know a better way or have any
suggestions.

Rating: 3
timestamp = optimistic locking October 08, 2002
Reviewer: A reader
Meyer,
      Your Implementation of timestamp more so fits optimistic locking.In a
stateless web environment I would think thats the way to go

Rating: 4
What about using SYS_GUID()? October 09, 2002
Reviewer: Ed from Cheshire, UK
Meyer,

Here we use GUID, rather than a timestamp.  A user 'locks' the data by
retrieving the GUID with the data, then, after submitting the changes, we check
that the GUID is the same before making the update (otherwise we return an error
message).

Just a thought.

Ed

Followup:
that works -- I used a checksum in owa_opt_lock to provide for backwards
compatibility with pre-existing applications that use pessimistic locking.
Since the checksum doesn't require an extra column and trigger to maintain it --
it was more appealing.

Rating: 3
Clarification October 09, 2002
Reviewer: Meyer from Baltimore, MD
If this sounds like "the same question again" I will drop it and start coding...
I wanted to give myself another attempt to explain because I do not think I
expressed my question clearly:

If you have a requirement that your scheduling application is to behave the way
it would with pessimistic locking, but you only have optimistic locking
available to you because you want to implement via mod-plsql (and yes it is
stateless and can not carry locks between pages).

Specific requirements:
When a row is sent to the update-a-single-row form it is flagged as "LOCKED_".
Rows can only be sent to the form if they are not "LOCKED_" or have not been in
the last N minutes.
LOCKED_ may or may not be a database lock (I realize I may need to do this in
the application code).

How would you emulate pessimistic locking at the application level of a
mod-plsql application. I am thinking time stamp column in the same table as the
data would be the best way... but want your thoughts.

Thanks,
Meyer

Followup:
Oh, ok -- well my answer is

"i wouldn't personally.  doesn't sound like a great idea"

but, you could use two columns

a date
a sessionid

and a given sessionid will "own" that row until that date and will be the only
one that can modify that row until that date ( a trigger can easily enforce this
behind the covers )

Rating: 4
Meyer October 09, 2002
Reviewer: Freek D'Hooge from Belgium
I think you are looking for the following

you have a function that accepts an id
It will then try to lock the row with that id
if it gets the lock, it can check if the lock timestamp (a date column in the
table) is empty or older then x minutes. If this is the case it will return
"success" and you will have the row otherwise it returns "failure" and you will
have to try again.

there is of course no obligation for the sql statements to respect this logic.
If they want they can always neglect your "lock" and insert/update anyway.

Rating: 5
locking December 26, 2002
Reviewer: mo
Tom:

1.  Are you sure that you cannot implement pessimistic locking for web app.
This would be devastating for users if 10 people start updating the same record
and then one saves the data and the other 9 get rejections because of
pessimistic locking.

2. Can i do im the procedure/page 'select record for update' and then in the 2nd
procedure I commit to release the lock?

3.  Well can i do the same thing by having a flag for each record.  Everytime a
user selects a record i flag it "L" and when other users try to access it you
tell them it is locked if it has "L" adn then after userA updates you set it
back to null.  It inolves more coding for updaing and checking the flag
everytime though?

Followup:
1) you mean because of OPTIMISTIC locking:

This would be devastating for users if 10 people start updating the same record
and then one saves the data and the other 9 get rejections because of
OPTIMISTIC locking.

If you are using a stateless connection -- not much of a choice, by definition
you are releasing all resources (stateless MEANS that)....

If you use a connection pool -- even in a stateful connection to the app server
-- not much of a choice, you give up the connection each time

If you use a stated connect from the app server to the database AND you keep the
connection across pages -- you can use pessimistic locking....

2) see #1.  Only if you retain physically the connection in the middle tier from
page to page....   it would be a "bad" idea.

3) and it'll almost certainly fail as you have to hire a full time person who
does nothing other then unlock "locked" records -- records that get locked and
then the browser crashes, or the user gets bored and jumps out of your
application, or the app server crashed or whatever.....

These locking schemes fail miserably everytime.

A timed based one might work -- add two fields:

username
locked_until

and to "lock" a row --

update t set username = USER, locked_until = sysdate+5/24/60
where primary key = VALUE
  and locked_until < sysdate;

if ( sql%rowcount = 0 )
then
   you DON'T got it
else
   you GOT it.
end if;

That'll "lock" the row for 5 minutes.  You also need to update then:

update t set ......
where primary key = value
  and username = USER
  and locked_until > sysdate;

if ( sql%rowcount = 0 )
then
   you lose
else
   you win
end if

Rating: 5
locking December 26, 2002
Reviewer: mo
Tom:

<If you use a stated connect from the app server to the database AND you keep
the
connection across pages -- you can use pessimistic locking....>

Can you do this with mod_plsql? You mean session management using an oracle
table here?

2.  Does "stateful" mean you keep using the same connection on the first login
page and stateless you have one connection/page?

3.  Why you do not like stateful? is it slow?

4.  I like your way of implementing timed locks but what is that second update
statement for? is to release the lock after user perfoms an update? Is this what
you typically use or you implement optimsitic lock on web apps? DO you need to
add these two columns to every table you have? this would be a nightmare to
manage.

Thank you,

Followup:
1) yes, read the documentation -- there is a way to do stated connections.

No, I've never done it.
No, I do not recommend it.
No, I cannot tell you how to set it up.
No, I don't know exactly what page it is on.

2) it means you keep a state (resources) on the application server and the
application server keeps a state from itself to the database.

3) suppose I used stateful connections here on asktom. I get about 8-10,000
users.  Now, at any point in time, there might be 3 or 4 of you actually
clicking on something at the same time.  Do I want to keep a couple of hundred
or even thousand session stats in memory in my app server for you?  are in my
database?  No way, not a chance.  I would need a HUGE machine, not the tiny
little 5 year old, $30,000 machine that runs asktom alongside 6 other websites
-- each generating traffic.  If I want (need) stateful, I'll be using a client
server tool or something heavier like forms.

4) the first update locked the row.

the second update -- well think about it -- it is the update to no only unlock
the row but to ACTUALLY do the update of the data!

I use optimistic locking in my web apps.

Why would adding two columns be a "nightmare"?  It should affect nothing unless
you use select * all over the place (and that is only a problem in plsql) or if
you use "insert into t values (....)" without using "insert into t (c1,c2,c3)
values ...."

Rating: 5
web locking December 27, 2002
Reviewer: mo
Tom:

When you implement optimistic locking on the web by copying the old values of a
record and then running update on the record only if the old values match: SINCE
some tables may be big and you are moving a lot of data why not create a flag
column that has a number/sequence and every time a user selects a record to
update you pick a number. then another user updates the record you update the
flag by adding the number.

When user A sends his update you see that flag is different so you reject
update. Is not that easier?

Followup:
The size of the table is not relevant.

The width of a row might be.

Existing applications that do not want a column added to their tables would be a
show stopper.

I believe i list quite a few ways to crack this nut - from copying old values,
using a timestamp column, using checksums, etc....

There are many ways to do this, many many ways.

None is the best.
None is the worst.
They all have a place.

December 28, 2002 Ratnaprabha -- Thanks for the question regarding "indexing question", version
You Asked
Suppose my index is a + b on a particular table but in
my query where clause I have to refer value of b column. If I
refer to the column b my Index will not be used and I dont want
to create any other Index. Is there any way where in I can make
use of this Index so that my query will be very fast.
I hope you understood my question
and we said...
If you are in fact using Oracle7, you will not be able to create
an index on (a+b) as function based indexes are a new feature of
Oracle8i, release 8.1.  See
http://osi.oracle.com/~tkyte/article1/index.html
for a discussion of function
based indexes.

If you use an index in Oracle8i, release 8.1 on the function
(a+b)  -- then ONLY when the predicate contains "where a+b ..."
will that index be used.  It quite simply cannot be used for
"when b ....".  If you think about it -- it makes sense.  Lets
say you had:

where b = 5

and tried to use the index on (a+b).  Since A can assume an
infinite set of values -- how could you make sense of the values
in the index?  When b = 5, a might be 1, 2, 3, ..... and so on.
You would end up having to look at each and every (a+b) index
value and going to the base table to inspect the value of b.

You will have to create an index on (b) if you want searches on
just b to make use of an index..

If you mean instead that you have a concatenated index on a And
b (eg: you have "create index x on t(a,b)" and NOT on (a+b), the
answer is still the same.  This index simply cannot be used one
B alone.  If you think about how the index is stored, this again
is sensible.  Consider the "where b = 5" question again.  A can
assume an infinite set of values -- we would end up scanning the
entire index to find the values of "b = 5".  In this case, we
could use an INDEX FAST FULL scan to avoid full scanning the
base table (and hence doing less IO) but we would in fact FULL
SCAN the index since "b=5" could be in any and every (or no)
index entry and we must look at them all.
Review & Followup
Rating: 3
indexed column order in where statement December 28, 2002
Reviewer: shineng from china
i do the following works,
create table tt ( x int ,y int,z int);
create index tt_index on tt(x,y);
then insert 10000 records into table tt;
i exec the following statements:
 1. select * from tt where x = 5 and y = 10;
 2. select * from tt where y = 10 and x = 5;
is there r any difference in exec time?why?

Followup:
they both reference x and y -- they are the same.

December 28, 2002 sush -- Thanks for the question regarding "Summary Tables", version 8.1.7
You Asked
create table table1(
   column1 number,
   column2 number,
   n_cost number,
   end_date date
)

table data :

column1  column2 cost end_date
100      1000    100$ 12-JAN-2003
100      1000    200$ 12-JUL-2003
101      1001    300$ 12-APR-2003

Expected Result :some sort of ageing summary
Target Table : Cost_summary table

column1 column2 n_cost_180 n_count_180 n_cost_90 n_count_90

100     1000    300$        2           100$      1

101     1001    300$        1             -       -

declare

cursor cur1(p_days) is select t.column1,
       t.column2,
       sum(n_cost) s_n_cost,
       count(*)
from table1 t
   where t.end_date between trunc(sysdate) and trunc(sysdate) + p_days
   group by t.column1,
            t.column2; /* p_days = 180 */

cursor cur2(p_days) is select t.column1,
       t.column2,
       sum(n_cost) s_n_cost,
       count(*) n_count
from table1 t
   where t.end_date between trunc(sysdate) and trunc(sysdate) + p_days
   group by t.column1,
            t.column2; /* p_days = 90 */

begin

for i in cur1 loop
insert into cost_summary(column1,column2,n_cost_180,n_count_180)
     values(i.column1,i.column2,i.n_cost_180,i.n_count_180);

end loop;

commit;

for j in cur2 loop
update cost_summary set n_cost_90 = j.s_n_cost , n_count_90 = j.n_count
where column1 = j.column1 and column2 = j.column2 ;
end loop;
 commit;
end;

I would like to know if there is any better way to achieve the same .

Thanks ,
Uma
and we said...
Just select it out in the first place...Consider this example:

ops$tkyte@ORA920> create table t
  2  as
  3  select owner column1, object_type column2, object_id cost, created end_date
  4    from all_objects;

Table created.

ops$tkyte@ORA920>
ops$tkyte@ORA920>
ops$tkyte@ORA920> select column1,
  2         column2,
  3         sum(more_than_180_days) n_cost_180, count(more_than_180_days)
n_count_180,
  4         sum(more_than_90_days) n_cost_90, count(more_than_90_days)
n_count_90,
  5         sum(within_90_days) n_cost_0, count(within_90_days) n_count_0
  6    from (
  7  select column1,
  8         column2,
  9             case when end_date > sysdate-180
 10                  then cost
 11                          else null
 12             end more_than_180_days,
 13             case when end_date <= sysdate-180 and end_date > sysdate-90
 14                  then cost
 15                          else null
 16                  end more_than_90_days,
 17             case when end_date <= sysdate-90
 18                  then cost
 19                          else null
 20                  end within_90_days
 21     from t
 22         )
 23    where column1 like 'SYS%'
 24    group by column1, column2
 25  /

COLUMN COLUMN2            N_COST_180 N_COUNT_180  N_COST_90 N_COUNT_90
N_COST_0  N_COUNT_0
------ ------------------ ---------- ----------- ---------- ----------
---------- ----------
SYS    TYPE                                    0                     0
4336820        565
SYS    VIEW                    34032           1                     0
6882078       2088
......

if you try this in PLSQL -- you'll hit an issue with CASE in 8i -- read this:
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:3027089372477
to see how to use a view or ref cursor to solve that issue.
December 28, 2002 khalid -- Thanks for the question regarding "v$session_longops rubbish data", version 8.1.7
You Asked
Dear Tom.

Some Times after I restart the database the v$session_longops
Contain rubbish data how can I clear it. If I query the view
I get
ERROR at line 6:
ORA-01861: literal does not match format string
Because the field start_time.

Best Regard,
and we said...
Only records for valid, currently logged in sessions are "inspectable"

The rest either have

o garbage (uninitialized)
o legacy information from the last time the record was used.

In general, add "where sid = SID and serial# = SERIAL and time_remaining > 0" to
any query against that table.
Review & Followup
Rating: 5
We can not live without Tom December 29, 2002
Reviewer: Khaild from Yemen
Best Regard

December 28, 2002 Rizwan -- Thanks for the question regarding "Tracing packages in SQL*Plus", version 8.1.7
You Asked
Tom,
   In our project we have written many packages. The main procedure in a package
calls different procedures in the same or other helper packages. This main
procedure is called from our Java Servlet/JSP application.
   Typically this main procedure takes around 6-7 parameters (mainly varchar2
and numbers) and returns around 10-15 ref cursors.
   I have the following questions:
a) If it is OK to return that many ref cursors in a single call?
b) How to trace such a package in SQL*Plus enviroment so that we can test
whether all the SQLs are indeed using bind variables. What I am looking is a
generalized methodology which will help me trace my package without actually
returning the resultsets?
  I do not know whether what I want in b) above is possible. If not, can you
suggest some way to trace these packages? Because if I trace it through my web
application, each hit(session) will produce a distinct trace file.

Thanks very much,
Rizwan
and we said...
a) absolutely

b) see
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1163635055580
that script reviews the contents of the shared pool looking for statements that
would have been the same if the literals were removed.

You'll find SQL statements that are not using bind variables -- then you need to
track down the code that generated them.
Review & Followup
Rating: 3
Tracing packages in SQL*Plus December 28, 2002
Reviewer: Rizwan Qazi from Hayward, CA USA
Tom,
  Thanks for the answer. What if I do not have access to the v$sqlarea view?
(Infact we do not have access to that view.)

Followup:
Ask your DBA for access to it...

Ask your DBA to run this for you...

It is to THEIR benefit more then anything else.

December 29, 2002 Om -- Thanks for the question regarding "PL/SQL Tuning", version 8.1.7
You Asked
Dear Tom,

I read an article from oracle on PL/SQL tuning.

Just could not understand the follwoing three tips which are as follows :-

1) VARCHAR2 variables with constraints up to 2000 are stack allocated where as
VARCHAR2 variables with constraints above 2000 are heap allocated.

2) Table of Record is slower for two dimensional representaion than Record of
table.

3) Recursive implementation of common algorithms is less efficient than
iterative
   implementation.

Could you please make it simple for us to understand by taking appropriate
exmaples.

Many thanks.

Best regards,
Om
and we said...
1) read
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:1542606219593
all that is saying is that strings less then 2000 characters will be allocated
once when defined for the max length.  They are statically allocated. So,
varchar2(1999) will allocate space for 1999 characters ONCE when defined.

A string varchar2(2001) will NOT pre-allocate the storage.  When you assign
something to it, it will dynamically allocate the storage just to fit.

So, varchar2(1999) takes a permanent amount of ram regardless of whether you
stuff "x" or rpad('x',1999,'x') into it. the memory is allocated once.

A varchar2(2001) will take a varying amount of memory and will allocate this
memory as needed.

2) they are saying that it is less efficient to have a table of RECORDS that you
index by:

    table(i).attribute

then it is to have a record of TABLES that you index by:

    table.attribute(i)

they would rather have scalar arrays then arrays of complex records is what that
says.

3) this is generally true.  Anytime you can implement something iteratively --
it is generally more efficient.  This holds true for virtually every programming
language (well, maybe not prolog which expects recursion and implements tail
recursion elimination in the compiler... but thats another story.).

The reason is that every function call has overhead -- overhead of setting up a
return address, calling the function, building the stack, initializing variables
and then returning.  All of that can be avoided in general.

But then again, if you plan on recursing just a couple of levels -- and the
alogirthm is enhanced by using recursion versus doing it iteratively (code
easier to read and write) -- go for it.
Review & Followup
Rating: 4
record of TABLES ? December 25, 2002
Reviewer: Jane from CT
>>a record of TABLES that you index by:
>>    table.attribute(i)

what's a record of TABLES ?
Can you pls give some real code example ?
Thanks

Followup:
here is a table of records:

ops$tkyte@ORA920> declare
  2      type emp_rec is record
  3      ( empno   number,
  4        ename   varchar2(30),
  5        job     varchar2(30)
  6      );
  7      type emp_rec_array is table of emp_rec index by binary_integer;
  8
  9      l_data emp_rec_array;
 10  begin
 11      select empno, ename, job BULK COLLECT into l_data from emp;
 12
 13      for i in 1 .. l_data.count
 14      loop
 15          dbms_output.put_line( l_data(i).ename || ',' || l_data(i).job );
 16      end loop;
 17  end;
 18  /
SMITH,CLERK
ALLEN,SALESMAN
WARD,SALESMAN
JONES,MANAGER
MARTIN,SALESMAN
BLAKE,MANAGER
CLARK,MANAGER
SCOTT,ANALYST
KING,PRESIDENT
TURNER,SALESMAN
ADAMS,CLERK
JAMES,CLERK
FORD,ANALYST
MILLER,CLERK

PL/SQL procedure successfully completed.

and here is a RECORD of TABLES

ops$tkyte@ORA920>
ops$tkyte@ORA920> declare
  2      type emp_rec is record
  3      ( empno   dbms_sql.number_Table,
  4        ename   dbms_sql.varchar2_Table,
  5        job     dbms_sql.varchar2_Table
  6      );
  7      l_rec emp_rec;
  8  begin
  9      select empno, ename, job BULK COLLECT into l_rec.empno, l_rec.ename,
l_rec.job
 10        from emp;
 11
 12      for i in 1 .. l_rec.empno.count
 13      loop
 14          dbms_output.put_line( l_rec.ename(i) || ',' || l_rec.job(i) );
 15      end loop;
 16  end;
 17  /
SMITH,CLERK
ALLEN,SALESMAN
WARD,SALESMAN
JONES,MANAGER
MARTIN,SALESMAN
BLAKE,MANAGER
CLARK,MANAGER
SCOTT,ANALYST
KING,PRESIDENT
TURNER,SALESMAN
ADAMS,CLERK
JAMES,CLERK
FORD,ANALYST
MILLER,CLERK

PL/SQL procedure successfully completed.

note that the table of records example will ONLY work in 9iR2 and up -- before
that you had to code:

ops$tkyte@ORA817DEV> declare
  2          type emp_rec is record
  3          ( empno   number,
  4            ename   varchar2(30),
  5            job     varchar2(30)
  6          );
  7          type emp_rec_array is table of emp_rec index by binary_integer;
  8          cursor c is select empno, ename, job from emp;
  9          l_data emp_rec_array;
 10  begin
 11          open c;
 12          loop
 13                  fetch c into l_data(l_data.count+1);
 14                  exit when c%notfound;
 15          end loop;
 16          close c;
 17
 18          for i in 1 .. l_data.count
 19          loop
 20                  dbms_output.put_line( l_data(i).ename || ',' ||
l_data(i).job );
 21          end loop;
 22  end;
 23  /

Rating: 5
December 26, 2002
Reviewer: A reader
Hi tom,

TABLE OF RECORD or RECORDS OF TABLES

i think both are same. we can achive same thing from both.

In what ways both are different from each others?

Followup:
they are not the same.

Try to pass the array of enames to a procedure when using a table of records...

Try to pass the 5th record of empno,ename,job to a procedure when using a record
of tables....

they are as different as night and day.

Rating: 4
Which is performance December 27, 2002
Reviewer: A reader
Hi,

Which is more performance assocrding to your comment,
table of records of records of table?

Thanks

Followup:
which do you need -- that'll dictate what you use.

Want to bulk fetch pre9i, record of tables.

Need to pass records to procedures for processing?  Table of records.

Rating: 5
An interesting "anomaly" in 9i December 28, 2002
Reviewer: Connor from UK
On a related note, if you look at INDEX BY BINARY_INTEGER versus INDEX BY
VARCHAR2, the numeric indices run faster than their character equivalents for
densely populated indices, whereas varchar2 outperforms the numeric equivalent
when the indices are sparsely arranged.

Suggests a different implementation for each perhaps?

hth
Connor

Followup:
How did you test it?  what was the example?

My first try involved lots of implicit number to string conversions and the
binary integer table killed varchar2 tables.  But when I normalized the test so
that there would be no such conversion -- they were very similar:

ops$tkyte@ORA920> create table t
  2  as
  3  select rownum bi, to_char(rownum) vc
  4    from all_objects;

Table created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5
  6      type biArray is table of number index by binary_integer;
  7      type vcArray is table of number index by varchar2(10);
  8
  9      biData biArray;
 10      vcData vcArray;
 11      num    number;
 12
 13  begin
 14      insert into run_stats select 'before', stats.* from stats;
 15
 16      l_start := dbms_utility.get_time;
 17      for x in ( select * from t )
 18      loop
 19          biData(x.bi) := x.bi;
 20      end loop;
 21      for i in 1 .. 5
 22      loop
 23          for x in ( select * from t )
 24          loop
 25              num := biData(x.bi);
 26          end loop;
 27         end loop;
 28      l_run1 := (dbms_utility.get_time-l_start);
 29      dbms_output.put_line( l_run1 || ' hsecs' );
 30
 31      insert into run_stats select 'after 1', stats.* from stats;
 32      l_start := dbms_utility.get_time;
 33      for x in ( select * from t )
 34      loop
 35          vcData(x.vc) := x.vc;
 36      end loop;
 37      for i in 1 .. 5
 38      loop
 39          for x in ( select * from t )
 40          loop
 41              num := vcData(x.vc);
 42          end loop;
 43         end loop;
 44      l_run2 := (dbms_utility.get_time-l_start);
 45      dbms_output.put_line( l_run2 || ' hsecs' );
 46      dbms_output.put_line
 47      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 48
 49      insert into run_stats select 'after 2', stats.* from stats;
 50  end;
 51  /
506 hsecs
520 hsecs
run 1 ran in 97.31% of the time

PL/SQL procedure successfully completed.

ops$tkyte@ORA920> create table t
  2  as
  3  select x bi, to_char(x) vc
  4    from (select dbms_random.random x, rownum from all_objects );

Table created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5
  6      type biArray is table of number index by binary_integer;
  7      type vcArray is table of number index by varchar2(40);
  8
  9      biData biArray;
 10      vcData vcArray;
 11      num    number;
 12
 13  begin
 14      insert into run_stats select 'before', stats.* from stats;
 15
 16      l_start := dbms_utility.get_time;
 17      for x in ( select * from t )
 18      loop
 19          biData(x.bi) := x.bi;
 20      end loop;
 21      for i in 1 .. 5
 22      loop
 23          for x in ( select * from t )
 24          loop
 25              num := biData(x.bi);
 26          end loop;
 27         end loop;
 28      l_run1 := (dbms_utility.get_time-l_start);
 29      dbms_output.put_line( l_run1 || ' hsecs' );
 30
 31      insert into run_stats select 'after 1', stats.* from stats;
 32      l_start := dbms_utility.get_time;
 33      for x in ( select * from t )
 34      loop
 35          vcData(x.vc) := x.vc;
 36      end loop;
 37      for i in 1 .. 5
 38      loop
 39          for x in ( select * from t )
 40          loop
 41              num := vcData(x.vc);
 42          end loop;
 43         end loop;
 44      l_run2 := (dbms_utility.get_time-l_start);
 45      dbms_output.put_line( l_run2 || ' hsecs' );
 46      dbms_output.put_line
 47      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 48
 49      insert into run_stats select 'after 2', stats.* from stats;
 50  end;
 51  /
557 hsecs
526 hsecs
run 1 ran in 105.89% of the time

PL/SQL procedure successfully completed.

so, while I do see that for "dense" binary integer arrays -- they marginally
beat "sparse" once -- but not to a degree that indicates a huge difference...

Rating: 5
varchar2 vs binary_int December 28, 2002
Reviewer: Connor from UK
I probably didn't explain myself too well - it was referring to navigation
throughout a plsql table.  The test I did (which I'll repeat/post once I get
access back to a 9.2 database) was  the same as yours in terms of linear versus
random (i think i used dbms_utility.get_hash), but then go from table.first,
table.next, etc, until I got to table.last.  In both cases, I populated the
plsql tables directly not via cursor loop from heap table.

Binary ints faster for linear, the varchar2 faster for the hashed data.

Watch this space for my test results again.

Followup:
Interesting - in my testing, binary integer was faster for both iterating
through.  I've never iterate through using .first, .next, .last except for
debugging (to dump a lookup) so never thought of testing that way.

So, I would agree there is a difference in implementation -- but for most uses
(as lookup tables -- turn this string in something else or turn this number into
something else) they would be more or less the same.

Here are my test results:

ops$tkyte@ORA920> drop table t;

Table dropped.

ops$tkyte@ORA920> create table t as select rownum bi, to_char(rownum) vc from
all_objects;

Table created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5
  6      type biArray is table of number index by binary_integer;
  7      type vcArray is table of number index by varchar2(40);
  8
  9      biData biArray;
 10      vcData vcArray;
 11      num    number;
 12      idx    number;
 13
 14  begin
 15      for x in ( select * from t )
 16      loop
 17          biData(x.bi) := x.bi;
 18      end loop;
 19
 20      l_start := dbms_utility.get_time;
 21      for i in 1 .. 10
 22      loop
 23          idx := biData.first;
 24          loop
 25              num := biData(idx);
 26              exit when ( idx = biData.last );
 27              idx := biData.next(idx);
 28          end loop;
 29      end loop;
 30      l_run1 := (dbms_utility.get_time-l_start);
 31      dbms_output.put_line( l_run1 || ' hsecs' );
 32
 33      for x in ( select * from t )
 34      loop
 35          vcData(x.vc) := x.vc;
 36      end loop;
 37
 38      l_start := dbms_utility.get_time;
 39      for i in 1 .. 10
 40      loop
 41          idx := vcData.first;
 42          loop
 43              num := vcData(idx);
 44              exit when ( idx = vcData.last );
 45              idx := vcData.next(idx);
 46          end loop;
 47      end loop;
 48      l_run2 := (dbms_utility.get_time-l_start);
 49      dbms_output.put_line( l_run2 || ' hsecs' );
 50      dbms_output.put_line
 51      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 52  end;
 53  /
70 hsecs
174 hsecs
run 1 ran in 40.23% of the time

PL/SQL procedure successfully completed.

ops$tkyte@ORA920>
ops$tkyte@ORA920>
ops$tkyte@ORA920>
ops$tkyte@ORA920> drop table t;

Table dropped.

ops$tkyte@ORA920> create table t
  2  as
  3  select x bi, to_char(x) vc
  4    from (select dbms_random.random x, rownum from all_objects );

Table created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> declare
  2      l_start number;
  3      l_run1  number;
  4      l_run2  number;
  5
  6      type biArray is table of number index by binary_integer;
  7      type vcArray is table of number index by varchar2(40);
  8
  9      biData biArray;
 10      vcData vcArray;
 11      num    number;
 12      idx    number;
 13
 14  begin
 15      for x in ( select * from t )
 16      loop
 17          biData(x.bi) := x.bi;
 18      end loop;
 19
 20      l_start := dbms_utility.get_time;
 21      for i in 1 .. 10
 22      loop
 23          idx := biData.first;
 24          loop
 25              num := biData(idx);
 26              exit when ( idx = biData.last );
 27              idx := biData.next(idx);
 28          end loop;
 29      end loop;
 30      l_run1 := (dbms_utility.get_time-l_start);
 31      dbms_output.put_line( l_run1 || ' hsecs' );
 32
 33      for x in ( select * from t )
 34      loop
 35          vcData(x.vc) := x.vc;
 36      end loop;
 37
 38      l_start := dbms_utility.get_time;
 39      for i in 1 .. 10
 40      loop
 41          idx := vcData.first;
 42          loop
 43              num := vcData(idx);
 44              exit when ( idx = vcData.last );
 45              idx := vcData.next(idx);
 46          end loop;
 47      end loop;
 48      l_run2 := (dbms_utility.get_time-l_start);
 49      dbms_output.put_line( l_run2 || ' hsecs' );
 50      dbms_output.put_line
 51      ( 'run 1 ran in ' || round(l_run1/l_run2*100,2) || '% of the time' );
 52  end;
 53  /
137 hsecs
187 hsecs
run 1 ran in 73.26% of the time

PL/SQL procedure successfully completed.

Rating: 5
A new test... December 29, 2002
Reviewer: Connor from UK
Just did this one...basic assignment test:

create or replace
procedure test1 is
 type tv is table of number(10) index by varchar2(20);
 type tb is table of number(10) index by binary_integer;
 x tv;
 z tb;
 t number;
 it number := 80000;
begin
  t := dbms_utility.get_time;
  for i in 1 .. it loop
    x(x.count+1) := i;
  end loop;
  dbms_output.put_line(dbms_utility.get_time-t);

  t := dbms_utility.get_time;
  for i in 1 .. it loop
    z(z.count+1) := i;
  end loop;
  dbms_output.put_line(dbms_utility.get_time-t);
end;
/

output:
98
75
so binary integer is "best"

create or replace
procedure test1 is
 type tv is table of number(10) index by varchar2(20);
 type tb is table of number(10) index by binary_integer;
 x tv;
 z tb;
 t number;
 it number := 80000;
begin
  t := dbms_utility.get_time;
  for i in 1 .. it loop
    x(dbms_utility.get_hash_value(i,0,50000000)) := i;
  end loop;
  dbms_output.put_line(dbms_utility.get_time-t);

  t := dbms_utility.get_time;
  for i in 1 .. it loop
    z(dbms_utility.get_hash_value(i,0,50000000)) := i;
  end loop;
  dbms_output.put_line(dbms_utility.get_time-t);
end;
/

output:
164
198
so varchar2 is "best".

Cheers
Connor

December 29, 2002 ashraf -- Thanks for the question regarding "long query", version 9.01
You Asked
hi tom

i was tying to run query on that table :
SQL> desc tcon
 Name                                      Null?    Type
 ----------------------------------------- -------- --------------------
 UNIT_PREFIX                                        VARCHAR2(40)
 UNIT_NUMBER                                        VARCHAR2(80)
 CONTAINER_SIZE                                     VARCHAR2(20)
 CONTAINER_TYPE                                     VARCHAR2(30)
 ACTIVITYDAY                                        VARCHAR2(20)
 ACTIVITYMONTH                                      VARCHAR2(20)
 ACTIVITYYEAR                                       VARCHAR2(20)
 ACTIVITYTIME                                       VARCHAR2(40)
 FULL_EMPTYINDICATOR                                VARCHAR2(10)
 TRANSSHIPMENT                                      VARCHAR2(10)
 SHIPPER_OWNED_UNIT                                 VARCHAR2(10)
 PLACE_OF_ORIGIN                                    VARCHAR2(50)
 PORT_OF_LOADING                                    VARCHAR2(50)
 PORT_OF_DISCHARGE                                  VARCHAR2(5)
 PLACE_OF_DELIVERY                                  VARCHAR2(5)
 VESSEL                                             VARCHAR2(6)
 VOYAGE                                             VARCHAR2(4)
 VOYAGE_LEG                                         VARCHAR2(1)
 ACTIVITY_CODE                                      VARCHAR2(4)
 TERRITORY                                          VARCHAR2(3)
 REGION_CODE                                        VARCHAR2(3)
 AREA_CODE                                          VARCHAR2(3)
 PORT                                               VARCHAR2(5)
 DEPOT_CODE                                         VARCHAR2(3)
 SERVICE                                            VARCHAR2(40)
 ARRIVAL                                            VARCHAR2(40)
 ARRIVALD                                           DATE
 REGION                                             VARCHAR2(40)
 ACTIVD                                             DATE

SQL> select count(*) from tcon;

  COUNT(*)
----------
   2340278
 not all the columns is impotant to my query so i will help in crate the table

create table tcon (UNIT_PREFIX VARCHAR2(40),port VARCHAR2(5),activity_code
VARCHAR2(4),activd date,region VARCHAR2(40))

the unit prefix IS A UNIT NUMBER AND activity_code like

DCHF,RCVC,SNTC,..

and data like

unit_prefix  activity_code        activd
-----------  -------------       --------
AIDU1010361    DCHF               10-JAN-02
AIDU1010361    RCVC               20-JAN-02
AIDU1010361    DCHE               30-JAN-02
AIDU1010361    DCHF               01-MAR-02
AIDU1010361    SNTC               10-MAR-02
AIDU1010361    DCHF               20-MAR-02
AIDU1010361    RCVC               29-MAR-02

I TRY TO FIND THE DIFFERENT BETWEEN ANY TWO ACTIVITY LIKE FOR EXAMPLE
DCHF-RCVC

UNIT AIDU1010361 DCHF AT 10-JAN-02 AND RCVC AT 20-JAN-02 SO THE DIFF. IS 10
DAY'S

BUT IN NEXT DCHF AT 01-MAR-02 THERE IS NO RCVC THEN DIFF IS NULL BECAUSE QUERY
WILL FIND THERE IS ANOTHER DCHF AT 20-MAR-02

THE TABLE  HAVE DISTINCT(unit_prefix) 30,000 units
I KNOW IT IS VERY HARD SO I WILL HELP

INSERT INTO TCON(unit_prefix,activity_code,activd) values
('AIDU1010361','DCHF','10-JAN-02');

INSERT INTO TCON(unit_prefix,activity_code,activd) values
('AIDU1010361','RCVC','20-JAN-02');
INSERT INTO TCON(unit_prefix,activity_code,activd) values
('AIDU1010361','DCHE','30-JAN-02');
INSERT INTO TCON(unit_prefix,activity_code,activd) values
('AIDU1010361','DCHF','01-MAR-02');
INSERT INTO TCON(unit_prefix,activity_code,activd) values
('AIDU1010361','SNTC','10-MAR-02');
INSERT INTO TCON(unit_prefix,activity_code,activd) values
('AIDU1010361','RCVC','29-MAR-02');

BEST REGARD
and we said...
do you mean like this:

ops$tkyte@ORA920> select * from tcon;

UNIT_PREFIX                              PORT  ACTI ACTIVD    REGION
---------------------------------------- ----- ---- ---------
----------------------------------------
AIDU1010361                                    DCHF 10-JAN-02
AIDU1010361                                    RCVC 20-JAN-02
AIDU1010361                                    DCHE 30-JAN-02
AIDU1010361                                    DCHF 01-MAR-02
AIDU1010361                                    SNTC 10-MAR-02
AIDU1010361                                    DCHF 20-MAR-02
AIDU1010361                                    RCVC 29-MAR-02

7 rows selected.

ops$tkyte@ORA920>
ops$tkyte@ORA920> select unit_prefix, activity_code, activd,
  2       last_activity_code, last_activd,
  3       decode( last_activity_code, 'RCVC', activd - last_activd, null ) diff
  4    from (
  5  select unit_prefix, activity_code, activd,
  6         lead(activity_code) over ( order by activd ) last_activity_code,
  7         lead(activd) over ( order by activd ) last_activd
  8    from tcon
  9   where activity_code in ( 'DCHF', 'RCVC' )
 10         )
 11   where activity_code = 'DCHF'
 12  /

UNIT_PREFIX                         ACTI ACTIVD    LAST LAST_ACTI       DIFF
----------------------------------- ---- --------- ---- --------- ----------
AIDU1010361                         DCHF 10-JAN-02 RCVC 20-JAN-02        -10
AIDU1010361                         DCHF 01-MAR-02 DCHF 20-MAR-02
AIDU1010361                         DCHF 20-MAR-02 RCVC 29-MAR-02         -9
Review & Followup
Rating: 1
not accurate December 29, 2002
Reviewer: ashraf
SQL>  select unit_prefix, activity_code, activd,
  2            last_activity_code, last_activd,
  3            decode( last_activity_code, 'RCVC', activd - last_activd, null )
diff
  4         from (
  5       select unit_prefix, activity_code, activd,
  6              lead(activity_code) over ( order by activd )
last_activity_code,
  7              lead(activd) over ( order by activd ) last_activd
  8         from tcon
  9  where activity_code in ( 'DCHF', 'RCVC' ))
 10  where activity_code = 'DCHF' and unit_prefix='CAXU2305466';

UNIT_PREFIX          ACTIVITY_COD ACTIVD          LAST_ACTIVIT LAST_ACTIVD
-------------------- ------------ --------------- ------------ ---------------
      DIFF
----------
CAXU2305466          DCHF         09-JAN-02       DCHF         09-JAN-02

CAXU2305466          DCHF         02-FEB-02       DCHF         02-FEB-02

CAXU2305466          DCHF         18-MAR-02       DCHF         18-MAR-02

UNIT_PREFIX          ACTIVITY_COD ACTIVD          LAST_ACTIVIT LAST_ACTIVD
-------------------- ------------ --------------- ------------ ---------------
      DIFF
----------
CAXU2305466          DCHF         11-APR-02       DCHF         11-APR-02

CAXU2305466          DCHF         01-JUL-02       DCHF         01-JUL-02

where

SQL> select unit_prefix, activity_code, activd from tcon where
  2   unit_prefix='CAXU2305466' order by activd;

UNIT_PREFIX          ACTIVITY_COD ACTIVD
-------------------- ------------ ---------------
CAXU2305466          RCVF         09-JAN-02
CAXU2305466          DCHF         09-JAN-02
CAXU2305466          TRFF         09-JAN-02
CAXU2305466          SNTC         25-JAN-02
CAXU2305466          RCVC         26-JAN-02
CAXU2305466          SNTS         28-JAN-02
CAXU2305466          RCVS         29-JAN-02
CAXU2305466          LODF         31-JAN-02
CAXU2305466          DCHF         02-FEB-02
CAXU2305466          RCVC         09-FEB-02
CAXU2305466          SNTC         09-FEB-02

UNIT_PREFIX          ACTIVITY_COD ACTIVD
-------------------- ------------ ---------------
CAXU2305466          SSTF         18-FEB-02
CAXU2305466          RSTF         19-FEB-02
CAXU2305466          LODF         27-FEB-02
CAXU2305466          DCHT         04-MAR-02
CAXU2305466          LODT         11-MAR-02
CAXU2305466          DCHF         18-MAR-02
CAXU2305466          RSTR         23-MAR-02
CAXU2305466          SSTR         23-MAR-02
CAXU2305466          RCVE         26-MAR-02
CAXU2305466          TRFE         26-MAR-02
CAXU2305466          RCVR         27-MAR-02

UNIT_PREFIX          ACTIVITY_COD ACTIVD
-------------------- ------------ ---------------
CAXU2305466          SNTR         27-MAR-02
CAXU2305466          SNTS         28-MAR-02
CAXU2305466          RCVS         03-APR-02
CAXU2305466          LODF         07-APR-02
CAXU2305466          DCHF         11-APR-02
CAXU2305466          SNTC         13-APR-02
CAXU2305466          RCVC         14-APR-02
CAXU2305466          LODE         18-APR-02
CAXU2305466          DCHE         07-MAY-02
CAXU2305466          TRFE         25-MAY-02
CAXU2305466          RCVE         26-MAY-02

UNIT_PREFIX          ACTIVITY_COD ACTIVD
-------------------- ------------ ---------------
CAXU2305466          SNTS         13-JUN-02
CAXU2305466          RCVS         14-JUN-02
CAXU2305466          RCVF         15-JUN-02
CAXU2305466          TRFF         15-JUN-02
CAXU2305466          LODF         17-JUN-02
CAXU2305466          DCHF         01-JUL-02
CAXU2305466          SNTC         08-JUL-02
CAXU2305466          RCVC         09-JUL-02
CAXU2305466          LODE         11-JUL-02
CAXU2305466          DCHE         27-JUL-02
CAXU2305466          RCVE         28-JUL-02

UNIT_PREFIX          ACTIVITY_COD ACTIVD
-------------------- ------------ ---------------
CAXU2305466          TRFE         28-JUL-02
CAXU2305466          OFHI         07-AUG-02
CAXU2305466          RCVE         07-AUG-02
CAXU2305466          TRFE         07-AUG-02

48 rows selected.

SQL>

i try the query but it give me the first out which is not what i ask

Followup:
No, accurate -- you just rewrote my original query wrong.

You'll want to try and understand what the SQL is doing before you just blindly
apply predicates to it.  Analytics are extremely powerful -- make sure you
understand them before applying them.

ops$tkyte@ORA920> select unit_prefix, activity_code, activd,
  2         last_activity_code, last_activd,
  3      decode( last_activity_code, 'RCVC', activd - last_activd, null ) diff
  4    from (
  5  select unit_prefix, activity_code, activd,
  6         lead(activity_code) over ( order by activd ) last_activity_code,
  7         lead(activd) over ( order by activd ) last_activd
  8    from tcon
  9   where activity_code in ( 'DCHF', 'RCVC' )
 10     and unit_prefix = 'CAXU2305466'
 11         )
 12   where activity_code = 'DCHF'
 13  /

UNIT_PREFIX                         ACTI ACTIVD    LAST LAST_ACTI       DIFF
----------------------------------- ---- --------- ---- --------- ----------
CAXU2305466                         DCHF 09-JAN-02 RCVC 26-JAN-02        -17
CAXU2305466                         DCHF 02-FEB-02 RCVC 09-FEB-02         -7
CAXU2305466                         DCHF 18-MAR-02 DCHF 11-APR-02
CAXU2305466                         DCHF 11-APR-02 RCVC 14-APR-02         -3
CAXU2305466                         DCHF 01-JUL-02 RCVC 09-JUL-02         -8

Or, if you want to do it for many unit_prefixes at once, try this:

ops$tkyte@ORA920> select unit_prefix, activity_code, activd,
  2         last_activity_code, last_activd,
  3         decode( last_activity_code, 'RCVC', activd - last_activd, null )
diff
  4    from (
  5  select unit_prefix, activity_code, activd,
  6         lead(activity_code) over ( partition by unit_prefix order by
activd ) last_activity_code,
  7         lead(activd) over (  partition by unit_prefix order by activd )
last_activd
  8    from tcon
  9   where activity_code in ( 'DCHF', 'RCVC' )
 10         )
 11   where activity_code = 'DCHF'
 12  /

UNIT_PREFIX                         ACTI ACTIVD    LAST LAST_ACTI       DIFF
----------------------------------- ---- --------- ---- --------- ----------
CAXU2305466                         DCHF 09-JAN-02 RCVC 26-JAN-02        -17
CAXU2305466                         DCHF 02-FEB-02 RCVC 09-FEB-02         -7
CAXU2305466                         DCHF 18-MAR-02 DCHF 11-APR-02
CAXU2305466                         DCHF 11-APR-02 RCVC 14-APR-02         -3
CAXU2305466                         DCHF 01-JUL-02 RCVC 09-JUL-02         -8

abcU2305466                         DCHF 09-JAN-02 RCVC 26-JAN-02        -17
abcU2305466                         DCHF 02-FEB-02 RCVC 09-FEB-02         -7
abcU2305466                         DCHF 18-MAR-02 DCHF 11-APR-02
abcU2305466                         DCHF 11-APR-02 RCVC 14-APR-02         -3

9 rows selected.

where I just duped some of your data to give me two unit prefixes.

Rating: 5
very accurate December 30, 2002
Reviewer: A reader
thank's TOM

December 29, 2002 Juan Carlos -- Thanks for the question regarding " NOT != or <>, disable index usage.", version 9.2
You Asked
Hi Tom,
I would like you help me in the paper
# 31274 Tunning Secrets from the dark room of a dba,
from Oracle World.

in the 5th page, it says that if you use NOT != or <>, disable index use.

I tested it, and it was true.

SQL> select * from deftransac where dtr_transac = 'x' ;

no rows selected

Elapsed: 00:00:00.00

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=182)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'DEFTRANSAC_ME' (Cost=2 C
          ard=1 Bytes=182)

   2    1     INDEX (UNIQUE SCAN) OF 'CST_DTR_TRANSAC' (UNIQUE) (Cost=
          1 Card=34632)

Statistics
----------------------------------------------------------
          8  recursive calls
          0  db block gets
         22  consistent gets
          0  physical reads
          0  redo size
       1676  bytes sent via SQL*Net to client
        372  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed

AND ////////////

SQL> select * from deftransac where not dtr_transac = 'x' ;

no rows selected

Elapsed: 00:00:30.03

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=144 Card=17315 Bytes
          =3151330)

   1    0   TABLE ACCESS (FULL) OF 'DEFTRANSAC_ME' (Cost=144 Card=1731
          5 Bytes=3151330)

Statistics
----------------------------------------------------------
     138528  recursive calls
          0  db block gets
     347271  consistent gets
          0  physical reads
          0  redo size
       1676  bytes sent via SQL*Net to client
        372  bytes received via SQL*Net from client
          1  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed

Changing the parameter to
OPTIMIZER_INDEX_COST_ADJ  =  10
OPTIMIZER_INDEX_CACHING   =  90
The "problem" persisted.

What is the best to do?, is this rigth or it's wrong?

Is There a way to create, maybe a function index to the NOT use the index?

Thank you
<:) Happy New Year  o o o >-|
and we said...
so, why do you think using an index would be the correct way to go?  Think about
how an index is used....

In general, when something like " where x = 5" implies we should use an index --
that would mean that x is fairly selective.  That "where x = 5" would return a
small portion of the table.  In that case, "where NOT x = 5" is the opposite --
that would in general return almost ALL rows from the table.

Your case is a degenerate bad example.  Since neither query returned any records
-- apparently the value for that column is NULL thru and thru.  Try it on a
table with *REAL* values.  Or, if you really want to use the index, hint it or
if using RBO, fake it out:

where  dtr_transac < 'x' or dtr_transac > 'x';

But trust me -- in general, you *don't want the index* to be used.  You must
repeat to yourself over and over the following:

o indexes are not goodness incarnate
o full table scans are not evil
o indexes do not mean FAST=TRUE
o full table scans do not mean "optimizer=buggy"

See
http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:6749454952894
for a great example.

Here is the classic example of why using an index for NOT x = 5 would be a bad
idea.  Consider:

select *
from
 big_table where object_id = 55

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        6      0.00       0.00          0         72          0          64
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        8      0.00       0.00          0         72          0          64

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 80

Rows     Row Source Operation
-------  ---------------------------------------------------
     64  TABLE ACCESS BY INDEX ROWID BIG_TABLE
     64   INDEX RANGE SCAN BIG_TABLE_IDX1 (object id 35338)

used an index -- very nice, very fast.  Got 64 rows -- what you might expect
from an index retrieval -- a few rows.  That is what indexes do best... Now, run
the negative query

select *
from
 big_table where NOT object_id = 55

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch   122254     18.07      20.91      10437     146047          0     1833792
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total   122256     18.07      20.91      10437     146047          0     1833792

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 80

Rows     Row Source Operation
-------  ---------------------------------------------------
1833792  TABLE ACCESS FULL BIG_TABLE

How dare it not use that index!  It full scanned.  It returned over 1.8
million records too (that is the NORMAL outcome -- if x=y returns small number,
NOT x=y returns the rest typically -- you had a strange case).  So, lets try to
make it use an index "to go faster" cause we all know that indexes are like
"fast=true" (not!)

select /*+ INDEX( big_table BIG_TABLE_IDX1 ) */ *
from
 big_table where NOT object_id = 55

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch   122254     35.24     145.62      25407    1959783          0     1833792
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total   122256     35.24     145.62      25407    1959783          0     1833792

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 80

Rows     Row Source Operation
-------  ---------------------------------------------------
1833792  TABLE ACCESS BY INDEX ROWID BIG_TABLE
1833792   INDEX FULL SCAN BIG_TABLE_IDX1 (object id 35338)

Oh, that's not too good -- 7x longer, tons more logical IO's -- it killed my
system... Hmm, maybe not using an index was good...
Review & Followup
Rating: 5
December 29, 2002
Reviewer: A reader
Thank you Tom :)

December 30, 2002 sush -- Thanks for the question regarding "Sql Query", version 8.1.7
You Asked
Hi Tom ,

Sample Table : table1(is derived by join of two tables)

column1 column2  column3
100      1000     A
100      1000     A
100      1000     B
101      1001     B
101      1001     C
101      1001     D
102      1002     C
102      1002     C
102      1002     D
102      1002     D
103      1003     D

The query result should be something like below
column1 column2  column3
100      1000     A
100      1000     A
101      1001     B
102      1002     C
102      1002     C
103      1003     D

The logic is as follows
Priority for column3 is in this order ...A B C D

For the combination of column1 and column2 only records with the highest
priority should return i.e based on the priority as mentioned above.If the
combination has two records but the column3 is B and D.In the result set I want
'B' only.

Thanks in advance..

Uma
and we said...
In your example, since the collate nicely, it could be as easy as:

ops$tkyte@ORA920> select c1, c2, c3
  2    from ( select t.*,
  3                  min(c3) over (partition by c1, c2) min_c3
  4                     from t
  5             )
  6   where c3 = min_c3
  7  /

        C1         C2 C
---------- ---------- -
       100       1000 A
       100       1000 A
       101       1001 B
       102       1002 C
       102       1002 C
       103       1003 D

6 rows selected.

but in general, it would be like this:

ops$tkyte@ORA920> select c1, c2, c3
  2    from ( select t.*,
  3                  min(decode(c3,'A',1,'B',2,'C',3,'D',4) )
  4                                    over (partition by c1, c2) min_c3
  5                     from t
  6             )
  7   where decode(c3,'A',1,'B',2,'C',3,'D',4) = min_c3
  8  /

        C1         C2 C
---------- ---------- -
       100       1000 A
       100       1000 A
       101       1001 B
       102       1002 C
       102       1002 C
       103       1003 D

6 rows selected.

which allows you to change it (the collating sequence) by changing the
decode:

ops$tkyte@ORA920> select c1, c2, c3
  2    from ( select t.*,
  3                  min(decode(c3,'C',1,'A',2,'B',3,'D',4) )
  4                                    over (partition by c1, c2) min_c3
  5                     from t
  6             )
  7   where decode(c3,'C',1,'A',2,'B',3,'D',4) = min_c3
  8  /

        C1         C2 C
---------- ---------- -
       100       1000 A
       100       1000 A
       101       1001 C
       102       1002 C
       102       1002 C
       103       1003 D

6 rows selected.
Review & Followup
Rating: 5
December 29, 2002
Reviewer: uma from san jose,USA
Thanks Tom for the solution...Is over (partition by c1, c2) is a new feature in
oracle 8i ? or it exists even in 7,8.0 versions ?

Followup:
Analytic functions are new in 8i and up (thats what these are called)

Rating: 5
Analytical Function December 30, 2002
Reviewer: Ashiq from Trichy, Tamil Nadu ,India
Analytical Functions are availble starting from Oracle 8i(ie Oracle 8.1.6)
onwards.

Rating: 5
Analytical Function December 30, 2002
Reviewer: Ashiq from Trichy, Tamil Nadu ,India
Analytical Functions are available starting from Oracle 8i(ie Oracle 8.1.6)
onwards.

December 30, 2002 -- Thanks for the question regarding "Does a Bitmap index in OLTP make long slowdowns?", version 8.1.7.2.0
You Asked
We use a bitmap index on a table frequently attacked by concurrent session
issuing DML statements.  We suspect that DMLs on this bitmap index may result in
slowdowns in a database. We should kill sessions usually enough which runs a
select  statement on a table indexed with a bitmap.Is there a manner to track it
down or check it out among system events? How to justify our assumption?
and we said...
bitmap indexes are for read only or read mostly data.

they are toally DEADLY in OLTP.  do not even consider them -- not even for a
second.

their structure is wholly unsuitable for concurrent modifications and they
quickly, rapidly and totally break down with singleton inserts/updates/deletes.

get rid of them.
Review & Followup
Rating: 4
October 09, 2002
Reviewer: A reader

Rating: 4
Does a Bitmap index in OLTP make long slowdowns? October 09, 2002
Reviewer: Rory Concepcion from Philippines
Hi Tom,

    I have a question which is closely related to this.
We recently encountered an ora-600 [12403] for a particular query. We were able
to file a tar for this but support I still have some questions re this.Our
database is version 8.0.6.
    The explain plan when the error occured was:
SELECT STATEMENT Hint=CHOOSE        1           640
  NESTED LOOPS        1      156      640
    NESTED LOOPS        95      6 K    355
      INDEX FULL SCAN    RCYCLE_IDX2    10      260      1
      TABLE ACCESS BY INDEX ROWID    RACCOUNT    639      27 K    19961
        BITMAP CONVERSION TO ROWIDS
          BITMAP AND
            BITMAP CONVERSION FROM ROWIDS
              INDEX RANGE SCAN    RACCOUNT_IDX_002
            BITMAP OR
              BITMAP INDEX SINGLE VALUE    RACCOUNT_IDX_004
              BITMAP INDEX SINGLE VALUE    RACCOUNT_IDX_004
              BITMAP INDEX SINGLE VALUE    RACCOUNT_IDX_004
              BITMAP INDEX SINGLE VALUE    RACCOUNT_IDX_004
            BITMAP CONVERSION FROM ROWIDS
              INDEX RANGE SCAN    RACCOUNT_IDX_001
    TABLE ACCESS BY INDEX ROWID    RSCHDULE    1 K    118 K    3
      INDEX RANGE SCAN    RSCHDULE_IDX_03    1 K         2

When we rebuilt the index, it was

Operation    Object Name    Rows    Bytes    Cost    TQ    In/Out    PStart
PStop

SELECT STATEMENT Hint=CHOOSE        1           306
  NESTED LOOPS        1      156      306
    NESTED LOOPS        95      6 K    21
      INDEX FULL SCAN    RCYCLE_IDX2    10      260      1
      TABLE ACCESS BY INDEX ROWID    RACCOUNT    639      27 K    2
      INDEX RANGE SCAN    RACCOUNT_IDX_002    639           1
    TABLE ACCESS BY INDEX ROWID    RSCHDULE    1 K    118 K    3
      INDEX RANGE SCAN    RSCHDULE_IDX_03    1 K         2
My question is, how come the index "RACCOUNT_IDX_002 is converted into a
bitmapped index when it was not originally one?

    Thanks.

Followup:
In this case, it was so the other bitmapped indexed could be used and anded
together.  we cannot "and" a conventional b*tree and a bitmap -- we can only do
that with all bitmaps -- so it converted it.

Rating: 4
Does a Bitmap index in OLTP make long slowdowns October 09, 2002
Reviewer: Rory Concepcion from Philippines
Just a follow-up sir.

Is there any way that we could prevent this from happening again?(The conversion
of the b-tree index into a bitmap.). And another thing, could you please give me
a reference (free) that would teach me more about this(b-tree being converted
into bitmap... and why's).

Thanks!

Followup:
You don't want this to be prevented.  If you hit an internal error, contact
support and get it fixed (you can ask support for further information as to
whether this can be disabled via some undocumented parameter).  There is
b_tree_bitmap_plans but that supposedly only enables the generation of bitmap
plans even with no bitmapped indexes.

See
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76992/ch6_acce.htm#2506

Rating: 4
one more on the same line October 10, 2002
Reviewer: Lebon Mathew from San Francisco, CA
Does a Function based index in OLTP make   slowdowns?", version 8.1.7.3.0

something like

create index emp_fn_idx on emp
      (nvl(COMM,SAL*.1));

Followup:
Depends.

Do you frequently query "where nvl(comm,sal*.1) = :x"

Do you infrequently insert or update comm and sal?  (infrequently as compared to
the number of times you query on it)

Every index -- EVERY index you add in oltp comes with a cost.  It'll impact the
cost of doing an insert, update or delete of that data.  We always offset that
cost with the cost of NOT having the index itself (I mean, can you image the HR
emp table without an index on EMPNO???  Neither can I).

So, is the cost of NOT having this index (forget about function based, just
consider INDEX) more then the cost of HAVING this index?  If so, create it, if
not -- don't.

(suppose that query avoids a full scan of a large table 50,000 times a day.  The
cost of NOT having it may well be really high)

Rating: 5
bitmap index for joins December 30, 2002
Reviewer: A reader
Hi

In datawarehouse I have a dimension (or parent table whatever you call them)
with 20 rows and a fact table (one of the smallest) with 10 million of rows, the
fact table's FK column referencing the dimension has few distinct values, if I
index this column with bitmap index will the joins (nested loop) use botmap
index? Or it must be b*tree index?

Followup:
You might consider a bitmap JOIN index to denormalize the attributes of the 20
rows into the fact table via the INDEX itself (9i and up only -- you tell me
nothing about the versions...)

You can bitmap index in 8i -- we'll use it when it makes sense.  You don't
really have any hard examples of when/what type of queries you would like to use
it for -- but yes, you can use the bitmap index.

December 30, 2002 Norman -- Thanks for the question regarding "Rename a column", version
You Asked
In Oracle 8i database, can you rename a column without
dropping
the table?
and we said...
please use the form in the future.  we use it for tracking
purposes.

No, you cannot rename a column.

In Oracle8i, you can drop a column from a table but not rename.

The way to rename a column would be to use a view.  To minimize
the impact on applications, you would simply:

rename TableName to TableName_TBL;
create view TableName
as
select c1 New_Name_For_C1, c2, c3, .... cn from TableName_Tbl;

then grant on the view what you normally granted on the table.

In that fashion, all existing applications will see the new
column name with the old table name.
Review & Followup
Rating: 2
rename a column December 27, 2002
Reviewer: Jack from CA, USA
Tom,
This is a nice solution. But what happens to triggers? how they will work. What
are the complications of updating this table, insertes, deletes?. pl. explain
briefly.
What is your solution if it is a production system with 5 million rows in that
table.
appreciate your help everytime.

Followup:
triggers work as always -- on base tables.

Inserts, updates, deletes -- work as normal.

If this is a production system -- why the heck are you renaming a column?  I
would refuse that -- I would only create a new view with the new name to satisfy
whomever was not satisified with the old name.

Rating: 5
Renaming a column... December 28, 2002
Reviewer: Connor from UK
I used to be of the "why the heck do want to rename column" school of
thought...Then I ran 'select count(*) from v$reserved_word' and got 800 or so
!!!

Then again, that was in 9i - and I can rename a column in 9i anyway :-)

Followup:
well, that is misleading for most of the reserved words are OK

sys@ORA920> declare
  2      cnt number := 0;
  3  begin
  4      for x in ( select * from v$reserved_words )
  5      loop
  6          begin
  7              execute immediate 'declare ' || x.keyword || ' int; begin null;
end;';
  8          exception
  9              when others then
 10                  dbms_output.put_line( x.keyword );
 11                  cnt := cnt + 1;
 12          end;
 13      end loop;
 14      dbms_output.put_line( cnt || ' bad ones' );
 15  end;
 16  /
&
|
:
,
-
=
>
[
<
(
.
+
]
)
!
....
WHERE
WITH
106 bad ones

PL/SQL procedure successfully completed.

there is only 106 really reserved words and many of them are not words at all..

and there is always quoted identifiers ;)

Rating: 5
true... December 28, 2002
Reviewer: Connor McDonald from UK
...but if there is a reserved word called (say) "RESERVED", typically its not
the kinda thing you want to see littered throughout columns, triggers, plsql
code etc...Tends to confuse things ;-)

And don't me started on quoted idents.  Can't remember the name of the package,
but there is a health care one in use in Australia - you know who you are :-),
in which ALL objects are in lower case in the dictionary.  Couple of months of
that and I was very close to kicking their software down the street :-)

Rating: 4
Timestamp December 30, 2002
Reviewer: Paul from UK
Tom,

We've got an application (8.1.7) where there are columns named "TIMESTAMP"
(DATE) littered all over the place.  Is this going to cause problems when we
eventually go to 9i?

I would try this myself, but don't have a 9i database to play on.

Followup:
It will give you issues in PLSQL and maybe other precompiler like environments.

Rating: 4
use the form ?? December 30, 2002
Reviewer: robert from PA
>> please use the form in the future.  we use it for tracking purposes.

What you mean ? you mean there is an "undocumented" way for
posting questions, an AskTom private line ....?

Followup:
They asked a question

I asked for some followup in this case (clarification)

Instead of using the form to supply the requested information -- they emailed me
the stuff.

I was in a good mood so rather then just deleting it, I put it into the form for
them and answered the question...

Most emails that get sent to me get this little blurb in return, so it is not
the best way to get in touch for a question.  It has helped to cut way down on
the size of my inbox in the morning!

...
Please do not take this personally, this is a generic response I send to
direct questions.

Please use
http://asktom.oracle.com/
to ask me questions.  When I have time,
I accept them there.  It is not a very scalable solution for me to answer
individual questions in email.  I cannot share these answers with many
thousands of other Oracle users.

You must understand there are many thousands of people like you and there is
really only one of me (i am not a team, a group, or anything like that --
there is really just me).  Answering individual questions via email is not a
good way for me to spend my time.
........

Rating: 4
One Tom and too many Oracle junkies December 30, 2002
Reviewer: Robert from PA
>> there is really only one of me (i am not a team, a group, or anything like
that -- there is really just me).

Right, you're THE man, Tom. You certainly done justice to the name of this
awesome site.
But Ive wondered why it's just you. Is it a (...shhh...) boss or Oracle Corp
thing...resource thing...?
I'd think you enjoy fielding questions and educate us Oracle junkies and there
must be people working with you who'd like to do the same and would offer to
pitch in.
Just curious, you know...

Followup:
well, there is devtrends.oracle.com -- Cameron works for me.

I do occasionally get Sean Dillon (xml), Chris Beck (portal/webdb/ultrasearch),
Marshall Presser (RAC/HA), Kim Better (portal), Clark Colombo (spatial), and
others to answer as well (they always get the credit -- answer starts with "i
asked our resident <technlogy here> guru "name" and they said:"

December 30, 2002 -- Thanks for the question regarding "Exp from oracle/Linux & imp on oracle/NT", version 8.1.7
You Asked
Hai Tom,

1)I wish to export from oracle 8.1.7(on linux) and import to other database
8.0.4 on NT.  I run catexp7.sql on linux server and I took export by running
exp(as when I executed exp70 ,it says command not found).  Then the dump file is
BINARY ftp'ied to NT machine.  File size is same on both machines.  When I tried
to import with imp80 on NT it gave following errors...

IMP-00069 : Could not convert to environment national character set's handle
IMP-00021 : operating system error - error code (dec 2, hex 0x2)
IMP-00000 : Import terminated unsuccessfully

2)  I want to enfore only SINGLE sign-on. For example SCOTT user, at a time only
ONE session should work for SCOTT user. please advice me on this.

Thanks for your help,

regs
Abdullah
and we said...
1) You need to use the 8.0.4 export (exp80) on the NT box over Net8 to do this.
dmp files are backwards, not forwards compatible (8.1.7 can read 8.0.4 but 8.0.4
cannot read 8.1.7 -- there are tons of things in an 8.1.7 database 8.0.4 doesn't
understand)

2)  see resource profiles.  they do this.  SQL Language ref (create profile) as
well as Admin guide for details (you must set an init.ora parameter
resource_limit)
Review & Followup
Rating: 5
imp 0069 December 30, 2002
Reviewer: A reader
D:\data>imp userid=system/manager@mydb

Import: Release 9.2.0.1.0 - Production on Mon Dec 30 12:41:53 2002

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

Connected to: Oracle8i Enterprise Edition Release 8.1.7.4.0 - Production
With the Partitioning option
JServer Release 8.1.7.4.0 - Production

IMP-00003: ORACLE error 942 encountered
ORA-00942: table or view does not exist
IMP-00023: Import views not installed, please notify your DBA
IMP-00000: Import terminated unsuccessfully

D:\data>

db server ora 8i ( 8.1.7.4)
Export created by EXPORT:V08.01.07 via conventional path
client : 9.2.0.1

what tables/views do I need to create ?
I have logged in as system

Thanks

Followup:
You need to use the 817 import tool to import into 817.

December 30, 2002 Jonas -- Thanks for the question regarding "Deadlock detection internals", version 8.1.7
You Asked
Im really curious how Oracle actually implements deadlock detection. From your
book, you discuss that locks are maintained within the block header, providing a
loosely coupled mechanism for lock administration (as opposed to a lock manager
as implemented in other RDBMS systems which can become a point of contention).
On the other hand, it seems like it would be an administrative nightmare to have
to search through all block headers in order to determine whether or not there
is a deadlock. Is the v$lock view (or the mysterious X tables) used for this?
What am i missing?
and we said...
in v$lock we can see quickly what transaction is waiting on what transaction.
every 3 seconds we actually look to see if transaction A is dependent on B and B
on A and then fail one of the two transactions.

we don't goto the blocks -- but rather to just the transaction enqueue tables to
see who is waiting on who.
Review & Followup
Rating: 5
What is the deciding factor for choosing Deadlock victim? December 30, 2002
Reviewer: Dave Anderson from Rhode Island, USA
Tom,
I've read the section on Deadlocks in your book and most of the Q&A's here, but
what is the magic formula for selecting the victim?

Thanks!
Dave

PS When and where are you speaking next?

Followup:
In my experience -- the one that was blocked first becomes the victim.  You can
see this in plus with 2 sessions:

select * from emp where emp no = 7934 for update;

                               select * from emp where empno = 7369 for update;

select * from emp where empno = 7369 for update; <<<= gets blocked

                BLOCKS and causes blocked session to get error
                ===>           select * from emp where emp no = 7934 for update;

select * from emp where empno = 7369 for update; <<<= gets blocked and
                                                 causes above session to get
                                                 error

see my homepage -- i put the speaking stuff there (and just added one)

Rating: 5
Thank You April 02, 2003
Reviewer: Dave Anderson from Rhode Island, USA!
Thanks as always Tom.  Keep up the fantastic work.

December 30, 2002 -- Thanks for the question regarding "Duplicate check constraints", version 8.0.5
You Asked
Hi Tom
In our database we import data from our prod db on a regular basis. We have some
check constraints in our database that have system generated names,sysc00..
Because of this we get duplicate constraints in our development database as I
import with the constraints=Y option. Is there a way ,a query ,to find out
duplicate constraints from the user_constraints table . The problem I see is
using the search_condition column of the user_constraints table in the where
clause as it is of the type "Long".
Any help is appreciated.
Thx
Tans
and we said...
Ahh yes, reason 524 to ALWAYS name your constraints!

You could create a function:

create function search_cond( p_cons_name in varchar2 ) return varchar2
as
   l_search_condition varchar2(4000);
begin
   select SEARCH_CONDITION into l_search_condition
     from user_constraints
    where constraint_name = p_cons_name;

   return l_search_condition;
end;
/

and then

select search_cond_vc, count(*)
from (
select search_cond( constraint_name ) search_cond_vc
  from user_constraints
 where constraint_name like 'SYS%'
     )
group by search_cond_vc
having count(*) > 1;
Review & Followup
Rating: 4
July 03, 2001
Reviewer: Pauline from NY, U.S.A.

Rating: 5
Peter December 19, 2001
Reviewer: Pierre Bilodeau from Canada
Thanks for your tips

Rating: 4
Safely fixing the duplicates May 08, 2002
Reviewer: Steve Reich from USA
Be careful deleting the duplicate constraints.  Some of the duplicate
constraints are the not null check constraints automatically created when you
define a column as not nullable.  If you drop the original check constraint then
the nullable field in user_tab_columns will be set to 'Y', even if you preserve
a duplicate constraint, so be sure not to drop the original.

Rating: 5
what constraints are created ? December 30, 2002
Reviewer: A reader
Hi tom,

  Can you please tell me or point me to the link(website)
where can look for

1.) how may and what constraints are created when I
    create a primary key
2.) how may and what constraints are created when I
    create a foreign key
3.) and How to name those implicit  constraints ?

Followup:
It is the Oracle SQL Reference guide.

ops$tkyte@ORA920> create table t ( x int , constraint t_pk primary key(x) )
  2  /

Table created.

ops$tkyte@ORA920>
ops$tkyte@ORA920> select count(*) from user_constraints;

  COUNT(*)
----------
         1

A constraint creates ONE constraint.  a primary key is a constraint, a foreign
key is a constraint.  one constraint each.

December 30, 2002 Pratap -- Thanks for the question regarding "Relational database - why the name", version 9.2.0
You Asked
Hi Tom

1. A basic doubt to clear - Why do we call databases "Relational" databases.
What is the meaning of "Relational".

2. Why relational database was better than its earlier counterparts.

3. Are there any new concepts coming up in database management - better than
RDBMS.

Thanks and regards

Pratap
and we said...
1)

Main Entry: re·la·tion·al
Pronunciation: -shn&l, -sh&-n&l
Function: adjective
Date: 1662
1 : of or relating to kinship
2 : characterized or constituted by relations
3 : having the function chiefly of indicating a relation of syntax <has is
notional in he has luck, relational in he has gone>
4 : relating to, using, or being a method of organizing data in a database so
that it is perceived by the user as a set of tables
- re·la·tion·al·ly adverb

it is the basic design of the RDBMS -- you have tuples that relate to one
another -- it is based on relational calculus and sets

2) better is a subjective term.  many of the precursors are still there.  Is
Java "better" then COBOL?  Is C++ better then C (i would say *no*).

RDBMS's caught on due to their relative ease of use and flexibility in storing
data.

3) there is that subjective term.  different -- sure, better -- no, just
different.  Do we incorporate them?  Yes -- for example, the object relational
stuff we have in the DB violates lots of the RDBMS rules.  Incorporating the
XMLType into the XMLDB rolls all of the functionality of an XML database into
the RDBMS...  Adding non-scalar datatypes like our Spatial Type, Text, Video,
Image, Audio, etc....
Review & Followup
Rating: 5
December 30, 2002
Reviewer: A reader
Not to take the liberty of correcting your english or anything but.....

You often use the word "then" in place of "than" (ie. more often than not you
say "better then" instead of "better than"). I thought it was probably an
american 'slang'-so to say, but after I came to US I know it is not. I just
wanted to point out that, sometimes atleast, this reduces the readability of
your sentences.

PS: I love this site. There is no other site where I spent somuch time. On
numerous times I have been able to clarify my doubts from you. Keep up the good
work! Thanks once again and Best wishes for a Great New Year.

--A fan of yours!

Followup:
you should have heard my editors -- yes, I have a really bad time with that one.
 for some reason -- they just sound the same to me.   I really do try to look at
it when I use "then" and try to figure out if its right.  Never get it just so.
I'll try to avoid the use of then and than!  (i hardly use "guarantee" because I
almost always spell it wrong -- "assure" is so much easier ;)

Rating: 4
...and than there was PLSQL December 30, 2002
Reviewer: robert from pa
I bet when you were doing just C , "than" is "than", but then comes plsql...

Rating: 5
Me too - I thought it was American December 30, 2002
Reviewer: A reader
Dear Tom,
  I have spent the last month or so reading through your book and am having a
tough time not using 'then' in my mails now:-)

Another fan

Rating: 5
Give the man a break December 31, 2002
Reviewer: Andrew Gilfrin from Dover, Kent, UK
I have often noticed that something Tom has written is not correct, he will miss
letters out or use then rather than than. But compare that with 90% of the
e-mails in which the grammar and spelling is far from good (I know many people
who post do not have English as a first language, I am in awe of people who can
communicate in a second language)

The advent of e-mail and electronic communication has meant that we use the
written word more often in a casual conversation style, a couple of miss spelt
words in my view are insignificant with the huge amount of information Tom gives
out, so come on give the guy a break.

Use Then, Than or whatever you like, I think we are intelligent enough to be
able to get the true point of your posts. Keep up the good work

Rating: 5
Come on professional Techos December 31, 2002
Reviewer: Tony
Hi Tom,
Happy New Year ‘2003’
Once again, I could not resist to acknowledge the help I got from you and this
site, which I could not get even buying and reading so many books.
I remember the critical moments (I am sure there might me others visiting this
site) when all doors are closed for finding a answer or clearing doubts, the
answer is already here or reply is so quick, that too without spending a cent in
this business world. I really appreciate the work for the Oracle community Tom
is doing, for which there is no substitute. Once I asked a question to prepare a
list for reasons to upgrade to Oracle 9i for data warehousing application.
Before I could even start my research, I got the reply, what a relief???
I sincerely thank you very much, and hope that you continue this work in the
years to come.
Your Book titled “expert one-on-one” is really great. The title is true
reflection of your assistance on this site.

In this uncertain world, this site is real world example of HELP.

another fan of  yours...

Tony

December 30, 2002 Anderson -- Thanks for the question regarding "8i to 8.0.5 - Subqueries", version 8.0.5
You Asked
Hi Tom,
I work with several queries like this :

select decode( d.id_dis_typ,
        'ISP', i.cd_pay_trm,
        'CMS', c.cd_pay_trm,
        'BSP', b.cd_pay_trm,
        'NSP', n.cd_pay_trm ) cd_pay_trm,
(     SELECT pt.nm_trm
    FROM PaymentTerm pt
    WHERE pt.cd_cpy = d.cd_cpy
    AND pt.cd_pay_trm = decode( d.id_dis_typ,
                    'ISP', i.cd_pay_trm,
                    'CMS', c.cd_pay_trm,
                    'BSP', b.cd_pay_trm,
                    'NSP', n.cd_pay_trm )
) nm_trm,
d.cd_cpy
from         Invoice i,
        Creditmemo c,
        Bill b,
        NotaFiscal n,
        Distribution d
where d.cd_cpy            = i.cd_cpy (+)
    and d.nu_seq_jnl_ent    = i.nu_seq_jnl_ent (+)
    and d.cd_cpy            = c.cd_cpy (+)
    and d.nu_seq_jnl_ent    = c.nu_seq_jnl_ent (+)
    and d.cd_cpy            = b.cd_cpy (+)
    and d.nu_seq_jnl_ent    = b.nu_seq_jnl_ent (+)
    and d.cd_cpy            = n.cd_cpy (+)
    and d.nu_seq_jnl_ent    = n.nu_seq_jnl_ent (+);

This query works fine in Oracle 8i, but not in 8.0.5:

(       SELECT pt.nm_trm
        *
ERROR at line 6:
ORA-00936: missing expression

What do you suggest me?
and we said...
The ability to "select a select" was added in Oracle8i.

quick fix:

create or replace function nm_trm_f( d_cd_cpy in varchar2,
                                     d_id_dis_typ in varchar2,
                                     i_id_dis_typ in varchar2,
                                     c_id_dis_typ in varchar2,
                                     b_id_dis_typ in varchar2,
                                     n_id_dis_typ in varchar2 )
return varchar2
as
  l_nm_trm varchar2(255);
begin
  SELECT pt.nm_trm into l_nm_trm
    FROM PaymentTerm pt
    WHERE pt.cd_cpy = d_cd_cpy
    AND pt.cd_pay_trm = decode( d_id_dis_typ,
                              'ISP', i_cd_pay_trm,
                              'CMS', c_cd_pay_trm,
                              'BSP', b_cd_pay_trm,
                               'NSP', n_cd_pay_trm );

   return l_nm_trm;
exception
   when no_data_found then return null;
end;
/

and select that funtion.  Now, if your query returns only a couple of rows (say
100 or so) - this will work fine.  for large result sets the overhead of calling
the plsql function might be too high -- in that case you should rewrite the
query as:

select pt.nm_trm, cd_pay_trm, cd_cpy
from (
select decode( d.id_dis_typ,
        'ISP', i.cd_pay_trm,
        'CMS', c.cd_pay_trm,
        'BSP', b.cd_pay_trm,
        'NSP', n.cd_pay_trm ) cd_pay_trm, d.cd_cpy
from    Invoice i,
        Creditmemo c,
        Bill b,
        NotaFiscal n,
        Distribution d
where d.cd_cpy            = i.cd_cpy (+)
    and d.nu_seq_jnl_ent    = i.nu_seq_jnl_ent (+)
    and d.cd_cpy            = c.cd_cpy (+)
    and d.nu_seq_jnl_ent    = c.nu_seq_jnl_ent (+)
    and d.cd_cpy            = b.cd_cpy (+)
    and d.nu_seq_jnl_ent    = b.nu_seq_jnl_ent (+)
    and d.cd_cpy            = n.cd_cpy (+)
    and d.nu_seq_jnl_ent    = n.nu_seq_jnl_ent (+)
) a, paymentTerm pt
where pt.cd_cpy(+) = a.cd_cpy
  and pt.cd_pay_trm(+) = cd_pay_trm;

it should return the same results.  the outer join to PT might not be necessary
if it always joins.
Review & Followup
Rating: 5
inline view and "select a select" July 07, 2002
Reviewer: Rajesh
Tom,
  What is the difference between an inline view and
"select a select" statements. Thanks.

Followup:
They are very similar.  The major difference is probably the same as the
difference between a "normal subquery" and a "correlated subquery".

An inline view is like a "normal subquery".
Selecting a select is more like a correlated subquery.

Consider:

select d.dname, nvl(e.cnt,0)
  from dept d, ( select deptno, count(*) cnt from emp group by deptno ) e
 where d.deptno = e.deptno(+);

that is an inline view -- it can "stand along", it is not correlated with the
outer query.  Now,

select dname, (select count(*) from emp where emp.deptno = dept.deptno ) cnt
  from dept;

that query returns the exact same results -- just using a correlated "select of
a select" -- it is very much like a correlated subquery.  In general, "selects
of a select" use a select statement that cannot "stand alone" -- it needs that
outer query to drive it.

But they are really quite similar (and equally handy)

Rating: 5
July 17, 2002
Reviewer: A reader
'
and select that funtion.  Now, if your query returns only a couple of rows (say
100 or so) - this will work fine.  for large result sets the overhead of calling

the plsql function might be too high -- in that case you should rewrite the
query as:
'

Tom, can you explain the different reasons why functions are so costly to use in
sql.

Followup:
they don't run as fast as when they are not there....

say you have a finely written PLSQL routine that runs in 1/100 of a second.  It
might also take 1/100 of a second to set up the "call environment, actually call
the procedure, and clean up the stack upon return -- including the time for the
Oracle context switch from the database kernel to the user code".  So, if you
call  it 100 times -- that query will take at least 2 seconds (just executing
the code).  Call it 1000 times and you are talking 20 seconds -- and so on.
Take a little number, mulitply by a bigger number and you get a sizable number
after a bit.  Each call is "fast" but 1000 of them -- start to feel slow.

Rating: 5
select a select December 30, 2002
Reviewer: Steve from NYC, USA
Hi tom,

For the select part of the following sql, I'd like to set
‘comp start date’ to add_months(b.open_date, 15) and  ‘comp end date’ to
nvl(b.close_date, sysdate). But I got Nothing
in COMP_START_DATE column. I don't know if it is possible using single SQL to do
it?

Thanks!

Steve

insert into srs_sales_comp_stores
(  STORE_NUM,  COMP_START_DATE, COMP_END_DATE, LY_COMP_START_DATE,
LY_COMP_END_DATE)
select b.store_num,  add_months(b.open_date, 15) comp_start_date,
nvl(b.close_date, sysdate) comp_end_date,
 ( select transact_date
   from   bn_period
   where  fiscal_yr||fiscal_wk||dow = (
                                      select fiscal_yr-1||fiscal_wk||6
                                      from bn_period
                                      where transact_date = ‘comp start date’
                                    ))  LY_COMP_START_DATE,
 ( select transact_date
   from   bn_period
   where  fiscal_yr||fiscal_wk||dow = (
                             select fiscal_yr-1||fiscal_wk||6
                            from bn_period
                            where transact_date = ‘comp end date’
                                       )) LY_COMP_END_DATE
from bn_store b
where b.status != 'Z'

Followup:
if you run the query by itself -- not as an insert-- do you see anything?

if not -- then there isn't any data being returned from the subquery.

Rating: 3
select a select December 30, 2002
Reviewer: Steve from NYC, USA
Hi tom,

The reason I am asking you is because
there isn't any data being returned from the subquery.
So I'd like to get an advice from you if there is a way to
get correlated column data back from a sigle query rather than adding a Updated
statement.

Thanks!

Steve

Followup:
not sure what you mean -- do you believe data *should be* returned and its not?

Cause if the select (select ).... returns nothing for the subquery the update
set x = (select ... ) would as well.

Rating: 5
December 30, 2002
Reviewer: Dave from Ohio
I notice that the query in the select-a-select doesn't get a mention in the
explain plan. Can we tell what the access path would be for the select-a-select
by isolating it and running an explain plan for just that query?

Followup:
Nope, it doesn't appear there and there is currently no way to see it.


Subject | Article | Last Updated (East coast time)
Locks & Concurrency | article only | Dec 31, 2002 08:47 am
encrypt data, spell checker | article only | Dec 31, 2002 10:02 am
Execute immediate and rollback segments | article only | Dec 31, 2002 12:10 pm
Oracle Replication as Failover | article only | Dec 31, 2002 01:06 pm
Character in Number | article only | Dec 31, 2002 04:31 pm
Recursive sql | article only | Dec 31, 2002 04:52 pm
ORA-00980: synonym translation is no longer valid | article only | Dec 31, 2002 07:12 pm
row(s) 1 - 7 of 7
December 31, 2002 sushant -- Thanks for the question regarding "Locks & Concurrency", version 8.1.7
You Asked
Hi Tom

1)  I was going through your book. My question is about the deadlock situation
mentioned in chapter 3 Locking & Concurrency where you mention that a
rare situation like a deadlock can arise in a parent child relation
 say when we delete or update a primary key value from the parent table
and the  foreign key column in child table is not indexed.

What i want to understand is how imposing a index on the foreign key
 prevents locking of the child table when any updation or deletion is
 performed on the parent table.

2)Can you give an example of lost update problem . I am not using
any front end just plain sqlplus with multiple sessions but never
seem to find one. Does it arise because of improper coding only
when one updates all the columns of a row rather than only the
modified column.

3)What is the default isolation level in oracle I reckon read committed.
What i am confused a bit is when say
i have 1 session which updates a row in emp table but does not commit.
In the second session i just select the same table it will return me the
unmodified data as it will fetch the data from the rollback segment &
reconstruct
the block.
But in the 1st session i get the modified data when i query the table even
without committing.Can you ellaborate a bit on this.

Thanks
Regards
Sushant
and we said...
1)
http://technet.oracle.com/docs/products/oracle8i/doc_library/817_doc/server.817/a76965/c24integ.htm#8565
explains it in detail

2) simple lost update.

open two windows (two sqlplus sessions...  use the following commands (in order)

session 1                              session 2

select ename, empno, deptno, sal
  from emp;

                                       select ename, empno, deptno, sal
                                         from emp;

-- at this point, you decided that people in DEPTNO = 10 are under paid
-- you give them a raise:

update emp set sal = sal * 1.10
where deptno = 10;

                                        update emp set sal = sal * 1.10
                                          where deptno = 10;

commit;
                                        commit;

There you go -- you both looked at data, came to a conclusion and then blindly
updated the rows, not checking to see if the data was the same data they used to
make their decisions on.

Or, just change the update of the sal to an update of an individuals DEPTNO.
Have one session change someone from dept 10 to 20, have the other change them
to 30 -- the second update causes the "lost update" since they both looked at
the emp, saw deptno = 10 and then blindly updated the information.

3) it is read committed and if you have my book as you say, I cover the
transactional stuff completely in the early chapters.

A session can always see the changes IT MADE.  No other session can see any
changes made by other sessions until it COMMITS.
Review & Followup
Rating: 2
Give it more December 31, 2002
Reviewer: Rajesh from Kerala,INDIA
Tom,
your answer is good ,But using  that example you should try to explain the
locks(pessimistic,optimistic) more details.
Any way you are doing a very good job to help the It world
I appriciate you for all these

regards
Rajesh

Followup:
did you try searching for it on this site?

I've talked about it till I'm blue in the face.  It is in the book "Expert one
on one Oracle" in detail as well.  Only so many minutes to type in this world...

give the search a try, you might be pleasantly surprised.

Its funny also that this question wasn't about optimistic/pessimistic locking at
all.  deadlocks, lost updates, isolation levels -- yes, that other thing -- no.

December 31, 2002 ASHRAF -- Thanks for the question regarding "encrypt data, spell checker", version 8i
You Asked
hi tom
can you pls. tell me about
1- how can i encrypt data through network
2- i make schedule to exam DBA all i have background operating system is windows
(NT,2000) and novell but i havn't any background about unix .... is it important
to go through unix ?
3- can u tell me about this spell check u make can i browse the source

best reg
ashraf gamal
and we said...
1) search for dbms_obfuscation_toolkit on my site.

2) no.

3) see
http://asktom.oracle.com/~tkyte/speling.tar
...

followup to comment

not sure what you mean by that.  DBMS_OBFUSCATION_TOOLKIT is a supplied pacakge
with Oracle.  It implements DES and 3DES encryption in the database.

No only can make use of your encrypted data unless they have your KEY.  Protect
your KEY and your data is protected.  Having access to a real encryption
algorithm does not make it any easier to crack the encrypted data.

followup to comment 3 below

you have the source code, you can make it do ANYTHING you want it to do.
Review & Followup
Rating: 3
it it useful but June 25, 2001
Reviewer: ashraf gamal from kuwait
this mean that any one can have this toolkit and browse the data i need
encryption that certified for internal or external database users

Rating: 4
ok June 26, 2001
Reviewer: ashraf gamal from kuwait
thanks tom

Rating: 4
disabling add new word in dictionary June 28, 2001
Reviewer: reader from india
how  can i disable add new word in dictionary.

Rating: 5
spell checker December 31, 2002
Reviewer: Jeff from Miami
Tom,
1.)  How do I start a new question on Ask Tom?  The HELP ? button at top did not
help me to figure out how to ask a new question.  I see no links for that?  I
only see reviews.

2.)  I want to spell check text or varchar2() columns?  Is there a spell check
function available?

Thanks,
jeff

Followup:
1) on the home page, upper right hand corner, it says:

Sorry I have a
large backlog
right now, please
ask a question later

To ask a question
click on the "Submit
a New Question" button
or Ask A Question.  <<== thats a link

when you see the "sorry", i'm not taking any.

2) download the spelling.tar from above, rip it apart and get just the routine
to do the spell check without the HTML stuff.  All of the routines are in there,
just use the one you want.

December 31, 2002 Akil -- Thanks for the question regarding "Execute immediate and rollback segments ", version 8.1.7
You Asked
Hi Tom,

I wanted to drop all the indexes of my schema. So I tried the following code:

begin
for i in (select index_name from user_indexes) loop
execute immediate 'drop index '||i.index_name||' ';
end loop;
end;
/

It started droping indexes but after some time I got an ORA-1555 error. I had
about 500 indexes to drop. It gave me ORA-1555 midway through. When I re-ran the
code, it droped the rest of the indexes.

I was surprised at this error. I could not understand what the rollback segments
has got anything to do with a drop index command. Can you please explain the
mechanism? Thanks for your help.

Regards,

Akil.
and we said...
The ora-01555 was the query against the data dictionary (user_indexes and its
underlying tables)

You are committing in a cursor for loop ;) DDL commits -- the changes it commits
were data dictionary updates against the very tables you were querying.  You
caused the 1555 yourself.

Consider if this was the process:

  for x in ( select * from t )
  loop
        delete from t where pk = x.pk;
        commit;
  end loop;

You are basically doing that.  A "safe" way to do this process would be:

begin
   loop
        select index_name into l_index_name from user_indexes where rownum=1;
        execute immediate 'drop index ' || i.index_name;
   end loop;
exception
   when no_data_found then null;
end;

Here, you do not keep the cursor open across commits.
Review & Followup
Rating: 5
Wonderful explanation. December 28, 2002
Reviewer: A reader
Tom,

You mean to say the code should be written this Way :

1  declare
  2  l_index_name user_indexes.index_name%TYPE;
  3  begin
  4     for i in (select index_name from user_indexes)
  5     loop
  6          select index_name into l_index_name from user_indexes where
rownum=1;
  7     --dbms_output.put_line('Index name : '|| i.index_name);
  8          execute immediate 'drop index ' || i.index_name;
  9     end loop;
 10  exception
 11     when no_data_found then null;
 12* end;

BTW, What is the purpose of this

select index_name into l_index_name from user_indexes where rownum=1;

query.

Thanks

Followup:
yes -- pretty much.

The select .. where rownum=1

just opens the implicit cursor, gets the first index name, drops it and then
does it all over again.  Eventually, you'll have no indexes left and the select
into will raise no data found -- ending the process.

Rating: 4
Misread ? December 29, 2002
Reviewer: Frank from NL
Did I or did you misread the previous posting ?
I don't think it is right.
You have to drop the for i in select bit !

hth,
Frank

Followup:
yes, I just glanced at it -- you are right -- that would get the ora-1555 again
;)

begin
   loop
        select index_name into l_index_name from user_indexes where rownum=1;
        execute immediate 'drop index ' || i.index_name;
   end loop;
exception
   when no_data_found then null;
end;

is what I had -- to flesh it out:

  1  declare
  2     l_index_name varchar2(30);
  3  begin
  4     loop
  5          select index_name into l_index_name from user_indexes where
rownum=1;
  6          execute immediate 'drop index ' || l_index_name;
  7     end loop;
  8  exception
  9     when no_data_found then null;
 10* end;

compiles and runs (but it'll fail of course when it hits an index used to
enforce a constraint!)

Rating: 4
clarification December 29, 2002
Reviewer: A reader
Thanks for the great explanation Tom.

I just want to re-confirm one thing. I was thinking that a "for i in (select x
from t) loop" will execute as follows:

1. The select statement will execute and fetch the rows into an implicit cursor.
2. The program control will loop through the cursor and execute the statements
inside the loop.
3. Exit the loop at the last record.

Now, from your reply, I think that, the select statement will execute once for
every iteration of the loop and the cursor will be repopulated with fresh data.

Is that so? Is my understanding right? Can you please give details? Thank you
somuch for your lucid examples.

Followup:
I see -- you think that a cursor gets the rows and then gives them to you --
that is the problem here -- the cursor is getting ROWS as you fetch them and
eventually -- due to all of your commits -- it cannot get the rows anymore since
the rollback doesn't exist.  We need to take this construct:

begin
   for i in (select index_name from user_indexes) loop
       execute immediate 'drop index '||i.index_name||' ';
       COMMIT against changes made to the tables behind
       the user_indexes view happens here...
   end loop;
end;

and remove the looping cursor -- for all of those commits of changes against the
data your are querying are causing the 1555.  I did that by recoding as an
"infinite loop"

declare
   l_index_name varchar2(30);
begin
   loop
      select index_name into l_index_name from user_indexes where rownum=1;
      execute immediate 'drop index ' || l_index_name;
   end loop;
exception
   when no_data_found then null;
end;

that works by looping -- getting the first index it finds -- dropping it (and
hence committing -- but that is OK, we don't have a cursor to fetch from
anymore) and going back around until eventually all indexes are dropped and we
get the no data found exception.

Rating: 5
December 30, 2002
Reviewer: A reader
Thanks for your time, Tom. Sorry if I am not getting what you already said. I
follow everything you say about how to fix the problem. But I am still confused
about the cursor fetch mechanism. What do you mean by "the cursor is getting the
rows as you fetch them"?

I can visualize only two ways of how this works:

1. The query executes ONCE and stores the result in a memory area called cursor.
Then we loop through the result set (cursor) and executes the remaining
statements. The memory area is not affected by the changes made to the base
tables on the disk. There is no scope for a ORA-1555 here (simply because the
query does not get executed again).

2. The query executes ONCE FOR EACH ITERATION OF THE LOOP. So the cursor will be
repopulated with fresh data every time. Naturally, there is scope of ORA-1555
because we are re-reading the modified data.

Earlier I was thinking it was like in case 1. But now I am confused. I don't
know if case 2 is right, becasue it does not make any sense to re-read the data
like that. But I can not visualize any other way we can get ORA-1555. Shall be
greatful if you can explain how it works. I hope I am clear about my question.
Thank you.

Followup:
1) is totally wrong.  A cursor is just a "state".  A pointer to the query plan
in the shared pool, the bind variable values, a pointer perhaps to where in the
query we last left off....

2) nope.

Have you ever read through a file a record at a time?  Same concept applies in
general for database tables.  When you open a file, you don't "copy it".  When
you want to read record 2 -- you don't fseek back to the beginning -- you just
pick up where you left off.

So, take a simple query like:

select * from t;

and t is a 500 block table with 10 rows / block.

You do this in sqlplus -- so you are fetching 15 rows/call by default(thats the
default arraysize)..  Ok,

o sqlplus does the first fetch
o the server reads block 1 of the table, gets 10 rows
o the server reads block 2 of the table, gets 5 rows
o 15 rows are returned
o sqlplus does 14 more fetches that don't bother the server (its already in
  the clients process)
o sqlplus goes to fetch row 16 - over to the server
o the server gets block 2 again, gets the remaining 5 rows -- it REMEMBERED
  where it left off
o the server gets block 3, gets 10 rows
o 15 rows are returned

o now at this point, someone issues "DELETE FROM T"; "COMMIT;".  There are NO
  rows in T anymore -- but your cursor will still see them all.  Why? because
  the UNDO for this delete was put in the rollback segments....

o sqlplus displays the 15 rows it just fetched
o sqlplus goes to fetch row 31 -- over to the server again
o the server gets block 3 -- notices "hey, the SCN on this block is too high
  for me, it has changes I'm not allowed to see.  Let me use the rollback data
  to rollback this block to the point in time that it is OK for me to look at
  it"
o the server reads from the rollback segment the necessary information, rolls
  the block back....
o the server reads the 10 rows off of the block (the 10 deleted rows!  they
  were undeleleted for us).
o the same thing happens when we go to get the next 5 rows from block 4
o sqlplus gets the data and displays it

(above process repeated over and over)....

o sqlplus goes to fetch row 400 -- over to the server
o server gets the block -- finds the SCN isn't right for it -- goes to rollback
  but discovers that the rollback that was generated by that delete is gone --
  somebody OVERWROTE it (our rollback segments are too small).  The query now
  gets an ora-1555.

Rating: 5
Excellent Explanation!! December 30, 2002
Reviewer: Ashish from Washington DC

Rating: 5
EXCELLENT December 30, 2002
Reviewer: A reader

Rating: 5
great explanation December 30, 2002
Reviewer: A reader
Clears it as crystal. Thank you.

Rating: 5
Splendid! December 31, 2002
Reviewer: A reader

Rating: 5
December 31, 2002
Reviewer: A reader
Good job Tom

Rating: 5
Superb Stuff December 31, 2002
Reviewer: Ik from BG

Rating: 5
December 31, 2002
Reviewer: A reader from India
Tom,

If this had been a regular table (SELECT in CURSOR and then UPDATE) would one
get an ORA 1555 error? I mean, on a regular table it would go for a FTS since
there is no where clause in the query and since the blocks would be read in a
sequential manner there would be no reason to re-read the same blocks again.
I know that one should never COMMIT in a for loop but iam just curious.

Some thing like -
begin
for i in (select i_name from reg_table) loop
......
update reg_table set a = :bindvar1 where i_name = :bindvar;
COMMIT;
end loop;
end;

Thanks in advance....

regards

Followup:
sure it could -- doesn't even have to be against the same table -- I can show
you an ORA-01555 against a table in a read only tablespace -- caused purely by
overcommitting...

December 31, 2002 oleg -- Thanks for the question regarding "Oracle Replication as Failover", version 8.1.5
You Asked
Hi, Tom.

I want to use Oracle Replication as a failover. I have created
for each table of  master site snapshots at the snapshotsite
with commando:

create snapshot emp  as select * from emp@prime
....
and have included all tables in one refresh group.

I have expected, that I have now 1:1 copy of primary DB at the
snapshot site, but no constraints  were created in the tables at
the snapshot site. I can not use this as a failover, because
after activating of the
snapshot as a production DB I can become inconsistents of my
data.

Could you please explain me, what can I do to become 1:1 copy of
 primary DB using replication (including all constraints,
triggers, indexes and sequences of the master) and what should I
do after prime node fails and have been repaired to propagade
all change was making to shapshot in the time master was off
back from snapshot to master.

Oleg.
and we said...
Replication is not really an appropriate technology for
failover.

We have a standby database feature designed specificially for
failover.  See
http://technet.oracle.com/doc/oracle8i_816/server.816/a76995/toc.htm
for more info on that.

I would strongly urge you to reconsider the use of a technology
such as replication to achieve failover site capabilities.  it
is really not appropriate.

replication adds a bit of overhead and complexity.  You would
not use simple READ ONLY snapshots as you have above to achieve
this.  You would have to use N-Way masters (advanced
replication).  It will replicate everything -- indexes,
procedures, triggers, constraints and all.  It replicates by
transaction -- not using refresh groups.

Again, I would not recommend using N-Way masters for failover
but if you are going to use replication for failover, that would
be the method to use.
Review & Followup
Rating: 3
July 15, 2001
Reviewer: Pichaimani from San Jose, CA USA

Rating: 4
standby failover October 22, 2002
Reviewer: Richard Tai from Pasadena, Ca., USA
Could primary DB failover to standby DB automatically at v9.2.0.1.0?

Followup:
I'm not sure what you mean here?

If you are asking "will the fail over be 100% hands off -- the machines will
just do it?"  the answer is -- no, not really and you don't want it to be.

Can the standby tell when the production is down and dead for good?  Should you
failover because of a 5 minute outage?  If failover the first thing you want to
do?

the answers (to me) are all no, no you do not.  failover is for catastrophic
"the machines are all a heap of burnt silicon" type of errors.  Not because the
standby cannot get in contact with the primary for example (do you really want
the standby to assume control because of a failure of the network between
standby and production?)

You can fully automate it with scripts -- but that would be a disaster waiting
to happen.

If that wasn't the question -- you'll need to be more clear please.

Rating: 5
October 22, 2002
Reviewer: A reader
Tom,

Can the standby database be used as read only and at the same time be in managed
recovery mode..If yes how???

thanks...

Followup:
In 9iR1 and before it is EITHER

o in managed recovery mode apply redo log files as they fill up

OR

o it is queueing up the redo logs and opened READ ONLY

In 9iR2 there is a logical standby database which must be opened read write all
of the time it is recovering and can be queried at the same time.

Rating: 5
Oracle Streams a Replacement for AQ and Replication ? October 23, 2002
Reviewer: Pasko from HH,Germany
Hi TOM,

Thanks for the comments above...i hear a whole lot of folks who are using
Replication as a Failover..but some say they have no choice  because i think a
StandBy Database Server can not placed elsewhere from the Primary Database ?

Also, i have seen  this New Concept in Oracle 9iR2 , of Oracle Streams...

Do we have to stop Reading Replication/AQ  manuals alltogether  and shift to
Streams instead ?

Could you please elaborate a little on Oracle Streams?

Thanks in advance.

Followup:
It would be silly if the standby could not be somewhere else from the primary.
In fact -- it is downright HARD to put them on the same box.

Standbys can be (SHOULD be) separated by large geographic distances.
Standbys are much much much less intrusive then replication.
Standbys require much much much less care and feeding.

Streams is the future direction yes.  (but don't put down that AQ manual --
guess what core technology is to be found underneath streams)

see
http://technet.oracle.com/docs/products/oracle9i/doc_library/release2/server.920/a96571/toc.htm
the streams guide, chapter 1 is an overview.

Rating: 4
Platform independence with logical standby October 23, 2002
Reviewer: Ramakrishna from India
Dear Tom,
  One of the reasons for replication being chosen as a failover could have been
that the platforms were different.  Will logical standby solve this?  Is logical
standby actually like having a 'ghost' user constantly issuing all the actual
SQL's (the ones that were LOGGED anyway) from the production DB into the standby
DB?  When you say the logical DB is opened read-write, does it mean read-write
ONLY for the 'ghost' user and read for everyone else?

thanks
Ramakrishna

Followup:
If you are serious about failover -- the platforms would not only be the same,
they would be very much exactly the same.  You are failing over lots more then
jsut a database, your data center is destroyed (that is why you fail over).
Failing over to a different hardware platform -- not something I would
recommend.

Logical standby currently requires a homogenous environment -- you can with
streams build a semi-hetergenous one (streams, when fed by redo logs, must be on
the same platform as the machine generating the redo logs, the target database
to which it applies the changes does not)

The logical DB is opened read write -- you would not be writing to the copy of
the production tables of course.  But you can have extra tables in there for
your own applications that want to run in the standby, scratch tables for
reporting, materialized views for reporting and so on.

Rating: 5
Not all shops are created equal(ly) December 31, 2002
Reviewer: Dean Reynolds from Nashville, TN
First off, I've never liked Oracle's replication.  For my needs, I've always
found better alternatives.  That can be simpler than other methods though...but
not really.

I once worked in a shop where they had 1 UNIX server(production) and every other
server was MS.  This was a (silly) business decision, but one I could not
change.  This was on 7.3.2 and I won't go into the hoops I jumped through to get
a "virtual standby database" up and maintained.  It was because of the different
platforms that I HAD to create a standby database and not rely on just backing
up the datafiles (where would I restore them to?).  I needed ALL the data, so
EXPORT/IMPORT was not viable as a (stand alone) solution.

So I can see those unfortunate shops that cannot duplicate the hardware and OS
of the production server.  But I would NEVER use replication for that.  If you
use 9i, I would definitely look at converting from replication to Oracle
Streams.  It gives you a lot more flexibility and has lower overhead ( I think)
than replication and can mimic Oracle's standard (physical and logical) standby
databases.  Even if you're not using 9i (you mentioned 8.1.5), there are better
ways in my opinion.  They're just not generic enough to explain here without
doing a system/business analysis and require a lot more time and
effort(development)than using replication.

Just wanted to add my 2 cents, 'cause I've been there.

Did I just post the last response of 2002?  Don't I get a free book or
something?  :)

Followup:
over 11hours to go before the end of 2002 from where I'm sitting ;)

December 31, 2002 Bo -- Thanks for the question regarding "Character in Number", version 8.1.6
You Asked
Hi, Tom.
Happy New Year.
I have a question regard to identify characters from Numbers.
I have a table t with several columns.  It looks like this.
t(...
  PERSONAL_ID  VARCHAR2(10)
  ...)

According to business rule, this column should ONLY contain NUMBER (i.e. 0 to
9).  However, some records are bad, because of data entry error.  The typical
error was the combination of numbers and characters, such as 234gh15460.  Some
records even have special character like "!" and "?".  For example, 453!!56789,
34??136901.

My question is, how to eliminate such bad records from table?

Thanks,
and we said...
1) I HATE that -- if it is a number, why the heck was it not defined as number
in the first place.

Man oh man.

Suggestion:  rebuild the table and use NUMBER(10).

You can write a function:

create function isNumber( p_string in varchar2 ) return number
as
   n number;
begin
   n := to_number(p_string);
   return 1;
exception
   when others then
       return 0;
end;

now, you can "select * from t where isNumber(c) = 0" to find the "bad ones"

A more performant way might be:

select *
  from t
 where replace( translate( c, '0123456789', '0000000000' ), '0', '' )
                      is not null
Review & Followup
Rating: 5
very good way to find the bad records January 01, 2003
Reviewer: Pauline from NY,U.S.A.

Rating: 5
That's a great idea. January 02, 2003
Reviewer: A reader

December 31, 2002 thomas -- Thanks for the question regarding "Recursive sql", version 8172
You Asked
What is a recursive sql error?
Why do we get this error?
How does one avoid this error.
and we said...
it is an error executing recursive sql -- which is a recursive definition ;)

recursive SQL is the sql we perform on your behalf - to do your sql.  For
example:

 select seq.nextval from dual;

might require recursive sql to:

o parse the query (query the data dictionary)
o update seq$ to get more sequence values into the cache

You get this error because an error was raised.

How to avoid?  Well, first you have to diagnose.  For example, the select
seq.nextval might get a recursive sql error if the update to seq$ failed for
some reason (space, whatever).

Recursive SQL errors should be exceedingly rare (except for self induced ones --
TRIGGERS).  They are super exceptional conditions and typically centered around
"i'm out of space, i'm out of memory" sort of errors...
Review & Followup
Rating: 3
recursive sql December 31, 2002
Reviewer: thomas benjamin from matthews,usa
Thanks,got the picture.

December 31, 2002 -- Thanks for the question regarding "ORA-00980: synonym translation is no longer valid", version 9i
You Asked
Hi Tom,

  I run Oracle 7.3 for years in Window NT machine with an C++ application and it
work fine.

  I recently updated my Oracle to 9i in Windows 2000 to my new machine
(different machine).  After Imported the database from my old machine to a new
one my application wrote in C++ no longer work and came up with an error

*********
ERROR:
CDBException Generated.
ORA-00980: synonym translation is no longer valid
Option value changed.

Please close and Restart Application.

***************

Thanks. your answer is greatly appreciated.

David
and we said...
that means you lost some object/access to some object when you did your move.
That error simply means "synonym is still here, but the object it points to is
inaccessible".  It could be inaccessible due to a missing grant, or due to the
object not being there.

You need to find out what synonym it is, query the data dictionary to figure out
what object it points to and figure out why you no longer have access to that
object.

ops$tkyte@ORA920> create synonym s for t;
Synonym created.

ops$tkyte@ORA920> select * from s where rownum = 1;

C
----------
1

ops$tkyte@ORA920> drop table t;
Table dropped.

ops$tkyte@ORA920> select * from s where rownum = 1;
select * from s where rownum = 1
              *
ERROR at line 1:
ORA-00980: synonym translation is no longer valid
Review & Followup
Rating: 5
ORA-00980: synonym translation is no longer valid", version 9i January 01, 2003
Reviewer: A reader
Thanks Tom your answer is great help.

David


Oracle9i SQL Reference

雪狼 作品

资料来源

资料来源：http://asktom.oracle.com

收集整理：Snowywolf

整理时间：2004-01

版权声明

您可以自由地将此文档复制发行，但请保留此声明页。

请勿将此文档用于任何商业用途。

联系作者

ITPUB号：Snowywolf

E m a i l：lisj@supermap.com

N a m e ：李绍俊

雪狼 作品